diff options
Diffstat (limited to 'source/XMPFiles')
86 files changed, 0 insertions, 42597 deletions
diff --git a/source/XMPFiles/FileHandlers/ASF_Handler.cpp b/source/XMPFiles/FileHandlers/ASF_Handler.cpp deleted file mode 100644 index ac6aad7..0000000 --- a/source/XMPFiles/FileHandlers/ASF_Handler.cpp +++ /dev/null @@ -1,352 +0,0 @@ -// ================================================================================================= -// ADOBE SYSTEMS INCORPORATED -// Copyright 2006 Adobe Systems Incorporated -// 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 "ASF_Handler.hpp" - -// ================================================================================================= -/// \file ASF_Handler.hpp -/// \brief File format handler for ASF. -/// -/// This handler ... -/// -// ================================================================================================= - -// ================================================================================================= -// ASF_MetaHandlerCTor -// ==================== - -XMPFileHandler * ASF_MetaHandlerCTor ( XMPFiles * parent ) -{ - return new ASF_MetaHandler ( parent ); - -} // ASF_MetaHandlerCTor - -// ================================================================================================= -// ASF_CheckFormat -// =============== - -bool ASF_CheckFormat ( XMP_FileFormat format, - XMP_StringPtr filePath, - LFA_FileRef fileRef, - XMPFiles * parent ) -{ - - IgnoreParam(format); IgnoreParam(fileRef); IgnoreParam(parent); - XMP_Assert ( format == kXMP_WMAVFile ); - - IOBuffer ioBuf; - - LFA_Seek ( fileRef, 0, SEEK_SET ); - if ( ! CheckFileSpace ( fileRef, &ioBuf, guidLen ) ) return false; - - GUID guid; - memcpy ( &guid, ioBuf.ptr, guidLen ); - - if ( ! IsEqualGUID ( ASF_Header_Object, guid ) ) return false; - - return true; - -} // ASF_CheckFormat - -// ================================================================================================= -// ASF_MetaHandler::ASF_MetaHandler -// ================================== - -ASF_MetaHandler::ASF_MetaHandler ( XMPFiles * _parent ) -{ - this->parent = _parent; - this->handlerFlags = kASF_HandlerFlags; - this->stdCharForm = kXMP_Char8Bit; - -} - -// ================================================================================================= -// ASF_MetaHandler::~ASF_MetaHandler -// =================================== - -ASF_MetaHandler::~ASF_MetaHandler() -{ - // Nothing extra to do. -} - -// ================================================================================================= -// ASF_MetaHandler::CacheFileData -// =============================== - -void ASF_MetaHandler::CacheFileData() -{ - - this->containsXMP = false; - - LFA_FileRef fileRef ( this->parent->fileRef ); - if ( fileRef == 0 ) return; - - ASF_Support support ( &this->legacyManager ); - ASF_Support::ObjectState objectState; - long numTags = support.OpenASF ( fileRef, objectState ); - if ( numTags == 0 ) return; - - if ( objectState.xmpLen != 0 ) { - - // XMP present - - XMP_Int32 len = XMP_Int32 ( objectState.xmpLen ); - - this->xmpPacket.reserve( len ); - this->xmpPacket.assign ( len, ' ' ); - - bool found = ASF_Support::ReadBuffer ( fileRef, objectState.xmpPos, objectState.xmpLen, - const_cast<char *>(this->xmpPacket.data()) ); - if ( found ) { - this->packetInfo.offset = objectState.xmpPos; - this->packetInfo.length = len; - this->containsXMP = true; - } - - } - -} // ASF_MetaHandler::CacheFileData - -// ================================================================================================= -// ASF_MetaHandler::ProcessXMP -// ============================ -// -// Process the raw XMP and legacy metadata that was previously cached. - -void ASF_MetaHandler::ProcessXMP() -{ - - this->processedXMP = true; // Make sure we only come through here once. - - // Process the XMP packet. - - if ( this->xmpPacket.empty() ) { - - // import legacy in any case, when no XMP present - legacyManager.ImportLegacy ( &this->xmpObj ); - this->legacyManager.SetDigest ( &this->xmpObj ); - - } else { - - XMP_Assert ( this->containsXMP ); - XMP_StringPtr packetStr = this->xmpPacket.c_str(); - XMP_StringLen packetLen = (XMP_StringLen)this->xmpPacket.size(); - - this->xmpObj.ParseFromBuffer ( packetStr, packetLen ); - - if ( ! legacyManager.CheckDigest ( this->xmpObj ) ) { - legacyManager.ImportLegacy ( &this->xmpObj ); - } - - } - - // Assume we now have something in the XMP. - this->containsXMP = true; - -} // ASF_MetaHandler::ProcessXMP - -// ================================================================================================= -// ASF_MetaHandler::UpdateFile -// ============================ - -void ASF_MetaHandler::UpdateFile ( bool doSafeUpdate ) -{ - - bool updated = false; - - if ( ! this->needsUpdate ) return; - - LFA_FileRef fileRef ( this->parent->fileRef ); - if ( fileRef == 0 ) return; - - ASF_Support support; - ASF_Support::ObjectState objectState; - long numTags = support.OpenASF ( fileRef, objectState ); - if ( numTags == 0 ) return; - - XMP_StringLen packetLen = (XMP_StringLen)xmpPacket.size(); - - this->legacyManager.ExportLegacy ( this->xmpObj ); - if ( this->legacyManager.hasLegacyChanged() ) { - - this->legacyManager.SetDigest ( &this->xmpObj ); - - // serialize with updated digest - if ( objectState.xmpLen == 0 ) { - - // XMP does not exist, use standard padding - this->xmpObj.SerializeToBuffer ( &this->xmpPacket, kXMP_UseCompactFormat ); - - } else { - - // re-use padding with static XMP size - try { - XMP_OptionBits compactExact = (kXMP_UseCompactFormat | kXMP_ExactPacketLength); - this->xmpObj.SerializeToBuffer ( &this->xmpPacket, compactExact, XMP_StringLen(objectState.xmpLen) ); - } catch ( ... ) { - // re-use padding with exact packet length failed (legacy-digest needed too much space): try again using standard padding - this->xmpObj.SerializeToBuffer ( &this->xmpPacket, kXMP_UseCompactFormat ); - } - - } - - } - - XMP_StringPtr packetStr = xmpPacket.c_str(); - packetLen = (XMP_StringLen)xmpPacket.size(); - if ( packetLen == 0 ) return; - - // value, when guessing for sufficient legacy padding (line-ending conversion etc.) - const int paddingTolerance = 50; - - bool xmpGrows = ( objectState.xmpLen && (packetLen > objectState.xmpLen) && ( ! objectState.xmpIsLastObject) ); - - bool legacyGrows = ( this->legacyManager.hasLegacyChanged() && - (this->legacyManager.getLegacyDiff() > (this->legacyManager.GetPadding() - paddingTolerance)) ); - - if ( doSafeUpdate || legacyGrows || xmpGrows ) { - - // do a safe update in any case - updated = SafeWriteFile(); - - } else { - - // possibly we can do an in-place update - - if ( objectState.xmpLen < packetLen ) { - - updated = SafeWriteFile(); - - } else { - - // current XMP chunk size is sufficient -> write (in place update) - updated = ASF_Support::WriteBuffer(fileRef, objectState.xmpPos, packetLen, packetStr ); - - // legacy update - if ( updated && this->legacyManager.hasLegacyChanged() ) { - - ASF_Support::ObjectIterator curPos = objectState.objects.begin(); - ASF_Support::ObjectIterator endPos = objectState.objects.end(); - - for ( ; curPos != endPos; ++curPos ) { - - ASF_Support::ObjectData object = *curPos; - - // find header-object - if ( IsEqualGUID ( ASF_Header_Object, object.guid ) ) { - // update header object - updated = support.UpdateHeaderObject ( fileRef, object, legacyManager ); - } - - } - - } - - } - - } - - if ( ! updated ) return; // If there's an error writing the chunk, bail. - - this->needsUpdate = false; - -} // ASF_MetaHandler::UpdateFile - -// ================================================================================================= -// ASF_MetaHandler::WriteFile -// =========================== - -void ASF_MetaHandler::WriteFile ( LFA_FileRef sourceRef, const std::string & sourcePath ) -{ - LFA_FileRef destRef = this->parent->fileRef; - - ASF_Support support; - ASF_Support::ObjectState objectState; - long numTags = support.OpenASF ( sourceRef, objectState ); - if ( numTags == 0 ) return; - - LFA_Truncate ( destRef, 0 ); - - ASF_Support::ObjectIterator curPos = objectState.objects.begin(); - ASF_Support::ObjectIterator endPos = objectState.objects.end(); - - for ( ; curPos != endPos; ++curPos ) { - - ASF_Support::ObjectData object = *curPos; - - // discard existing XMP object - if ( object.xmp ) continue; - - // update header-object, when legacy needs update - if ( IsEqualGUID ( ASF_Header_Object, object.guid) && this->legacyManager.hasLegacyChanged( ) ) { - // rewrite header object - support.WriteHeaderObject ( sourceRef, destRef, object, this->legacyManager, false ); - } else { - // copy any other object - ASF_Support::CopyObject ( sourceRef, destRef, object ); - } - - // write XMP object immediately after the (one and only) top-level DataObject - if ( IsEqualGUID ( ASF_Data_Object, object.guid ) ) { - XMP_StringPtr packetStr = xmpPacket.c_str(); - XMP_StringLen packetLen = (XMP_StringLen)xmpPacket.size(); - ASF_Support::WriteXMPObject ( destRef, packetLen, packetStr ); - } - - } - - support.UpdateFileSize ( destRef ); - -} // ASF_MetaHandler::WriteFile - -// ================================================================================================= -// ASF_MetaHandler::SafeWriteFile -// =========================== - -bool ASF_MetaHandler::SafeWriteFile () -{ - bool ret = false; - - std::string origPath = this->parent->filePath; - LFA_FileRef origRef = this->parent->fileRef; - - std::string updatePath; - LFA_FileRef updateRef = 0; - - CreateTempFile ( origPath, &updatePath, kCopyMacRsrc ); - updateRef = LFA_Open ( updatePath.c_str(), 'w' ); - - this->parent->filePath = updatePath; - this->parent->fileRef = updateRef; - - try { - this->WriteFile ( origRef, origPath ); - ret = true; - } catch ( ... ) { - LFA_Close ( updateRef ); - LFA_Delete ( updatePath.c_str() ); - this->parent->filePath = origPath; - this->parent->fileRef = origRef; - throw; - } - - LFA_Close ( origRef ); - LFA_Delete ( origPath.c_str() ); - - LFA_Close ( updateRef ); - LFA_Rename ( updatePath.c_str(), origPath.c_str() ); - this->parent->filePath = origPath; - - this->parent->fileRef = 0; - - return ret; - -} // ASF_MetaHandler::SafeWriteFile - -// ================================================================================================= diff --git a/source/XMPFiles/FileHandlers/ASF_Handler.hpp b/source/XMPFiles/FileHandlers/ASF_Handler.hpp deleted file mode 100644 index e5fec1c..0000000 --- a/source/XMPFiles/FileHandlers/ASF_Handler.hpp +++ /dev/null @@ -1,64 +0,0 @@ -#ifndef __ASF_Handler_hpp__ -#define __ASF_Handler_hpp__ 1 - -// ================================================================================================= -// ADOBE SYSTEMS INCORPORATED -// Copyright 2006 Adobe Systems Incorporated -// 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 "XMPFiles_Impl.hpp" -#include "ASF_Support.hpp" - -// ================================================================================================= -/// \file ASF_Handler.hpp -/// \brief File format handler for ASF. -/// -/// This header ... -/// -// ================================================================================================= - -// *** Could derive from Basic_Handler - buffer file tail in a temp file. - -extern XMPFileHandler* ASF_MetaHandlerCTor ( XMPFiles* parent ); - -extern bool ASF_CheckFormat ( XMP_FileFormat format, - XMP_StringPtr filePath, - LFA_FileRef fileRef, - XMPFiles * parent ); - -static const XMP_OptionBits kASF_HandlerFlags = ( kXMPFiles_CanInjectXMP | - kXMPFiles_CanExpand | - kXMPFiles_PrefersInPlace | - kXMPFiles_CanReconcile | - kXMPFiles_AllowsOnlyXMP | - kXMPFiles_ReturnsRawPacket | - kXMPFiles_NeedsReadOnlyPacket ); - -class ASF_MetaHandler : public XMPFileHandler -{ -public: - - void CacheFileData(); - void ProcessXMP(); - - void UpdateFile ( bool doSafeUpdate ); - void WriteFile ( LFA_FileRef sourceRef, const std::string& sourcePath ); - - bool SafeWriteFile (); - - ASF_MetaHandler ( XMPFiles* parent ); - virtual ~ASF_MetaHandler(); - -private: - - ASF_LegacyManager legacyManager; - -}; // ASF_MetaHandler - -// ================================================================================================= - -#endif /* __ASF_Handler_hpp__ */ diff --git a/source/XMPFiles/FileHandlers/AVCHD_Handler.cpp b/source/XMPFiles/FileHandlers/AVCHD_Handler.cpp deleted file mode 100644 index f842a73..0000000 --- a/source/XMPFiles/FileHandlers/AVCHD_Handler.cpp +++ /dev/null @@ -1,2060 +0,0 @@ -// ================================================================================================= -// ADOBE SYSTEMS INCORPORATED -// Copyright 2008 Adobe Systems Incorporated -// 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 "AVCHD_Handler.hpp" - -#include "MD5.h" -#include "UnicodeConversions.hpp" - -using namespace std; - -// AVCHD maker ID values. Panasonic has confirmed their Maker ID with us, the others come from examining -// sample data files. -#define kMakerIDPanasonic 0x103 -#define kMakerIDSony 0x108 -#define kMakerIDCanon 0x1011 - -// ================================================================================================= -/// \file AVCHD_Handler.cpp -/// \brief Folder format handler for AVCHD. -/// -/// This handler is for the AVCHD video format. -/// -/// A typical AVCHD layout looks like: -/// -/// BDMV/ -/// index.bdmv -/// MovieObject.bdmv -/// PLAYLIST/ -/// 00000.mpls -/// 00001.mpls -/// STREAM/ -/// 00000.m2ts -/// 00001.m2ts -/// CLIPINF/ -/// 00000.clpi -/// 00001.clpi -/// BACKUP/ -/// -// ================================================================================================= - -// ================================================================================================= - -// AVCHD Format. Book 1: Playback System Basic Specifications V 1.01. p. 76 - -struct AVCHD_blkProgramInfo -{ - XMP_Uns32 mLength; - XMP_Uns8 mReserved1[2]; - XMP_Uns32 mSPNProgramSequenceStart; - XMP_Uns16 mProgramMapPID; - XMP_Uns8 mNumberOfStreamsInPS; - XMP_Uns8 mReserved2; - - // Video stream. - struct - { - XMP_Uns8 mPresent; - XMP_Uns8 mVideoFormat; - XMP_Uns8 mFrameRate; - XMP_Uns8 mAspectRatio; - XMP_Uns8 mCCFlag; - } mVideoStream; - - // Audio stream. - struct - { - XMP_Uns8 mPresent; - XMP_Uns8 mAudioPresentationType; - XMP_Uns8 mSamplingFrequency; - XMP_Uns8 mAudioLanguageCode[4]; - } mAudioStream; - - // Pverlay bitmap stream. - struct - { - XMP_Uns8 mPresent; - XMP_Uns8 mOBLanguageCode[4]; - } mOverlayBitmapStream; - - // Menu bitmap stream. - struct - { - XMP_Uns8 mPresent; - XMP_Uns8 mBMLanguageCode[4]; - } mMenuBitmapStream; - -}; - -// AVCHD Format, Panasonic proprietary PRO_PlayListMark block - -struct AVCCAM_blkProPlayListMark -{ - XMP_Uns8 mPresent; - XMP_Uns8 mProTagID; - XMP_Uns8 mFillItem1; - XMP_Uns16 mLength; - XMP_Uns8 mMarkType; - - // Entry mark - struct - { - XMP_Uns8 mGlobalClipID[32]; - XMP_Uns8 mStartTimeCode[4]; - XMP_Uns8 mStreamTimecodeInfo; - XMP_Uns8 mStartBinaryGroup[4]; - XMP_Uns8 mLastUpdateTimeZone; - XMP_Uns8 mLastUpdateDate[7]; - XMP_Uns16 mFillItem; - } mEntryMark; - - // Shot Mark - struct - { - XMP_Uns8 mPresent; - XMP_Uns8 mShotMark; - XMP_Uns8 mFillItem[3]; - } mShotMark; - - // Access - struct - { - XMP_Uns8 mPresent; - XMP_Uns8 mCreatorCharacterSet; - XMP_Uns8 mCreatorLength; - XMP_Uns8 mCreator[32]; - XMP_Uns8 mLastUpdatePersonCharacterSet; - XMP_Uns8 mLastUpdatePersonLength; - XMP_Uns8 mLastUpdatePerson[32]; - } mAccess; - - // Device - struct - { - XMP_Uns8 mPresent; - XMP_Uns16 mMakerID; - XMP_Uns16 mMakerModelCode; - XMP_Uns8 mSerialNoCharacterCode; - XMP_Uns8 mSerialNoLength; - XMP_Uns8 mSerialNo[24]; - XMP_Uns16 mFillItem; - } mDevice; - - // Shoot - struct - { - XMP_Uns8 mPresent; - XMP_Uns8 mShooterCharacterSet; - XMP_Uns8 mShooterLength; - XMP_Uns8 mShooter[32]; - XMP_Uns8 mStartDateTimeZone; - XMP_Uns8 mStartDate[7]; - XMP_Uns8 mEndDateTimeZone; - XMP_Uns8 mEndDate[7]; - XMP_Uns16 mFillItem; - } mShoot; - - // Location - struct - { - XMP_Uns8 mPresent; - XMP_Uns8 mSource; - XMP_Uns32 mGPSLatitudeRef; - XMP_Uns32 mGPSLatitude1; - XMP_Uns32 mGPSLatitude2; - XMP_Uns32 mGPSLatitude3; - XMP_Uns32 mGPSLongitudeRef; - XMP_Uns32 mGPSLongitude1; - XMP_Uns32 mGPSLongitude2; - XMP_Uns32 mGPSLongitude3; - XMP_Uns32 mGPSAltitudeRef; - XMP_Uns32 mGPSAltitude; - XMP_Uns8 mPlaceNameCharacterSet; - XMP_Uns8 mPlaceNameLength; - XMP_Uns8 mPlaceName[64]; - XMP_Uns8 mFillItem; - } mLocation; -}; - -// AVCHD Format, Panasonic proprietary extension data (AVCCAM) - -struct AVCCAM_Pro_PlayListInfo -{ - XMP_Uns8 mPresent; - XMP_Uns8 mTagID; - XMP_Uns8 mTagVersion; - XMP_Uns16 mFillItem1; - XMP_Uns32 mLength; - XMP_Uns16 mNumberOfPlayListMarks; - XMP_Uns16 mFillItem2; - - // Although a playlist may contain multiple marks, we only store the one that corresponds to - // the clip/shot of interest. - AVCCAM_blkProPlayListMark mPlayListMark; -}; - -// AVCHD Format, Panasonic proprietary extension data (AVCCAM) - -struct AVCHD_blkPanasonicPrivateData -{ - XMP_Uns8 mPresent; - XMP_Uns16 mNumberOfData; - XMP_Uns16 mReserved; - - struct - { - XMP_Uns8 mPresent; - XMP_Uns8 mTagID; - XMP_Uns8 mTagVersion; - XMP_Uns16 mTagLength; - XMP_Uns8 mProfessionalMetaID[16]; - } mProMetaIDBlock; - - struct - { - XMP_Uns8 mPresent; - XMP_Uns8 mTagID; - XMP_Uns8 mTagVersion; - XMP_Uns16 mTagLength; - XMP_Uns8 mGlobalClipID[32]; - XMP_Uns8 mStartTimecode[4]; - XMP_Uns32 mStartBinaryGroup; - } mProClipIDBlock; - - AVCCAM_Pro_PlayListInfo mProPlaylistInfoBlock; -}; - -// AVCHD Format. Book 2: Recording Extension Specifications, section 4.2.4.2. plus Panasonic extensions - -struct AVCHD_blkMakersPrivateData -{ - XMP_Uns8 mPresent; - XMP_Uns32 mLength; - XMP_Uns32 mDataBlockStartAddress; - XMP_Uns8 mReserved[3]; - XMP_Uns8 mNumberOfMakerEntries; - XMP_Uns16 mMakerID; - XMP_Uns16 mMakerModelCode; - AVCHD_blkPanasonicPrivateData mPanasonicPrivateData; -}; - -// AVCHD Format. Book 2: Recording Extension Specifications, section 4.4.2.1 - -struct AVCHD_blkClipInfoExt -{ - XMP_Uns32 mLength; - XMP_Uns16 mMakerID; - XMP_Uns16 mMakerModelCode; -}; - -// AVCHD Format. Book 2: Recording Extension Specifications, section 4.4.1.2 - -struct AVCHD_blkClipExtensionData -{ - XMP_Uns8 mPresent; - XMP_Uns8 mTypeIndicator[4]; - XMP_Uns8 mReserved1[4]; - XMP_Uns32 mProgramInfoExtStartAddress; - XMP_Uns32 mMakersPrivateDataStartAddress; - - AVCHD_blkClipInfoExt mClipInfoExt; - AVCHD_blkMakersPrivateData mMakersPrivateData; -}; - -// AVCHD Format. Book 2: Recording Extension Specifications, section 4.3.3.1 -- although each playlist -// may contain a list of these, we only record the one that matches our target shot/clip. - -struct AVCHD_blkPlayListMarkExt -{ - XMP_Uns32 mLength; - XMP_Uns16 mNumberOfPlaylistMarks; - bool mPresent; - XMP_Uns16 mMakerID; - XMP_Uns16 mMakerModelCode; - XMP_Uns8 mReserved1[3]; - XMP_Uns8 mFlags; // bit 0: MarkWriteProtectFlag, bits 1-2: pulldown - XMP_Uns16 mRefToMarkThumbnailIndex; - XMP_Uns8 mBlkTimezone; - XMP_Uns8 mRecordDataAndTime[7]; - XMP_Uns8 mMarkCharacterSet; - XMP_Uns8 mMarkNameLength; - XMP_Uns8 mMarkName[24]; - XMP_Uns8 mMakersInformation[16]; - XMP_Uns8 mBlkTimecode[4]; - XMP_Uns16 mReserved2; -}; - -// AVCHD Format. Book 2: Recording Extension Specifications, section 4.3.2.1 - -struct AVCHD_blkPlaylistMeta -{ - XMP_Uns32 mLength; - XMP_Uns16 mMakerID; - XMP_Uns16 mMakerModelCode; - XMP_Uns32 mReserved1; - XMP_Uns16 mRefToMenuThumbnailIndex; - XMP_Uns8 mBlkTimezone; - XMP_Uns8 mRecordDataAndTime[7]; - XMP_Uns8 mReserved2; - XMP_Uns8 mPlaylistCharacterSet; - XMP_Uns8 mPlaylistNameLength; - XMP_Uns8 mPlaylistName[255]; -}; - -// AVCHD Format. Book 2: Recording Extension Specifications, section 4.3.1.2 - -struct AVCHD_blkPlayListExtensionData -{ - XMP_Uns8 mPresent; - char mTypeIndicator[4]; - XMP_Uns8 mReserved[4]; - XMP_Uns32 mPlayListMarkExtStartAddress; - XMP_Uns32 mMakersPrivateDataStartAddress; - - AVCHD_blkPlaylistMeta mPlaylistMeta; - AVCHD_blkPlayListMarkExt mPlaylistMarkExt; - AVCHD_blkMakersPrivateData mMakersPrivateData; -}; - -// AVCHD Format. Book 1: Playback System Basic Specifications V 1.01. p. 38 -struct AVCHD_blkExtensionData -{ - XMP_Uns32 mLength; - XMP_Uns32 mDataBlockStartAddress; - XMP_Uns8 mReserved[3]; - XMP_Uns8 mNumberOfDataEntries; - - struct AVCHD_blkExtDataEntry - { - XMP_Uns16 mExtDataType; - XMP_Uns16 mExtDataVersion; - XMP_Uns32 mExtDataStartAddress; - XMP_Uns32 mExtDataLength; - } mExtDataEntry; -}; - -// Simple container for the various AVCHD legacy metadata structures we care about for an AVCHD clip - -struct AVCHD_LegacyMetadata -{ - AVCHD_blkProgramInfo mProgramInfo; - AVCHD_blkClipExtensionData mClipExtensionData; - AVCHD_blkPlayListExtensionData mPlaylistExtensionData; -}; - -// ================================================================================================= -// MakeLeafPath -// ============ - -static bool MakeLeafPath ( std::string * path, XMP_StringPtr root, XMP_StringPtr group, - XMP_StringPtr clip, XMP_StringPtr suffix, bool checkFile = false ) -{ - size_t partialLen; - - *path = root; - *path += kDirChar; - *path += "BDMV"; - *path += kDirChar; - *path += group; - *path += kDirChar; - *path += clip; - partialLen = path->size(); - *path += suffix; - - if ( ! checkFile ) return true; - if ( GetFileMode ( path->c_str() ) == kFMode_IsFile ) return true; - - // Convert the suffix to uppercase and try again. Even on Mac/Win, in case a remote file system is sensitive. - for ( char* chPtr = ((char*)path->c_str() + partialLen); *chPtr != 0; ++chPtr ) { - if ( (0x61 <= *chPtr) && (*chPtr <= 0x7A) ) *chPtr -= 0x20; - } - if ( GetFileMode ( path->c_str() ) == kFMode_IsFile ) return true; - - if ( XMP_LitMatch ( suffix, ".clpi" ) ) { // Special case of ".cpi" for the clip file. - - path->erase ( partialLen ); - *path += ".cpi"; - if ( GetFileMode ( path->c_str() ) == kFMode_IsFile ) return true; - - path->erase ( partialLen ); - *path += ".CPI"; - if ( GetFileMode ( path->c_str() ) == kFMode_IsFile ) return true; - - } else if ( XMP_LitMatch ( suffix, ".mpls" ) ) { // Special case of ".mpl" for the playlist file. - - path->erase ( partialLen ); - *path += ".mpl"; - if ( GetFileMode ( path->c_str() ) == kFMode_IsFile ) return true; - - path->erase ( partialLen ); - *path += ".MPL"; - if ( GetFileMode ( path->c_str() ) == kFMode_IsFile ) return true; - - } - - // Still not found, revert to the original suffix. - path->erase ( partialLen ); - *path += suffix; - return false; - -} // MakeLeafPath - -// ================================================================================================= -// AVCHD_CheckFormat -// ================= -// -// This version checks for the presence of a top level BPAV directory, and the required files and -// directories immediately within it. The CLIPINF, PLAYLIST, and STREAM subfolders are required, as -// are the index.bdmv and MovieObject.bdmv files. -// -// The state of the string parameters depends on the form of the path passed by the client. If the -// client passed a logical clip path, like ".../MyMovie/00001", the parameters are: -// rootPath - ".../MyMovie" -// gpName - empty -// parentName - empty -// leafName - "00001" -// If the client passed a full file path, like ".../MyMovie/BDMV/CLIPINF/00001.clpi", they are: -// rootPath - ".../MyMovie" -// gpName - "BDMV" -// parentName - "CLIPINF" or "PALYLIST" or "STREAM" -// leafName - "00001" - -// ! The common code has shifted the gpName, parentName, and leafName strings to upper case. It has -// ! also made sure that for a logical clip path the rootPath is an existing folder, and that the -// ! file exists for a full file path. - -// ! Using explicit '/' as a separator when creating paths, it works on Windows. - -// ! Sample files show that the ".bdmv" extension can sometimes be ".bdm". Allow either. - -bool AVCHD_CheckFormat ( XMP_FileFormat format, - const std::string & rootPath, - const std::string & gpName, - const std::string & parentName, - const std::string & leafName, - XMPFiles * parent ) -{ - if ( gpName.empty() != parentName.empty() ) return false; // Must be both empty or both non-empty. - - if ( ! gpName.empty() ) { - if ( gpName != "BDMV" ) return false; - if ( (parentName != "CLIPINF") && (parentName != "PLAYLIST") && (parentName != "STREAM") ) return false; - } - - // Check the rest of the required general structure. Look for both ".bdmv" and ".bmd" extensions. - - std::string bdmvPath ( rootPath ); - bdmvPath += kDirChar; - bdmvPath += "BDMV"; - - if ( GetChildMode ( bdmvPath, "CLIPINF" ) != kFMode_IsFolder ) return false; - if ( GetChildMode ( bdmvPath, "PLAYLIST" ) != kFMode_IsFolder ) return false; - if ( GetChildMode ( bdmvPath, "STREAM" ) != kFMode_IsFolder ) return false; - - if ( (GetChildMode ( bdmvPath, "index.bdmv" ) != kFMode_IsFile) && - (GetChildMode ( bdmvPath, "index.bdm" ) != kFMode_IsFile) && - (GetChildMode ( bdmvPath, "INDEX.BDMV" ) != kFMode_IsFile) && // Some usage is all caps. - (GetChildMode ( bdmvPath, "INDEX.BDM" ) != kFMode_IsFile) ) return false; - - if ( (GetChildMode ( bdmvPath, "MovieObject.bdmv" ) != kFMode_IsFile) && - (GetChildMode ( bdmvPath, "MovieObj.bdm" ) != kFMode_IsFile) && - (GetChildMode ( bdmvPath, "MOVIEOBJECT.BDMV" ) != kFMode_IsFile) && // Some usage is all caps. - (GetChildMode ( bdmvPath, "MOVIEOBJ.BDM" ) != kFMode_IsFile) ) return false; - - - // Make sure the .clpi file exists. - std::string tempPath; - bool foundClpi = MakeLeafPath ( &tempPath, rootPath.c_str(), "CLIPINF", leafName.c_str(), ".clpi", true /* checkFile */ ); - if ( ! foundClpi ) return false; - - // And now save the pseudo path for the handler object. - tempPath = rootPath; - tempPath += kDirChar; - tempPath += leafName; - size_t pathLen = tempPath.size() + 1; // Include a terminating nul. - parent->tempPtr = malloc ( pathLen ); - if ( parent->tempPtr == 0 ) XMP_Throw ( "No memory for AVCHD clip info", kXMPErr_NoMemory ); - memcpy ( parent->tempPtr, tempPath.c_str(), pathLen ); - - return true; - -} // AVCHD_CheckFormat - -// ================================================================================================= -// ReadAVCHDProgramInfo -// ============ - -static bool ReadAVCHDProgramInfo ( LFA_FileRef& cpiFileRef, AVCHD_blkProgramInfo& avchdProgramInfo ) -{ - avchdProgramInfo.mLength = LFA_ReadUns32_BE ( cpiFileRef ); - LFA_Read ( cpiFileRef, avchdProgramInfo.mReserved1, 2 ); - avchdProgramInfo.mSPNProgramSequenceStart = LFA_ReadUns32_BE ( cpiFileRef ); - avchdProgramInfo.mProgramMapPID = LFA_ReadUns16_BE ( cpiFileRef ); - LFA_Read ( cpiFileRef, &avchdProgramInfo.mNumberOfStreamsInPS, 1 ); - LFA_Read ( cpiFileRef, &avchdProgramInfo.mReserved2, 1 ); - - XMP_Uns16 streamPID = 0; - for ( int i=0; i<avchdProgramInfo.mNumberOfStreamsInPS; ++i ) { - - XMP_Uns8 length = 0; - XMP_Uns8 streamCodingType = 0; - - streamPID = LFA_ReadUns16_BE ( cpiFileRef ); - LFA_Read ( cpiFileRef, &length, 1 ); - - XMP_Int64 pos = LFA_Tell ( cpiFileRef ); - - LFA_Read ( cpiFileRef, &streamCodingType, 1 ); - - switch ( streamCodingType ) { - - case 0x1B : // Video stream case. - { - XMP_Uns8 videoFormatAndFrameRate; - LFA_Read ( cpiFileRef, &videoFormatAndFrameRate, 1 ); - avchdProgramInfo.mVideoStream.mVideoFormat = videoFormatAndFrameRate >> 4; // hi 4 bits - avchdProgramInfo.mVideoStream.mFrameRate = videoFormatAndFrameRate & 0x0f; // lo 4 bits - - XMP_Uns8 aspectRatioAndReserved = 0; - LFA_Read ( cpiFileRef, &aspectRatioAndReserved, 1 ); - avchdProgramInfo.mVideoStream.mAspectRatio = aspectRatioAndReserved >> 4; // hi 4 bits - - XMP_Uns8 ccFlag = 0; - LFA_Read ( cpiFileRef, &ccFlag, 1 ); - avchdProgramInfo.mVideoStream.mCCFlag = ccFlag; - - avchdProgramInfo.mVideoStream.mPresent = 1; - } - break; - - case 0x80 : // Fall through. - case 0x81 : // Audio stream case. - { - XMP_Uns8 audioPresentationTypeAndFrequency = 0; - LFA_Read ( cpiFileRef, &audioPresentationTypeAndFrequency, 1 ); - - avchdProgramInfo.mAudioStream.mAudioPresentationType = audioPresentationTypeAndFrequency >> 4; // hi 4 bits - avchdProgramInfo.mAudioStream.mSamplingFrequency = audioPresentationTypeAndFrequency & 0x0f; // lo 4 bits - - LFA_Read ( cpiFileRef, avchdProgramInfo.mAudioStream.mAudioLanguageCode, 3 ); - avchdProgramInfo.mAudioStream.mAudioLanguageCode[3] = 0; - - avchdProgramInfo.mAudioStream.mPresent = 1; - } - break; - - case 0x90 : // Overlay bitmap stream case. - LFA_Read ( cpiFileRef, &avchdProgramInfo.mOverlayBitmapStream.mOBLanguageCode, 3 ); - avchdProgramInfo.mOverlayBitmapStream.mOBLanguageCode[3] = 0; - avchdProgramInfo.mOverlayBitmapStream.mPresent = 1; - break; - - case 0x91 : // Menu bitmap stream. - LFA_Read ( cpiFileRef, &avchdProgramInfo.mMenuBitmapStream.mBMLanguageCode, 3 ); - avchdProgramInfo.mMenuBitmapStream.mBMLanguageCode[3] = 0; - avchdProgramInfo.mMenuBitmapStream.mPresent = 1; - break; - - default : - break; - - } - - LFA_Seek ( cpiFileRef, pos + length, SEEK_SET ); - - } - - return true; -} - -// ================================================================================================= -// ReadAVCHDExtensionData -// ============ - -static bool ReadAVCHDExtensionData ( LFA_FileRef& cpiFileRef, AVCHD_blkExtensionData& extensionDataHeader ) -{ - extensionDataHeader.mLength = LFA_ReadUns32_BE ( cpiFileRef ); - - if ( extensionDataHeader.mLength == 0 ) { - // Nothing to read - return true; - } - - extensionDataHeader.mDataBlockStartAddress = LFA_ReadUns32_BE ( cpiFileRef ); - LFA_Read ( cpiFileRef, extensionDataHeader.mReserved, 3 ); - LFA_Read ( cpiFileRef, &extensionDataHeader.mNumberOfDataEntries, 1 ); - - if ( extensionDataHeader.mNumberOfDataEntries != 1 ) { - // According to AVCHD Format. Book1. v. 1.01. p 38, "This field shall be set to 1 in this format." - return false; - } - - extensionDataHeader.mExtDataEntry.mExtDataType = LFA_ReadUns16_BE ( cpiFileRef ); - extensionDataHeader.mExtDataEntry.mExtDataVersion = LFA_ReadUns16_BE ( cpiFileRef ); - extensionDataHeader.mExtDataEntry.mExtDataStartAddress = LFA_ReadUns32_BE ( cpiFileRef ); - extensionDataHeader.mExtDataEntry.mExtDataLength = LFA_ReadUns32_BE ( cpiFileRef ); - - if ( extensionDataHeader.mExtDataEntry.mExtDataType != 0x1000 ) { - // According to AVCHD Format. Book1. v. 1.01. p 38, "If the metadata is for an AVCHD application, - // this value shall be set to 'Ox1OOO'." - return false; - } - - return true; -} - -// ================================================================================================= -// ReadAVCCAMProMetaID-- read Panasonic's proprietary PRO_MetaID block -// ============ - -static bool ReadAVCCAMProMetaID ( LFA_FileRef& cpiFileRef, XMP_Uns8 tagID, AVCHD_blkPanasonicPrivateData& extensionDataHeader ) -{ - extensionDataHeader.mPresent = 1; - extensionDataHeader.mProMetaIDBlock.mPresent = 1; - extensionDataHeader.mProMetaIDBlock.mTagID = tagID; - LFA_Read ( cpiFileRef, &extensionDataHeader.mProMetaIDBlock.mTagVersion, 1); - extensionDataHeader.mProMetaIDBlock.mTagLength = LFA_ReadUns16_BE ( cpiFileRef ); - LFA_Read ( cpiFileRef, &extensionDataHeader.mProMetaIDBlock.mProfessionalMetaID, 16); - - return true; -} - -// ================================================================================================= -// ReadAVCCAMProClipInfo-- read Panasonic's proprietary PRO_ClipInfo block -// ============ - -static bool ReadAVCCAMProClipInfo ( LFA_FileRef& cpiFileRef, XMP_Uns8 tagID, AVCHD_blkPanasonicPrivateData& extensionDataHeader ) -{ - extensionDataHeader.mPresent = 1; - extensionDataHeader.mProClipIDBlock.mPresent = 1; - extensionDataHeader.mProClipIDBlock.mTagID = tagID; - LFA_Read ( cpiFileRef, &extensionDataHeader.mProClipIDBlock.mTagVersion, 1); - extensionDataHeader.mProClipIDBlock.mTagLength = LFA_ReadUns16_BE ( cpiFileRef ); - LFA_Read ( cpiFileRef, &extensionDataHeader.mProClipIDBlock.mGlobalClipID, 32); - LFA_Read ( cpiFileRef, &extensionDataHeader.mProClipIDBlock.mStartTimecode, 4 ); - extensionDataHeader.mProClipIDBlock.mStartBinaryGroup = LFA_ReadUns32_BE ( cpiFileRef ); - - return true; -} - -// ================================================================================================= -// ReadAVCCAM_blkPRO_ShotMark -- read Panasonic's proprietary PRO_ShotMark block -// ============ - -static bool ReadAVCCAM_blkPRO_ShotMark ( LFA_FileRef& mplFileRef, AVCCAM_blkProPlayListMark& proMark ) -{ - proMark.mShotMark.mPresent = 1; - LFA_Read ( mplFileRef, &proMark.mShotMark.mShotMark, 1); - LFA_Read ( mplFileRef, &proMark.mShotMark.mFillItem, 3); - - return true; -} - -// ================================================================================================= -// ReadAVCCAM_blkPRO_Access -- read Panasonic's proprietary PRO_Access block -// ============ - -static bool ReadAVCCAM_blkPRO_Access ( LFA_FileRef& mplFileRef, AVCCAM_blkProPlayListMark& proMark ) -{ - proMark.mAccess.mPresent = 1; - LFA_Read ( mplFileRef, &proMark.mAccess.mCreatorCharacterSet, 1 ); - LFA_Read ( mplFileRef, &proMark.mAccess.mCreatorLength, 1 ); - LFA_Read ( mplFileRef, &proMark.mAccess.mCreator, 32 ); - LFA_Read ( mplFileRef, &proMark.mAccess.mLastUpdatePersonCharacterSet, 1 ); - LFA_Read ( mplFileRef, &proMark.mAccess.mLastUpdatePersonLength, 1 ); - LFA_Read ( mplFileRef, &proMark.mAccess.mLastUpdatePerson, 32 ); - - return true; -} - -// ================================================================================================= -// ReadAVCCAM_blkPRO_Device -- read Panasonic's proprietary PRO_Device block -// ============ - -static bool ReadAVCCAM_blkPRO_Device ( LFA_FileRef& mplFileRef, AVCCAM_blkProPlayListMark& proMark ) -{ - proMark.mDevice.mPresent = 1; - proMark.mDevice.mMakerID = LFA_ReadUns16_BE ( mplFileRef ); - proMark.mDevice.mMakerModelCode = LFA_ReadUns16_BE ( mplFileRef ); - LFA_Read ( mplFileRef, &proMark.mDevice.mSerialNoCharacterCode, 1 ); - LFA_Read ( mplFileRef, &proMark.mDevice.mSerialNoLength, 1 ); - LFA_Read ( mplFileRef, &proMark.mDevice.mSerialNo, 24 ); - LFA_Read ( mplFileRef, &proMark.mDevice.mFillItem, 2 ); - - return true; -} - -// ================================================================================================= -// ReadAVCCAM_blkPRO_Shoot -- read Panasonic's proprietary PRO_Shoot block -// ============ - -static bool ReadAVCCAM_blkPRO_Shoot ( LFA_FileRef& mplFileRef, AVCCAM_blkProPlayListMark& proMark ) -{ - proMark.mShoot.mPresent = 1; - LFA_Read ( mplFileRef, &proMark.mShoot.mShooterCharacterSet, 1 ); - LFA_Read ( mplFileRef, &proMark.mShoot.mShooterLength, 1 ); - LFA_Read ( mplFileRef, &proMark.mShoot.mShooter, 32 ); - LFA_Read ( mplFileRef, &proMark.mShoot.mStartDateTimeZone, 1 ); - LFA_Read ( mplFileRef, &proMark.mShoot.mStartDate, 7 ); - LFA_Read ( mplFileRef, &proMark.mShoot.mEndDateTimeZone, 1 ); - LFA_Read ( mplFileRef, &proMark.mShoot.mEndDate, 7 ); - LFA_Read ( mplFileRef, &proMark.mShoot.mFillItem, 2 ); - - return true; -} - -// ================================================================================================= -// ReadAVCCAM_blkPRO_Location -- read Panasonic's proprietary PRO_Location block -// ============ - -static bool ReadAVCCAM_blkPRO_Location ( LFA_FileRef& mplFileRef, AVCCAM_blkProPlayListMark& proMark ) -{ - proMark.mLocation.mPresent = 1; - LFA_Read ( mplFileRef, &proMark.mLocation.mSource, 1 ); - proMark.mLocation.mGPSLatitudeRef = LFA_ReadUns32_BE ( mplFileRef ); - proMark.mLocation.mGPSLatitude1 = LFA_ReadUns32_BE ( mplFileRef ); - proMark.mLocation.mGPSLatitude2 = LFA_ReadUns32_BE ( mplFileRef ); - proMark.mLocation.mGPSLatitude3 = LFA_ReadUns32_BE ( mplFileRef ); - proMark.mLocation.mGPSLongitudeRef = LFA_ReadUns32_BE ( mplFileRef ); - proMark.mLocation.mGPSLongitude1 = LFA_ReadUns32_BE ( mplFileRef ); - proMark.mLocation.mGPSLongitude2 = LFA_ReadUns32_BE ( mplFileRef ); - proMark.mLocation.mGPSLongitude3 = LFA_ReadUns32_BE ( mplFileRef ); - proMark.mLocation.mGPSAltitudeRef = LFA_ReadUns32_BE ( mplFileRef ); - proMark.mLocation.mGPSAltitude = LFA_ReadUns32_BE ( mplFileRef ); - LFA_Read ( mplFileRef, &proMark.mLocation.mPlaceNameCharacterSet, 1 ); - LFA_Read ( mplFileRef, &proMark.mLocation.mPlaceNameLength, 1 ); - LFA_Read ( mplFileRef, &proMark.mLocation.mPlaceName, 64 ); - LFA_Read ( mplFileRef, &proMark.mLocation.mFillItem, 1 ); - - return true; -} - -// ================================================================================================= -// ReadAVCCAMProPlaylistInfo -- read Panasonic's proprietary PRO_PlayListInfo block -// ============ - -static bool ReadAVCCAMProPlaylistInfo ( LFA_FileRef& mplFileRef, - XMP_Uns8 tagID, - XMP_Uns16 playlistMarkID, - AVCHD_blkPanasonicPrivateData& extensionDataHeader ) -{ - AVCCAM_Pro_PlayListInfo& playlistBlock = extensionDataHeader.mProPlaylistInfoBlock; - - playlistBlock.mTagID = tagID; - LFA_Read ( mplFileRef, &playlistBlock.mTagVersion, 1); - LFA_Read ( mplFileRef, &playlistBlock.mFillItem1, 2); - playlistBlock.mLength = LFA_ReadUns32_BE ( mplFileRef ); - playlistBlock.mNumberOfPlayListMarks = LFA_ReadUns16_BE ( mplFileRef ); - LFA_Read ( mplFileRef, &playlistBlock.mFillItem2, 2); - - if ( playlistBlock.mNumberOfPlayListMarks == 0 ) return true; - - extensionDataHeader.mPresent = 1; - - XMP_Uns64 blockStart = 0; - - for ( int i = 0; i < playlistBlock.mNumberOfPlayListMarks; ++i ) { - AVCCAM_blkProPlayListMark& currMark = playlistBlock.mPlayListMark; - - LFA_Read ( mplFileRef, &currMark.mProTagID, 1); - LFA_Read ( mplFileRef, &currMark.mFillItem1, 1); - currMark.mLength = LFA_ReadUns16_BE ( mplFileRef ); - blockStart = LFA_Tell ( mplFileRef ); - LFA_Read ( mplFileRef, &currMark.mMarkType, 1 ); - - if ( ( currMark.mProTagID == 0x40 ) && ( currMark.mMarkType == 0x01 ) ) { - LFA_Read ( mplFileRef, &currMark.mEntryMark.mGlobalClipID, 32); - - // skip marks for different clips - if ( i == playlistMarkID ) { - playlistBlock.mPresent = 1; - currMark.mPresent = 1; - LFA_Read ( mplFileRef, &currMark.mEntryMark.mStartTimeCode, 4); - LFA_Read ( mplFileRef, &currMark.mEntryMark.mStreamTimecodeInfo, 1); - LFA_Read ( mplFileRef, &currMark.mEntryMark.mStartBinaryGroup, 4); - LFA_Read ( mplFileRef, &currMark.mEntryMark.mLastUpdateTimeZone, 1); - LFA_Read ( mplFileRef, &currMark.mEntryMark.mLastUpdateDate, 7); - LFA_Read ( mplFileRef, &currMark.mEntryMark.mFillItem, 2); - - XMP_Uns64 currPos = LFA_Tell ( mplFileRef ); - XMP_Uns8 blockTag = 0; - XMP_Uns8 blockFill; - XMP_Uns16 blockLength = 0; - - while ( currPos < ( blockStart + currMark.mLength ) ) { - LFA_Read ( mplFileRef, &blockTag, 1); - LFA_Read ( mplFileRef, &blockFill, 1); - blockLength = LFA_ReadUns16_BE ( mplFileRef ); - currPos += 4; - - switch ( blockTag ) { - case 0x20: - if ( ! ReadAVCCAM_blkPRO_ShotMark ( mplFileRef, currMark ) ) return false; - break; - - - case 0x21: - if ( ! ReadAVCCAM_blkPRO_Access ( mplFileRef, currMark ) ) return false; - break; - - case 0x22: - if ( ! ReadAVCCAM_blkPRO_Device ( mplFileRef, currMark ) ) return false; - break; - - case 0x23: - if ( ! ReadAVCCAM_blkPRO_Shoot ( mplFileRef, currMark ) ) return false; - break; - - case 0x24: - if (! ReadAVCCAM_blkPRO_Location ( mplFileRef, currMark ) ) return false; - break; - - default : break; - } - - currPos += blockLength; - LFA_Seek ( mplFileRef, currPos, SEEK_SET ); - } - } - } - - LFA_Seek ( mplFileRef, blockStart + currMark.mLength, SEEK_SET ); - } - - return true; -} - -// ================================================================================================= -// ReadAVCCAMMakersPrivateData -- read Panasonic's implementation of an AVCCAM "Maker's Private Data" -// block. Panasonic calls their extensions "AVCCAM." -// ============ - -static bool ReadAVCCAMMakersPrivateData ( LFA_FileRef& fileRef, - XMP_Uns16 playlistMarkID, - AVCHD_blkPanasonicPrivateData& avccamPrivateData ) -{ - const XMP_Uns64 blockStart = LFA_Tell(fileRef); - - avccamPrivateData.mNumberOfData = LFA_ReadUns16_BE ( fileRef ); - LFA_Read ( fileRef, &avccamPrivateData.mReserved, 2 ); - - for (int i = 0; i < avccamPrivateData.mNumberOfData; ++i) { - const XMP_Uns8 tagID = LFA_ReadUns8 ( fileRef ); - - switch ( tagID ) { - case 0xe0: ReadAVCCAMProMetaID ( fileRef, tagID, avccamPrivateData ); - break; - case 0xe2: ReadAVCCAMProClipInfo( fileRef, tagID, avccamPrivateData ); - break; - case 0xf0: ReadAVCCAMProPlaylistInfo( fileRef, tagID, playlistMarkID, avccamPrivateData ); - break; - - default: - // Ignore any blocks we don't now or care about - break; - } - } - - return true; -} - -// ================================================================================================= -// ReadAVCHDMakersPrivateData (AVCHD Format. Book 2: Recording Extension Specifications, section 4.2.4.2) -// ============ - -static bool ReadAVCHDMakersPrivateData ( LFA_FileRef& mplFileRef, - XMP_Uns16 playlistMarkID, - AVCHD_blkMakersPrivateData& avchdLegacyData ) -{ - const XMP_Uns64 blockStart = LFA_Tell ( mplFileRef ); - - avchdLegacyData.mLength = LFA_ReadUns32_BE ( mplFileRef ); - - if ( avchdLegacyData.mLength == 0 ) return false; - - avchdLegacyData.mPresent = 1; - avchdLegacyData.mDataBlockStartAddress = LFA_ReadUns32_BE ( mplFileRef ); - LFA_Read ( mplFileRef, &avchdLegacyData.mReserved, 3 ); - LFA_Read ( mplFileRef, &avchdLegacyData.mNumberOfMakerEntries, 1 ); - - if ( avchdLegacyData.mNumberOfMakerEntries == 0 ) return true; - - XMP_Uns16 makerID; - XMP_Uns16 makerModelCode; - XMP_Uns32 mpdStartAddress; - XMP_Uns32 mpdLength; - - for ( int i = 0; i < avchdLegacyData.mNumberOfMakerEntries; ++i ) { - makerID = LFA_ReadUns16_BE ( mplFileRef ); - makerModelCode = LFA_ReadUns16_BE ( mplFileRef ); - mpdStartAddress = LFA_ReadUns32_BE ( mplFileRef ); - mpdLength = LFA_ReadUns32_BE ( mplFileRef ); - - // We only have documentation for Panasonic's Maker's Private Data blocks, so we'll ignore everyone else's - if ( makerID == kMakerIDPanasonic ) { - avchdLegacyData.mMakerID = makerID; - avchdLegacyData.mMakerModelCode = makerModelCode; - LFA_Seek ( mplFileRef, blockStart + mpdStartAddress, SEEK_SET ); - - if (! ReadAVCCAMMakersPrivateData ( mplFileRef, playlistMarkID, avchdLegacyData.mPanasonicPrivateData ) ) return false; - } - } - - return true; -} - -// ================================================================================================= -// ReadAVCHDClipExtensionData -// ============ - -static bool ReadAVCHDClipExtensionData ( LFA_FileRef& cpiFileRef, AVCHD_blkClipExtensionData& avchdExtensionData ) -{ - const XMP_Int64 extensionBlockStart = LFA_Tell ( cpiFileRef ); - AVCHD_blkExtensionData extensionDataHeader; - - if ( ! ReadAVCHDExtensionData ( cpiFileRef, extensionDataHeader ) ) { - return false; - } - - if ( extensionDataHeader.mLength == 0 ) { - return true; - } - - const XMP_Int64 dataBlockStart = extensionBlockStart + extensionDataHeader.mDataBlockStartAddress; - - LFA_Seek ( cpiFileRef, dataBlockStart, SEEK_SET ); - LFA_Read ( cpiFileRef, avchdExtensionData.mTypeIndicator, 4 ); - - if ( strncmp ( reinterpret_cast<const char*>( avchdExtensionData.mTypeIndicator ), "CLEX", 4 ) != 0 ) return false; - - avchdExtensionData.mPresent = 1; - LFA_Read ( cpiFileRef, avchdExtensionData.mReserved1, 4 ); - avchdExtensionData.mProgramInfoExtStartAddress = LFA_ReadUns32_BE ( cpiFileRef ); - avchdExtensionData.mMakersPrivateDataStartAddress = LFA_ReadUns32_BE ( cpiFileRef ); - - // read Clip info extension - LFA_Seek ( cpiFileRef, dataBlockStart + 40, SEEK_SET ); - avchdExtensionData.mClipInfoExt.mLength = LFA_ReadUns32_BE ( cpiFileRef ); - avchdExtensionData.mClipInfoExt.mMakerID = LFA_ReadUns16_BE ( cpiFileRef ); - avchdExtensionData.mClipInfoExt.mMakerModelCode = LFA_ReadUns16_BE ( cpiFileRef ); - - if ( avchdExtensionData.mMakersPrivateDataStartAddress == 0 ) return true; - - if ( avchdExtensionData.mClipInfoExt.mMakerID == kMakerIDPanasonic ) { - // Read Maker's Private Data block -- we only have Panasonic's definition for their AVCCAM models - // at this point, so we'll ignore the block if its from a different manufacturer. - LFA_Seek ( cpiFileRef, dataBlockStart + avchdExtensionData.mMakersPrivateDataStartAddress, SEEK_SET ); - - if ( ! ReadAVCHDMakersPrivateData ( cpiFileRef, 0, avchdExtensionData.mMakersPrivateData ) ) { - return false; - } - } - - return true; -} - -// ================================================================================================= -// AVCHD_PlaylistContainsClip -- returns true of the specified AVCHD playlist block references the -// specified clip, or false if not. -// ==================== - -static bool AVCHD_PlaylistContainsClip ( LFA_FileRef& mplFileRef, XMP_Uns16& playItemID, const std::string& strClipName ) -{ - // Read clip header. ( AVCHD Format. Book1. v. 1.01. p 45 ) - struct AVCHD_blkPlayList - { - XMP_Uns32 mLength; - XMP_Uns16 mReserved; - XMP_Uns16 mNumberOfPlayItems; - XMP_Uns16 mNumberOfSubPaths; - }; - - AVCHD_blkPlayList blkPlayList; - blkPlayList.mLength = LFA_ReadUns32_BE ( mplFileRef ); - LFA_Read ( mplFileRef, &blkPlayList.mReserved, 2 ); - blkPlayList.mNumberOfPlayItems = LFA_ReadUns16_BE ( mplFileRef ); - blkPlayList.mNumberOfSubPaths = LFA_ReadUns16_BE ( mplFileRef ); - - // Search the play items. ( AVCHD Format. Book1. v. 1.01. p 47 ) - struct AVCHD_blkPlayItem - { - XMP_Uns16 mLength; - char mClipInformationFileName[5]; - // Note: remaining fields omitted because we don't care about them - }; - - AVCHD_blkPlayItem currPlayItem; - XMP_Uns64 blockStart = 0; - for ( playItemID = 0; playItemID < blkPlayList.mNumberOfPlayItems; ++playItemID ) { - currPlayItem.mLength = LFA_ReadUns16_BE ( mplFileRef ); - - // mLength is measured from the end of mLength, not the start of the block ( AVCHD Format. Book1. v. 1.01. p 47 ) - blockStart = LFA_Tell ( mplFileRef ); - LFA_Read ( mplFileRef, currPlayItem.mClipInformationFileName, 5 ); - - if ( strncmp ( strClipName.c_str(), currPlayItem.mClipInformationFileName, 5 ) == 0 ) return true; - - LFA_Seek ( mplFileRef, blockStart + currPlayItem.mLength, SEEK_SET ); - } - - return false; -} - -// ================================================================================================= -// ReadAVCHDPlaylistMetadataBlock -// ============ - -static bool ReadAVCHDPlaylistMetadataBlock ( LFA_FileRef& mplFileRef, - AVCHD_blkPlaylistMeta& avchdLegacyData ) -{ - avchdLegacyData.mLength = LFA_ReadUns32_BE ( mplFileRef ); - - if ( avchdLegacyData.mLength < sizeof ( AVCHD_blkPlaylistMeta ) ) return false; - - avchdLegacyData.mMakerID = LFA_ReadUns16_BE ( mplFileRef ); - avchdLegacyData.mMakerModelCode = LFA_ReadUns16_BE ( mplFileRef ); - LFA_Read ( mplFileRef, &avchdLegacyData.mReserved1, 4 ); - avchdLegacyData.mRefToMenuThumbnailIndex = LFA_ReadUns16_BE ( mplFileRef ); - LFA_Read ( mplFileRef, &avchdLegacyData.mBlkTimezone, 1 ); - LFA_Read ( mplFileRef, &avchdLegacyData.mRecordDataAndTime, 7 ); - LFA_Read ( mplFileRef, &avchdLegacyData.mReserved2, 1 ); - LFA_Read ( mplFileRef, &avchdLegacyData.mPlaylistCharacterSet, 1 ); - LFA_Read ( mplFileRef, &avchdLegacyData.mPlaylistNameLength, 1 ); - LFA_Read ( mplFileRef, &avchdLegacyData.mPlaylistName, avchdLegacyData.mPlaylistNameLength ); - - return true; -} - -// ================================================================================================= -// ReadAVCHDPlaylistMarkExtension -// ============ - -static bool ReadAVCHDPlaylistMarkExtension ( LFA_FileRef& mplFileRef, - XMP_Uns16 playlistMarkID, - AVCHD_blkPlayListMarkExt& avchdLegacyData ) -{ - avchdLegacyData.mLength = LFA_ReadUns32_BE ( mplFileRef ); - - if ( avchdLegacyData.mLength == 0 ) return false; - - avchdLegacyData.mNumberOfPlaylistMarks = LFA_ReadUns16_BE ( mplFileRef ); - - if ( avchdLegacyData.mNumberOfPlaylistMarks <= playlistMarkID ) return true; - - // Number of bytes in blkMarkExtension, AVCHD Book 2, section 4.3.3.1 - const XMP_Uns64 markExtensionSize = 66; - - // Entries in the mark extension block correspond one-to-one with entries in - // blkPlaylistMark, so we'll only read the one that corresponds to the - // chosen clip. - const XMP_Uns64 markOffset = markExtensionSize * playlistMarkID; - - avchdLegacyData.mPresent = 1; - LFA_Seek ( mplFileRef, markOffset, SEEK_CUR ); - avchdLegacyData.mMakerID = LFA_ReadUns16_BE ( mplFileRef ); - avchdLegacyData.mMakerModelCode = LFA_ReadUns16_BE ( mplFileRef ); - LFA_Read ( mplFileRef, &avchdLegacyData.mReserved1, 3 ); - LFA_Read ( mplFileRef, &avchdLegacyData.mFlags, 1 ); - avchdLegacyData.mRefToMarkThumbnailIndex = LFA_ReadUns16_BE ( mplFileRef ); - LFA_Read ( mplFileRef, &avchdLegacyData.mBlkTimezone, 1 ); - LFA_Read ( mplFileRef, &avchdLegacyData.mRecordDataAndTime, 7 ); - LFA_Read ( mplFileRef, &avchdLegacyData.mMarkCharacterSet, 1 ); - LFA_Read ( mplFileRef, &avchdLegacyData.mMarkNameLength, 1 ); - LFA_Read ( mplFileRef, &avchdLegacyData.mMarkName, 24 ); - LFA_Read ( mplFileRef, &avchdLegacyData.mMakersInformation, 16 ); - LFA_Read ( mplFileRef, &avchdLegacyData.mBlkTimecode, 4 ); - LFA_Read ( mplFileRef, &avchdLegacyData.mReserved2, 2 ); - - return true; -} - -// ================================================================================================= -// ReadAVCHDPlaylistMarkID -- read the playlist mark block to find the ID of the playlist mark that -// matches the specified playlist item. -// ============ - -static bool ReadAVCHDPlaylistMarkID ( LFA_FileRef& mplFileRef, - XMP_Uns16 playItemID, - XMP_Uns16& markID ) -{ - XMP_Uns32 length = LFA_ReadUns32_BE ( mplFileRef ); - XMP_Uns16 numberOfPlayListMarks = LFA_ReadUns16_BE ( mplFileRef ); - - if ( length == 0 ) return false; - - XMP_Uns8 reserved; - XMP_Uns8 markType; - XMP_Uns16 refToPlayItemID; - - for ( int i = 0; i < numberOfPlayListMarks; ++i ) { - LFA_Read ( mplFileRef, &reserved, 1 ); - LFA_Read ( mplFileRef, &markType, 1 ); - refToPlayItemID = LFA_ReadUns16_BE ( mplFileRef ); - - if ( ( markType == 0x01 ) && ( refToPlayItemID == playItemID ) ) { - markID = i; - return true; - } - - LFA_Seek ( mplFileRef, 10, SEEK_CUR ); - } - - return false; -} - -// ================================================================================================= -// ReadAVCHDPlaylistExtensionData -// ============ - -static bool ReadAVCHDPlaylistExtensionData ( LFA_FileRef& mplFileRef, - AVCHD_LegacyMetadata& avchdLegacyData, - XMP_Uns16 playlistMarkID ) -{ - const XMP_Int64 extensionBlockStart = LFA_Tell ( mplFileRef ); - AVCHD_blkExtensionData extensionDataHeader; - - if ( ! ReadAVCHDExtensionData ( mplFileRef, extensionDataHeader ) ) { - return false; - } - - if ( extensionDataHeader.mLength == 0 ) { - return true; - } - - const XMP_Int64 dataBlockStart = extensionBlockStart + extensionDataHeader.mDataBlockStartAddress; - AVCHD_blkPlayListExtensionData& extensionData = avchdLegacyData.mPlaylistExtensionData; - const int reserved2Len = 24; - - LFA_Seek ( mplFileRef, dataBlockStart, SEEK_SET ); - LFA_Read ( mplFileRef, extensionData.mTypeIndicator, 4 ); - - if ( strncmp ( extensionData.mTypeIndicator, "PLEX", 4 ) != 0 ) return false; - - extensionData.mPresent = true; - LFA_Read ( mplFileRef, extensionData.mReserved, 4 ); - extensionData.mPlayListMarkExtStartAddress = LFA_ReadUns32_BE ( mplFileRef ); - extensionData.mMakersPrivateDataStartAddress = LFA_ReadUns32_BE ( mplFileRef ); - LFA_Seek ( mplFileRef, reserved2Len, SEEK_CUR ); - - if ( ! ReadAVCHDPlaylistMetadataBlock ( mplFileRef, extensionData.mPlaylistMeta ) ) return false; - - LFA_Seek ( mplFileRef, dataBlockStart + extensionData.mPlayListMarkExtStartAddress, SEEK_SET ); - - if ( ! ReadAVCHDPlaylistMarkExtension ( mplFileRef, playlistMarkID, extensionData.mPlaylistMarkExt ) ) return false; - - if ( extensionData.mMakersPrivateDataStartAddress > 0 ) { - - if ( ! avchdLegacyData.mClipExtensionData.mMakersPrivateData.mPanasonicPrivateData.mPresent ) return false; - - LFA_Seek ( mplFileRef, dataBlockStart + extensionData.mMakersPrivateDataStartAddress, SEEK_SET ); - - if ( ! ReadAVCHDMakersPrivateData ( mplFileRef, playlistMarkID, extensionData.mMakersPrivateData ) ) return false; - - } - - return true; -} - -// ================================================================================================= -// ReadAVCHDLegacyClipFile -- read the legacy metadata stored in an AVCHD .CPI file -// ==================== - -static bool ReadAVCHDLegacyClipFile ( const std::string& strPath, AVCHD_LegacyMetadata& avchdLegacyData ) -{ - bool success = false; - - try { - - AutoFile cpiFile; - cpiFile.fileRef = LFA_Open ( strPath.c_str(), 'r' ); - if ( cpiFile.fileRef == 0 ) return false; // The open failed. - - memset ( &avchdLegacyData, 0, sizeof(AVCHD_LegacyMetadata) ); - - // Read clip header. ( AVCHD Format. Book1. v. 1.01. p 64 ) - struct AVCHD_ClipInfoHeader - { - char mTypeIndicator[4]; - char mTypeIndicator2[4]; - XMP_Uns32 mSequenceInfoStartAddress; - XMP_Uns32 mProgramInfoStartAddress; - XMP_Uns32 mCPIStartAddress; - XMP_Uns32 mClipMarkStartAddress; - XMP_Uns32 mExtensionDataStartAddress; - XMP_Uns8 mReserved[12]; - }; - - // Read the AVCHD header. - AVCHD_ClipInfoHeader avchdHeader; - LFA_Read ( cpiFile.fileRef, avchdHeader.mTypeIndicator, 4 ); - LFA_Read ( cpiFile.fileRef, avchdHeader.mTypeIndicator2, 4 ); - - if ( strncmp ( avchdHeader.mTypeIndicator, "HDMV", 4 ) != 0 ) return false; - if ( strncmp ( avchdHeader.mTypeIndicator2, "0100", 4 ) != 0 ) return false; - - avchdHeader.mSequenceInfoStartAddress = LFA_ReadUns32_BE ( cpiFile.fileRef ); - avchdHeader.mProgramInfoStartAddress = LFA_ReadUns32_BE ( cpiFile.fileRef ); - avchdHeader.mCPIStartAddress = LFA_ReadUns32_BE ( cpiFile.fileRef ); - avchdHeader.mClipMarkStartAddress = LFA_ReadUns32_BE ( cpiFile.fileRef ); - avchdHeader.mExtensionDataStartAddress = LFA_ReadUns32_BE ( cpiFile.fileRef ); - LFA_Read ( cpiFile.fileRef, avchdHeader.mReserved, 12 ); - - // Seek to the program header. (AVCHD Format. Book1. v. 1.01. p 77 ) - LFA_Seek ( cpiFile.fileRef, avchdHeader.mProgramInfoStartAddress, SEEK_SET ); - - // Read the program info block - success = ReadAVCHDProgramInfo ( cpiFile.fileRef, avchdLegacyData.mProgramInfo ); - - if ( success && ( avchdHeader.mExtensionDataStartAddress != 0 ) ) { - // Seek to the program header. (AVCHD Format. Book1. v. 1.01. p 77 ) - LFA_Seek ( cpiFile.fileRef, avchdHeader.mExtensionDataStartAddress, SEEK_SET ); - success = ReadAVCHDClipExtensionData ( cpiFile.fileRef, avchdLegacyData.mClipExtensionData ); - } - - } catch ( ... ) { - - return false; - - } - - return success; -} - -// ================================================================================================= -// ReadAVCHDLegacyPlaylistFile -- read the legacy metadata stored in an AVCHD .MPL file -// ==================== - -static bool ReadAVCHDLegacyPlaylistFile ( const std::string& strRootPath, - const std::string& strClipName, - AVCHD_LegacyMetadata& avchdLegacyData ) -{ - bool success = false; - std::string mplPath; - char playlistName [10]; - const int rootPlaylistNum = atoi(strClipName.c_str()); - - // Find the corresponding .MPL file -- because of clip spanning the .MPL name may not match the .CPI name for - // a given clip -- we need to open .MPL files and look for one that contains a reference to the clip name. To speed - // up the search we'll start with the playlist with the same number/name as the clip and search backwards. Assuming - // this directory was generated by a camera, the clip numbers will increase sequentially across the playlist files, - // though one playlist file may reference more than one clip. - for ( int i = rootPlaylistNum; i >= 0; --i ) { - - sprintf ( playlistName, "%05d", i ); - - if ( MakeLeafPath ( &mplPath, strRootPath.c_str(), "PLAYLIST", playlistName, ".mpl", true /* checkFile */ ) ) { - - try { - - AutoFile mplFile; - mplFile.fileRef = LFA_Open ( mplPath.c_str(), 'r' ); - if ( mplFile.fileRef == 0 ) return false; // The open failed. - - // Read playlist header. ( AVCHD Format. Book1. v. 1.01. p 43 ) - struct AVCHD_PlaylistFileHeader - { - char mTypeIndicator[4]; - char mTypeIndicator2[4]; - XMP_Uns32 mPlaylistStartAddress; - XMP_Uns32 mPlaylistMarkStartAddress; - XMP_Uns32 mExtensionDataStartAddress; - }; - - // Read the AVCHD playlist file header. - AVCHD_PlaylistFileHeader avchdHeader; - LFA_Read ( mplFile.fileRef, avchdHeader.mTypeIndicator, 4 ); - LFA_Read ( mplFile.fileRef, avchdHeader.mTypeIndicator2, 4 ); - - if ( strncmp ( avchdHeader.mTypeIndicator, "MPLS", 4 ) != 0 ) return false; - if ( strncmp ( avchdHeader.mTypeIndicator2, "0100", 4 ) != 0 ) return false; - - avchdHeader.mPlaylistStartAddress = LFA_ReadUns32_BE ( mplFile.fileRef ); - avchdHeader.mPlaylistMarkStartAddress = LFA_ReadUns32_BE ( mplFile.fileRef ); - avchdHeader.mExtensionDataStartAddress = LFA_ReadUns32_BE ( mplFile.fileRef ); - - if ( avchdHeader.mExtensionDataStartAddress == 0 ) return false; - - // Seek to the start of the Playlist block. (AVCHD Format. Book1. v. 1.01. p 45 ) - LFA_Seek ( mplFile.fileRef, avchdHeader.mPlaylistStartAddress, SEEK_SET ); - - XMP_Uns16 playItemID = 0xFFFF; - XMP_Uns16 playlistMarkID = 0xFFFF; - - if ( AVCHD_PlaylistContainsClip ( mplFile.fileRef, playItemID, strClipName ) ) { - LFA_Seek ( mplFile.fileRef, avchdHeader.mPlaylistMarkStartAddress, SEEK_SET ); - - if ( ! ReadAVCHDPlaylistMarkID ( mplFile.fileRef, playItemID, playlistMarkID ) ) return false; - - LFA_Seek ( mplFile.fileRef, avchdHeader.mExtensionDataStartAddress, SEEK_SET ); - success = ReadAVCHDPlaylistExtensionData ( mplFile.fileRef, avchdLegacyData, playlistMarkID ); - } - } catch ( ... ) { - - return false; - - } - } - - } - - return success; -} - -// ================================================================================================= -// ReadAVCHDLegacyMetadata -- read the legacy metadata stored in an AVCHD .CPI file -// ==================== - -static bool ReadAVCHDLegacyMetadata ( const std::string& strPath, - const std::string& strRootPath, - const std::string& strClipName, - AVCHD_LegacyMetadata& avchdLegacyData ) -{ - bool success = ReadAVCHDLegacyClipFile ( strPath, avchdLegacyData ); - - if ( success && avchdLegacyData.mClipExtensionData.mPresent ) { - success = ReadAVCHDLegacyPlaylistFile ( strRootPath, strClipName, avchdLegacyData ); - } - - return success; - -} // ReadAVCHDLegacyMetadata - -// ================================================================================================= -// AVCCAM_SetXMPStartTimecode -// ============================= - -static void AVCCAM_SetXMPStartTimecode ( SXMPMeta& xmpObj, const XMP_Uns8* avccamTimecode, XMP_Uns8 avchdFrameRate ) -{ - // Timecode in SMPTE 12M format, according to Panasonic's documentation - if ( *reinterpret_cast<const XMP_Uns32*>( avccamTimecode ) == 0xFFFFFFFF ) { - // 0xFFFFFFFF means timecode not specified - return; - } - - const XMP_Uns8 isColor = ( avccamTimecode[0] >> 7 ) & 0x01; - const XMP_Uns8 isDropFrame = ( avccamTimecode[0] >> 6 ) & 0x01; - const XMP_Uns8 frameTens = ( avccamTimecode[0] >> 4 ) & 0x03; - const XMP_Uns8 frameUnits = avccamTimecode[0] & 0x0f; - const XMP_Uns8 secondTens = ( avccamTimecode[1] >> 4 ) & 0x07; - const XMP_Uns8 secondUnits = avccamTimecode[1] & 0x0f; - const XMP_Uns8 minuteTens = ( avccamTimecode[2] >> 4 ) & 0x07; - const XMP_Uns8 minuteUnits = avccamTimecode[2] & 0x0f; - const XMP_Uns8 hourTens = ( avccamTimecode[3] >> 4 ) & 0x03; - const XMP_Uns8 hourUnits = avccamTimecode[3] & 0x0f; - char tcSeparator = ':'; - const char* dmTimeFormat = NULL; - const char* dmTimeScale = NULL; - const char* dmTimeSampleSize = NULL; - - switch ( avchdFrameRate ) { - case 1 : - // 23.976i - dmTimeFormat = "23976Timecode"; - dmTimeScale = "24000"; - dmTimeSampleSize = "1001"; - break; - - case 2 : - // 24p - dmTimeFormat = "24Timecode"; - dmTimeScale = "24"; - dmTimeSampleSize = "1"; - break; - - case 3 : - case 6 : - // 50i or 25p - dmTimeFormat = "25Timecode"; - dmTimeScale = "25"; - dmTimeSampleSize = "1"; - break; - - case 4 : - case 7 : - // 29.97p or 59.94i - if ( isDropFrame ) { - dmTimeFormat = "2997DropTimecode"; - tcSeparator = ';'; - } else { - dmTimeFormat = "2997NonDropTimecode"; - } - - dmTimeScale = "30000"; - dmTimeSampleSize = "1001"; - - break; - } - - if ( dmTimeFormat != NULL ) { - char timecodeBuff [12]; - - sprintf ( timecodeBuff, "%d%d%c%d%d%c%d%d%c%d%d", hourTens, hourUnits, tcSeparator, - minuteTens, minuteUnits, tcSeparator, secondTens, secondUnits, tcSeparator, frameTens, frameUnits); - - xmpObj.SetProperty( kXMP_NS_DM, "startTimeScale", dmTimeScale, kXMP_DeleteExisting ); - xmpObj.SetProperty( kXMP_NS_DM, "startTimeSampleSize", dmTimeSampleSize, kXMP_DeleteExisting ); - xmpObj.SetStructField ( kXMP_NS_DM, "startTimecode", kXMP_NS_DM, "timeValue", timecodeBuff, 0 ); - xmpObj.SetStructField ( kXMP_NS_DM, "startTimecode", kXMP_NS_DM, "timeFormat", dmTimeFormat, 0 ); - } -} - -// ================================================================================================= -// AVCHD_SetXMPMakeAndModel -// ============================= - -static bool AVCHD_SetXMPMakeAndModel ( SXMPMeta& xmpObj, const AVCHD_blkClipExtensionData& clipExtData ) -{ - if ( ! clipExtData.mPresent ) return false; - - XMP_StringPtr xmpValue = 0; - - // Set the Make. Use a hex string for unknown makes. - { - char hexMakeNumber [7]; - - switch ( clipExtData.mClipInfoExt.mMakerID ) { - case kMakerIDCanon : xmpValue = "Canon"; break; - case kMakerIDPanasonic : xmpValue = "Panasonic"; break; - case kMakerIDSony : xmpValue = "Sony"; break; - default : - std::sprintf ( hexMakeNumber, "0x%04x", clipExtData.mClipInfoExt.mMakerID ); - xmpValue = hexMakeNumber; - - break; - } - - xmpObj.SetProperty ( kXMP_NS_TIFF, "Make", xmpValue, kXMP_DeleteExisting ); - } - - // Set the Model number. Use a hex string for unknown model numbers so they can still be distinguished. - { - char hexModelNumber [7]; - - xmpValue = 0; - - switch ( clipExtData.mClipInfoExt.mMakerID ) { - case kMakerIDCanon : - switch ( clipExtData.mClipInfoExt.mMakerModelCode ) { - case 0x1000 : xmpValue = "HR10"; break; - case 0x2000 : xmpValue = "HG10"; break; - case 0x2001 : xmpValue = "HG21"; break; - case 0x3000 : xmpValue = "HF100"; break; - case 0x3003 : xmpValue = "HF S10"; break; - default : break; - } - break; - - case kMakerIDPanasonic : - switch ( clipExtData.mClipInfoExt.mMakerModelCode ) { - case 0x0202 : xmpValue = "HD-writer"; break; - case 0x0400 : xmpValue = "AG-HSC1U"; break; - case 0x0401 : xmpValue = "AG-HMC70"; break; - case 0x0410 : xmpValue = "AG-HMC150"; break; - case 0x0411 : xmpValue = "AG-HMC40"; break; - case 0x0412 : xmpValue = "AG-HMC80"; break; - case 0x0413 : xmpValue = "AG-3DA1"; break; - case 0x0414 : xmpValue = "AG-AF100"; break; - case 0x0450 : xmpValue = "AG-HMR10"; break; - case 0x0451 : xmpValue = "AJ-YCX250"; break; - case 0x0452 : xmpValue = "AG-MDR15"; break; - case 0x0490 : xmpValue = "AVCCAM Restorer"; break; - case 0x0491 : xmpValue = "AVCCAM Viewer"; break; - case 0x0492 : xmpValue = "AVCCAM Viewer for Mac"; break; - default : break; - } - - break; - - default : break; - } - - if ( ( xmpValue == 0 ) && ( clipExtData.mClipInfoExt.mMakerID != kMakerIDSony ) ) { - // Panasonic has said that if we don't have a string for the model number, they'd like to see the code - // anyway. We'll do the same for every manufacturer except Sony, who have said that they use - // the same model number for multiple cameras. - std::sprintf ( hexModelNumber, "0x%04x", clipExtData.mClipInfoExt.mMakerModelCode ); - xmpValue = hexModelNumber; - } - - if ( xmpValue != 0 ) xmpObj.SetProperty ( kXMP_NS_TIFF, "Model", xmpValue, kXMP_DeleteExisting ); - } - - return true; -} - -// ================================================================================================= -// AVCHD_StringFieldToXMP -// ============================= - -static std::string AVCHD_StringFieldToXMP ( XMP_Uns8 avchdLength, - XMP_Uns8 avchdCharacterSet, - const XMP_Uns8* avchdField, - XMP_Uns8 avchdFieldSize ) -{ - std::string xmpString; - - if ( avchdCharacterSet == 0x02 ) { - // UTF-16, Big Endian - UTF8Unit utf8Name [512]; - const XMP_Uns8 avchdMaxChars = ( avchdFieldSize / 2); - size_t utf16Read; - size_t utf8Written; - - // The spec doesn't say whether AVCHD length fields count bytes or characters, so we'll - // clamp to the max number of UTF-16 characters just in case. - const int stringLength = ( avchdLength > avchdMaxChars ) ? avchdMaxChars : avchdLength; - - UTF16BE_to_UTF8 ( reinterpret_cast<const UTF16Unit*> ( avchdField ), stringLength, - utf8Name, 512, &utf16Read, &utf8Written ); - xmpString.assign ( reinterpret_cast<const char*> ( utf8Name ), utf8Written ); - } else { - // AVCHD supports many character encodings, but UTF-8 (0x01) and ASCII (0x90) are the only ones I've - // seen in the wild at this point. We'll treat the other character sets as UTF-8 on the assumption that - // at least a few characters will come across, and something is better than nothing. - const int stringLength = ( avchdLength > avchdFieldSize ) ? avchdFieldSize : avchdLength; - - xmpString.assign ( reinterpret_cast<const char*> ( avchdField ), stringLength ); - } - - return xmpString; -} - -// ================================================================================================= -// AVCHD_SetXMPShotName -// ============================= - -static void AVCHD_SetXMPShotName ( SXMPMeta& xmpObj, const AVCHD_blkPlayListMarkExt& markExt, const std::string& strClipName ) -{ - if ( markExt.mPresent ) { - const std::string shotName = AVCHD_StringFieldToXMP ( markExt.mMarkNameLength, markExt.mMarkCharacterSet, markExt.mMarkName, 24 ); - - if ( ! shotName.empty() ) xmpObj.SetProperty ( kXMP_NS_DC, "shotName", shotName.c_str(), kXMP_DeleteExisting ); - } -} - -// ================================================================================================= -// BytesToHex -// ============================= - -#define kHexDigits "0123456789ABCDEF" - -static std::string BytesToHex ( const XMP_Uns8* inClipIDBytes, int inNumBytes ) -{ - const int numChars = ( inNumBytes * 2 ); - std::string hexStr; - - hexStr.reserve(numChars); - - for ( int i = 0; i < inNumBytes; ++i ) { - const XMP_Uns8 byte = inClipIDBytes[i]; - hexStr.push_back ( kHexDigits [byte >> 4] ); - hexStr.push_back ( kHexDigits [byte & 0xF] ); - } - - return hexStr; -} - -// ================================================================================================= -// AVCHD_DateFieldToXMP (AVCHD Format Book 2, section 4.2.2.2) -// ============================= - -static std::string AVCHD_DateFieldToXMP ( XMP_Uns8 avchdTimeZone, const XMP_Uns8* avchdDateTime ) -{ - const XMP_Uns8 daylightSavingsTime = ( avchdTimeZone >> 6 ) & 0x01; - const XMP_Uns8 timezoneSign = ( avchdTimeZone >> 5 ) & 0x01; - const XMP_Uns8 timezoneValue = ( avchdTimeZone >> 1 ) & 0x0F; - const XMP_Uns8 halfHourFlag = avchdTimeZone & 0x01; - int utcOffsetHours = 0; - unsigned int utcOffsetMinutes = 0; - - // It's not entirely clear how to interpret the daylightSavingsTime flag from the documentation -- my best - // guess is that it should only be used if trying to display local time, not the UTC-relative time that - // XMP specifies. - if ( timezoneValue != 0xF ) { - utcOffsetHours = timezoneSign ? -timezoneValue : timezoneValue; - utcOffsetMinutes = 30 * halfHourFlag; - } - - char dateBuff [26]; - - sprintf ( dateBuff, - "%01d%01d%01d%01d-%01d%01d-%01d%01dT%01d%01d:%01d%01d:%01d%01d%+02d:%02d", - (avchdDateTime[0] >> 4), (avchdDateTime[0] & 0x0F), - (avchdDateTime[1] >> 4), (avchdDateTime[1] & 0x0F), - (avchdDateTime[2] >> 4), (avchdDateTime[2] & 0x0F), - (avchdDateTime[3] >> 4), (avchdDateTime[3] & 0x0F), - (avchdDateTime[4] >> 4), (avchdDateTime[4] & 0x0F), - (avchdDateTime[5] >> 4), (avchdDateTime[5] & 0x0F), - (avchdDateTime[6] >> 4), (avchdDateTime[6] & 0x0F), - utcOffsetHours, utcOffsetMinutes ); - - return std::string(dateBuff); -} - -// ================================================================================================= -// AVCHD_MetaHandlerCTor -// ===================== - -XMPFileHandler * AVCHD_MetaHandlerCTor ( XMPFiles * parent ) -{ - return new AVCHD_MetaHandler ( parent ); - -} // AVCHD_MetaHandlerCTor - -// ================================================================================================= -// AVCHD_MetaHandler::AVCHD_MetaHandler -// ==================================== - -AVCHD_MetaHandler::AVCHD_MetaHandler ( XMPFiles * _parent ) -{ - this->parent = _parent; // Inherited, can't set in the prefix. - this->handlerFlags = kAVCHD_HandlerFlags; - this->stdCharForm = kXMP_Char8Bit; - - // Extract the root path and clip name. - - XMP_Assert ( this->parent->tempPtr != 0 ); - - this->rootPath.assign ( (char*) this->parent->tempPtr ); - free ( this->parent->tempPtr ); - this->parent->tempPtr = 0; - - SplitLeafName ( &this->rootPath, &this->clipName ); - -} // AVCHD_MetaHandler::AVCHD_MetaHandler - -// ================================================================================================= -// AVCHD_MetaHandler::~AVCHD_MetaHandler -// ===================================== - -AVCHD_MetaHandler::~AVCHD_MetaHandler() -{ - - if ( this->parent->tempPtr != 0 ) { - free ( this->parent->tempPtr ); - this->parent->tempPtr = 0; - } - -} // AVCHD_MetaHandler::~AVCHD_MetaHandler - -// ================================================================================================= -// AVCHD_MetaHandler::MakeClipInfoPath -// =================================== - -bool AVCHD_MetaHandler::MakeClipInfoPath ( std::string * path, XMP_StringPtr suffix, bool checkFile /* = false */ ) const -{ - return MakeLeafPath ( path, this->rootPath.c_str(), "CLIPINF", this->clipName.c_str(), suffix, checkFile ); -} // AVCHD_MetaHandler::MakeClipInfoPath - -// ================================================================================================= -// AVCHD_MetaHandler::MakeClipStreamPath -// ===================================== - -bool AVCHD_MetaHandler::MakeClipStreamPath ( std::string * path, XMP_StringPtr suffix, bool checkFile /* = false */ ) const -{ - return MakeLeafPath ( path, this->rootPath.c_str(), "STREAM", this->clipName.c_str(), suffix, checkFile ); -} // AVCHD_MetaHandler::MakeClipStreamPath - -// ================================================================================================= -// AVCHD_MetaHandler::MakePlaylistPath -// ===================================== - -bool AVCHD_MetaHandler::MakePlaylistPath ( std::string * path, XMP_StringPtr suffix, bool checkFile /* = false */ ) const -{ - return MakeLeafPath ( path, this->rootPath.c_str(), "PLAYLIST", this->clipName.c_str(), suffix, checkFile ); -} // AVCHD_MetaHandler::MakePlaylistPath - -// ================================================================================================= -// AVCHD_MetaHandler::MakeLegacyDigest -// =================================== - -void AVCHD_MetaHandler::MakeLegacyDigest ( std::string * digestStr ) -{ - std::string strClipPath; - std::string strPlaylistPath; - std::vector<XMP_Uns8> legacyBuff; - - bool ok = this->MakeClipInfoPath ( &strClipPath, ".clpi", true /* checkFile */ ); - if ( ! ok ) return; - - ok = this->MakePlaylistPath ( &strPlaylistPath, ".mpls", true /* checkFile */ ); - if ( ! ok ) return; - - try { - { - AutoFile cpiFile; - cpiFile.fileRef = LFA_Open ( strClipPath.c_str(), 'r' ); - if ( cpiFile.fileRef == 0 ) return; // The open failed. - - // Read at most the first 2k of data from the cpi file to use in the digest - // (every CPI file I've seen is less than 1k). - const XMP_Int64 cpiLen = LFA_Measure ( cpiFile.fileRef ); - const XMP_Int64 buffLen = (cpiLen <= 2048) ? cpiLen : 2048; - - legacyBuff.resize ( (unsigned int) buffLen ); - LFA_Read ( cpiFile.fileRef, &(legacyBuff[0]), static_cast<XMP_Int32> ( buffLen ) ); - } - - { - AutoFile mplFile; - mplFile.fileRef = LFA_Open ( strPlaylistPath.c_str(), 'r' ); - if ( mplFile.fileRef == 0 ) return; // The open failed. - - // Read at most the first 2k of data from the cpi file to use in the digest - // (every playlist file I've seen is less than 1k). - const XMP_Int64 mplLen = LFA_Measure ( mplFile.fileRef ); - const XMP_Int64 buffLen = (mplLen <= 2048) ? mplLen : 2048; - const XMP_Int64 clipBuffLen = legacyBuff.size(); - - legacyBuff.resize ( (unsigned int) (clipBuffLen + buffLen) ); - LFA_Read ( mplFile.fileRef, &( legacyBuff [(unsigned int)clipBuffLen] ), (XMP_Int32)buffLen ); - } - } catch (...) { - return; - } - - MD5_CTX context; - unsigned char digestBin [16]; - - MD5Init ( &context ); - MD5Update ( &context, (XMP_Uns8*)&(legacyBuff[0]), (unsigned int) legacyBuff.size() ); - MD5Final ( digestBin, &context ); - - *digestStr = BytesToHex ( digestBin, 16 ); -} // AVCHD_MetaHandler::MakeLegacyDigest - -// ================================================================================================= -// AVCHD_MetaHandler::CacheFileData -// ================================ - -void AVCHD_MetaHandler::CacheFileData() -{ - XMP_Assert ( ! this->containsXMP ); - - // See if the clip's .XMP file exists. - - std::string xmpPath; - bool found = this->MakeClipStreamPath ( &xmpPath, ".xmp", true /* checkFile */ ); - if ( ! found ) return; - - // Read the entire .XMP file. - - char openMode = 'r'; - if ( this->parent->openFlags & kXMPFiles_OpenForUpdate ) openMode = 'w'; - - LFA_FileRef xmpFile = LFA_Open ( xmpPath.c_str(), openMode ); - if ( xmpFile == 0 ) return; // The open failed. - - XMP_Int64 xmpLen = LFA_Measure ( xmpFile ); - if ( xmpLen > 100*1024*1024 ) { - XMP_Throw ( "AVCHD XMP is outrageously large", kXMPErr_InternalFailure ); // Sanity check. - } - - this->xmpPacket.erase(); - this->xmpPacket.reserve ( (size_t ) xmpLen ); - this->xmpPacket.append ( (size_t ) xmpLen, ' ' ); - - XMP_Int32 ioCount = LFA_Read ( xmpFile, (void*)this->xmpPacket.data(), (XMP_Int32)xmpLen, kLFA_RequireAll ); - XMP_Assert ( ioCount == xmpLen ); - - this->packetInfo.offset = 0; - this->packetInfo.length = (XMP_Int32)xmpLen; - FillPacketInfo ( this->xmpPacket, &this->packetInfo ); - - XMP_Assert ( this->parent->fileRef == 0 ); - if ( openMode == 'r' ) { - LFA_Close ( xmpFile ); - } else { - this->parent->fileRef = xmpFile; - } - - this->containsXMP = true; - -} // AVCHD_MetaHandler::CacheFileData - -// ================================================================================================= -// AVCHD_MetaHandler::ProcessXMP -// ============================= - -void AVCHD_MetaHandler::ProcessXMP() -{ - if ( this->processedXMP ) return; - this->processedXMP = true; // Make sure only called once. - - if ( this->containsXMP ) { - this->xmpObj.ParseFromBuffer ( this->xmpPacket.c_str(), (XMP_StringLen)this->xmpPacket.size() ); - } - - // read clip info - AVCHD_LegacyMetadata avchdLegacyData; - std::string strPath; - - bool ok = this->MakeClipInfoPath ( &strPath, ".clpi", true /* checkFile */ ); - if ( ok ) ReadAVCHDLegacyMetadata ( strPath, this->rootPath, this->clipName, avchdLegacyData ); - if ( ! ok ) return; - - const AVCHD_blkPlayListMarkExt& markExt = avchdLegacyData.mPlaylistExtensionData.mPlaylistMarkExt; - XMP_Uns8 pulldownFlag = 0; - - if ( markExt.mPresent ) { - const std::string dateString = AVCHD_DateFieldToXMP ( markExt.mBlkTimezone, markExt.mRecordDataAndTime ); - - if ( ! dateString.empty() ) this->xmpObj.SetProperty ( kXMP_NS_DM, "shotDate", dateString.c_str(), kXMP_DeleteExisting ); - AVCHD_SetXMPShotName ( this->xmpObj, markExt, this->clipName ); - AVCCAM_SetXMPStartTimecode ( this->xmpObj, markExt.mBlkTimecode, avchdLegacyData.mProgramInfo.mVideoStream.mFrameRate ); - pulldownFlag = (markExt.mFlags >> 1) & 0x03; // bits 1 and 2 - } - - // Video Stream. AVCHD Format v. 1.01 p. 78 - - const bool has2_2pulldown = (pulldownFlag == 0x01); - const bool has3_2pulldown = (pulldownFlag == 0x10); - XMP_StringPtr xmpValue = 0; - - if ( avchdLegacyData.mProgramInfo.mVideoStream.mPresent ) { - - // XMP videoFrameSize. - xmpValue = 0; - int frameIndex = -1; - bool isProgressiveHD = false; - const char* frameWidth[4] = { "720", "720", "1280", "1920" }; - const char* frameHeight[4] = { "480", "576", "720", "1080" }; - - switch ( avchdLegacyData.mProgramInfo.mVideoStream.mVideoFormat ) { - case 1 : frameIndex = 0; break; // 480i - case 2 : frameIndex = 1; break; // 576i - case 3 : frameIndex = 0; break; // 480p - case 4 : frameIndex = 3; break; // 1080i - case 5 : frameIndex = 2; isProgressiveHD = true; break; // 720p - case 6 : frameIndex = 3; isProgressiveHD = true; break; // 1080p - default: break; - } - - if ( frameIndex != -1 ) { - xmpValue = frameWidth[frameIndex]; - this->xmpObj.SetStructField ( kXMP_NS_DM, "videoFrameSize", kXMP_NS_XMP_Dimensions, "w", xmpValue, 0 ); - xmpValue = frameHeight[frameIndex]; - this->xmpObj.SetStructField ( kXMP_NS_DM, "videoFrameSize", kXMP_NS_XMP_Dimensions, "h", xmpValue, 0 ); - xmpValue = "pixels"; - this->xmpObj.SetStructField ( kXMP_NS_DM, "videoFrameSize", kXMP_NS_XMP_Dimensions, "unit", xmpValue, 0 ); - } - - // XMP videoFrameRate. The logic below seems pretty tortured, but matches "Table 4-4 pulldown" on page 31 of Book 2 of the AVCHD - // spec, if you interepret "frame_mbs_only_flag" as "isProgressiveHD", "frame-rate [Hz]" as the frame rate encoded in - // mVideoStream.mFrameRate, and "Video Scan Type" as the desired xmp output value. The algorithm produces correct results for - // all the AVCHD media I've tested. - xmpValue = 0; - if ( isProgressiveHD ) { - - switch ( avchdLegacyData.mProgramInfo.mVideoStream.mFrameRate ) { - case 1 : xmpValue = "23.98p"; break; // "23.976" - case 2 : xmpValue = "24p"; break; // "24" - case 3 : xmpValue = "25p"; break; // "25" - case 4 : xmpValue = has2_2pulldown ? "29.97p" : "59.94p"; break; // "29.97" - case 6 : xmpValue = has2_2pulldown ? "25p" : "50p"; break; // "50" - case 7 : // "59.94" - if ( has2_2pulldown ) - xmpValue = "29.97p"; - else - xmpValue = has3_2pulldown ? "23.98p" : "59.94p"; - - break; - default: break; - } - - } else { - - switch ( avchdLegacyData.mProgramInfo.mVideoStream.mFrameRate ) { - case 3 : xmpValue = has2_2pulldown ? "25p" : "50i"; break; // "25" (but 1080p25 is reported as 1080i25 with 2:2 pulldown...) - case 4 : // "29.97" - if ( has2_2pulldown ) - xmpValue = "29.97p"; - else - xmpValue = has3_2pulldown ? "23.98p" : "59.94i"; - - break; - default: break; - } - - } - - if ( xmpValue != 0 ) { - this->xmpObj.SetProperty ( kXMP_NS_DM, "videoFrameRate", xmpValue, kXMP_DeleteExisting ); - } - - this->containsXMP = true; - - } - - // Audio Stream. - if ( avchdLegacyData.mProgramInfo.mAudioStream.mPresent ) { - - xmpValue = 0; - switch ( avchdLegacyData.mProgramInfo.mAudioStream.mAudioPresentationType ) { - case 1 : xmpValue = "Mono"; break; - case 3 : xmpValue = "Stereo"; break; - default : break; - } - if ( xmpValue != 0 ) { - this->xmpObj.SetProperty ( kXMP_NS_DM, "audioChannelType", xmpValue, kXMP_DeleteExisting ); - } - - xmpValue = 0; - switch ( avchdLegacyData.mProgramInfo.mAudioStream.mSamplingFrequency ) { - case 1 : xmpValue = "48000"; break; - case 4 : xmpValue = "96000"; break; - case 5 : xmpValue = "192000"; break; - default : break; - } - if ( xmpValue != 0 ) { - this->xmpObj.SetProperty ( kXMP_NS_DM, "audioSampleRate", xmpValue, kXMP_DeleteExisting ); - } - - this->containsXMP = true; - } - - // Proprietary vendor extensions - if ( AVCHD_SetXMPMakeAndModel ( this->xmpObj, avchdLegacyData.mClipExtensionData ) ) this->containsXMP = true; - - this->xmpObj.SetProperty ( kXMP_NS_DM, "title", this->clipName.c_str(), kXMP_DeleteExisting ); - this->containsXMP = true; - - if ( avchdLegacyData.mClipExtensionData.mMakersPrivateData.mPresent && - ( avchdLegacyData.mClipExtensionData.mClipInfoExt.mMakerID == kMakerIDPanasonic ) ) { - - const AVCHD_blkPanasonicPrivateData& panasonicClipData = avchdLegacyData.mClipExtensionData.mMakersPrivateData.mPanasonicPrivateData; - - if ( panasonicClipData.mProClipIDBlock.mPresent ) { - const std::string globalClipIDString = BytesToHex ( panasonicClipData.mProClipIDBlock.mGlobalClipID, 32 ); - - this->xmpObj.SetProperty ( kXMP_NS_DC, "identifier", globalClipIDString.c_str(), kXMP_DeleteExisting ); - } - - const AVCHD_blkPanasonicPrivateData& panasonicPlaylistData = - avchdLegacyData.mPlaylistExtensionData.mMakersPrivateData.mPanasonicPrivateData; - - if ( panasonicPlaylistData.mProPlaylistInfoBlock.mPlayListMark.mPresent ) { - const AVCCAM_blkProPlayListMark& playlistMark = panasonicPlaylistData.mProPlaylistInfoBlock.mPlayListMark; - - if ( playlistMark.mShotMark.mPresent ) { - // Unlike P2, where "shotmark" is a boolean, Panasonic treats their AVCCAM shotmark as a bit field with - // 8 user-definable bits. For now we're going to treat any bit being set as xmpDM::good == true, and all - // bits being clear as xmpDM::good == false. - const bool isGood = ( playlistMark.mShotMark.mShotMark != 0 ); - - xmpObj.SetProperty_Bool ( kXMP_NS_DM, "good", isGood, kXMP_DeleteExisting ); - } - - if ( playlistMark.mAccess.mPresent && ( playlistMark.mAccess.mCreatorLength > 0 ) ) { - const std::string creatorString = AVCHD_StringFieldToXMP ( - playlistMark.mAccess.mCreatorLength, playlistMark.mAccess.mCreatorCharacterSet, playlistMark.mAccess.mCreator, 32 ) ; - - if ( ! creatorString.empty() ) { - xmpObj.DeleteProperty ( kXMP_NS_DC, "creator" ); - xmpObj.AppendArrayItem ( kXMP_NS_DC, "creator", kXMP_PropArrayIsOrdered, creatorString.c_str() ); - } - } - - if ( playlistMark.mDevice.mPresent && ( playlistMark.mDevice.mSerialNoLength > 0 ) ) { - const std::string serialNoString = AVCHD_StringFieldToXMP ( - playlistMark.mDevice.mSerialNoLength, playlistMark.mDevice.mSerialNoCharacterCode, playlistMark.mDevice.mSerialNo, 24 ) ; - - if ( ! serialNoString.empty() ) xmpObj.SetProperty ( kXMP_NS_EXIF_Aux, "SerialNumber", serialNoString.c_str(), kXMP_DeleteExisting ); - } - - if ( playlistMark.mLocation.mPresent && ( playlistMark.mLocation.mPlaceNameLength > 0 ) ) { - const std::string placeNameString = AVCHD_StringFieldToXMP ( - playlistMark.mLocation.mPlaceNameLength, playlistMark.mLocation.mPlaceNameCharacterSet, playlistMark.mLocation.mPlaceName, 64 ) ; - - if ( ! placeNameString.empty() ) xmpObj.SetProperty ( kXMP_NS_DM, "shotLocation", placeNameString.c_str(), kXMP_DeleteExisting ); - } - } - } - -} // AVCHD_MetaHandler::ProcessXMP - -// ================================================================================================= -// AVCHD_MetaHandler::UpdateFile -// ============================= -// -// Note that UpdateFile is only called from XMPFiles::CloseFile, so it is OK to close the file here. - -void AVCHD_MetaHandler::UpdateFile ( bool doSafeUpdate ) -{ - if ( ! this->needsUpdate ) return; - this->needsUpdate = false; // Make sure only called once. - - std::string newDigest; - this->MakeLegacyDigest ( &newDigest ); - this->xmpObj.SetStructField ( kXMP_NS_XMP, "NativeDigests", kXMP_NS_XMP, "AVCHD", newDigest.c_str(), kXMP_DeleteExisting ); - - LFA_FileRef oldFile = this->parent->fileRef; - - this->xmpObj.SerializeToBuffer ( &this->xmpPacket, this->GetSerializeOptions() ); - - if ( oldFile == 0 ) { - - // The XMP does not exist yet. - - std::string xmpPath; - this->MakeClipStreamPath ( &xmpPath, ".xmp" ); - - LFA_FileRef xmpFile = LFA_Create ( xmpPath.c_str() ); - if ( xmpFile == 0 ) XMP_Throw ( "Failure creating AVCHD XMP file", kXMPErr_ExternalFailure ); - LFA_Write ( xmpFile, this->xmpPacket.data(), (XMP_StringLen)this->xmpPacket.size() ); - LFA_Close ( xmpFile ); - - } else if ( ! doSafeUpdate ) { - - // Over write the existing XMP file. - - LFA_Seek ( oldFile, 0, SEEK_SET ); - LFA_Truncate ( oldFile, 0 ); - LFA_Write ( oldFile, this->xmpPacket.data(), (XMP_StringLen)this->xmpPacket.size() ); - LFA_Close ( oldFile ); - - } else { - - // Do a safe update. - - // *** We really need an LFA_SwapFiles utility. - - std::string xmpPath, tempPath; - - bool found = this->MakeClipStreamPath ( &xmpPath, ".xmp", true /* checkFile */ ); - if ( ! found ) XMP_Throw ( "AVCHD_MetaHandler::UpdateFile - XMP is supposed to exist", kXMPErr_InternalFailure ); - - CreateTempFile ( xmpPath, &tempPath ); - LFA_FileRef tempFile = LFA_Open ( tempPath.c_str(), 'w' ); - LFA_Write ( tempFile, this->xmpPacket.data(), (XMP_StringLen)this->xmpPacket.size() ); - LFA_Close ( tempFile ); - - LFA_Close ( oldFile ); - LFA_Delete ( xmpPath.c_str() ); - LFA_Rename ( tempPath.c_str(), xmpPath.c_str() ); - - } - - this->parent->fileRef = 0; - -} // AVCHD_MetaHandler::UpdateFile - -// ================================================================================================= -// AVCHD_MetaHandler::WriteFile -// ============================ - -void AVCHD_MetaHandler::WriteFile ( LFA_FileRef sourceRef, const std::string & sourcePath ) -{ - - // ! WriteFile is not supposed to be called for handlers that own the file. - XMP_Throw ( "AVCHD_MetaHandler::WriteFile should not be called", kXMPErr_InternalFailure ); - -} // AVCHD_MetaHandler::WriteFile - -// ================================================================================================= diff --git a/source/XMPFiles/FileHandlers/AVCHD_Handler.hpp b/source/XMPFiles/FileHandlers/AVCHD_Handler.hpp deleted file mode 100644 index 4123089..0000000 --- a/source/XMPFiles/FileHandlers/AVCHD_Handler.hpp +++ /dev/null @@ -1,79 +0,0 @@ -#ifndef __AVCHD_Handler_hpp__ -#define __AVCHD_Handler_hpp__ 1 - -// ================================================================================================= -// ADOBE SYSTEMS INCORPORATED -// Copyright 2008 Adobe Systems Incorporated -// 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 "XMP_Environment.h" // ! This must be the first include. - -#include "XMPFiles_Impl.hpp" - -#include "ExpatAdapter.hpp" - -// ================================================================================================= -/// \file AVCHD_Handler.hpp -/// \brief Folder format handler for AVCHD. -/// -/// This header ... -/// -// ================================================================================================= - -extern XMPFileHandler * AVCHD_MetaHandlerCTor ( XMPFiles * parent ); - -extern bool AVCHD_CheckFormat ( XMP_FileFormat format, - const std::string & rootPath, - const std::string & gpName, - const std::string & parentName, - const std::string & leafName, - XMPFiles * parent ); - -static const XMP_OptionBits kAVCHD_HandlerFlags = (kXMPFiles_CanInjectXMP | - kXMPFiles_CanExpand | - kXMPFiles_CanRewrite | - kXMPFiles_PrefersInPlace | - kXMPFiles_CanReconcile | - kXMPFiles_AllowsOnlyXMP | - kXMPFiles_ReturnsRawPacket | - kXMPFiles_HandlerOwnsFile | - kXMPFiles_AllowsSafeUpdate | - kXMPFiles_FolderBasedFormat); - -class AVCHD_MetaHandler : public XMPFileHandler -{ -public: - - void CacheFileData(); - void ProcessXMP(); - - XMP_OptionBits GetSerializeOptions() // *** These should be standard for standalone XMP files. - { return (kXMP_UseCompactFormat | kXMP_OmitPacketWrapper); }; - - void UpdateFile ( bool doSafeUpdate ); - void WriteFile ( LFA_FileRef sourceRef, const std::string & sourcePath ); - - AVCHD_MetaHandler ( XMPFiles * _parent ); - virtual ~AVCHD_MetaHandler(); - -private: - - AVCHD_MetaHandler() {}; // Hidden on purpose. - - bool MakeClipInfoPath ( std::string * path, XMP_StringPtr suffix, bool checkFile = false ) const; - bool MakeClipStreamPath ( std::string * path, XMP_StringPtr suffix, bool checkFile = false ) const; - bool MakePlaylistPath ( std::string * path, XMP_StringPtr suffix, bool checkFile = false ) const; - - void MakeLegacyDigest ( std::string * digestStr ); - - std::string rootPath, clipName; - -}; // AVCHD_MetaHandler - -// ================================================================================================= - -#endif /* __AVCHD_Handler_hpp__ */ diff --git a/source/XMPFiles/FileHandlers/Basic_Handler.cpp b/source/XMPFiles/FileHandlers/Basic_Handler.cpp deleted file mode 100644 index 3efd739..0000000 --- a/source/XMPFiles/FileHandlers/Basic_Handler.cpp +++ /dev/null @@ -1,247 +0,0 @@ -// ================================================================================================= -// ADOBE SYSTEMS INCORPORATED -// Copyright 2004 Adobe Systems Incorporated -// 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 "Basic_Handler.hpp" - -using namespace std; - -// ================================================================================================= -/// \file Basic_Handler.cpp -/// \brief Base class for basic handlers that only process in-place XMP. -/// -/// This header ... -/// -// ================================================================================================= - -// ================================================================================================= -// Basic_MetaHandler::~Basic_MetaHandler -// ===================================== - -Basic_MetaHandler::~Basic_MetaHandler() -{ - // ! Inherit the base cleanup. - -} // Basic_MetaHandler::~Basic_MetaHandler - -// ================================================================================================= -// Basic_MetaHandler::UpdateFile -// ============================= - -// ! This must be called from the destructor for all derived classes. It can't be called from the -// ! Basic_MetaHandler destructor, by then calls to the virtual functions would not go to the -// ! actual implementations for the derived class. - -void Basic_MetaHandler::UpdateFile ( bool doSafeUpdate ) -{ - IgnoreParam ( doSafeUpdate ); - XMP_Assert ( ! doSafeUpdate ); // Not supported at this level. - if ( ! this->needsUpdate ) return; - - LFA_FileRef fileRef = this->parent->fileRef; - XMP_PacketInfo & packetInfo = this->packetInfo; - std::string & xmpPacket = this->xmpPacket; - - XMP_AbortProc abortProc = this->parent->abortProc; - void * abortArg = this->parent->abortArg; - const bool checkAbort = (abortProc != 0); - - this->CaptureFileEnding(); // ! Do this first, before any location info changes. - if ( checkAbort && abortProc(abortArg) ) { - XMP_Throw ( "Basic_MetaHandler::UpdateFile - User abort", kXMPErr_UserAbort ); - } - - this->NoteXMPRemoval(); - this->ShuffleTrailingContent(); - if ( checkAbort && abortProc(abortArg) ) { - XMP_Throw ( "Basic_MetaHandler::UpdateFile - User abort", kXMPErr_UserAbort ); - } - - XMP_Int64 tempLength = this->xmpFileOffset - this->xmpPrefixSize + this->trailingContentSize; - LFA_Truncate ( fileRef, tempLength ); - LFA_Flush ( fileRef ); - - packetInfo.offset = tempLength + this->xmpPrefixSize; - this->NoteXMPInsertion(); - - LFA_Seek ( fileRef, 0, SEEK_END ); - this->WriteXMPPrefix(); - LFA_Write ( fileRef, xmpPacket.c_str(), (XMP_StringLen)xmpPacket.size() ); - this->WriteXMPSuffix(); - if ( checkAbort && abortProc(abortArg) ) { - XMP_Throw ( "Basic_MetaHandler::UpdateFile - User abort", kXMPErr_UserAbort ); - } - - this->RestoreFileEnding(); - LFA_Flush ( fileRef ); - - this->xmpFileOffset = packetInfo.offset; - this->xmpFileSize = packetInfo.length; - this->needsUpdate = false; - -} // Basic_MetaHandler::UpdateFile - -// ================================================================================================= -// Basic_MetaHandler::WriteFile -// ============================ - -// *** What about computing the new file length and pre-allocating the file? - -void Basic_MetaHandler::WriteFile ( LFA_FileRef sourceRef, const std::string & sourcePath ) -{ - IgnoreParam ( sourcePath ); - - XMP_AbortProc abortProc = this->parent->abortProc; - void * abortArg = this->parent->abortArg; - const bool checkAbort = (abortProc != 0); - - LFA_FileRef destRef = this->parent->fileRef; - - // Capture the "back" of the source file. - - this->parent->fileRef = sourceRef; - this->CaptureFileEnding(); - this->parent->fileRef = destRef; - if ( checkAbort && abortProc(abortArg) ) { - XMP_Throw ( "Basic_MetaHandler::UpdateFile - User abort", kXMPErr_UserAbort ); - } - - // Seek to the beginning of the source and destination files, truncate the destination. - - LFA_Seek ( sourceRef, 0, SEEK_SET ); - LFA_Seek ( destRef, 0, SEEK_SET ); - LFA_Truncate ( destRef, 0 ); - - // Copy the front of the source file to the destination. Note the XMP (pseudo) removal and - // insertion. This mainly updates info about the new XMP length. - - XMP_Int64 xmpSectionOffset = this->xmpFileOffset - this->xmpPrefixSize; - XMP_Int32 oldSectionLength = this->xmpPrefixSize + this->xmpFileSize + this->xmpSuffixSize; - - LFA_Copy ( sourceRef, destRef, xmpSectionOffset, abortProc, abortArg ); - this->NoteXMPRemoval(); - packetInfo.offset = this->xmpFileOffset; // ! The packet offset does not change. - this->NoteXMPInsertion(); - LFA_Seek ( destRef, 0, SEEK_END ); - if ( checkAbort && abortProc(abortArg) ) { - XMP_Throw ( "Basic_MetaHandler::WriteFile - User abort", kXMPErr_UserAbort ); - } - - // Write the new XMP section to the destination. - - this->WriteXMPPrefix(); - LFA_Write ( destRef, this->xmpPacket.c_str(), (XMP_StringLen)this->xmpPacket.size() ); - this->WriteXMPSuffix(); - if ( checkAbort && abortProc(abortArg) ) { - XMP_Throw ( "Basic_MetaHandler::WriteFile - User abort", kXMPErr_UserAbort ); - } - - // Copy the trailing file content from the source and write the "back" of the file. - - XMP_Int64 remainderOffset = xmpSectionOffset + oldSectionLength; - - LFA_Seek ( sourceRef, remainderOffset, SEEK_SET ); - LFA_Copy ( sourceRef, destRef, this->trailingContentSize, abortProc, abortArg ); - this->RestoreFileEnding(); - - // Done. - - LFA_Flush ( destRef ); - - this->xmpFileOffset = packetInfo.offset; - this->xmpFileSize = packetInfo.length; - this->needsUpdate = false; - -} // Basic_MetaHandler::WriteFile - -// ================================================================================================= -// ShuffleTrailingContent -// ====================== -// -// Shuffle the trailing content portion of a file forward. This does not include the final "back" -// portion of the file, just the arbitrary length content between the XMP section and the back. -// Don't use LFA_Copy, that assumes separate files and hence separate I/O positions. - -// ! The XMP packet location and prefix/suffix sizes must still reflect the XMP section that is in -// ! the process of being removed. - -void Basic_MetaHandler::ShuffleTrailingContent() -{ - LFA_FileRef fileRef = this->parent->fileRef; - - XMP_Int64 readOffset = this->packetInfo.offset + xmpSuffixSize; - XMP_Int64 writeOffset = this->packetInfo.offset - xmpPrefixSize; - - XMP_Int64 remainingLength = this->trailingContentSize; - - enum { kBufferSize = 64*1024 }; - char buffer [kBufferSize]; - - XMP_AbortProc abortProc = this->parent->abortProc; - void * abortArg = this->parent->abortArg; - const bool checkAbort = (abortProc != 0); - - while ( remainingLength > 0 ) { - - XMP_Int32 ioCount = kBufferSize; - if ( remainingLength < kBufferSize ) ioCount = (XMP_Int32)remainingLength; - - LFA_Seek ( fileRef, readOffset, SEEK_SET ); - LFA_Read ( fileRef, buffer, ioCount, kLFA_RequireAll ); - LFA_Seek ( fileRef, writeOffset, SEEK_SET ); - LFA_Write ( fileRef, buffer, ioCount ); - - readOffset += ioCount; - writeOffset += ioCount; - remainingLength -= ioCount; - - if ( checkAbort && abortProc(abortArg) ) { - XMP_Throw ( "Basic_MetaHandler::ShuffleTrailingContent - User abort", kXMPErr_UserAbort ); - } - - } - - LFA_Flush ( fileRef ); - -} // ShuffleTrailingContent - -// ================================================================================================= -// Dummies needed for VS.Net -// ========================= - -void Basic_MetaHandler::WriteXMPPrefix() -{ - XMP_Throw ( "Basic_MetaHandler::WriteXMPPrefix - Needs specific override", kXMPErr_InternalFailure ); -} - -void Basic_MetaHandler::WriteXMPSuffix() -{ - XMP_Throw ( "Basic_MetaHandler::WriteXMPSuffix - Needs specific override", kXMPErr_InternalFailure ); -} - -void Basic_MetaHandler::NoteXMPRemoval() -{ - XMP_Throw ( "Basic_MetaHandler::NoteXMPRemoval - Needs specific override", kXMPErr_InternalFailure ); -} - -void Basic_MetaHandler::NoteXMPInsertion() -{ - XMP_Throw ( "Basic_MetaHandler::NoteXMPInsertion - Needs specific override", kXMPErr_InternalFailure ); -} - -void Basic_MetaHandler::CaptureFileEnding() -{ - XMP_Throw ( "Basic_MetaHandler::CaptureFileEnding - Needs specific override", kXMPErr_InternalFailure ); -} - -void Basic_MetaHandler::RestoreFileEnding() -{ - XMP_Throw ( "Basic_MetaHandler::RestoreFileEnding - Needs specific override", kXMPErr_InternalFailure ); -} - -// ================================================================================================= diff --git a/source/XMPFiles/FileHandlers/Basic_Handler.hpp b/source/XMPFiles/FileHandlers/Basic_Handler.hpp deleted file mode 100644 index 17b9775..0000000 --- a/source/XMPFiles/FileHandlers/Basic_Handler.hpp +++ /dev/null @@ -1,104 +0,0 @@ -#ifndef __Basic_Handler_hpp__ -#define __Basic_Handler_hpp__ 1 - -// ================================================================================================= -// ADOBE SYSTEMS INCORPORATED -// Copyright 2004 Adobe Systems Incorporated -// 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 "XMPFiles_Impl.hpp" - -// ================================================================================================= -/// \file Basic_Handler.hpp -/// -/// \brief Base class for handlers that support a simple file model allowing insertion and expansion -/// of XMP, but probably not reconciliation with other forms of metadata. Reconciliation would have -/// to be done within the I/O model presented here. -/// -/// \note Any specific derived handler might not be able to do insertion, but all must support -/// expansion. If a handler can't do either it should be derived from Trivial_Handler. Common code -/// must check the actual canInject flag where appropriate. -/// -/// The model for a basic handler divides the file into 6 portions: -/// -/// \li The front of the file. This portion can be arbitrarily large. Files over 4GB are supported. -/// Adding or expanding the XMP must not require expanding this portion of the file. The XMP offset -/// or length might be written into reserved space in this section though. -/// -/// \li A prefix for the XMP section. The prefix and suffix for the XMP "section" are the format -/// specific portions that surround the raw XMP packet. They must be generated on the fly, even when -/// updating existing XMP with or without expansion. Their length must not depend on the XMP packet. -/// -/// \li The XMP packet, as created by SXMPMeta::SerializeToBuffer. The size must be less than 2GB. -/// -/// \li A suffix for the XMP section. -/// -/// \li Trailing file content. This portion can be arbitarily large. It must be possible to remove -/// the XMP, move this portion of the file forward, then reinsert the XMP after this portion. This -/// is actually how the XMP is expanded. There must not be any embedded file offsets in this part, -/// this content must not change if the XMP changes size. -/// -/// \li The back of the file. This portion must have modest size, and/or be generated on the fly. -/// When inserting XMP, part of this may be buffered in RAM (hence the modest size requirement), the -/// XMP section is written, then this portion is rewritten. There must not be any embedded file -/// offsets in this part, this content must not change if the XMP changes size. -/// -/// \note There is no general promise here about crash-safe I/O. An update to an existing file might -/// have invalid partial state, for example while moving the trailing content portion forward if the -/// XMP increases in size or even rewriting existing XMP in-place. Crash-safe updates are managed at -/// a higher level of XMPFiles, using a temporary file and final swap of file content. -/// -// ================================================================================================= - -static const XMP_OptionBits kBasic_HandlerFlags = (kXMPFiles_CanInjectXMP | - kXMPFiles_CanExpand | - kXMPFiles_CanRewrite | - kXMPFiles_PrefersInPlace | - kXMPFiles_AllowsOnlyXMP | - kXMPFiles_ReturnsRawPacket | - kXMPFiles_AllowsSafeUpdate); - -class Basic_MetaHandler : public XMPFileHandler -{ -public: - - Basic_MetaHandler() : - xmpFileOffset(0), xmpFileSize(0), xmpPrefixSize(0), xmpSuffixSize(0), trailingContentSize(0) {}; - ~Basic_MetaHandler(); - - virtual void CacheFileData() = 0; // Sets offset for insertion if no XMP yet. - - void UpdateFile ( bool doSafeUpdate ); - void WriteFile ( LFA_FileRef sourceRef, const std::string & sourcePath ); - -protected: - - virtual void WriteXMPPrefix() = 0; // ! Must have override in actual handlers! - virtual void WriteXMPSuffix() = 0; // ! Must have override in actual handlers! - - virtual void NoteXMPRemoval() = 0; // ! Must have override in actual handlers! - virtual void NoteXMPInsertion() = 0; // ! Must have override in actual handlers! - - virtual void CaptureFileEnding() = 0; // ! Must have override in actual handlers! - virtual void RestoreFileEnding() = 0; // ! Must have override in actual handlers! - - void ShuffleTrailingContent(); // Move the trailing content portion forward. Excludes "back" of the file. - - XMP_Uns64 xmpFileOffset; // The offset of the XMP in the file. - XMP_Uns32 xmpFileSize; // The size of the XMP in the file. - // ! The packetInfo offset and length are updated by PutXMP, before the file is updated! - - XMP_Uns32 xmpPrefixSize; // The size of the existing header for the XMP section. - XMP_Uns32 xmpSuffixSize; // The size of the existing trailer for the XMP section. - - XMP_Uns64 trailingContentSize; // The size of the existing trailing content. Excludes "back" of the file. - -}; // Basic_MetaHandler - -// ================================================================================================= - -#endif /* __Basic_Handler_hpp__ */ diff --git a/source/XMPFiles/FileHandlers/FLV_Handler.cpp b/source/XMPFiles/FileHandlers/FLV_Handler.cpp deleted file mode 100644 index f9624ed..0000000 --- a/source/XMPFiles/FileHandlers/FLV_Handler.cpp +++ /dev/null @@ -1,751 +0,0 @@ -// ================================================================================================= -// ADOBE SYSTEMS INCORPORATED -// Copyright 2007 Adobe Systems Incorporated -// 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 "FLV_Handler.hpp" - -#include "MD5.h" - -using namespace std; - -// ================================================================================================= -/// \file FLV_Handler.cpp -/// \brief File format handler for FLV. -/// -/// FLV is a fairly simple format, with a strong orientation to streaming use. It consists of a -/// small file header then a sequence of tags that can contain audio data, video data, or -/// ActionScript data. All integers in FLV are big endian. -/// -/// For FLV version 1, the file header contains: -/// -/// UI24 signature - the characters "FLV" -/// UI8 version - 1 -/// UI8 flags - 0x01 = has video tags, 0x04 = has audio tags -/// UI32 length in bytes of file header -/// -/// For FLV version 1, each tag begins with an 11 byte header: -/// -/// UI8 tag type - 8 = audio tag, 9 = video tag, 18 = script data tag -/// UI24 content length in bytes -/// UI24 time - low order 3 bytes -/// UI8 time - high order byte -/// UI24 stream ID -/// -/// This is followed by the tag's content, then a UI32 "back pointer" which is the header size plus -/// the content size. A UI32 zero is placed between the file header and the first tag as a -/// terminator for backward scans. The time in a tag header is the start of playback for that tag. -/// The tags must be in ascending time order. For a given time it is preferred that script data tags -/// precede audio and video tags. -/// -/// For metadata purposes only the script data tags are of interest. Script data information becomes -/// accessible to ActionScript at the playback moment of the script data tag through a call to a -/// registered data handler. The content of a script data tag contains a string and an ActionScript -/// data value. The string is the name of the handler to be invoked, the data value is passed as an -/// ActionScript Object parameter to the handler. -/// -/// The XMP is placed in a script data tag with the name "onXMPData". A variety of legacy metadata -/// is contained in a script data tag with the name "onMetaData". This contains only "internal" -/// information (like duration or width/height), nothing that is user or author editiable (like -/// title or description). Some of these legacy items are imported into the XMP, none are updated -/// from the XMP. -/// -/// A script data tag's content is: -/// -/// UI8 0x02 -/// UI16 name length - includes nul terminator if present -/// UI8n object name - UTF-8, possibly with nul terminator -/// ... object value - serialized ActionScript value (SCRIPTDATAVALUE in FLV spec) -/// -/// The onXMPData and onMetaData values are both ECMA arrays. These have more in common with XMP -/// structs than arrays, the items have arbitrary string names. The serialized form is: -/// -/// UI8 0x08 -/// UI32 array length - need not be exact, an optimization hint -/// array items -/// UI16 name length - includes nul terminator if present -/// UI8n item name - UTF-8, possibly with nul terminator -/// ... object value - serialized ActionScript value (SCRIPTDATAVALUE in FLV spec) -/// UI24 0x000009 - array terminator -/// -/// The object names and array item names in sample files do not have a nul terminator. The policy -/// here is to treat them as optional when reading, and to omit them when writing. -/// -/// The onXMPData array typically has one item named "liveXML". The value of this is a short or long -/// string as necessary: -/// -/// UI8 type - 2 for a short string, 12 for a long string -/// UIx value length - UI16 for a short string, UI32 for a long string, includes nul terminator -/// UI8n value - UTF-8 with nul terminator -/// -// ================================================================================================= - -static inline XMP_Uns32 GetUns24BE ( const void * addr ) -{ - return (GetUns32BE(addr) >> 8); -} - -static inline void PutUns24BE ( XMP_Uns32 value, void * addr ) -{ - XMP_Uns8 * bytes = (XMP_Uns8*)addr; - bytes[0] = (XMP_Uns8)(value >> 16); - bytes[1] = (XMP_Uns8)(value >> 8); - bytes[2] = (XMP_Uns8)(value); -} - -// ================================================================================================= -// FLV_CheckFormat -// =============== -// -// Check for "FLV" and 1 in the first 4 bytes, that the header length is at least 9, that the file -// size is at least as big as the header, and that the leading 0 back pointer is present if the file -// is bigger than the header. - -#define kFLV1 0x464C5601UL - -bool FLV_CheckFormat ( XMP_FileFormat format, - XMP_StringPtr filePath, - LFA_FileRef fileRef, - XMPFiles * parent ) -{ - XMP_Uns8 buffer [9]; - - LFA_Seek ( fileRef, 0, SEEK_SET ); - XMP_Uns32 ioCount = LFA_Read ( fileRef, buffer, 9 ); - if ( ioCount != 9 ) return false; - - XMP_Uns32 fileSignature = GetUns32BE ( &buffer[0] ); - if ( fileSignature != kFLV1 ) return false; - - XMP_Uns32 headerSize = GetUns32BE ( &buffer[5] ); - XMP_Uns64 fileSize = LFA_Measure ( fileRef ); - if ( (fileSize < (headerSize + 4)) && (fileSize != headerSize) ) return false; - - if ( fileSize >= (headerSize + 4) ) { - XMP_Uns32 bpZero; - LFA_Seek ( fileRef, headerSize, SEEK_SET ); - ioCount = LFA_Read ( fileRef, &bpZero, 4 ); - if ( (ioCount != 4) || (bpZero != 0) ) return false; - } - - return true; - -} // FLV_CheckFormat - -// ================================================================================================= -// FLV_MetaHandlerCTor -// =================== - -XMPFileHandler * FLV_MetaHandlerCTor ( XMPFiles * parent ) -{ - - return new FLV_MetaHandler ( parent ); - -} // FLV_MetaHandlerCTor - -// ================================================================================================= -// FLV_MetaHandler::FLV_MetaHandler -// ================================ - -FLV_MetaHandler::FLV_MetaHandler ( XMPFiles * _parent ) - : flvHeaderLen(0), longXMP(false), xmpTagPos(0), omdTagPos(0), xmpTagLen(0), omdTagLen(0) -{ - - this->parent = _parent; // Inherited, can't set in the prefix. - this->handlerFlags = kFLV_HandlerFlags; - this->stdCharForm = kXMP_Char8Bit; - -} // FLV_MetaHandler::FLV_MetaHandler - -// ================================================================================================= -// FLV_MetaHandler::~FLV_MetaHandler -// ================================= - -FLV_MetaHandler::~FLV_MetaHandler() -{ - - // Nothing to do yet. - -} // FLV_MetaHandler::~FLV_MetaHandler - -// ================================================================================================= -// GetTagInfo -// ========== -// -// Seek to the start of a tag and extract the type, data size, and timestamp. Leave the file -// positioned at the first byte of data. - -struct TagInfo { - XMP_Uns8 type; - XMP_Uns32 time; - XMP_Uns32 dataSize; -}; - -static void GetTagInfo ( LFA_FileRef fileRef, XMP_Uns64 tagPos, TagInfo * info ) -{ - XMP_Uns8 buffer [11]; - - LFA_Seek ( fileRef, tagPos, SEEK_SET ); - LFA_Read ( fileRef, buffer, 11, kLFA_RequireAll ); - - info->type = buffer[0]; - info->time = GetUns24BE ( &buffer[4] ) || (buffer[7] << 24); - info->dataSize = GetUns24BE ( &buffer[1] ); - -} // GetTagInfo - -// ================================================================================================= -// GetASValueLen -// ============= -// -// Return the full length of a serialized ActionScript value, including the type byte, zero if unknown. - -static XMP_Uns32 GetASValueLen ( const XMP_Uns8 * asValue, const XMP_Uns8 * asLimit ) -{ - XMP_Uns32 valueLen = 0; - const XMP_Uns8 * itemPtr; - XMP_Uns32 arrayCount; - - switch ( asValue[0] ) { - - case 0 : // IEEE double - valueLen = 1 + 8; - break; - - case 1 : // UI8 Boolean - valueLen = 1 + 1; - break; - - case 2 : // Short string - valueLen = 1 + 2 + GetUns16BE ( &asValue[1] ); - break; - - case 3 : // ActionScript object, a name and value. - itemPtr = &asValue[1]; - itemPtr += 2 + GetUns16BE ( itemPtr ); // Move past the name portion. - itemPtr += GetASValueLen ( itemPtr, asLimit ); // And past the data portion. - valueLen = (XMP_Uns32) (itemPtr - asValue); - break; - - case 4 : // Short string (movie clip path) - valueLen = 1 + 2 + GetUns16BE ( &asValue[1] ); - break; - - case 5 : // Null - valueLen = 1; - break; - - case 6 : // Undefined - valueLen = 1; - break; - - case 7 : // UI16 reference ID - valueLen = 1 + 2; - break; - - case 8 : // ECMA array, ignore the count, look for the 0x000009 terminator. - itemPtr = &asValue[5]; - while ( itemPtr < asLimit ) { - XMP_Uns16 nameLen = GetUns16BE ( itemPtr ); - itemPtr += 2 + nameLen; // Move past the name portion. - if ( (nameLen == 0) && (*itemPtr == 9) ) { - itemPtr += 1; - break; // Done, found the 0x000009 terminator. - } - itemPtr += GetASValueLen ( itemPtr, asLimit ); // And past the data portion. - } - valueLen = (XMP_Uns32) (itemPtr - asValue); - break; - - case 10 : // Strict array, has an exact count. - arrayCount = GetUns32BE ( &asValue[1] ); - itemPtr = &asValue[5]; - for ( ; (arrayCount > 0) && (itemPtr < asLimit); --arrayCount ) { - itemPtr += 2 + GetUns16BE ( itemPtr ); // Move past the name portion. - itemPtr += GetASValueLen ( itemPtr, asLimit ); // And past the data portion. - } - valueLen = (XMP_Uns32) (itemPtr - asValue); - break; - - case 11 : // Date - valueLen = 1 + 8 + 2; - break; - - case 12: // Long string - valueLen = 1 + 4 + GetUns32BE ( &asValue[1] ); - break; - - } - - return valueLen; - -} // GetASValueLen - -// ================================================================================================= -// CheckName -// ========= -// -// Check for the name portion of a script data tag or array item, with optional nul terminator. The -// wantedLen must not count the terminator. - -static inline bool CheckName ( XMP_StringPtr inputName, XMP_Uns16 inputLen, - XMP_StringPtr wantedName, XMP_Uns16 wantedLen ) -{ - - if ( inputLen == wantedLen+1 ) { - if ( inputName[wantedLen] != 0 ) return false; // Extra byte must be terminating nul. - --inputLen; - } - - if ( (inputLen == wantedLen) && XMP_LitNMatch ( inputName, wantedName, wantedLen ) ) return true; - return false; - -} // CheckName - -// ================================================================================================= -// FLV_MetaHandler::CacheFileData -// ============================== -// -// Look for the onXMPData and onMetaData script data tags at time 0. Cache all of onMetaData, it -// shouldn't be that big and this removes a need to know what is reconciled. It can't be more than -// 16MB anyway, the size field is only 24 bits. - -void FLV_MetaHandler::CacheFileData() -{ - XMP_Assert ( ! this->containsXMP ); - - XMP_AbortProc abortProc = this->parent->abortProc; - void * abortArg = this->parent->abortArg; - const bool checkAbort = (abortProc != 0); - - LFA_FileRef fileRef = this->parent->fileRef; - XMP_Uns64 fileSize = LFA_Measure ( fileRef ); - - XMP_Uns8 buffer [16]; // Enough for 1+2+"onMetaData"+nul. - XMP_Uns32 ioCount; - TagInfo info; - - LFA_Seek ( fileRef, 5, SEEK_SET ); - LFA_Read ( fileRef, buffer, 4, kLFA_RequireAll ); - - this->flvHeaderLen = GetUns32BE ( &buffer[0] ); - XMP_Uns32 firstTagPos = this->flvHeaderLen + 4; // Include the initial zero back pointer. - - if ( firstTagPos >= fileSize ) return; // Quit now if the file is just a header. - - for ( XMP_Uns64 tagPos = firstTagPos; tagPos < fileSize; tagPos += (11 + info.dataSize + 4) ) { - - if ( checkAbort && abortProc(abortArg) ) { - XMP_Throw ( "FLV_MetaHandler::LookForMetadata - User abort", kXMPErr_UserAbort ); - } - - GetTagInfo ( fileRef, tagPos, &info ); // ! GetTagInfo seeks to the tag offset. - if ( info.time != 0 ) break; - if ( info.type != 18 ) continue; - - XMP_Assert ( sizeof(buffer) >= (1+2+10+1) ); // 02 000B onMetaData 00 - ioCount = LFA_Read ( fileRef, buffer, sizeof(buffer) ); - if ( (ioCount < 4) || (buffer[0] != 0x02) ) continue; - - XMP_Uns16 nameLen = GetUns16BE ( &buffer[1] ); - XMP_StringPtr namePtr = (XMP_StringPtr)(&buffer[3]); - - if ( this->onXMP.empty() && CheckName ( namePtr, nameLen, "onXMPData", 9 ) ) { - - // ! Put the raw data in onXMPData, analyze the value in ProcessXMP. - - this->xmpTagPos = tagPos; - this->xmpTagLen = 11 + info.dataSize + 4; // ! Includes the trailing back pointer. - - this->packetInfo.offset = tagPos + 11 + 1+2+nameLen; // ! Not the real offset yet, the offset of the onXMPData value. - - ioCount = info.dataSize - (1+2+nameLen); // Just the onXMPData value portion. - this->onXMP.reserve ( ioCount ); - this->onXMP.assign ( ioCount, ' ' ); - LFA_Seek ( fileRef, this->packetInfo.offset, SEEK_SET ); - LFA_Read ( fileRef, (void*)this->onXMP.data(), ioCount, kLFA_RequireAll ); - - if ( ! this->onMetaData.empty() ) break; // Done if we've found both. - - } else if ( this->onMetaData.empty() && CheckName ( namePtr, nameLen, "onMetaData", 10 ) ) { - - this->omdTagPos = tagPos; - this->omdTagLen = 11 + info.dataSize + 4; // ! Includes the trailing back pointer. - - ioCount = info.dataSize - (1+2+nameLen); // Just the onMetaData value portion. - this->onMetaData.reserve ( ioCount ); - this->onMetaData.assign ( ioCount, ' ' ); - LFA_Seek ( fileRef, (tagPos + 11 + 1+2+nameLen), SEEK_SET ); - LFA_Read ( fileRef, (void*)this->onMetaData.data(), ioCount, kLFA_RequireAll ); - - if ( ! this->onXMP.empty() ) break; // Done if we've found both. - - } - - } - -} // FLV_MetaHandler::CacheFileData - -// ================================================================================================= -// FLV_MetaHandler::MakeLegacyDigest -// ================================= - -#define kHexDigits "0123456789ABCDEF" - -void FLV_MetaHandler::MakeLegacyDigest ( std::string * digestStr ) -{ - MD5_CTX context; - unsigned char digestBin [16]; - - MD5Init ( &context ); - MD5Update ( &context, (XMP_Uns8*)this->onMetaData.data(), (unsigned int)this->onMetaData.size() ); - MD5Final ( digestBin, &context ); - - char buffer [40]; - for ( int in = 0, out = 0; in < 16; in += 1, out += 2 ) { - XMP_Uns8 byte = digestBin[in]; - buffer[out] = kHexDigits [ byte >> 4 ]; - buffer[out+1] = kHexDigits [ byte & 0xF ]; - } - buffer[32] = 0; - digestStr->erase(); - digestStr->append ( buffer, 32 ); - -} // FLV_MetaHandler::MakeLegacyDigest - -// ================================================================================================= -// FLV_MetaHandler::ExtractLiveXML -// =============================== -// -// Extract the XMP packet from the cached onXMPData ECMA array's "liveXMP" item. - -void FLV_MetaHandler::ExtractLiveXML() -{ - if ( this->onXMP[0] != 0x08 ) return; // Make sure onXMPData is an ECMA array. - const XMP_Uns8 * ecmaArray = (const XMP_Uns8 *) this->onXMP.c_str(); - const XMP_Uns8 * ecmaLimit = ecmaArray + this->onXMP.size(); - - if ( this->onXMP.size() >= 3 ) { // Omit the 0x000009 terminator, simplifies the loop. - if ( GetUns24BE ( ecmaLimit-3 ) == 9 ) ecmaLimit -= 3; - } - - for ( const XMP_Uns8 * itemPtr = ecmaArray + 5; itemPtr < ecmaLimit; /* internal increment */ ) { - - // Find the "liveXML" array item, make sure it is a short or long string. - - XMP_Uns16 nameLen = GetUns16BE ( itemPtr ); - const XMP_Uns8 * namePtr = itemPtr + 2; - - itemPtr += (2 + nameLen); // Move to the value portion. - XMP_Uns32 valueLen = GetASValueLen ( itemPtr, ecmaLimit ); - if ( valueLen == 0 ) return; // ! Unknown value type, can't look further. - - if ( CheckName ( (char*)namePtr, nameLen, "liveXML", 7 ) ) { - - XMP_Uns32 lenLen = 2; // Assume a short string. - if ( *itemPtr == 12 ) { - lenLen = 4; - this->longXMP = true; - } else if ( *itemPtr != 2 ) { - return; // Not a short or long string. - } - - valueLen -= (1 + lenLen); - itemPtr += (1 + lenLen); - - this->packetInfo.offset += (itemPtr - ecmaArray); - this->packetInfo.length += valueLen; - - this->xmpPacket.reserve ( valueLen ); - this->xmpPacket.assign ( (char*)itemPtr, valueLen ); - - return; - - } - - itemPtr += valueLen; // Move past the value portion. - - } - -} // FLV_MetaHandler::ExtractLiveXML - -// ================================================================================================= -// FLV_MetaHandler::ProcessXMP -// =========================== - -void FLV_MetaHandler::ProcessXMP() -{ - if ( this->processedXMP ) return; - this->processedXMP = true; // Make sure only called once. - - if ( ! this->onXMP.empty() ) { // Look for the XMP packet. - - this->ExtractLiveXML(); - if ( ! this->xmpPacket.empty() ) { - FillPacketInfo ( this->xmpPacket, &this->packetInfo ); - this->xmpObj.ParseFromBuffer ( this->xmpPacket.c_str(), (XMP_StringLen)this->xmpPacket.size() ); - this->containsXMP = true; - } - - } - - // Now process the legacy, if necessary. - - if ( this->onMetaData.empty() ) return; // No legacy, we're done. - - std::string oldDigest; - bool oldDigestFound = this->xmpObj.GetStructField ( kXMP_NS_XMP, "NativeDigests", kXMP_NS_XMP, "FLV", &oldDigest, 0 ); - - if ( oldDigestFound ) { - std::string newDigest; - this->MakeLegacyDigest ( &newDigest ); - if ( oldDigest == newDigest ) return; // No legacy changes. - } - - // *** No spec yet for what legacy to reconcile. - -} // FLV_MetaHandler::ProcessXMP - -// ================================================================================================= -// FLV_MetaHandler::UpdateFile -// =========================== - -void FLV_MetaHandler::UpdateFile ( bool doSafeUpdate ) -{ - if ( ! this->needsUpdate ) return; - XMP_Assert ( ! doSafeUpdate ); // This should only be called for "unsafe" updates. - - XMP_AbortProc abortProc = this->parent->abortProc; - void * abortArg = this->parent->abortArg; - const bool checkAbort = (abortProc != 0); - - LFA_FileRef fileRef = this->parent->fileRef; - XMP_Uns64 fileSize = LFA_Measure ( fileRef ); - - // Make sure the XMP has a legacy digest if appropriate. - - if ( ! this->onMetaData.empty() ) { - - std::string newDigest; - this->MakeLegacyDigest ( &newDigest ); - this->xmpObj.SetStructField ( kXMP_NS_XMP, "NativeDigests", - kXMP_NS_XMP, "FLV", newDigest.c_str(), kXMP_DeleteExisting ); - - try { - XMP_StringLen xmpLen = (XMP_StringLen)this->xmpPacket.size(); - this->xmpObj.SerializeToBuffer ( &this->xmpPacket, (kXMP_UseCompactFormat | kXMP_ExactPacketLength), xmpLen ); - } catch ( ... ) { - this->xmpObj.SerializeToBuffer ( &this->xmpPacket, kXMP_UseCompactFormat ); - } - - } - - // Rewrite the packet in-place if it fits. Otherwise rewrite the whole file. - - if ( this->xmpPacket.size() == (size_t)this->packetInfo.length ) { - - LFA_Seek ( fileRef, this->packetInfo.offset, SEEK_SET ); - LFA_Write ( fileRef, this->xmpPacket.data(), (XMP_Int32)this->xmpPacket.size() ); - - } else { - - std::string origPath = this->parent->filePath; - LFA_FileRef origRef = this->parent->fileRef; - - std::string updatePath; - LFA_FileRef updateRef = 0; - - CreateTempFile ( origPath, &updatePath ); - updateRef = LFA_Open ( updatePath.c_str(), 'w' ); - - this->parent->filePath = updatePath; - this->parent->fileRef = updateRef; - - try { - this->WriteFile ( origRef, origPath ); - } catch ( ... ) { - LFA_Close ( updateRef ); - LFA_Delete ( updatePath.c_str() ); - this->parent->filePath = origPath; - this->parent->fileRef = origRef; - throw; - } - - LFA_Close ( origRef ); - LFA_Delete ( origPath.c_str() ); - - LFA_Close ( updateRef ); - LFA_Rename ( updatePath.c_str(), origPath.c_str() ); - this->parent->filePath = origPath; - this->parent->fileRef = 0; - - } - - this->needsUpdate = false; - -} // FLV_MetaHandler::UpdateFile - -// ================================================================================================= -// WriteOnXMP -// ========== -// -// Write the XMP packet wrapped up in an ECMA array script data tag: -// -// 0 UI8 tag type : 18 -// 1 UI24 content length : 1+2+9+1+4+2+7+1 + <2 or 4> + XMP packet size + 1 + 3 -// 4 UI24 time low : 0 -// 7 UI8 time high : 0 -// 8 UI24 stream ID : 0 -// -// 11 UI8 0x02 -// 12 UI16 name length : 9 -// 14 str9 tag name : "onXMPData", no nul terminator -// 23 UI8 value type : 8 -// 24 UI32 array count : 1 -// 28 UI16 name length : 7 -// 30 str7 item name : "liveXML", no nul terminator -// -// 37 UI8 value type : 2 for a short string, 12 for a long string -// 38 UIn XMP packet size + 1, UI16 or UI32 as needed -// -- str XMP packet, with nul terminator -// -// -- UI24 array terminator : 0x000009 -// -- UI32 back pointer : content length + 11 - -static void WriteOnXMP ( LFA_FileRef fileRef, const std::string & xmpPacket ) -{ - char buffer [64]; - bool longXMP = false; - XMP_Uns32 tagLen = 1+2+9+1+4+2+7+1 + 2 + (XMP_Uns32)xmpPacket.size() + 1 + 3; - - if ( xmpPacket.size() > 0xFFFE ) { - longXMP = true; - tagLen += 2; - } - - if ( tagLen > 16*1024*1024 ) XMP_Throw ( "FLV tags can't be larger than 16MB", kXMPErr_TBD ); - - // Fill in the script data tag header. - - buffer[0] = 18; - PutUns24BE ( tagLen, &buffer[1] ); - PutUns24BE ( 0, &buffer[4] ); - buffer[7] = 0; - PutUns24BE ( 0, &buffer[8] ); - - // Fill in the "onXMPData" name, ECMA array start, and "liveXML" name. - - buffer[11] = 2; - PutUns16BE ( 9, &buffer[12] ); - memcpy ( &buffer[14], "onXMPData", 9 ); // AUDIT: Safe, buffer has 64 chars. - buffer[23] = 8; - PutUns32BE ( 1, &buffer[24] ); - PutUns16BE ( 7, &buffer[28] ); - memcpy ( &buffer[30], "liveXML", 7 ); // AUDIT: Safe, buffer has 64 chars. - - // Fill in the XMP packet string type and length, write what we have so far. - - LFA_Seek ( fileRef, 0, SEEK_END ); - if ( ! longXMP ) { - buffer[37] = 2; - PutUns16BE ( (XMP_Uns16)xmpPacket.size()+1, &buffer[38] ); - LFA_Write ( fileRef, buffer, 40 ); - } else { - buffer[37] = 12; - PutUns32BE ( (XMP_Uns32)xmpPacket.size()+1, &buffer[38] ); - LFA_Write ( fileRef, buffer, 42 ); - } - - // Write the XMP packet, nul terminator, array terminator, and back pointer. - - LFA_Write ( fileRef, xmpPacket.c_str(), (XMP_Int32)xmpPacket.size()+1 ); - PutUns24BE ( 9, &buffer[0] ); - PutUns32BE ( tagLen+11, &buffer[3] ); - LFA_Write ( fileRef, buffer, 7 ); - -} // WriteOnXMP - -// ================================================================================================= -// FLV_MetaHandler::WriteFile -// ========================== -// -// Use a source (old) file and the current XMP to build a destination (new) file. All of the source -// file is copied except for previous XMP. The current XMP is inserted after onMetaData, or at least -// before the first time 0 audio or video tag. - -// ! We do not currently update anything in onMetaData. - -void FLV_MetaHandler::WriteFile ( LFA_FileRef sourceRef, const std::string & sourcePath ) -{ - if ( ! this->needsUpdate ) return; - - XMP_AbortProc abortProc = this->parent->abortProc; - void * abortArg = this->parent->abortArg; - const bool checkAbort = (abortProc != 0); - - LFA_FileRef destRef = this->parent->fileRef; - - XMP_Uns64 sourceLen = LFA_Measure ( sourceRef ); - XMP_Uns64 sourcePos = 0; - - LFA_Seek ( sourceRef, 0, SEEK_SET ); - LFA_Seek ( destRef, 0, SEEK_SET ); - LFA_Truncate ( destRef, 0 ); - - // First do whatever is needed to put the new XMP after any existing onMetaData tag, or as the - // first time 0 tag. - - if ( this->omdTagPos == 0 ) { - - // There is no onMetaData tag. Copy the file header, then write the new XMP as the first tag. - // Allow the degenerate case of a file with just a header, no initial back pointer or tags. - - LFA_Copy ( sourceRef, destRef, this->flvHeaderLen, abortProc, abortArg ); - - XMP_Uns32 zero = 0; // Don't require the initial 0 back pointer to be in the source file. - LFA_Write ( destRef, &zero, 4 ); - - sourcePos = this->flvHeaderLen + 4; - - WriteOnXMP ( destRef, this->xmpPacket ); - - } else { - - // There is an onMetaData tag. Copy the front of the file through the onMetaData tag, - // skipping any XMP that happens to be in the way. The XMP should not be before onMetaData, - // but let's be robust. Write the new XMP immediately after onMetaData, at the same time. - - XMP_Uns64 omdEnd = this->omdTagPos + this->omdTagLen; - - if ( (this->xmpTagPos != 0) && (this->xmpTagPos < this->omdTagPos) ) { - LFA_Copy ( sourceRef, destRef, this->xmpTagPos, abortProc, abortArg ); - sourcePos = this->xmpTagPos + this->xmpTagLen; - LFA_Seek ( sourceRef, sourcePos, SEEK_SET ); - } - - LFA_Copy ( sourceRef, destRef, (omdEnd - sourcePos), abortProc, abortArg ); - sourcePos = omdEnd; - - WriteOnXMP ( destRef, this->xmpPacket ); - - } - - // Copy the rest of the file, skipping any XMP that is in the way. - - if ( (this->xmpTagPos != 0) && (this->xmpTagPos >= sourcePos) ) { - LFA_Copy ( sourceRef, destRef, (this->xmpTagPos - sourcePos), abortProc, abortArg ); - sourcePos = this->xmpTagPos + this->xmpTagLen; - LFA_Seek ( sourceRef, sourcePos, SEEK_SET ); - } - - LFA_Copy ( sourceRef, destRef, (sourceLen - sourcePos), abortProc, abortArg ); - - this->needsUpdate = false; - -} // FLV_MetaHandler::WriteFile - -// ================================================================================================= diff --git a/source/XMPFiles/FileHandlers/FLV_Handler.hpp b/source/XMPFiles/FileHandlers/FLV_Handler.hpp deleted file mode 100644 index 80ed1a0..0000000 --- a/source/XMPFiles/FileHandlers/FLV_Handler.hpp +++ /dev/null @@ -1,73 +0,0 @@ -#ifndef __FLV_Handler_hpp__ -#define __FLV_Handler_hpp__ 1 - -// ================================================================================================= -// ADOBE SYSTEMS INCORPORATED -// Copyright 2007 Adobe Systems Incorporated -// 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 "XMPFiles_Impl.hpp" - -// ================================================================================================ -/// \file FLV_Handler.hpp -/// \brief File format handler for FLV. -/// -/// This header ... -/// -// ================================================================================================ - -extern XMPFileHandler * FLV_MetaHandlerCTor ( XMPFiles * parent ); - -extern bool FLV_CheckFormat ( XMP_FileFormat format, - XMP_StringPtr filePath, - LFA_FileRef fileRef, - XMPFiles * parent ); - -static const XMP_OptionBits kFLV_HandlerFlags = ( kXMPFiles_CanInjectXMP | - kXMPFiles_CanExpand | - kXMPFiles_CanRewrite | - kXMPFiles_PrefersInPlace | - kXMPFiles_CanReconcile | - kXMPFiles_AllowsOnlyXMP | - kXMPFiles_ReturnsRawPacket | - kXMPFiles_AllowsSafeUpdate - ); - -class FLV_MetaHandler : public XMPFileHandler -{ -public: - - void CacheFileData(); - void ProcessXMP(); - - void UpdateFile ( bool doSafeUpdate ); - void WriteFile ( LFA_FileRef sourceRef, const std::string & sourcePath ); - - FLV_MetaHandler ( XMPFiles * _parent ); - virtual ~FLV_MetaHandler(); - -private: - - FLV_MetaHandler() : flvHeaderLen(0), longXMP(false), - xmpTagPos(0), omdTagPos(0), xmpTagLen(0), omdTagLen(0) {}; // Hidden on purpose. - - void ExtractLiveXML(); - void MakeLegacyDigest ( std::string * digestStr ); - - XMP_Uns32 flvHeaderLen; - bool longXMP; // True if the stored XMP is a long string (4 byte length). - - XMP_Uns64 xmpTagPos, omdTagPos; // The file offset and length of onXMP and onMetaData tags. - XMP_Uns32 xmpTagLen, omdTagLen; // Zero if the tag is not present. - - std::string onXMP, onMetaData; // ! Actually contains structured binary data. - -}; // FLV_MetaHandler - -// ================================================================================================= - -#endif // __FLV_Handler_hpp__ diff --git a/source/XMPFiles/FileHandlers/InDesign_Handler.cpp b/source/XMPFiles/FileHandlers/InDesign_Handler.cpp deleted file mode 100644 index 2f9845e..0000000 --- a/source/XMPFiles/FileHandlers/InDesign_Handler.cpp +++ /dev/null @@ -1,423 +0,0 @@ -// ================================================================================================= -// ADOBE SYSTEMS INCORPORATED -// Copyright 2004 Adobe Systems Incorporated -// 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 "InDesign_Handler.hpp" - -using namespace std; - -// ================================================================================================= -/// \file InDesign_Handler.cpp -/// \brief File format handler for InDesign files. -/// -/// This header ... -/// -/// The layout of an InDesign file in terms of the Basic_MetaHandler model is: -/// -/// \li The front of the file. This is everything up to the XMP contiguous object section. The file -/// starts with a pair of master pages, followed by the data pages, followed by contiguous object -/// sections, finished with padding to a page boundary. -/// -/// \li A prefix for the XMP section. This is the contiguous object header. The offset is -/// (this->packetInfo.offset - this->xmpPrefixSize). -/// -/// \li The XMP packet. The offset is this->packetInfo.offset. -/// -/// \li A suffix for the XMP section. This is the contiguous object header. The offset is -/// (this->packetInfo.offset + this->packetInfo.length). -/// -/// \li Trailing file content. This is the contiguous objects that follow the XMP. The offset is -/// (this->packetInfo.offset + this->packetInfo.length + this->xmpSuffixSize). -/// -/// \li The back of the file. This is the final padding to a page boundary. The offset is -/// (this->packetInfo.offset + this->packetInfo.length + this->xmpSuffixSize + this->trailingContentSize). -/// -// ================================================================================================= - -// *** Add PutXMP overrides that throw if the file does not contain XMP. - -#ifndef TraceInDesignHandler - #define TraceInDesignHandler 0 -#endif - -enum { kInDesignGUIDSize = 16 }; - -struct InDesignMasterPage { - XMP_Uns8 fGUID [kInDesignGUIDSize]; - XMP_Uns8 fMagicBytes [8]; - XMP_Uns8 fObjectStreamEndian; - XMP_Uns8 fIrrelevant1 [239]; - XMP_Uns64 fSequenceNumber; - XMP_Uns8 fIrrelevant2 [8]; - XMP_Uns32 fFilePages; - XMP_Uns8 fIrrelevant3 [3812]; -}; - -enum { - kINDD_PageSize = 4096, - kINDD_PageMask = (kINDD_PageSize - 1), - kINDD_LittleEndian = 1, - kINDD_BigEndian = 2 }; - -struct InDesignContigObjMarker { - XMP_Uns8 fGUID [kInDesignGUIDSize]; - XMP_Uns32 fObjectUID; - XMP_Uns32 fObjectClassID; - XMP_Uns32 fStreamLength; - XMP_Uns32 fChecksum; -}; - -static const XMP_Uns8 * kINDD_MasterPageGUID = - (const XMP_Uns8 *) "\x06\x06\xED\xF5\xD8\x1D\x46\xE5\xBD\x31\xEF\xE7\xFE\x74\xB7\x1D"; - -static const XMP_Uns8 * kINDDContigObjHeaderGUID = - (const XMP_Uns8 *) "\xDE\x39\x39\x79\x51\x88\x4B\x6C\x8E\x63\xEE\xF8\xAE\xE0\xDD\x38"; - -static const XMP_Uns8 * kINDDContigObjTrailerGUID = - (const XMP_Uns8 *) "\xFD\xCE\xDB\x70\xF7\x86\x4B\x4F\xA4\xD3\xC7\x28\xB3\x41\x71\x06"; - -// ================================================================================================= -// InDesign_MetaHandlerCTor -// ======================== - -XMPFileHandler * InDesign_MetaHandlerCTor ( XMPFiles * parent ) -{ - return new InDesign_MetaHandler ( parent ); - -} // InDesign_MetaHandlerCTor - -// ================================================================================================= -// InDesign_CheckFormat -// ==================== -// -// For InDesign we check that the pair of master pages begin with the 16 byte GUID. - -bool InDesign_CheckFormat ( XMP_FileFormat format, - XMP_StringPtr filePath, - LFA_FileRef fileRef, - XMPFiles * parent ) -{ - IgnoreParam(format); IgnoreParam(filePath); IgnoreParam(parent); - XMP_Assert ( format == kXMP_InDesignFile ); - XMP_Assert ( strlen ( (const char *) kINDD_MasterPageGUID ) == kInDesignGUIDSize ); - - enum { kBufferSize = 2*kINDD_PageSize }; - XMP_Uns8 buffer [kBufferSize]; - - XMP_Int64 filePos = 0; - XMP_Uns8 * bufPtr = buffer; - XMP_Uns8 * bufLimit = bufPtr + kBufferSize; - - LFA_Seek ( fileRef, 0, SEEK_SET ); - size_t bufLen = LFA_Read ( fileRef, buffer, kBufferSize ); - if ( bufLen != kBufferSize ) return false; - - if ( ! CheckBytes ( bufPtr, kINDD_MasterPageGUID, kInDesignGUIDSize ) ) return false; - if ( ! CheckBytes ( bufPtr+kINDD_PageSize, kINDD_MasterPageGUID, kInDesignGUIDSize ) ) return false; - - return true; - -} // InDesign_CheckFormat - -// ================================================================================================= -// InDesign_MetaHandler::InDesign_MetaHandler -// ========================================== - -InDesign_MetaHandler::InDesign_MetaHandler ( XMPFiles * _parent ) : streamBigEndian(0), xmpObjID(0), xmpClassID(0) -{ - this->parent = _parent; - this->handlerFlags = kInDesign_HandlerFlags; - this->stdCharForm = kXMP_Char8Bit; - -} // InDesign_MetaHandler::InDesign_MetaHandler - -// ================================================================================================= -// InDesign_MetaHandler::~InDesign_MetaHandler -// =========================================== - -InDesign_MetaHandler::~InDesign_MetaHandler() -{ - // Nothing to do here. - -} // InDesign_MetaHandler::~InDesign_MetaHandler - -// ================================================================================================= -// InDesign_MetaHandler::CacheFileData -// =================================== -// -// Look for the XMP in an InDesign database file. This is a paged database using 4K byte pages, -// followed by redundant "contiguous object streams". Each contiguous object stream is a copy of a -// database object stored as a contiguous byte stream. The XMP that we want is one of these. -// -// The first 2 pages of the database are alternating master pages. A generation number is used to -// select the active master page. The master page contains an offset to the start of the contiguous -// object streams. Each of the contiguous object streams contains a header and trailer, allowing -// fast motion from one stream to the next. -// -// There is no unique "what am I" tagging to the contiguous object streams, so we simply pick the -// first one that looks right. At present this is a 4 byte little endian packet size followed by the -// packet. - -// ! Note that insertion of XMP is not allowed for InDesign, the XMP must be a contiguous copy of an -// ! internal database object. So we don't set the packet offset to an insertion point if not found. - -void InDesign_MetaHandler::CacheFileData() -{ - LFA_FileRef fileRef = this->parent->fileRef; - XMP_PacketInfo & packetInfo = this->packetInfo; - - IOBuffer ioBuf; - size_t dbPages; - XMP_Uns8 cobjEndian; - - XMP_AbortProc abortProc = this->parent->abortProc; - void * abortArg = this->parent->abortArg; - const bool checkAbort = (abortProc != 0); - - XMP_Assert ( kINDD_PageSize == sizeof(InDesignMasterPage) ); - XMP_Assert ( kIOBufferSize >= (2 * kINDD_PageSize) ); - - this->containsXMP = false; - - // --------------------------------------------------------------------------------- - // Figure out which master page is active and seek to the contiguous object portion. - - { - ioBuf.filePos = 0; - ioBuf.ptr = ioBuf.limit; // Make sure RefillBuffer does a simple read. - LFA_Seek ( fileRef, ioBuf.filePos, SEEK_SET ); - RefillBuffer ( fileRef, &ioBuf ); - if ( ioBuf.len < (2 * kINDD_PageSize) ) XMP_Throw ( "GetMainPacket/ScanInDesignFile: Read failure", kXMPErr_ExternalFailure ); - - InDesignMasterPage * masters = (InDesignMasterPage *) ioBuf.ptr; - XMP_Uns64 seq0 = GetUns64LE ( (XMP_Uns8 *) &masters[0].fSequenceNumber ); - XMP_Uns64 seq1 = GetUns64LE ( (XMP_Uns8 *) &masters[1].fSequenceNumber ); - - dbPages = GetUns32LE ( (XMP_Uns8 *) &masters[0].fFilePages ); - cobjEndian = masters[0].fObjectStreamEndian; - if ( seq1 > seq0 ) { - dbPages = GetUns32LE ( (XMP_Uns8 *) &masters[1].fFilePages ); - cobjEndian = masters[1].fObjectStreamEndian; - } - } - - XMP_Assert ( ! this->streamBigEndian ); - if ( cobjEndian == kINDD_BigEndian ) this->streamBigEndian = true; - - // --------------------------------------------------------------------------------------------- - // Look for the XMP contiguous object stream. Most of the time there will be just one stream and - // it will be the XMP. So we might as well fill the whole buffer and not worry about reading too - // much and seeking back to the start of the following stream. - - XMP_Int64 cobjPos = (XMP_Int64)dbPages * kINDD_PageSize; // ! Use a 64 bit multiply! - cobjPos -= (2 * sizeof(InDesignContigObjMarker)); // ! For the first pass in the loop. - XMP_Uns32 streamLength = 0; // ! For the first pass in the loop. - - while ( true ) { - - if ( checkAbort && abortProc(abortArg) ) { - XMP_Throw ( "InDesign_MetaHandler::LocateXMP - User abort", kXMPErr_UserAbort ); - } - - // Fetch the start of the next stream and check the contiguous object header. - // ! The writeable bit of fObjectClassID is ignored, we use the packet trailer flag. - - cobjPos += streamLength + (2 * sizeof(InDesignContigObjMarker)); - ioBuf.filePos = cobjPos; - ioBuf.ptr = ioBuf.limit; // Make sure RefillBuffer does a simple read. - LFA_Seek ( fileRef, ioBuf.filePos, SEEK_SET ); - RefillBuffer ( fileRef, &ioBuf ); - if ( ioBuf.len < (2 * sizeof(InDesignContigObjMarker)) ) break; // Too small, must be end of file. - - const InDesignContigObjMarker * cobjHeader = (const InDesignContigObjMarker *) ioBuf.ptr; - if ( ! CheckBytes ( Uns8Ptr(&cobjHeader->fGUID), kINDDContigObjHeaderGUID, kInDesignGUIDSize ) ) break; // Not a contiguous object header. - this->xmpObjID = cobjHeader->fObjectUID; // Save these now while the buffer is good. - this->xmpClassID = cobjHeader->fObjectClassID; - streamLength = GetUns32LE ( (XMP_Uns8 *) &cobjHeader->fStreamLength ); - ioBuf.ptr += sizeof ( InDesignContigObjMarker ); - - // See if this is the XMP stream. Only check for UTF-8, others get caught in fallback scanning. - - if ( ! CheckFileSpace ( fileRef, &ioBuf, 4 ) ) continue; // Too small, can't possibly be XMP. - - XMP_Uns32 innerLength = GetUns32LE ( ioBuf.ptr ); - if ( this->streamBigEndian ) innerLength = GetUns32BE ( ioBuf.ptr ); - if ( innerLength != (streamLength - 4) ) continue; // Not legit XMP. - ioBuf.ptr += 4; - - if ( ! CheckFileSpace ( fileRef, &ioBuf, kUTF8_PacketHeaderLen ) ) continue; // Too small, can't possibly be XMP. - - if ( ! CheckBytes ( ioBuf.ptr, kUTF8_PacketStart, strlen((char*)kUTF8_PacketStart) ) ) continue; - ioBuf.ptr += strlen((char*)kUTF8_PacketStart); - - XMP_Uns8 quote = *ioBuf.ptr; - if ( (quote != '\'') && (quote != '"') ) continue; - ioBuf.ptr += 1; - if ( *ioBuf.ptr != quote ) { - if ( ! CheckBytes ( ioBuf.ptr, Uns8Ptr("\xEF\xBB\xBF"), 3 ) ) continue; - ioBuf.ptr += 3; - } - if ( *ioBuf.ptr != quote ) continue; - ioBuf.ptr += 1; - - if ( ! CheckBytes ( ioBuf.ptr, Uns8Ptr(" id="), 4 ) ) continue; - ioBuf.ptr += 4; - quote = *ioBuf.ptr; - if ( (quote != '\'') && (quote != '"') ) continue; - ioBuf.ptr += 1; - if ( ! CheckBytes ( ioBuf.ptr, kUTF8_PacketID, strlen((char*)kUTF8_PacketID) ) ) continue; - ioBuf.ptr += strlen((char*)kUTF8_PacketID); - if ( *ioBuf.ptr != quote ) continue; - ioBuf.ptr += 1; - - // We've seen enough, it is the XMP. To fit the Basic_Handler model we need to compute the - // total size of remaining contiguous objects, the trailingContentSize. - - this->xmpPrefixSize = sizeof(InDesignContigObjMarker) + 4; - this->xmpSuffixSize = sizeof(InDesignContigObjMarker); - packetInfo.offset = cobjPos + this->xmpPrefixSize; - packetInfo.length = innerLength; - - - XMP_Int64 tcStart = cobjPos + streamLength + (2 * sizeof(InDesignContigObjMarker)); - while ( true ) { - if ( checkAbort && abortProc(abortArg) ) { - XMP_Throw ( "InDesign_MetaHandler::LocateXMP - User abort", kXMPErr_UserAbort ); - } - cobjPos += streamLength + (2 * sizeof(InDesignContigObjMarker)); - ioBuf.filePos = cobjPos; - ioBuf.ptr = ioBuf.limit; // Make sure RefillBuffer does a simple read. - LFA_Seek ( fileRef, ioBuf.filePos, SEEK_SET ); - RefillBuffer ( fileRef, &ioBuf ); - if ( ioBuf.len < sizeof(InDesignContigObjMarker) ) break; // Too small, must be end of file. - cobjHeader = (const InDesignContigObjMarker *) ioBuf.ptr; - if ( ! CheckBytes ( Uns8Ptr(&cobjHeader->fGUID), kINDDContigObjHeaderGUID, kInDesignGUIDSize ) ) break; // Not a contiguous object header. - streamLength = GetUns32LE ( (XMP_Uns8 *) &cobjHeader->fStreamLength ); - } - this->trailingContentSize = cobjPos - tcStart; - - #if TraceInDesignHandler - XMP_Uns32 pktOffset = (XMP_Uns32)this->packetInfo.offset; - printf ( "Found XMP in InDesign file, offsets:\n" ); - printf ( " CObj head %X, XMP %X, CObj tail %X, file tail %X, padding %X\n", - (pktOffset - this->xmpPrefixSize), pktOffset, (pktOffset + this->packetInfo.length), - (pktOffset + this->packetInfo.length + this->xmpSuffixSize), - (pktOffset + this->packetInfo.length + this->xmpSuffixSize + (XMP_Uns32)this->trailingContentSize) ); - #endif - - this->containsXMP = true; - break; - - } - - if ( this->containsXMP ) { - this->xmpFileOffset = packetInfo.offset; - this->xmpFileSize = packetInfo.length; - ReadXMPPacket ( this ); - } - -} // InDesign_MetaHandler::CacheFileData - -// ================================================================================================= -// InDesign_MetaHandler::WriteXMPPrefix -// ==================================== - -void InDesign_MetaHandler::WriteXMPPrefix() -{ - // Write the contiguous object header and the 4 byte length of the XMP packet. - - LFA_FileRef fileRef = this->parent->fileRef; - XMP_Uns32 packetSize = (XMP_Uns32)this->xmpPacket.size(); - - InDesignContigObjMarker header; - memcpy ( header.fGUID, kINDDContigObjHeaderGUID, sizeof(header.fGUID) ); // AUDIT: Use of dest sizeof for length is safe. - header.fObjectUID = this->xmpObjID; - header.fObjectClassID = this->xmpClassID; - header.fStreamLength = MakeUns32LE ( 4 + packetSize ); - header.fChecksum = (XMP_Uns32)(-1); - LFA_Write ( fileRef, &header, sizeof(header) ); - - XMP_Uns32 pktLength = MakeUns32LE ( packetSize ); - if ( this->streamBigEndian ) pktLength = MakeUns32BE ( packetSize ); - LFA_Write ( fileRef, &pktLength, sizeof(pktLength) ); - -} // InDesign_MetaHandler::WriteXMPPrefix - -// ================================================================================================= -// InDesign_MetaHandler::WriteXMPSuffix -// ==================================== - -void InDesign_MetaHandler::WriteXMPSuffix() -{ - // Write the contiguous object trailer. - - LFA_FileRef fileRef = this->parent->fileRef; - XMP_Uns32 packetSize = (XMP_Uns32)this->xmpPacket.size(); - - InDesignContigObjMarker trailer; - - memcpy ( trailer.fGUID, kINDDContigObjTrailerGUID, sizeof(trailer.fGUID) ); // AUDIT: Use of dest sizeof for length is safe. - trailer.fObjectUID = this->xmpObjID; - trailer.fObjectClassID = this->xmpClassID; - trailer.fStreamLength = MakeUns32LE ( 4 + packetSize ); - trailer.fChecksum = (XMP_Uns32)(-1); - - LFA_Write ( fileRef, &trailer, sizeof(trailer) ); - -} // InDesign_MetaHandler::WriteXMPSuffix - -// ================================================================================================= -// InDesign_MetaHandler::NoteXMPRemoval -// ==================================== - -void InDesign_MetaHandler::NoteXMPRemoval() -{ - // Nothing to do. - -} // InDesign_MetaHandler::NoteXMPRemoval - -// ================================================================================================= -// InDesign_MetaHandler::NoteXMPInsertion -// ====================================== - -void InDesign_MetaHandler::NoteXMPInsertion() -{ - // Nothing to do. - -} // InDesign_MetaHandler::NoteXMPInsertion - -// ================================================================================================= -// InDesign_MetaHandler::CaptureFileEnding -// ======================================= - -void InDesign_MetaHandler::CaptureFileEnding() -{ - // Nothing to do. The back of an InDesign file is the final zero padding. - -} // InDesign_MetaHandler::CaptureFileEnding - -// ================================================================================================= -// InDesign_MetaHandler::RestoreFileEnding -// ======================================= - -void InDesign_MetaHandler::RestoreFileEnding() -{ - // Pad the file with zeros to a page boundary. - - LFA_FileRef fileRef = this->parent->fileRef; - - XMP_Int64 dataLength = LFA_Measure ( fileRef ); - XMP_Int32 padLength = (kINDD_PageSize - ((XMP_Int32)dataLength & kINDD_PageMask)) & kINDD_PageMask; - - XMP_Uns8 buffer [kINDD_PageSize]; - memset ( buffer, 0, kINDD_PageSize ); - LFA_Write ( fileRef, buffer, padLength ); - -} // InDesign_MetaHandler::RestoreFileEnding - -// ================================================================================================= diff --git a/source/XMPFiles/FileHandlers/InDesign_Handler.hpp b/source/XMPFiles/FileHandlers/InDesign_Handler.hpp deleted file mode 100644 index aded52e..0000000 --- a/source/XMPFiles/FileHandlers/InDesign_Handler.hpp +++ /dev/null @@ -1,60 +0,0 @@ -#ifndef __InDesign_Handler_hpp__ -#define __InDesign_Handler_hpp__ 1 - -// ================================================================================================= -// ADOBE SYSTEMS INCORPORATED -// Copyright 2004 Adobe Systems Incorporated -// 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 "Basic_Handler.hpp" - -// ================================================================================================= -/// \file InDesign_Handler.hpp -/// \brief File format handler for InDesign files. -/// -/// This header ... -/// -// ================================================================================================= - -extern XMPFileHandler * InDesign_MetaHandlerCTor ( XMPFiles * parent ); - -extern bool InDesign_CheckFormat ( XMP_FileFormat format, - XMP_StringPtr filePath, - LFA_FileRef fileRef, - XMPFiles * parent ); - -static const XMP_OptionBits kInDesign_HandlerFlags = kBasic_HandlerFlags & (~kXMPFiles_CanInjectXMP); // ! InDesign can't inject. - -class InDesign_MetaHandler : public Basic_MetaHandler -{ -public: - - InDesign_MetaHandler ( XMPFiles * parent ); - ~InDesign_MetaHandler(); - - void CacheFileData(); - -protected: - - void WriteXMPPrefix(); - void WriteXMPSuffix(); - - void NoteXMPRemoval(); - void NoteXMPInsertion(); - - void CaptureFileEnding(); - void RestoreFileEnding(); - - bool streamBigEndian; // Set from master page's fObjectStreamEndian. - XMP_Uns32 xmpObjID; // Set from contiguous object's fObjectID, still as little endian. - XMP_Uns32 xmpClassID; // Set from contiguous object's fObjectClassID, still as little endian. - -}; // InDesign_MetaHandler - -// ================================================================================================= - -#endif /* __InDesign_Handler_hpp__ */ diff --git a/source/XMPFiles/FileHandlers/JPEG_Handler.cpp b/source/XMPFiles/FileHandlers/JPEG_Handler.cpp deleted file mode 100644 index a3842f8..0000000 --- a/source/XMPFiles/FileHandlers/JPEG_Handler.cpp +++ /dev/null @@ -1,1027 +0,0 @@ -// ================================================================================================= -// ADOBE SYSTEMS INCORPORATED -// Copyright 2004 Adobe Systems Incorporated -// 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 "JPEG_Handler.hpp" - -#include "TIFF_Support.hpp" -#include "PSIR_Support.hpp" -#include "IPTC_Support.hpp" -#include "ReconcileLegacy.hpp" -#include "Reconcile_Impl.hpp" - -#include "MD5.h" - -using namespace std; - -// ================================================================================================= -/// \file JPEG_Handler.cpp -/// \brief File format handler for JPEG. -/// -/// This handler ... -/// -// ================================================================================================= - -static const char * kExifSignatureString = "Exif\0\x00"; -static const char * kExifSignatureAltStr = "Exif\0\xFF"; -static const size_t kExifSignatureLength = 6; -static const size_t kExifMaxDataLength = 0xFFFF - 2 - kExifSignatureLength; - -static const char * kPSIRSignatureString = "Photoshop 3.0\0"; -static const size_t kPSIRSignatureLength = 14; -static const size_t kPSIRMaxDataLength = 0xFFFF - 2 - kPSIRSignatureLength; - -static const char * kMainXMPSignatureString = "http://ns.adobe.com/xap/1.0/\0"; -static const size_t kMainXMPSignatureLength = 29; - -static const char * kExtXMPSignatureString = "http://ns.adobe.com/xmp/extension/\0"; -static const size_t kExtXMPSignatureLength = 35; -static const size_t kExtXMPPrefixLength = kExtXMPSignatureLength + 32 + 4 + 4; - -typedef std::map < XMP_Uns32 /* offset */, std::string /* portion */ > ExtXMPPortions; - -struct ExtXMPContent { - XMP_Uns32 length; - ExtXMPPortions portions; - ExtXMPContent() : length(0) {}; - ExtXMPContent ( XMP_Uns32 _length ) : length(_length) {}; -}; - -typedef std::map < JPEG_MetaHandler::GUID_32 /* guid */, ExtXMPContent /* content */ > ExtendedXMPInfo; - -#ifndef Trace_UnlimitedJPEG - #define Trace_UnlimitedJPEG 0 -#endif - -// ================================================================================================= -// JPEG_MetaHandlerCTor -// ==================== - -XMPFileHandler * JPEG_MetaHandlerCTor ( XMPFiles * parent ) -{ - return new JPEG_MetaHandler ( parent ); - -} // JPEG_MetaHandlerCTor - -// ================================================================================================= -// JPEG_CheckFormat -// ================ - -// For JPEG we just check for the initial SOI standalone marker followed by any of the other markers -// that might, well, follow it. A more aggressive check might be to read 4KB then check for legit -// marker segments within that portion. Probably won't buy much, and thrashes the dCache more. We -// tolerate only a small amount of 0xFF padding between the SOI and following marker. This formally -// violates the rules of JPEG, but in practice there won't be any padding anyway. -// -// ! The CheckXyzFormat routines don't track the filePos, that is left to ScanXyzFile. - -bool JPEG_CheckFormat ( XMP_FileFormat format, - XMP_StringPtr filePath, - LFA_FileRef fileRef, - XMPFiles * parent ) -{ - IgnoreParam(format); IgnoreParam(filePath); IgnoreParam(parent); - XMP_Assert ( format == kXMP_JPEGFile ); - - IOBuffer ioBuf; - - LFA_Seek ( fileRef, 0, SEEK_SET ); - if ( ! CheckFileSpace ( fileRef, &ioBuf, 4 ) ) return false; // We need at least 4, the buffer is filled anyway. - - // First look for the SOI standalone marker. Then skip all 0xFF bytes, padding plus the high - // order byte of the next marker. Finally see if the next marker is legit. - - if ( ! CheckBytes ( ioBuf.ptr, "\xFF\xD8", 2 ) ) return false; - ioBuf.ptr += 2; // Move past the SOI. - while ( (ioBuf.ptr < ioBuf.limit) && (*ioBuf.ptr == 0xFF) ) ++ioBuf.ptr; - if ( ioBuf.ptr == ioBuf.limit ) return false; - - XMP_Uns8 id = *ioBuf.ptr; - if ( id >= 0xDD ) return true; // The most probable cases. - if ( (id < 0xC0) || ((id & 0xF8) == 0xD0) || (id == 0xD8) || (id == 0xDA) || (id == 0xDC) ) return false; - return true; - -} // JPEG_CheckFormat - -// ================================================================================================= -// JPEG_MetaHandler::JPEG_MetaHandler -// ================================== - -JPEG_MetaHandler::JPEG_MetaHandler ( XMPFiles * _parent ) - : exifMgr(0), psirMgr(0), iptcMgr(0), skipReconcile(false) -{ - this->parent = _parent; - this->handlerFlags = kJPEG_HandlerFlags; - this->stdCharForm = kXMP_Char8Bit; - -} // JPEG_MetaHandler::JPEG_MetaHandler - -// ================================================================================================= -// JPEG_MetaHandler::~JPEG_MetaHandler -// =================================== - -JPEG_MetaHandler::~JPEG_MetaHandler() -{ - - if ( exifMgr != 0 ) delete ( exifMgr ); - if ( psirMgr != 0 ) delete ( psirMgr ); - if ( iptcMgr != 0 ) delete ( iptcMgr ); - -} // JPEG_MetaHandler::~JPEG_MetaHandler - -// ================================================================================================= -// JPEG_MetaHandler::CacheFileData -// =============================== -// -// Look for the Exif metadata, Photoshop image resources, and XMP in a JPEG (JFIF) file. The native -// thumbnail is inside the Exif. The general layout of a JPEG file is: -// SOI marker, 2 bytes, 0xFFD8 -// Marker segments for tables and metadata -// SOFn marker segment -// Image data -// EOI marker, 2 bytes, 0xFFD9 -// -// Each marker segment begins with a 2 byte big endian marker and a 2 byte big endian length. The -// length includes the 2 bytes of the length field but not the marker. The high order byte of a -// marker is 0xFF, the low order byte tells what kind of marker. A marker can be preceeded by any -// number of 0xFF fill bytes, however there are no alignment constraints. -// -// There are virtually no constraints on the order of the marker segments before the SOFn. A reader -// must be prepared to handle any order. -// -// The Exif metadata is in an APP1 marker segment with a 6 byte signature string of "Exif\0\0" at -// the start of the data. The rest of the data is a TIFF stream. -// -// The Photoshop image resources are in an APP13 marker segment with a 14 byte signature string of -// "Photoshop 3.0\0". The rest of the data is a sequence of image resources. -// -// The main XMP is in an APP1 marker segment with a 29 byte signature string of -// "http://ns.adobe.com/xap/1.0/\0". The rest of the data is the serialized XMP packet. This is the -// only XMP if everything fits within the 64KB limit for marker segment data. If not, there will be -// a series of XMP extension segments. -// -// Each XMP extension segment is an APP1 marker segment whose data contains: -// - A 35 byte signature string of "http://ns.adobe.com/xmp/extension/\0". -// - A 128 bit GUID stored as 32 ASCII hex digits, capital A-F, no nul termination. -// - A 32 bit unsigned integer length for the full extended XMP serialization. -// - A 32 bit unsigned integer offset for this portion of the extended XMP serialization. -// - A portion of the extended XMP serialization, up to about 65400 bytes (at most 65458). -// -// A reader must be prepared to encounter the extended XMP portions out of order. Also to encounter -// defective files that have differing extended XMP according to the GUID. The main XMP contains the -// GUID for the associated extended XMP. - -// *** This implementation simply returns when invalid JPEG is encountered. Should we throw instead? - -void JPEG_MetaHandler::CacheFileData() -{ - LFA_FileRef fileRef = this->parent->fileRef; - XMP_PacketInfo & packetInfo = this->packetInfo; - - size_t segLen; - bool ok; - IOBuffer ioBuf; - - XMP_AbortProc abortProc = this->parent->abortProc; - void * abortArg = this->parent->abortArg; - const bool checkAbort = (abortProc != 0); - - ExtendedXMPInfo extXMP; - - XMP_Assert ( ! this->containsXMP ); - // Set containsXMP to true here only if the standard XMP packet is found. - - XMP_Assert ( kPSIRSignatureLength == (strlen(kPSIRSignatureString) + 1) ); - XMP_Assert ( kMainXMPSignatureLength == (strlen(kMainXMPSignatureString) + 1) ); - XMP_Assert ( kExtXMPSignatureLength == (strlen(kExtXMPSignatureString) + 1) ); - - // ------------------------------------------------------------------------------------------- - // Look for any of the Exif, PSIR, main XMP, or extended XMP marker segments. Quit when we hit - // an SOFn, EOI, or invalid/unexpected marker. - - LFA_Seek ( fileRef, 2, SEEK_SET ); // Skip the SOI. The JPEG header has already been verified. - ioBuf.filePos = 2; - RefillBuffer ( fileRef, &ioBuf ); - - while ( true ) { - - if ( checkAbort && abortProc(abortArg) ) { - XMP_Throw ( "JPEG_MetaHandler::CacheFileData - User abort", kXMPErr_UserAbort ); - } - - if ( ! CheckFileSpace ( fileRef, &ioBuf, 2 ) ) return; - - if ( *ioBuf.ptr != 0xFF ) return; // All valid markers have a high byte of 0xFF. - while ( *ioBuf.ptr == 0xFF ) { // Skip padding 0xFF bytes and the marker's high byte. - ++ioBuf.ptr; - if ( ! CheckFileSpace ( fileRef, &ioBuf, 1 ) ) return; - } - - XMP_Uns16 marker = 0xFF00 + *ioBuf.ptr; - - if ( (marker == 0xFFDA) || (marker == 0xFFD9) ) break; // Quit reading at the first SOS marker or at EOI. - - if ( (marker == 0xFF01) || // Ill-formed file if we encounter a TEM or RSTn marker. - ((0xFFD0 <= marker) && (marker <= 0xFFD7)) ) return; - - if ( marker == 0xFFED ) { - - // This is an APP13 marker, is it the Photoshop image resources? - - ++ioBuf.ptr; // Move ioBuf.ptr to the marker segment length field. - if ( ! CheckFileSpace ( fileRef, &ioBuf, 2 ) ) return; - - segLen = GetUns16BE ( ioBuf.ptr ); - if ( segLen < 2 ) return; // Invalid JPEG. - - ioBuf.ptr += 2; // Move ioBuf.ptr to the marker segment content. - segLen -= 2; // Adjust segLen to count just the content portion. - - ok = CheckFileSpace ( fileRef, &ioBuf, kPSIRSignatureLength ); - if ( ok && (segLen >= kPSIRSignatureLength) && - CheckBytes ( ioBuf.ptr, kPSIRSignatureString, kPSIRSignatureLength ) ) { - - // This is the Photoshop image resources, cache the contents. - - ioBuf.ptr += kPSIRSignatureLength; // Move ioBuf.ptr to the image resources. - segLen -= kPSIRSignatureLength; // Adjust segLen to count just the image resources. - ok = CheckFileSpace ( fileRef, &ioBuf, segLen ); // Buffer the full content portion. - if ( ! ok ) return; // Must be a truncated file. - - this->psirContents.assign ( (XMP_StringPtr)ioBuf.ptr, segLen ); - ioBuf.ptr += segLen; - - } else { - - // This is the not Photoshop image resources, skip the marker segment's content. - - if ( segLen <= size_t(ioBuf.limit - ioBuf.ptr) ) { - ioBuf.ptr += segLen; // The next marker is in this buffer. - } else { - // The next marker is beyond this buffer, RefillBuffer assumes we're doing sequential reads. - size_t skipCount = segLen - (ioBuf.limit - ioBuf.ptr); // The amount to move beyond this buffer. - ioBuf.filePos = LFA_Seek ( fileRef, skipCount, SEEK_CUR ); - ioBuf.ptr = ioBuf.limit; // No data left in the buffer. - } - - } - - continue; // Move on to the next marker. - - } else if ( marker == 0xFFE1 ) { - - // This is an APP1 marker, is it the Exif, main XMP, or extended XMP? - // ! Check in that order, which happens to be increasing signature string length. - - ++ioBuf.ptr; // Move ioBuf.ptr to the marker segment length field. - if ( ! CheckFileSpace ( fileRef, &ioBuf, 2 ) ) return; - - segLen = GetUns16BE ( ioBuf.ptr ); - if ( segLen < 2 ) return; // Invalid JPEG. - - ioBuf.ptr += 2; // Move ioBuf.ptr to the marker segment content. - segLen -= 2; // Adjust segLen to count just the content portion. - - // Check for the Exif APP1 marker segment. - - ok = CheckFileSpace ( fileRef, &ioBuf, kExifSignatureLength ); - if ( ok && (segLen >= kExifSignatureLength) && - (CheckBytes ( ioBuf.ptr, kExifSignatureString, kExifSignatureLength ) || - CheckBytes ( ioBuf.ptr, kExifSignatureAltStr, kExifSignatureLength )) ) { - - // This is the Exif metadata, cache the contents. - - ioBuf.ptr += kExifSignatureLength; // Move ioBuf.ptr to the TIFF stream. - segLen -= kExifSignatureLength; // Adjust segLen to count just the TIFF stream. - ok = CheckFileSpace ( fileRef, &ioBuf, segLen ); // Buffer the full content portion. - if ( ! ok ) return; // Must be a truncated file. - - this->exifContents.assign ( (XMP_StringPtr)ioBuf.ptr, segLen ); - ioBuf.ptr += segLen; - - continue; // Move on to the next marker. - - } - - // Check for the main XMP APP1 marker segment. - - ok = CheckFileSpace ( fileRef, &ioBuf, kMainXMPSignatureLength ); - if ( ok && (segLen >= kMainXMPSignatureLength) && - CheckBytes ( ioBuf.ptr, kMainXMPSignatureString, kMainXMPSignatureLength ) ) { - - // This is the main XMP, cache the contents. - - ioBuf.ptr += kMainXMPSignatureLength; // Move ioBuf.ptr to the XMP Packet. - segLen -= kMainXMPSignatureLength; // Adjust segLen to count just the XMP Packet. - ok = CheckFileSpace ( fileRef, &ioBuf, segLen ); // Buffer the full content portion. - if ( ! ok ) return; // Must be a truncated file. - - this->packetInfo.offset = ioBuf.filePos + (ioBuf.ptr - &ioBuf.data[0]); - this->packetInfo.length = (XMP_Int32)segLen; - this->packetInfo.padSize = 0; // Assume for now, set these properly in ProcessXMP. - this->packetInfo.charForm = kXMP_CharUnknown; - this->packetInfo.writeable = true; - - this->xmpPacket.assign ( (XMP_StringPtr)ioBuf.ptr, segLen ); - ioBuf.ptr += segLen; // ! Set this->packetInfo.offset first! - - this->containsXMP = true; // Found the standard XMP packet. - continue; // Move on to the next marker. - - } - - // Check for an extension XMP APP1 marker segment. - - ok = CheckFileSpace ( fileRef, &ioBuf, kExtXMPPrefixLength ); // ! The signature, GUID, length, and offset. - if ( ok && (segLen >= kExtXMPPrefixLength) && - CheckBytes ( ioBuf.ptr, kExtXMPSignatureString, kExtXMPSignatureLength ) ) { - - // This is a portion of the extended XMP, cache the contents. This is complicated by - // the need to tolerate files where the extension portions are not in order. The - // local ExtendedXMPInfo map uses the GUID as the key and maps that to a struct that - // has the full length and a map of the known portions. This known portion map uses - // the offset of the portion as the key and maps that to a string. Only fully seen - // extended XMP streams are kept, the right one gets picked in ProcessXMP. - - segLen -= kExtXMPPrefixLength; // Adjust segLen to count just the XMP stream portion. - - ioBuf.ptr += kExtXMPSignatureLength; // Move ioBuf.ptr to the GUID. - GUID_32 guid; - XMP_Assert ( sizeof(guid.data) == 32 ); - memcpy ( &guid.data[0], ioBuf.ptr, sizeof(guid.data) ); // AUDIT: Use of sizeof(guid.data) is safe. - - ioBuf.ptr += 32; // Move ioBuf.ptr to the length and offset. - XMP_Uns32 fullLen = GetUns32BE ( ioBuf.ptr ); - XMP_Uns32 offset = GetUns32BE ( ioBuf.ptr+4 ); - - ioBuf.ptr += 8; // Move ioBuf.ptr to the XMP stream portion. - - #if Trace_UnlimitedJPEG - printf ( "New extended XMP portion: fullLen %d, offset %d, GUID %.32s\n", fullLen, offset, guid.data ); - #endif - - // Find the ExtXMPContent for this GUID, and the string for this portion's offset. - - ExtendedXMPInfo::iterator guidPos = extXMP.find ( guid ); - if ( guidPos == extXMP.end() ) { - ExtXMPContent newExtContent ( fullLen ); - guidPos = extXMP.insert ( extXMP.begin(), ExtendedXMPInfo::value_type ( guid, newExtContent ) ); - } - - ExtXMPPortions::iterator offsetPos; - ExtXMPContent & extContent = guidPos->second; - - if ( extContent.portions.empty() ) { - // When new create a full size offset 0 string, to which all in-order portions will get appended. - offsetPos = extContent.portions.insert ( extContent.portions.begin(), - ExtXMPPortions::value_type ( 0, std::string() ) ); - offsetPos->second.reserve ( extContent.length ); - } - - // Try to append this portion to a logically contiguous preceeding one. - - if ( offset == 0 ) { - offsetPos = extContent.portions.begin(); - XMP_Assert ( (offsetPos->first == 0) && (offsetPos->second.size() == 0) ); - } else { - offsetPos = extContent.portions.lower_bound ( offset ); - --offsetPos; // Back up to the portion whose offset is less than the new offset. - if ( (offsetPos->first + offsetPos->second.size()) != offset ) { - // Can't append, create a new portion. - offsetPos = extContent.portions.insert ( extContent.portions.begin(), - ExtXMPPortions::value_type ( offset, std::string() ) ); - } - } - - // Cache this portion of the extended XMP. - - std::string & extPortion = offsetPos->second; - ok = CheckFileSpace ( fileRef, &ioBuf, segLen ); // Buffer the full content portion. - if ( ! ok ) return; // Must be a truncated file. - extPortion.append ( (XMP_StringPtr)ioBuf.ptr, segLen ); - ioBuf.ptr += segLen; - - continue; // Move on to the next marker. - - } - - // If we get here this is some other uninteresting APP1 marker segment, skip it. - - if ( segLen <= size_t(ioBuf.limit - ioBuf.ptr) ) { - ioBuf.ptr += segLen; // The next marker is in this buffer. - } else { - // The next marker is beyond this buffer, RefillBuffer assumes we're doing sequential reads. - size_t skipCount = segLen - (ioBuf.limit - ioBuf.ptr); // The amount to move beyond this buffer. - ioBuf.filePos = LFA_Seek ( fileRef, skipCount, SEEK_CUR ); - ioBuf.ptr = ioBuf.limit; // No data left in the buffer. - } - - } else { - - // This is a non-terminating but uninteresting marker segment. Skip it. - - ++ioBuf.ptr; // Move ioBuf.ptr to the marker segment length field. - if ( ! CheckFileSpace ( fileRef, &ioBuf, 2 ) ) return; - - segLen = GetUns16BE ( ioBuf.ptr ); // Remember that the length includes itself. - if ( segLen < 2 ) return; // Invalid JPEG. - - if ( segLen <= size_t(ioBuf.limit - ioBuf.ptr) ) { - ioBuf.ptr += segLen; // The next marker is in this buffer. - } else { - // The next marker is beyond this buffer, RefillBuffer assumes we're doing sequential reads. - size_t skipCount = segLen - (ioBuf.limit - ioBuf.ptr); // The amount to move beyond this buffer. - ioBuf.filePos = LFA_Seek ( fileRef, skipCount, SEEK_CUR ); - ioBuf.ptr = ioBuf.limit; // No data left in the buffer. - } - - continue; // Move on to the next marker. - - } - - } - - if ( ! extXMP.empty() ) { - - // We have extended XMP. Find out which ones are complete, collapse them into a single - // string, and save them for ProcessXMP. - - ExtendedXMPInfo::iterator guidPos = extXMP.begin(); - ExtendedXMPInfo::iterator guidEnd = extXMP.end(); - - for ( ; guidPos != guidEnd; ++guidPos ) { - - ExtXMPContent & thisContent = guidPos->second; - ExtXMPPortions::iterator partZero = thisContent.portions.begin(); - ExtXMPPortions::iterator partEnd = thisContent.portions.end(); - ExtXMPPortions::iterator partPos = partZero; - - #if Trace_UnlimitedJPEG - printf ( "Extended XMP portions for GUID %.32s, full length %d\n", - guidPos->first.data, guidPos->second.length ); - printf ( " Offset %d, length %d, next offset %d\n", - partZero->first, partZero->second.size(), (partZero->first + partZero->second.size()) ); - #endif - - for ( ++partPos; partPos != partEnd; ++partPos ) { - #if Trace_UnlimitedJPEG - printf ( " Offset %d, length %d, next offset %d\n", - partPos->first, partPos->second.size(), (partPos->first + partPos->second.size()) ); - #endif - if ( partPos->first != partZero->second.size() ) break; // Quit if not contiguous. - partZero->second.append ( partPos->second ); - } - - if ( (partPos == partEnd) && (partZero->first == 0) && (partZero->second.size() == thisContent.length) ) { - // This is a complete extended XMP stream. - this->extendedXMP.insert ( ExtendedXMPMap::value_type ( guidPos->first, partZero->second ) ); - #if Trace_UnlimitedJPEG - printf ( "Full extended XMP for GUID %.32s, full length %d\n", - guidPos->first.data, partZero->second.size() ); - #endif - } - - } - - } - -} // JPEG_MetaHandler::CacheFileData - -// ================================================================================================= -// TrimFullExifAPP1 -// ================ -// -// Try to trim trailing padding from full Exif APP1 segment written by some Nikon cameras. Do a -// temporary read-only parse of the Exif APP1 contents, determine the highest used offset, trim the -// padding if all zero bytes. - -static const char * IFDNames[] = { "Primary", "TNail", "Exif", "GPS", "Interop", }; - -static void TrimFullExifAPP1 ( std::string * exifContents ) -{ - TIFF_MemoryReader tempMgr; - TIFF_MemoryReader::TagInfo tagInfo; - bool tagFound, isNikon; - - // ! Make a copy of the data to parse! The RO memory TIFF manager will flip bytes in-place! - tempMgr.ParseMemoryStream ( exifContents->data(), (XMP_Uns32)exifContents->size(), true /* copy data */ ); - - // Only trim the Exif APP1 from Nikon cameras. - tagFound = tempMgr.GetTag ( kTIFF_PrimaryIFD, kTIFF_Make, &tagInfo ); - isNikon = tagFound && (tagInfo.type == kTIFF_ASCIIType) && (tagInfo.count >= 5) && - (memcmp ( tagInfo.dataPtr, "NIKON", 5) == 0); - if ( ! isNikon ) return; - - // Determine the highest used offset (actually 1 beyond that). Look at the IFD structure, and - // the thumbnail info. Ignore the MakerNote tag, Nikon says they are self-contained. - - XMP_Uns32 maxOffset = 0; - - for ( XMP_Uns8 ifd = 0; ifd < kTIFF_KnownIFDCount; ++ifd ) { - - TIFF_MemoryReader::TagInfoMap tagMap; - bool ifdFound = tempMgr.GetIFD ( ifd, &tagMap ); - if ( ! ifdFound ) continue; - - TIFF_MemoryReader::TagInfoMap::const_iterator mapPos = tagMap.begin(); - TIFF_MemoryReader::TagInfoMap::const_iterator mapEnd = tagMap.end(); - - for ( ; mapPos != mapEnd; ++mapPos ) { - const TIFF_MemoryReader::TagInfo & tagInfo = mapPos->second; - XMP_Uns32 tagEnd = tempMgr.GetValueOffset ( ifd, tagInfo.id ) + tagInfo.dataLen; - if ( tagEnd > maxOffset ) maxOffset = tagEnd; - } - - } - - tagFound = tempMgr.GetTag ( kTIFF_TNailIFD, kTIFF_JPEGInterchangeFormat, &tagInfo ); - if ( tagFound ) { - XMP_Uns32 tnailOffset = tempMgr.GetUns32 ( tagInfo.dataPtr ); - tagFound = tempMgr.GetTag ( kTIFF_TNailIFD, kTIFF_JPEGInterchangeFormatLength, &tagInfo ); - if ( ! tagFound ) return; // Don't trim if there is a TNail offset but no length. - tnailOffset += tempMgr.GetUns32 ( tagInfo.dataPtr ); - if ( tnailOffset > maxOffset ) maxOffset = tnailOffset; - } - - if ( maxOffset >= exifContents->size() ) return; // Sanity check for in bounds maximum offset. - - for ( size_t i = maxOffset, limit = exifContents->size(); i < limit; ++i ) { - if ( (*exifContents)[i] != 0 ) return; // Don't trim if unless the trailer is all zero. - } - - exifContents->erase ( maxOffset ); - -} // TrimFullExifAPP1 - -// ================================================================================================= -// JPEG_MetaHandler::ProcessXMP -// ============================ -// -// Process the raw XMP and legacy metadata that was previously cached. - -void JPEG_MetaHandler::ProcessXMP() -{ - - XMP_Assert ( ! this->processedXMP ); - this->processedXMP = true; // Make sure we only come through here once. - - // Create the PSIR and IPTC handlers, even if there is no legacy. They might be needed for updates. - - XMP_Assert ( (this->psirMgr == 0) && (this->iptcMgr == 0) ); // ProcessTNail might create the exifMgr. - - bool readOnly = ((this->parent->openFlags & kXMPFiles_OpenForUpdate) == 0); - - if ( readOnly ) { - if ( this->exifMgr == 0 ) this->exifMgr = new TIFF_MemoryReader(); - this->psirMgr = new PSIR_MemoryReader(); - this->iptcMgr = new IPTC_Reader(); // ! Parse it later. - } else { - if ( this->exifContents.size() == (65534 - 2 - 6) ) TrimFullExifAPP1 ( &this->exifContents ); - if ( this->exifMgr == 0 ) this->exifMgr = new TIFF_FileWriter(); - this->psirMgr = new PSIR_FileWriter(); - this->iptcMgr = new IPTC_Writer(); // ! Parse it later. - } - - // Set up everything for the legacy import, but don't do it yet. This lets us do a forced legacy - // import if the XMP packet gets parsing errors. - - TIFF_Manager & exif = *this->exifMgr; // Give the compiler help in recognizing non-aliases. - PSIR_Manager & psir = *this->psirMgr; - IPTC_Manager & iptc = *this->iptcMgr; - - bool haveExif = (! this->exifContents.empty()); - if ( haveExif ) { - exif.ParseMemoryStream ( this->exifContents.c_str(), (XMP_Uns32)this->exifContents.size() ); - } - - bool havePSIR = (! this->psirContents.empty()); - if ( havePSIR ) { - psir.ParseMemoryResources ( this->psirContents.c_str(), (XMP_Uns32)this->psirContents.size() ); - } - - PSIR_Manager::ImgRsrcInfo iptcInfo; - bool haveIPTC = false; - if ( havePSIR ) haveIPTC = psir.GetImgRsrc ( kPSIR_IPTC, &iptcInfo );; - int iptcDigestState = kDigestMatches; - - if ( haveIPTC ) { - - bool haveDigest = false; - PSIR_Manager::ImgRsrcInfo digestInfo; - if ( havePSIR ) haveDigest = psir.GetImgRsrc ( kPSIR_IPTCDigest, &digestInfo ); - if ( digestInfo.dataLen != 16 ) haveDigest = false; - - if ( ! haveDigest ) { - iptcDigestState = kDigestMissing; - } else { - iptcDigestState = PhotoDataUtils::CheckIPTCDigest ( iptcInfo.dataPtr, iptcInfo.dataLen, digestInfo.dataPtr ); - } - - } - - XMP_OptionBits options = 0; - if ( this->containsXMP ) options |= k2XMP_FileHadXMP; - if ( haveExif ) options |= k2XMP_FileHadExif; - if ( haveIPTC ) options |= k2XMP_FileHadIPTC; - - // Process the main XMP packet. If it fails to parse, do a forced legacy import but still throw - // an exception. This tells the caller that an error happened, but gives them recovered legacy - // should they want to proceed with that. - - bool haveXMP = false; - - if ( ! this->xmpPacket.empty() ) { - XMP_Assert ( this->containsXMP ); - // Common code takes care of packetInfo.charForm, .padSize, and .writeable. - XMP_StringPtr packetStr = this->xmpPacket.c_str(); - XMP_StringLen packetLen = (XMP_StringLen)this->xmpPacket.size(); - try { - this->xmpObj.ParseFromBuffer ( packetStr, packetLen ); - haveXMP = true; - } catch ( ... ) { - XMP_ClearOption ( options, k2XMP_FileHadXMP ); - if ( haveIPTC ) iptc.ParseMemoryDataSets ( iptcInfo.dataPtr, iptcInfo.dataLen ); - if ( iptcDigestState == kDigestMatches ) iptcDigestState = kDigestMissing; - ImportPhotoData ( exif, iptc, psir, iptcDigestState, &this->xmpObj, options ); - throw; // ! Rethrow the exception, don't absorb it. - } - } - - // Process the extended XMP if it has a matching GUID. - - if ( ! this->extendedXMP.empty() ) { - - bool found; - GUID_32 g32; - std::string extGUID, extPacket; - ExtendedXMPMap::iterator guidPos = this->extendedXMP.end(); - - found = this->xmpObj.GetProperty ( kXMP_NS_XMP_Note, "HasExtendedXMP", &extGUID, 0 ); - if ( found && (extGUID.size() == sizeof(g32.data)) ) { - XMP_Assert ( sizeof(g32.data) == 32 ); - memcpy ( g32.data, extGUID.c_str(), sizeof(g32.data) ); // AUDIT: Use of sizeof(g32.data) is safe. - guidPos = this->extendedXMP.find ( g32 ); - this->xmpObj.DeleteProperty ( kXMP_NS_XMP_Note, "HasExtendedXMP" ); // ! Must only be in the file. - #if Trace_UnlimitedJPEG - printf ( "%s extended XMP for GUID %s\n", - ((guidPos != this->extendedXMP.end()) ? "Found" : "Missing"), extGUID.c_str() ); - #endif - } - - if ( guidPos != this->extendedXMP.end() ) { - try { - XMP_StringPtr extStr = guidPos->second.c_str(); - XMP_StringLen extLen = (XMP_StringLen)guidPos->second.size(); - SXMPMeta extXMP ( extStr, extLen ); - SXMPUtils::MergeFromJPEG ( &this->xmpObj, extXMP ); - } catch ( ... ) { - // Ignore failures, let the rest of the XMP and legacy be kept. - } - } - - } - - // Process the legacy metadata. - - if ( haveIPTC && (! haveXMP) && (iptcDigestState == kDigestMatches) ) iptcDigestState = kDigestMissing; - bool parseIPTC = (iptcDigestState != kDigestMatches) || (! readOnly); - if ( parseIPTC ) iptc.ParseMemoryDataSets ( iptcInfo.dataPtr, iptcInfo.dataLen ); - ImportPhotoData ( exif, iptc, psir, iptcDigestState, &this->xmpObj, options ); - - this->containsXMP = true; // Assume we had something for the XMP. - -} // JPEG_MetaHandler::ProcessXMP - -// ================================================================================================= -// JPEG_MetaHandler::UpdateFile -// ============================ - -void JPEG_MetaHandler::UpdateFile ( bool doSafeUpdate ) -{ - XMP_Assert ( ! doSafeUpdate ); // This should only be called for "unsafe" updates. - - XMP_Int64 oldPacketOffset = this->packetInfo.offset; - XMP_Int32 oldPacketLength = this->packetInfo.length; - - if ( oldPacketOffset == kXMPFiles_UnknownOffset ) oldPacketOffset = 0; // ! Simplify checks. - if ( oldPacketLength == kXMPFiles_UnknownLength ) oldPacketLength = 0; - - bool fileHadXMP = ((oldPacketOffset != 0) && (oldPacketLength != 0)); - - // Update the IPTC-IIM and native TIFF/Exif metadata. ExportPhotoData also trips the tiff: and - // exif: copies from the XMP, so reserialize the now final XMP packet. - - ExportPhotoData ( kXMP_JPEGFile, &this->xmpObj, this->exifMgr, this->iptcMgr, this->psirMgr ); - - try { - XMP_OptionBits options = kXMP_UseCompactFormat; - if ( fileHadXMP ) options |= kXMP_ExactPacketLength; - this->xmpObj.SerializeToBuffer ( &this->xmpPacket, options, oldPacketLength ); - } catch ( ... ) { - this->xmpObj.SerializeToBuffer ( &this->xmpPacket, kXMP_UseCompactFormat ); - } - - // Decide whether to do an in-place update. This can only happen if all of the following are true: - // - There is a standard packet in the file. - // - There is no extended XMP in the file. - // - The are no changes to the legacy Exif or PSIR portions. (The IPTC is in the PSIR.) - // - The new XMP can fit in the old space, without extensions. - - bool doInPlace = (fileHadXMP && (this->xmpPacket.size() <= (size_t)oldPacketLength)); - - if ( ! this->extendedXMP.empty() ) doInPlace = false; - - if ( (this->exifMgr != 0) && (this->exifMgr->IsLegacyChanged()) ) doInPlace = false; - if ( (this->psirMgr != 0) && (this->psirMgr->IsLegacyChanged()) ) doInPlace = false; - - if ( doInPlace ) { - - #if GatherPerformanceData - sAPIPerf->back().extraInfo += ", JPEG in-place update"; - #endif - - if ( this->xmpPacket.size() < (size_t)this->packetInfo.length ) { - // They ought to match, cheap to be sure. - size_t extraSpace = (size_t)this->packetInfo.length - this->xmpPacket.size(); - this->xmpPacket.append ( extraSpace, ' ' ); - } - - LFA_FileRef liveFile = this->parent->fileRef; - std::string & newPacket = this->xmpPacket; - - XMP_Assert ( newPacket.size() == (size_t)oldPacketLength ); // ! Done by common PutXMP logic. - - LFA_Seek ( liveFile, oldPacketOffset, SEEK_SET ); - LFA_Write ( liveFile, newPacket.c_str(), (XMP_Int32)newPacket.size() ); - - } else { - - #if GatherPerformanceData - sAPIPerf->back().extraInfo += ", JPEG copy update"; - #endif - - std::string origPath = this->parent->filePath; - LFA_FileRef origRef = this->parent->fileRef; - - std::string updatePath; - LFA_FileRef updateRef = 0; - - CreateTempFile ( origPath, &updatePath, kCopyMacRsrc ); - updateRef = LFA_Open ( updatePath.c_str(), 'w' ); - - this->parent->filePath = updatePath; - this->parent->fileRef = updateRef; - - try { - XMP_Assert ( ! this->skipReconcile ); - this->skipReconcile = true; - this->WriteFile ( origRef, origPath ); - this->skipReconcile = false; - } catch ( ... ) { - this->skipReconcile = false; - LFA_Close ( updateRef ); - LFA_Delete ( updatePath.c_str() ); - this->parent->filePath = origPath; - this->parent->fileRef = origRef; - throw; - } - - LFA_Close ( origRef ); - LFA_Delete ( origPath.c_str() ); - - LFA_Close ( updateRef ); - LFA_Rename ( updatePath.c_str(), origPath.c_str() ); - this->parent->filePath = origPath; - this->parent->fileRef = 0; - - } - - this->needsUpdate = false; - -} // JPEG_MetaHandler::UpdateFile - -// ================================================================================================= -// JPEG_MetaHandler::WriteFile -// =========================== -// -// The metadata parts of a JPEG file are APP1 marker segments for Exif and XMP, and an APP13 marker -// segment for Photoshop image resources which contain the IPTC. Corresponding marker segments in -// the source file are ignored, other parts of the source file are copied. Any initial APP0 marker -// segments are copied first. Then the new Exif, XMP, and PSIR marker segments are written. Then the -// rest of the file is copied, skipping the old Exif, XMP, and PSIR. The checking for old metadata -// stops at the first SOFn marker. - -// *** What about Mac resources? - -void JPEG_MetaHandler::WriteFile ( LFA_FileRef sourceRef, const std::string & sourcePath ) -{ - LFA_FileRef destRef = this->parent->fileRef; - - XMP_AbortProc abortProc = this->parent->abortProc; - void * abortArg = this->parent->abortArg; - const bool checkAbort = (abortProc != 0); - - XMP_Uns16 marker; - size_t segLen; // ! Must be a size to hold at least 64k+2. - IOBuffer ioBuf; - XMP_Uns32 first4; - - XMP_Assert ( kIOBufferSize >= (2 + 64*1024) ); // Enough for a marker plus maximum contents. - - if ( LFA_Measure ( sourceRef ) == 0 ) return; // Tolerate empty files. - LFA_Seek ( sourceRef, 0, SEEK_SET ); - LFA_Truncate (destRef, 0 ); - - if ( ! skipReconcile ) { - // Update the IPTC-IIM and native TIFF/Exif metadata, and reserialize the now final XMP packet. - ExportPhotoData ( kXMP_JPEGFile, &this->xmpObj, this->exifMgr, this->iptcMgr, this->psirMgr ); - this->xmpObj.SerializeToBuffer ( &this->xmpPacket, kXMP_UseCompactFormat ); - } - - RefillBuffer ( sourceRef, &ioBuf ); - if ( ! CheckFileSpace ( sourceRef, &ioBuf, 4 ) ) { - XMP_Throw ( "JPEG must have at least SOI and EOI markers", kXMPErr_BadJPEG ); - } - - marker = GetUns16BE ( ioBuf.ptr ); - if ( marker != 0xFFD8 ) XMP_Throw ( "Missing SOI marker", kXMPErr_BadJPEG ); - LFA_Write ( destRef, ioBuf.ptr, 2 ); - ioBuf.ptr += 2; - - // Copy the leading APP0 marker segments. - - while ( true ) { - - if ( checkAbort && abortProc(abortArg) ) { - XMP_Throw ( "JPEG_MetaHandler::WriteFile - User abort", kXMPErr_UserAbort ); - } - - if ( ! CheckFileSpace ( sourceRef, &ioBuf, 2 ) ) XMP_Throw ( "Unexpected end to JPEG", kXMPErr_BadJPEG ); - marker = GetUns16BE ( ioBuf.ptr ); - if ( marker == 0xFFFF ) { - LFA_Write ( destRef, ioBuf.ptr, 1 ); // Copy the 0xFF pad byte. - ++ioBuf.ptr; - continue; - } - - if ( marker != 0xFFE0 ) break; - - if ( ! CheckFileSpace ( sourceRef, &ioBuf, 4 ) ) XMP_Throw ( "Unexpected end to JPEG", kXMPErr_BadJPEG ); - segLen = GetUns16BE ( ioBuf.ptr+2 ); - segLen += 2; // ! Don't do above in case machine does 16 bit "+". - - if ( ! CheckFileSpace ( sourceRef, &ioBuf, segLen ) ) XMP_Throw ( "Unexpected end to JPEG", kXMPErr_BadJPEG ); - LFA_Write ( destRef, ioBuf.ptr, (XMP_Int32)segLen ); - ioBuf.ptr += segLen; - - } - - // Write the new Exif APP1 marker segment. - - if ( this->exifMgr != 0 ) { - - void* exifPtr; - XMP_Uns32 exifLen = this->exifMgr->UpdateMemoryStream ( &exifPtr ); - if ( exifLen > kExifMaxDataLength ) exifLen = this->exifMgr->UpdateMemoryStream ( &exifPtr, true /* compact */ ); - if ( exifLen > kExifMaxDataLength ) XMP_Throw ( "Overflow of Exif APP1 data", kXMPErr_BadJPEG ); - - if ( exifLen > 0 ) { - first4 = MakeUns32BE ( 0xFFE10000 + 2 + kExifSignatureLength + exifLen ); - LFA_Write ( destRef, &first4, 4 ); - LFA_Write ( destRef, kExifSignatureString, kExifSignatureLength ); - LFA_Write ( destRef, exifPtr, exifLen ); - } - - } - - // Write the new XMP APP1 marker segment, with possible extension marker segments. - - std::string mainXMP, extXMP, extDigest; - SXMPUtils::PackageForJPEG ( this->xmpObj, &mainXMP, &extXMP, &extDigest ); - XMP_Assert ( (extXMP.size() == 0) || (extDigest.size() == 32) ); - - first4 = MakeUns32BE ( 0xFFE10000 + 2 + kMainXMPSignatureLength + (XMP_Uns32)mainXMP.size() ); - LFA_Write ( destRef, &first4, 4 ); - LFA_Write ( destRef, kMainXMPSignatureString, kMainXMPSignatureLength ); - LFA_Write ( destRef, mainXMP.c_str(), (XMP_Int32)mainXMP.size() ); - - size_t extPos = 0; - size_t extLen = extXMP.size(); - - while ( extLen > 0 ) { - - size_t partLen = extLen; - if ( partLen > 65000 ) partLen = 65000; - - first4 = MakeUns32BE ( 0xFFE10000 + 2 + kExtXMPPrefixLength + (XMP_Uns32)partLen ); - LFA_Write ( destRef, &first4, 4 ); - - LFA_Write ( destRef, kExtXMPSignatureString, kExtXMPSignatureLength ); - LFA_Write ( destRef, extDigest.c_str(), (XMP_Int32)extDigest.size() ); - - first4 = MakeUns32BE ( (XMP_Int32)extXMP.size() ); - LFA_Write ( destRef, &first4, 4 ); - first4 = MakeUns32BE ( (XMP_Int32)extPos ); - LFA_Write ( destRef, &first4, 4 ); - - LFA_Write ( destRef, &extXMP[extPos], (XMP_Int32)partLen ); - - extPos += partLen; - extLen -= partLen; - - } - - // Write the new PSIR APP13 marker segment. - - if ( this->psirMgr != 0 ) { - - void* psirPtr; - XMP_Uns32 psirLen = this->psirMgr->UpdateMemoryResources ( &psirPtr ); - if ( psirLen > kPSIRMaxDataLength ) XMP_Throw ( "Overflow of PSIR APP13 data", kXMPErr_BadJPEG ); - - if ( psirLen > 0 ) { - first4 = MakeUns32BE ( 0xFFED0000 + 2 + kPSIRSignatureLength + psirLen ); - LFA_Write ( destRef, &first4, 4 ); - LFA_Write ( destRef, kPSIRSignatureString, kPSIRSignatureLength ); - LFA_Write ( destRef, psirPtr, psirLen ); - } - - } - - // Copy remaining marker segments, skipping old metadata, to the first SOS marker or to EOI. - - while ( true ) { - - if ( checkAbort && abortProc(abortArg) ) { - XMP_Throw ( "JPEG_MetaHandler::WriteFile - User abort", kXMPErr_UserAbort ); - } - - if ( ! CheckFileSpace ( sourceRef, &ioBuf, 2 ) ) XMP_Throw ( "Unexpected end to JPEG", kXMPErr_BadJPEG ); - marker = GetUns16BE ( ioBuf.ptr ); - if ( marker == 0xFFFF ) { - LFA_Write ( destRef, ioBuf.ptr, 1 ); // Copy the 0xFF pad byte. - ++ioBuf.ptr; - continue; - } - - if ( (marker == 0xFFDA) || (marker == 0xFFD9) ) break; // Quit at the first SOS marker or at EOI. - - if ( (marker == 0xFF01) || // Ill-formed file if we encounter a TEM or RSTn marker. - ((0xFFD0 <= marker) && (marker <= 0xFFD7)) ) { - XMP_Throw ( "Unexpected TEM or RSTn marker", kXMPErr_BadJPEG ); - } - - if ( ! CheckFileSpace ( sourceRef, &ioBuf, 4 ) ) XMP_Throw ( "Unexpected end to JPEG", kXMPErr_BadJPEG ); - segLen = GetUns16BE ( ioBuf.ptr+2 ); - - if ( ! CheckFileSpace ( sourceRef, &ioBuf, 2+segLen ) ) XMP_Throw ( "Unexpected end to JPEG", kXMPErr_BadJPEG ); - - bool copySegment = true; - XMP_Uns8* signaturePtr = ioBuf.ptr + 4; - - if ( marker == 0xFFED ) { - if ( (segLen >= kPSIRSignatureLength) && - CheckBytes ( signaturePtr, kPSIRSignatureString, kPSIRSignatureLength ) ) { - copySegment = false; - } - } else if ( marker == 0xFFE1 ) { - if ( (segLen >= kExifSignatureLength) && - (CheckBytes ( signaturePtr, kExifSignatureString, kExifSignatureLength ) || - CheckBytes ( signaturePtr, kExifSignatureAltStr, kExifSignatureLength )) ) { - copySegment = false; - } else if ( (segLen >= kMainXMPSignatureLength) && - CheckBytes ( signaturePtr, kMainXMPSignatureString, kMainXMPSignatureLength ) ) { - copySegment = false; - } else if ( (segLen >= kExtXMPPrefixLength) && - CheckBytes ( signaturePtr, kExtXMPSignatureString, kExtXMPSignatureLength ) ) { - copySegment = false; - } - } - - if ( copySegment ) LFA_Write ( destRef, ioBuf.ptr, (XMP_Int32)(2+segLen) ); - - ioBuf.ptr += 2+segLen; - - } - - // Copy the remainder of the source file. - - size_t bufTail = ioBuf.len - (ioBuf.ptr - &ioBuf.data[0]); - LFA_Write ( destRef, ioBuf.ptr, (XMP_Int32)bufTail ); - ioBuf.ptr += bufTail; - - while ( true ) { - RefillBuffer ( sourceRef, &ioBuf ); - if ( ioBuf.len == 0 ) break; - LFA_Write ( destRef, ioBuf.ptr, (XMP_Int32)ioBuf.len ); - ioBuf.ptr += ioBuf.len; - } - - this->needsUpdate = false; - -} // JPEG_MetaHandler::WriteFile diff --git a/source/XMPFiles/FileHandlers/JPEG_Handler.hpp b/source/XMPFiles/FileHandlers/JPEG_Handler.hpp deleted file mode 100644 index 650a5b5..0000000 --- a/source/XMPFiles/FileHandlers/JPEG_Handler.hpp +++ /dev/null @@ -1,93 +0,0 @@ -#ifndef __JPEG_Handler_hpp__ -#define __JPEG_Handler_hpp__ 1 - -// ================================================================================================= -// ADOBE SYSTEMS INCORPORATED -// Copyright 2004 Adobe Systems Incorporated -// 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 "TIFF_Support.hpp" -#include "PSIR_Support.hpp" -#include "IPTC_Support.hpp" - -// ================================================================================================= -/// \file JPEG_Handler.hpp -/// \brief File format handler for JPEG. -/// -/// This header ... -/// -// ================================================================================================= - -// *** Could derive from Basic_Handler - buffer file tail in a temp file. - -extern XMPFileHandler * JPEG_MetaHandlerCTor ( XMPFiles * parent ); - -extern bool JPEG_CheckFormat ( XMP_FileFormat format, - XMP_StringPtr filePath, - LFA_FileRef fileRef, - XMPFiles * parent ); - -static const XMP_OptionBits kJPEG_HandlerFlags = (kXMPFiles_CanInjectXMP | - kXMPFiles_CanExpand | - kXMPFiles_CanRewrite | - kXMPFiles_PrefersInPlace | - kXMPFiles_CanReconcile | - kXMPFiles_AllowsOnlyXMP | - kXMPFiles_ReturnsRawPacket | - kXMPFiles_AllowsSafeUpdate); - -class JPEG_MetaHandler : public XMPFileHandler -{ -public: - - void CacheFileData(); - void ProcessXMP(); - - void UpdateFile ( bool doSafeUpdate ); - void WriteFile ( LFA_FileRef sourceRef, const std::string & sourcePath ); - - struct GUID_32 { // A hack to get an assignment operator for an array. - char data [32]; - void operator= ( const GUID_32 & in ) - { - memcpy ( this->data, in.data, sizeof(this->data) ); // AUDIT: Use of sizeof(this->data) is safe. - }; - bool operator< ( const GUID_32 & right ) const - { - return (memcmp ( this->data, right.data, sizeof(this->data) ) < 0); - }; - bool operator== ( const GUID_32 & right ) const - { - return (memcmp ( this->data, right.data, sizeof(this->data) ) == 0); - }; - }; - - JPEG_MetaHandler ( XMPFiles * parent ); - virtual ~JPEG_MetaHandler(); - -private: - - JPEG_MetaHandler() : exifMgr(0), psirMgr(0), iptcMgr(0), skipReconcile(false) {}; // Hidden on purpose. - - std::string exifContents; - std::string psirContents; - - TIFF_Manager * exifMgr; // The Exif manager will be created by ProcessTNail or ProcessXMP. - PSIR_Manager * psirMgr; // Need to use pointers so we can properly select between read-only and - IPTC_Manager * iptcMgr; // read-write modes of usage. - - bool skipReconcile; // ! Used between UpdateFile and WriteFile. - - typedef std::map < GUID_32, std::string > ExtendedXMPMap; - - ExtendedXMPMap extendedXMP; // ! Only contains those with complete data. - -}; // JPEG_MetaHandler - -// ================================================================================================= - -#endif /* __JPEG_Handler_hpp__ */ diff --git a/source/XMPFiles/FileHandlers/MP3_Handler.cpp b/source/XMPFiles/FileHandlers/MP3_Handler.cpp deleted file mode 100644 index 2c8f547..0000000 --- a/source/XMPFiles/FileHandlers/MP3_Handler.cpp +++ /dev/null @@ -1,748 +0,0 @@ -// ================================================================================================= -// ADOBE SYSTEMS INCORPORATED -// Copyright 2008 Adobe Systems Incorporated -// 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 "XMP_Environment.h" // ! This must be the first include. -#include "MP3_Handler.hpp" - -// ================================================================================================= -/// \file MP3_Handler.cpp -/// \brief MP3 handler class. -// ================================================================================================= - -// ================================================================================================= -// Helper structs and private routines -// ==================== -struct ReconProps { - const char* frameID; - const char* ns; - const char* prop; -}; - -const static XMP_Uns32 XMP_FRAME_ID = 0x50524956; - -const static ReconProps reconProps[] = { - { "TPE1", kXMP_NS_DM, "artist" }, - { "TALB", kXMP_NS_DM, "album" }, - { "TRCK", kXMP_NS_DM, "trackNumber" }, - // exceptions that need attention: - { "TCON", kXMP_NS_DM, "genre" }, // genres might be numeric - { "TIT2", kXMP_NS_DC, "title" }, // ["x-default"] language alternatives - { "COMM", kXMP_NS_DM, "logComment" }, // two distinct strings, language alternative - - { "TYER", kXMP_NS_XMP, "CreateDate" }, // Year (YYYY) Deprecated since 2.4 - { "TDAT", kXMP_NS_XMP, "CreateDate" }, // Date (DDMM) Deprecated since 2.4 - { "TIME", kXMP_NS_XMP, "CreateDate" }, // Time (HHMM) Deprecated since 2.4 - { "TDRC", kXMP_NS_XMP, "CreateDate" }, // assemble date/time v2.4 - - // new reconciliations introduced in Version 5 - { "TCMP", kXMP_NS_DM, "partOfCompilation" }, // presence/absence of TCMP frame dedides - { "USLT", kXMP_NS_DM, "lyrics" }, - { "TCOM", kXMP_NS_DM, "composer" }, - { "TPOS", kXMP_NS_DM, "discNumber" }, // * a text field! might contain "/<total>" - { "TCOP", kXMP_NS_DC, "rights" }, // ["x-default"] language alternatives - { "TPE4", kXMP_NS_DM, "engineer" }, - { "WCOP", kXMP_NS_XMP_Rights , "WebStatement" }, - - { 0, 0, 0 } // must be last as a sentinel -}; - -// ================================================================================================= -// MP3_MetaHandlerCTor -// ==================== -XMPFileHandler * MP3_MetaHandlerCTor ( XMPFiles * parent ) -{ - return new MP3_MetaHandler ( parent ); -} - -// ================================================================================================= -// MP3_CheckFormat -// =============== -// For MP3 we check parts .... See the MP3 spec for offset info. -bool MP3_CheckFormat ( XMP_FileFormat format, - XMP_StringPtr filePath, - LFA_FileRef file, - XMPFiles * parent ) -{ - IgnoreParam(filePath); IgnoreParam(parent); //supress warnings - XMP_Assert ( format == kXMP_MP3File ); //standard assert - LFA_Rewind( file ); - - XMP_Uns8 header[3]; - LFA_Read( file, header, 3, true ); - if ( !CheckBytes( &header[0], "ID3", 3 )) - return (parent->format == kXMP_MP3File); // No ID3 signature -> depend on first call hint. - - XMP_Uns8 major = LFA_ReadUns8( file ); - XMP_Uns8 minor = LFA_ReadUns8( file ); - - if ( (major<3 || major>4) || (minor == 0xFF) ) - return false; // only support IDv2.3 and ID3v2.4, minor must not be 0xFF - - XMP_Uns8 flags = LFA_ReadUns8( file ); - - //TODO - if ( flags & 0x10 ) - XMP_Throw("no support for MP3 with footer",kXMPErr_Unimplemented); //no support for MP3 with footer - if ( flags & 0x80 ) - return false; //no support for unsynchronized MP3 (as before, also see [1219125]) - if ( flags & 0x0F ) - XMP_Throw("illegal header lower bits",kXMPErr_Unimplemented); - - XMP_Uns32 size = LFA_ReadUns32_BE( file ); - if ( size & 0x80808080 ) return false; //if any bit survives -> not a valid synchsafe 32 bit integer - - return true; -} // MP3_CheckFormat - - -// ================================================================================================= -// MP3_MetaHandler::MP3_MetaHandler -// ================================ - -MP3_MetaHandler::MP3_MetaHandler ( XMPFiles * _parent ) -{ - this->parent = _parent; - this->handlerFlags = kMP3_HandlerFlags; - this->stdCharForm = kXMP_Char8Bit; -} - -// ================================================================================================= -// MP3_MetaHandler::~MP3_MetaHandler -// ================================= - -MP3_MetaHandler::~MP3_MetaHandler() -{ - // free frames - ID3v2Frame* curFrame; - while( !this->framesVector.empty() ) - { - curFrame = this->framesVector.back(); - delete curFrame; - framesVector.pop_back(); - } -} - -// ================================================================================================= -// 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 - - -// ================================================================================================= -// MP3_MetaHandler::ProcessXMP -// =========================== -// -// Process the raw XMP and legacy metadata that was previously cached. -void MP3_MetaHandler::ProcessXMP() -{ - // Process the XMP packet. - if ( ! this->xmpPacket.empty() ) { - XMP_Assert ( this->containsXMP ); - XMP_StringPtr packetStr = this->xmpPacket.c_str(); - XMP_StringLen packetLen = (XMP_StringLen) this->xmpPacket.size(); - this->xmpObj.ParseFromBuffer ( packetStr, packetLen ); - this->processedXMP = true; - } - - /////////////////////////////////////////////////////////////////// - // assumptions on presence-absence "flag tags" - // ( unless no xmp whatsoever present ) - if ( ! this->xmpPacket.empty() ) { - this->xmpObj.SetProperty( kXMP_NS_DM, "partOfCompilation", "false" ); - } - - //////////////////////////////////////////////////////////////////// - // import of legacy properties - ID3v2Frame* curFrame; - XMP_Bool hasTDRC = false; - XMP_DateTime newDateTime; - - if (this->hasID3Tag) // otherwise pretty pointless... - for (int r=0; reconProps[r].frameID != 0; r++) - { - //get the frame ID to look for - XMP_Uns32 frameID = GetUns32BE( reconProps[r].frameID ); - - // deal with each such frame in the frameVector - // (since there might be several, some of them not applicable, i.e. COMM) - for ( vector<ID3_Support::ID3v2Frame*>::iterator it = framesVector.begin(); it!=framesVector.end(); ++it) - { - curFrame = *it; - if (frameID != curFrame->id) // nothing applicable. Next! - continue; - - // go deal with it! - // get the property - std::string utf8string; - bool result = curFrame->getFrameValue(majorVersion, frameID, &utf8string); - - if (! result) - continue; //ignore but preserve this frame (i.e. not applicable COMM frame) - - ////////////////////////////////////////////////////////////////////////////////// - // if we come as far as here, it's proven that there's a relevant XMP property - this->containsXMP = true; - - ID3_Support::ID3v2Frame* t = framesMap[ frameID ]; - if ( t != 0 ) // an (earlier, relevant) frame? - t->active = false; - - // add this to map (needed on reconciliation) - // note: above code reaches, that COMM/USLT frames - // only then reach this map, if they are 'eng'(lish) - // multiple occurences indeed leads to last one survives - // ( in this map, all survive in the file ) - framesMap[ frameID ] = curFrame; - - // now write away as needed; - // merely based on existence, relevant even if empty: - if ( frameID == 0x54434D50) // TCMP if exists: part of compilation - { - this->xmpObj.SetProperty( kXMP_NS_DM, "partOfCompilation", "true" ); - } else if ( ! utf8string.empty() ) - switch( frameID ) - { - case 0x54495432: // TIT2 -> title["x-default"] - case 0x54434F50: // TCOP -> rights["x-default"] - this->xmpObj.SetLocalizedText( reconProps[r].ns , reconProps[r].prop,"" , "x-default" , utf8string ); - break; - case 0x54434F4E: // TCON -> genre ( might be numeric string. prior to 2.3 a one-byte numeric value? ) - { - XMP_Int32 pos = 0; // going through input string - if ( utf8string[pos] == '(' ) { // number in brackets? - pos++; - XMP_Uns8 iGenre = (XMP_Uns8) atoi( &utf8string.c_str()[1] ); - if ( (iGenre > 0) && (iGenre < 127) ) { - utf8string.assign( Genres[iGenre] ); - } else { - utf8string.assign( Genres[12] ); // "Other" - } - } else { - // Text, let's "try" to find it anyway (for best upper/lower casing) - int i; - const char* genreCString = utf8string.c_str(); - for ( i=0; i < 127; ++i ) { - if ( - (strlen( genreCString ) == strlen(Genres[i])) && //fixing buggy stricmp behaviour on PPC - (stricmp( genreCString, Genres[i] ) == 0 )) { - utf8string.assign( Genres[i] ); // found, let's use the one in the list - break; - } - } - // otherwise (if for-loop runs through): leave as is - } - // write out property (derived or direct, but certainly non-numeric) - this->xmpObj.SetProperty( reconProps[r].ns, reconProps[r].prop, utf8string ); - } - break; - case 0x54594552: // TYER -> xmp:CreateDate.year - { - try { // Don't let wrong dates in id3 stop import. - if ( !hasTDRC ) - { - newDateTime.year = SXMPUtils::ConvertToInt( utf8string ); - newDateTime.hasDate = true; - } - } catch ( ... ) { - // Do nothing, let other imports proceed. - } - break; - } - case 0x54444154: //TDAT -> xmp:CreateDate.month and day - { - try { // Don't let wrong dates in id3 stop import. - // only if no TDRC has been found before - //&& must have the format DDMM - if ( !hasTDRC && utf8string.length() == 4 ) - { - newDateTime.day = SXMPUtils::ConvertToInt(utf8string.substr(0,2)); - newDateTime.month = SXMPUtils::ConvertToInt( utf8string.substr(2,2)); - newDateTime.hasDate = true; - } - } catch ( ... ) { - // Do nothing, let other imports proceed. - } - break; - } - case 0x54494D45: //TIME -> xmp:CreateDate.hours and minutes - { - try { // Don't let wrong dates in id3 stop import. - // only if no TDRC has been found before - // && must have the format HHMM - if ( !hasTDRC && utf8string.length() == 4 ) - { - newDateTime.hour = SXMPUtils::ConvertToInt(utf8string.substr(0,2)); - newDateTime.minute = SXMPUtils::ConvertToInt( utf8string.substr(2,2)); - newDateTime.hasTime = true; - } - } catch ( ... ) { - // Do nothing, let other imports proceed. - } - break; - } - case 0x54445243: // TDRC -> xmp:CreateDate //id3 v2.4 - { - try { // Don't let wrong dates in id3 stop import. - hasTDRC = true; - // This always wins over TYER, TDAT and TIME - SXMPUtils::ConvertToDate( utf8string, &newDateTime ); - } catch ( ... ) { - // Do nothing, let other imports proceed. - } - break; - } - default: - // NB: COMM/USLT need no special fork regarding language alternatives/multiple occurence. - // relevant code forks are in ID3_Support::getFrameValue() - this->xmpObj.SetProperty( reconProps[r].ns, reconProps[r].prop, utf8string ); - break; - }//switch - } //for iterator - }//for reconProps - - // import DateTime - XMP_DateTime oldDateTime; - xmpObj.GetProperty_Date( kXMP_NS_XMP, "CreateDate", &oldDateTime, 0 ); - - - - // NOTE: no further validation nessesary the function "SetProperty_Date" will care about validating date and time - // any exception will be caught and block import - try { - // invalid year will be catched and blocks import - XMP_Validate( (newDateTime.year > 0 && newDateTime.year < 9999), "", kXMPErr_BadParam ); - - // 2. if year has changed --> everything (date/time) has changed --> overwrite old DateTime with new DateTime - if ( ( newDateTime.year != oldDateTime.year ) || // year has changed? - // or has same year but new day/month (checking existance month indicates both (day and month) in our case) - (( newDateTime.month != 0 ) && ( newDateTime.day != oldDateTime.day || newDateTime.month != oldDateTime.month )) || - // or has same year and same date but different time - ( newDateTime.hasTime && ( newDateTime.hour != oldDateTime.minute || newDateTime.hour != oldDateTime.minute )) ) - { - this->xmpObj.SetProperty_Date( kXMP_NS_XMP, "CreateDate", newDateTime ); - } // ..else: keep old dateTime to don't loose data - - } catch ( ... ) { - // Dont import invalid dates from ID3 - } - - - // very important to avoid multiple runs! (in which case I'd need to clean certain - // fields (i.e. regarding ->active setting) - this->processedXMP = true; - -} // MP3_MetaHandler::ProcessXMP - - -// ================================================================================================= -// 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::WriteFile -// ========================== - -void MP3_MetaHandler::WriteFile ( LFA_FileRef sourceRef, - const std::string & sourcePath ) -{ - IgnoreParam(sourceRef); IgnoreParam(sourcePath); - XMP_Throw ( "MP3_MetaHandler::WriteFile: Not supported", kXMPErr_Unimplemented ); -} // MP3_MetaHandler::WriteFile diff --git a/source/XMPFiles/FileHandlers/MP3_Handler.hpp b/source/XMPFiles/FileHandlers/MP3_Handler.hpp deleted file mode 100644 index d6be9cf..0000000 --- a/source/XMPFiles/FileHandlers/MP3_Handler.hpp +++ /dev/null @@ -1,92 +0,0 @@ -#ifndef __MP3_Handler_hpp__ -#define __MP3_Handler_hpp__ 1 - -// ================================================================================================= -// ADOBE SYSTEMS INCORPORATED -// Copyright 2008 Adobe Systems Incorporated -// 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 "XMP_Environment.h" // ! This must be the first include. -#include "XMPFiles_Impl.hpp" -#include "ID3_Support.hpp" - -using namespace std; -using namespace ID3_Support; - -extern XMPFileHandler * MP3_MetaHandlerCTor ( XMPFiles * parent ); - -extern bool MP3_CheckFormat ( XMP_FileFormat format, - XMP_StringPtr filePath, - LFA_FileRef fileRef, - XMPFiles * parent ); - -static const XMP_OptionBits kMP3_HandlerFlags = (kXMPFiles_CanInjectXMP | - kXMPFiles_CanExpand | - kXMPFiles_PrefersInPlace | - kXMPFiles_AllowsOnlyXMP | - kXMPFiles_ReturnsRawPacket| - kXMPFiles_CanReconcile); - -class MP3_MetaHandler : public XMPFileHandler -{ -public: - MP3_MetaHandler ( XMPFiles * parent ); - ~MP3_MetaHandler(); - - void CacheFileData(); - - void UpdateFile ( bool doSafeUpdate ); - void WriteFile ( LFA_FileRef sourceRef, const std::string & sourcePath ); - void ProcessXMP(); - -private: - //////////////////////////////////////////////////////////////////////////////////// - // instance vars - XMP_Int64 oldTagSize; // the entire tag, including padding, including 10B header - XMP_Int64 oldPadding; // number of padding bytes - XMP_Int64 oldFramesSize; // actual space needed by frames := oldTagSize - 10 - oldPadding - - XMP_Int64 newTagSize; - XMP_Int64 newPadding; - XMP_Int64 newFramesSize; - - // decision making: - bool tagIsDirty; // true, if any legacy properties changed. - bool mustShift; // entire tag to rewrite? (or possibly just XMP?) - - - XMP_Uns8 majorVersion, minorVersion; // Version Number post ID3v2, i.e. 3 0 ==> ID3v2.3.0 - bool hasID3Tag; //incoming file has an ID3 tag? - bool hasFooter; - bool legacyChanged; // tag rewrite certainly needed? - - ID3Header id3Header; - - XMP_Int64 extHeaderSize; - bool hasExtHeader; - - // the frames - // * all to be kept till write-out - // * parsed/understood only if needed - // * vector used to free memory in handler destructor - std::vector<ID3_Support::ID3v2Frame*> framesVector; - - // ID3v1 - treated as a single object - ID3v1Tag id3v1Tag; - - // * also kept in a map for better import<->export access - // * only keeps legacy 'relevant frames' (i.e. no abused COMM frames) - // * only keeps last relevant frame - // * earlier 'relevant frames' will be deleted. This map also helps in this - // * key shall be the FrameID, always interpreted as BE - std::map<XMP_Uns32,ID3_Support::ID3v2Frame*> framesMap; - -}; // MP3_MetaHandler - -// ================================================================================================= - -#endif /* __MP3_Handler_hpp__ */ diff --git a/source/XMPFiles/FileHandlers/MPEG2_Handler.cpp b/source/XMPFiles/FileHandlers/MPEG2_Handler.cpp deleted file mode 100644 index 438e705..0000000 --- a/source/XMPFiles/FileHandlers/MPEG2_Handler.cpp +++ /dev/null @@ -1,234 +0,0 @@ -// ================================================================================================= -// ADOBE SYSTEMS INCORPORATED -// Copyright 2005 Adobe Systems Incorporated -// 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. -// ================================================================================================= - -#if XMP_WinBuild - #pragma warning ( disable : 4996 ) // '...' was declared deprecated -#endif - -#include "MPEG2_Handler.hpp" - -using namespace std; - -// ================================================================================================= -/// \file MPEG2_Handler.cpp -/// \brief File format handler for MPEG2. -/// -/// BLECH! YUCK! GAG! MPEG-2 is done using a sidecar and recognition only by file extension! BARF!!!!! -/// -// ================================================================================================= - -// ================================================================================================= -// FindFileExtension -// ================= - -static inline XMP_StringPtr FindFileExtension ( XMP_StringPtr filePath ) -{ - - XMP_StringPtr pathEnd = filePath + strlen(filePath); - XMP_StringPtr extPtr; - - for ( extPtr = pathEnd-1; extPtr > filePath; --extPtr ) { - if ( (*extPtr == '.') || (*extPtr == '/') ) break; - #if XMP_WinBuild - if ( (*extPtr == '\\') || (*extPtr == ':') ) break; - #endif - } - - if ( (extPtr < filePath) || (*extPtr != '.') ) return pathEnd; - return extPtr; - -} // FindFileExtension - -// ================================================================================================= -// MPEG2_MetaHandlerCTor -// ===================== - -XMPFileHandler * MPEG2_MetaHandlerCTor ( XMPFiles * parent ) -{ - return new MPEG2_MetaHandler ( parent ); - -} // MPEG2_MetaHandlerCTor - -// ================================================================================================= -// MPEG2_CheckFormat -// ================= - -// The MPEG-2 handler uses just the file extension, not the file content. Worse yet, it also uses a -// sidecar file for the XMP. This works better if the handler owns the file, we open the sidecar -// instead of the actual MPEG-2 file. - -bool MPEG2_CheckFormat ( XMP_FileFormat format, - XMP_StringPtr filePath, - LFA_FileRef fileRef, - XMPFiles * parent ) -{ - IgnoreParam(format); IgnoreParam(filePath); IgnoreParam(fileRef); - - XMP_Assert ( (format == kXMP_MPEGFile) || (format == kXMP_MPEG2File) ); - XMP_Assert ( fileRef == 0 ); - - return ( (parent->format == kXMP_MPEGFile) || (parent->format == kXMP_MPEGFile) ); // ! Just use the first call's format hint. - -} // MPEG2_CheckFormat - -// ================================================================================================= -// MPEG2_MetaHandler::MPEG2_MetaHandler -// ==================================== - -MPEG2_MetaHandler::MPEG2_MetaHandler ( XMPFiles * _parent ) -{ - this->parent = _parent; - this->handlerFlags = kMPEG2_HandlerFlags; - this->stdCharForm = kXMP_Char8Bit; - -} // MPEG2_MetaHandler::MPEG2_MetaHandler - -// ================================================================================================= -// MPEG2_MetaHandler::~MPEG2_MetaHandler -// ===================================== - -MPEG2_MetaHandler::~MPEG2_MetaHandler() -{ - // Nothing to do. - -} // MPEG2_MetaHandler::~MPEG2_MetaHandler - -// ================================================================================================= -// MPEG2_MetaHandler::CacheFileData -// ================================ - -void MPEG2_MetaHandler::CacheFileData() -{ - bool readOnly = (! (this->parent->openFlags & kXMPFiles_OpenForUpdate)); - - this->containsXMP = false; - this->processedXMP = true; // Whatever we do here is all that we do for XMPFiles::OpenFile. - - // Try to open the sidecar XMP file. Tolerate an open failure, there might not be any XMP. - // Note that MPEG2_CheckFormat can't save the sidecar path because the handler doesn't exist then. - - XMP_StringPtr filePath = this->parent->filePath.c_str(); - XMP_StringPtr extPtr = FindFileExtension ( filePath ); - this->sidecarPath.assign ( filePath, (extPtr - filePath) ); - this->sidecarPath += ".xmp"; - - if ( readOnly ) { - - try { // *** At this time LFA_Open throws for a failure. - this->parent->fileRef = LFA_Open ( this->sidecarPath.c_str(), 'r' ); - if ( this->parent->fileRef == 0 ) return; // *** Could someday check for a permission failure. - } catch ( ... ) { - return; // *** Could someday check for a permission failure. - } - - } else { - - try { // *** At this time LFA_Open throws for a failure. - this->parent->fileRef = LFA_Open ( this->sidecarPath.c_str(), 'w' ); - } catch ( ... ) { - this->parent->fileRef = 0; // *** Could someday check for a permission failure. - } - - if ( this->parent->fileRef == 0 ) { - // Try to create a file if it does not yet exist. - // *** Could someday check for a permission failure versus no .xmp file. - this->parent->fileRef = LFA_Create ( this->sidecarPath.c_str() ); - if ( this->parent->fileRef == 0 ) XMP_Throw ( "Can't create MPEG-2 sidecar", kXMPErr_ExternalFailure ); - } - - } - - // Extract the sidecar's contents and parse. - - this->packetInfo.offset = 0; // We take the whole sidecar file. - this->packetInfo.length = (XMP_Int32) LFA_Measure ( this->parent->fileRef ); - - if ( this->packetInfo.length > 0 ) { - - this->xmpPacket.assign ( this->packetInfo.length, ' ' ); - LFA_Read ( this->parent->fileRef, (void*)this->xmpPacket.c_str(), this->packetInfo.length, kLFA_RequireAll ); - if ( readOnly ) { - LFA_Close ( this->parent->fileRef ); - this->parent->fileRef = 0; - } - - this->xmpObj.ParseFromBuffer ( this->xmpPacket.c_str(), (XMP_StringLen)this->xmpPacket.size() ); - this->containsXMP = true; - - } - -} // MPEG2_MetaHandler::CacheFileData - -// ================================================================================================= -// MPEG2_MetaHandler::UpdateFile -// ============================= - -void MPEG2_MetaHandler::UpdateFile ( bool doSafeUpdate ) -{ - if ( ! this->needsUpdate ) return; - - LFA_FileRef fileRef = this->parent->fileRef; - XMP_Assert ( fileRef != 0 ); - - XMP_StringPtr packetStr = this->xmpPacket.c_str(); - XMP_StringLen packetLen = (XMP_StringLen)this->xmpPacket.size(); - - if ( ! doSafeUpdate ) { - - // Not doing a crash-safe update, simply rewrite the existing sidecar file. - LFA_Seek ( fileRef, 0, SEEK_SET ); - LFA_Truncate ( fileRef, 0 ); - LFA_Write ( fileRef, packetStr, packetLen ); - - } else { - - // Do the usual crash-safe update dance. - - LFA_FileRef tempFileRef = 0; - std::string tempFilePath; - - try { - - CreateTempFile ( this->sidecarPath, &tempFilePath, kCopyMacRsrc ); - tempFileRef = LFA_Open ( tempFilePath.c_str(), 'w' ); - LFA_Write ( tempFileRef, packetStr, packetLen ); - - LFA_Close ( fileRef ); - this->parent->fileRef = 0; - LFA_Close ( tempFileRef ); - tempFileRef = 0; - - LFA_Delete ( this->sidecarPath.c_str() ); - LFA_Rename ( tempFilePath.c_str(), this->sidecarPath.c_str() ); - - } catch ( ... ) { - - if ( tempFileRef != 0 ) LFA_Close ( tempFileRef ); - if ( ! tempFilePath.empty() ) LFA_Delete ( tempFilePath.c_str() ); - - } - - } - - this->needsUpdate = false; - -} // MPEG2_MetaHandler::UpdateFile - -// ================================================================================================= -// MPEG2_MetaHandler::WriteFile -// ============================ - -void MPEG2_MetaHandler::WriteFile ( LFA_FileRef sourceRef, - const std::string & sourcePath ) -{ - IgnoreParam(sourceRef); IgnoreParam(sourcePath); - - XMP_Throw ( "MPEG2_MetaHandler::WriteFile: Should never be called", kXMPErr_Unavailable ); - -} // MPEG2_MetaHandler::WriteFile diff --git a/source/XMPFiles/FileHandlers/MPEG2_Handler.hpp b/source/XMPFiles/FileHandlers/MPEG2_Handler.hpp deleted file mode 100644 index 96ef407..0000000 --- a/source/XMPFiles/FileHandlers/MPEG2_Handler.hpp +++ /dev/null @@ -1,57 +0,0 @@ -#ifndef __MPEG2_Handler_hpp__ -#define __MPEG2_Handler_hpp__ 1 - -// ================================================================================================= -// ADOBE SYSTEMS INCORPORATED -// Copyright 2005 Adobe Systems Incorporated -// 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 "XMPFiles_Impl.hpp" - -// ================================================================================================= -/// \file MPEG2_Handler.hpp -/// \brief File format handler for MPEG2. -/// -/// This header ... -/// -// ================================================================================================= - -extern XMPFileHandler * MPEG2_MetaHandlerCTor ( XMPFiles * parent ); - -extern bool MPEG2_CheckFormat ( XMP_FileFormat format, - XMP_StringPtr filePath, - LFA_FileRef fileRef, - XMPFiles * parent); - -static const XMP_OptionBits kMPEG2_HandlerFlags = ( kXMPFiles_CanInjectXMP | - kXMPFiles_CanExpand | - kXMPFiles_CanRewrite | - kXMPFiles_AllowsOnlyXMP | - kXMPFiles_ReturnsRawPacket | - kXMPFiles_HandlerOwnsFile | - kXMPFiles_AllowsSafeUpdate | - kXMPFiles_UsesSidecarXMP ); - -class MPEG2_MetaHandler : public XMPFileHandler -{ -public: - - std::string sidecarPath; - - MPEG2_MetaHandler ( XMPFiles * parent ); - ~MPEG2_MetaHandler(); - - void CacheFileData(); - - void UpdateFile ( bool doSafeUpdate ); - void WriteFile ( LFA_FileRef sourceRef, const std::string & sourcePath ); - -}; // MPEG2_MetaHandler - -// ================================================================================================= - -#endif /* __MPEG2_Handler_hpp__ */ diff --git a/source/XMPFiles/FileHandlers/MPEG4_Handler.cpp b/source/XMPFiles/FileHandlers/MPEG4_Handler.cpp deleted file mode 100644 index 06400d1..0000000 --- a/source/XMPFiles/FileHandlers/MPEG4_Handler.cpp +++ /dev/null @@ -1,2456 +0,0 @@ -// ================================================================================================= -// ADOBE SYSTEMS INCORPORATED -// Copyright 2006 Adobe Systems Incorporated -// 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 "MPEG4_Handler.hpp" -#include "ISOBaseMedia_Support.hpp" -#include "MOOV_Support.hpp" - -#include "UnicodeConversions.hpp" -#include "MD5.h" - -#if XMP_WinBuild - #pragma warning ( disable : 4996 ) // '...' was declared deprecated -#endif - -using namespace std; - -// ================================================================================================= -/// \file MPEG4_Handler.cpp -/// \brief File format handler for MPEG-4, a flavor of the ISO Base Media File Format. -/// -/// This handler ... -/// -// ================================================================================================= - -// The basic content of a timecode sample description table entry. Does not include trailing boxes. - -#pragma pack ( push, 1 ) - -struct stsdBasicEntry { - XMP_Uns32 entrySize; - XMP_Uns32 format; - XMP_Uns8 reserved_1 [6]; - XMP_Uns16 dataRefIndex; - XMP_Uns32 reserved_2; - XMP_Uns32 flags; - XMP_Uns32 timeScale; - XMP_Uns32 frameDuration; - XMP_Uns8 frameCount; - XMP_Uns8 reserved_3; -}; - -#pragma pack ( pop ) - -// ================================================================================================= - -// ! The buffer and constants are both already big endian. -#define Get4CharCode(buffPtr) (*((XMP_Uns32*)(buffPtr))) - -// ================================================================================================= - -static inline bool IsClassicQuickTimeBox ( XMP_Uns32 boxType ) -{ - if ( (boxType == ISOMedia::k_moov) || (boxType == ISOMedia::k_mdat) || (boxType == ISOMedia::k_pnot) || - (boxType == ISOMedia::k_free) || (boxType == ISOMedia::k_skip) || (boxType == ISOMedia::k_wide) ) return true; - return false; -} // IsClassicQuickTimeBox - -// ================================================================================================= - -// Pairs of 3 letter ISO 639-2 codes mapped to 2 letter ISO 639-1 codes from: -// http://www.loc.gov/standards/iso639-2/php/code_list.php -// Would have to write an "==" operator to use std::map, must compare values not pointers. -// ! Not fully sorted, do not use a binary search. - -static XMP_StringPtr kKnownLangs[] = - { "aar", "aa", "abk", "ab", "afr", "af", "aka", "ak", "alb", "sq", "sqi", "sq", "amh", "am", - "ara", "ar", "arg", "an", "arm", "hy", "hye", "hy", "asm", "as", "ava", "av", "ave", "ae", - "aym", "ay", "aze", "az", "bak", "ba", "bam", "bm", "baq", "eu", "eus", "eu", "bel", "be", - "ben", "bn", "bih", "bh", "bis", "bi", "bod", "bo", "tib", "bo", "bos", "bs", "bre", "br", - "bul", "bg", "bur", "my", "mya", "my", "cat", "ca", "ces", "cs", "cze", "cs", "cha", "ch", - "che", "ce", "chi", "zh", "zho", "zh", "chu", "cu", "chv", "cv", "cor", "kw", "cos", "co", - "cre", "cr", "cym", "cy", "wel", "cy", "cze", "cs", "ces", "cs", "dan", "da", "deu", "de", - "ger", "de", "div", "dv", "dut", "nl", "nld", "nl", "dzo", "dz", "ell", "el", "gre", "el", - "eng", "en", "epo", "eo", "est", "et", "eus", "eu", "baq", "eu", "ewe", "ee", "fao", "fo", - "fas", "fa", "per", "fa", "fij", "fj", "fin", "fi", "fra", "fr", "fre", "fr", "fre", "fr", - "fra", "fr", "fry", "fy", "ful", "ff", "geo", "ka", "kat", "ka", "ger", "de", "deu", "de", - "gla", "gd", "gle", "ga", "glg", "gl", "glv", "gv", "gre", "el", "ell", "el", "grn", "gn", - "guj", "gu", "hat", "ht", "hau", "ha", "heb", "he", "her", "hz", "hin", "hi", "hmo", "ho", - "hrv", "hr", "scr", "hr", "hun", "hu", "hye", "hy", "arm", "hy", "ibo", "ig", "ice", "is", - "isl", "is", "ido", "io", "iii", "ii", "iku", "iu", "ile", "ie", "ina", "ia", "ind", "id", - "ipk", "ik", "isl", "is", "ice", "is", "ita", "it", "jav", "jv", "jpn", "ja", "kal", "kl", - "kan", "kn", "kas", "ks", "kat", "ka", "geo", "ka", "kau", "kr", "kaz", "kk", "khm", "km", - "kik", "ki", "kin", "rw", "kir", "ky", "kom", "kv", "kon", "kg", "kor", "ko", "kua", "kj", - "kur", "ku", "lao", "lo", "lat", "la", "lav", "lv", "lim", "li", "lin", "ln", "lit", "lt", - "ltz", "lb", "lub", "lu", "lug", "lg", "mac", "mk", "mkd", "mk", "mah", "mh", "mal", "ml", - "mao", "mi", "mri", "mi", "mar", "mr", "may", "ms", "msa", "ms", "mkd", "mk", "mac", "mk", - "mlg", "mg", "mlt", "mt", "mol", "mo", "mon", "mn", "mri", "mi", "mao", "mi", "msa", "ms", - "may", "ms", "mya", "my", "bur", "my", "nau", "na", "nav", "nv", "nbl", "nr", "nde", "nd", - "ndo", "ng", "nep", "ne", "nld", "nl", "dut", "nl", "nno", "nn", "nob", "nb", "nor", "no", - "nya", "ny", "oci", "oc", "oji", "oj", "ori", "or", "orm", "om", "oss", "os", "pan", "pa", - "per", "fa", "fas", "fa", "pli", "pi", "pol", "pl", "por", "pt", "pus", "ps", "que", "qu", - "roh", "rm", "ron", "ro", "rum", "ro", "rum", "ro", "ron", "ro", "run", "rn", "rus", "ru", - "sag", "sg", "san", "sa", "scc", "sr", "srp", "sr", "scr", "hr", "hrv", "hr", "sin", "si", - "slk", "sk", "slo", "sk", "slo", "sk", "slk", "sk", "slv", "sl", "sme", "se", "smo", "sm", - "sna", "sn", "snd", "sd", "som", "so", "sot", "st", "spa", "es", "sqi", "sq", "alb", "sq", - "srd", "sc", "srp", "sr", "scc", "sr", "ssw", "ss", "sun", "su", "swa", "sw", "swe", "sv", - "tah", "ty", "tam", "ta", "tat", "tt", "tel", "te", "tgk", "tg", "tgl", "tl", "tha", "th", - "tib", "bo", "bod", "bo", "tir", "ti", "ton", "to", "tsn", "tn", "tso", "ts", "tuk", "tk", - "tur", "tr", "twi", "tw", "uig", "ug", "ukr", "uk", "urd", "ur", "uzb", "uz", "ven", "ve", - "vie", "vi", "vol", "vo", "wel", "cy", "cym", "cy", "wln", "wa", "wol", "wo", "xho", "xh", - "yid", "yi", "yor", "yo", "zha", "za", "zho", "zh", "chi", "zh", "zul", "zu", - 0, 0 }; - -static inline XMP_StringPtr Lookup2LetterLang ( XMP_StringPtr lang3 ) -{ - for ( size_t i = 0; kKnownLangs[i] != 0; i += 2 ) { - if ( XMP_LitMatch ( lang3, kKnownLangs[i] ) ) return kKnownLangs[i+1]; - } - return ""; -} - -static inline XMP_StringPtr Lookup3LetterLang ( XMP_StringPtr lang2 ) -{ - for ( size_t i = 0; kKnownLangs[i] != 0; i += 2 ) { - if ( XMP_LitMatch ( lang2, kKnownLangs[i+1] ) ) return kKnownLangs[i]; - } - return ""; -} - -// ================================================================================================= -// MPEG4_CheckFormat -// ================= -// -// There are 3 variations of recognized file: -// - Normal MPEG-4 - has an 'ftyp' box containing a known compatible brand but not 'qt '. -// - Modern QuickTime - has an 'ftyp' box containing 'qt ' as a compatible brand. -// - Classic QuickTime - has no 'ftyp' box, should have recognized top level boxes. -// -// An MPEG-4 or modern QuickTime file is an instance of an ISO Base Media file, ISO 14496-12 and -14. -// A classic QuickTime file has the same physical box structure, but somewhat different box types. -// The ISO files must begin with an 'ftyp' box containing 'mp41', 'mp42', 'f4v ', or 'qt ' in the -// compatible brands. -// -// The general box structure is: -// -// 0 4 uns32 box size, 0 means "to EoF", 1 means 64-bit size follows -// 4 4 uns32 box type -// 8 8 uns64 box size, present only if uns32 size is 1 -// - * box content -// -// The 'ftyp' box content is: -// -// - 4 uns32 major brand -// - 4 uns32 minor version -// - * uns32 sequence of compatible brands, to the end of the box - -// ! With the addition of QuickTime support there is some change in behavior in OpenFile when the -// ! kXMPFiles_OpenStrictly option is used wth a specific file format. The kXMP_MPEG4File and -// ! kXMP_MOVFile formats are treated uniformly, you can't force "real MOV" or "real MPEG-4". You -// ! can check afterwards using GetFileInfo to see what the file happens to be. - -bool MPEG4_CheckFormat ( XMP_FileFormat format, - XMP_StringPtr filePath, - LFA_FileRef fileRef, - XMPFiles* parent ) -{ - XMP_Uns8 buffer [4*1024]; - XMP_Uns32 ioCount, brandCount, brandOffset; - XMP_Uns64 fileSize, nextOffset; - ISOMedia::BoxInfo currBox; - - #define IsTolerableBoxChar(ch) ( ((0x20 <= (ch)) && ((ch) <= 0x7E)) || ((ch) == 0xA9) ) - - XMP_AbortProc abortProc = parent->abortProc; - void * abortArg = parent->abortArg; - const bool checkAbort = (abortProc != 0); - - bool openStrictly = XMP_OptionIsSet ( parent->openFlags, kXMPFiles_OpenStrictly); - - // Get the first box's info, see if it is 'ftyp' or not. - - XMP_Assert ( (parent->tempPtr == 0) && (parent->tempUI32 == 0) ); - - fileSize = LFA_Measure ( fileRef ); - nextOffset = ISOMedia::GetBoxInfo ( fileRef, 0, fileSize, &currBox ); - if ( currBox.headerSize < 8 ) return false; // Can't be an ISO or QuickTime file. - - if ( currBox.boxType == ISOMedia::k_ftyp ) { - - // Have an 'ftyp' box, look through the compatible brands. If 'qt ' is present then this is - // a modern QuickTime file, regardless of what else is found. Otherwise this is plain ISO if - // any of the other recognized brands are found. - - if ( currBox.contentSize < 12 ) return false; // No compatible brands at all. - if ( currBox.contentSize > 1024*1024 ) return false; // Sanity check and make sure count fits in 32 bits. - brandCount = ((XMP_Uns32)currBox.contentSize - 8) >> 2; - - LFA_Seek ( fileRef, 8, SEEK_CUR ); // Skip the major and minor brands. - ioCount = brandOffset = 0; - - bool haveCompatibleBrand = false; - - for ( ; brandCount > 0; --brandCount, brandOffset += 4 ) { - - if ( brandOffset >= ioCount ) { - if ( checkAbort && abortProc(abortArg) ) { - XMP_Throw ( "MPEG4_CheckFormat - User abort", kXMPErr_UserAbort ); - } - ioCount = 4 * brandCount; - if ( ioCount > sizeof(buffer) ) ioCount = sizeof(buffer); - ioCount = LFA_Read ( fileRef, buffer, ioCount, kLFA_RequireAll ); - brandOffset = 0; - } - - XMP_Uns32 brand = GetUns32BE ( &buffer[brandOffset] ); - if ( brand == ISOMedia::k_qt ) { // Don't need to look further. - if ( openStrictly && (format != kXMP_MOVFile) ) return false; - parent->format = kXMP_MOVFile; - parent->tempUI32 = MOOV_Manager::kFileIsModernQT; - return true; - } else if ( (brand == ISOMedia::k_mp41) || (brand == ISOMedia::k_mp42) || (brand == ISOMedia::k_f4v) ) { - haveCompatibleBrand = true; // Need to keep looking in case 'qt ' follows. - } - - } - - if ( ! haveCompatibleBrand ) return false; - if ( openStrictly && (format != kXMP_MPEG4File) ) return false; - parent->format = kXMP_MPEG4File; - parent->tempUI32 = MOOV_Manager::kFileIsNormalISO; - return true; - - } else { - - // No 'ftyp', look for classic QuickTime: 'moov', 'mdat', 'pnot', 'free', 'skip', and 'wide'. - // As an expedient, quit when a 'moov' box is found. Tolerate other boxes, they are in the - // wild for ill-formed files, e.g. seen when 'moov'/'udta' children get left at top level. - - while ( currBox.boxType != ISOMedia::k_moov ) { - - if ( ! IsClassicQuickTimeBox ( currBox.boxType ) ) { - // Make sure the box type is 4 ASCII characters or 0xA9 (MacRoman copyright). - XMP_Uns8 b1 = (XMP_Uns8) (currBox.boxType >> 24); - XMP_Uns8 b2 = (XMP_Uns8) ((currBox.boxType >> 16) & 0xFF); - XMP_Uns8 b3 = (XMP_Uns8) ((currBox.boxType >> 8) & 0xFF); - XMP_Uns8 b4 = (XMP_Uns8) (currBox.boxType & 0xFF); - bool ok = IsTolerableBoxChar(b1) && IsTolerableBoxChar(b2) && - IsTolerableBoxChar(b3) && IsTolerableBoxChar(b4); - if ( ! ok ) return false; - } - if ( nextOffset >= fileSize ) return false; - if ( checkAbort && abortProc(abortArg) ) { - XMP_Throw ( "MPEG4_CheckFormat - User abort", kXMPErr_UserAbort ); - } - nextOffset = ISOMedia::GetBoxInfo ( fileRef, nextOffset, fileSize, &currBox ); - - } - - if ( openStrictly && (format != kXMP_MOVFile) ) return false; - parent->format = kXMP_MOVFile; - parent->tempUI32 = MOOV_Manager::kFileIsTraditionalQT; - return true; - - } - - return false; - -} // MPEG4_CheckFormat - -// ================================================================================================= -// MPEG4_MetaHandlerCTor -// ===================== - -XMPFileHandler * MPEG4_MetaHandlerCTor ( XMPFiles * parent ) -{ - - return new MPEG4_MetaHandler ( parent ); - -} // MPEG4_MetaHandlerCTor - -// ================================================================================================= -// MPEG4_MetaHandler::MPEG4_MetaHandler -// ==================================== - -MPEG4_MetaHandler::MPEG4_MetaHandler ( XMPFiles * _parent ) - : fileMode(0), havePreferredXMP(false), xmpBoxPos(0), moovBoxPos(0), xmpBoxSize(0), moovBoxSize(0) -{ - - this->parent = _parent; // Inherited, can't set in the prefix. - this->handlerFlags = kMPEG4_HandlerFlags; - this->stdCharForm = kXMP_Char8Bit; - this->fileMode = (XMP_Uns8)_parent->tempUI32; - _parent->tempUI32 = 0; - -} // MPEG4_MetaHandler::MPEG4_MetaHandler - -// ================================================================================================= -// MPEG4_MetaHandler::~MPEG4_MetaHandler -// ===================================== - -MPEG4_MetaHandler::~MPEG4_MetaHandler() -{ - - // Nothing to do. - -} // MPEG4_MetaHandler::~MPEG4_MetaHandler - -// ================================================================================================= -// SecondsToXMPDate -// ================ - -// *** ASF has similar code with different origin, should make a shared utility. - -static void SecondsToXMPDate ( XMP_Uns64 isoSeconds, XMP_DateTime * xmpDate ) -{ - memset ( xmpDate, 0, sizeof(XMP_DateTime) ); // AUDIT: Using sizeof(XMP_DateTime) is safe. - - XMP_Int32 days = (XMP_Int32) (isoSeconds / 86400); - isoSeconds -= ((XMP_Uns64)days * 86400); - - XMP_Int32 hour = (XMP_Int32) (isoSeconds / 3600); - isoSeconds -= ((XMP_Uns64)hour * 3600); - - XMP_Int32 minute = (XMP_Int32) (isoSeconds / 60); - isoSeconds -= ((XMP_Uns64)minute * 60); - - XMP_Int32 second = (XMP_Int32)isoSeconds; - - xmpDate->year = 1904; // Start with the ISO origin. - xmpDate->month = 1; - xmpDate->day = 1; - - xmpDate->day += days; // Add in the delta. - xmpDate->hour = hour; - xmpDate->minute = minute; - xmpDate->second = second; - - xmpDate->hasTimeZone = true; // ! Needed for ConvertToUTCTime to do anything. - SXMPUtils::ConvertToUTCTime ( xmpDate ); // Normalize the date/time. - -} // SecondsToXMPDate - -// ================================================================================================= -// XMPDateToSeconds -// ================ - -// *** ASF has similar code with different origin, should make a shared utility. - -static bool IsLeapYear ( XMP_Int32 year ) -{ - if ( year < 0 ) year = -year + 1; // Fold the negative years, assuming there is a year 0. - if ( (year % 4) != 0 ) return false; // Not a multiple of 4. - if ( (year % 100) != 0 ) return true; // A multiple of 4 but not a multiple of 100. - if ( (year % 400) == 0 ) return true; // A multiple of 400. - return false; // A multiple of 100 but not a multiple of 400. -} - -// ------------------------------------------------------------------------------------------------- - -static XMP_Int32 DaysInMonth ( XMP_Int32 year, XMP_Int32 month ) -{ - static XMP_Int32 daysInMonth[13] = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; - // Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec - XMP_Int32 days = daysInMonth[month]; - if ( (month == 2) && IsLeapYear(year) ) days += 1; - return days; -} - -// ------------------------------------------------------------------------------------------------- - -static void XMPDateToSeconds ( const XMP_DateTime & _xmpDate, XMP_Uns64 * isoSeconds ) -{ - XMP_DateTime xmpDate = _xmpDate; - SXMPUtils::ConvertToUTCTime ( &xmpDate ); - - XMP_Uns64 tempSeconds = (XMP_Uns64)xmpDate.second; - tempSeconds += (XMP_Uns64)xmpDate.minute * 60; - tempSeconds += (XMP_Uns64)xmpDate.hour * 3600; - - XMP_Int32 days = (xmpDate.day - 1); - - --xmpDate.month; - while ( xmpDate.month >= 1 ) { - days += DaysInMonth ( xmpDate.year, xmpDate.month ); - --xmpDate.month; - } - - --xmpDate.year; - while ( xmpDate.year >= 1904 ) { - days += (IsLeapYear ( xmpDate.year) ? 366 : 365 ); - --xmpDate.year; - } - - tempSeconds += (XMP_Uns64)days * 86400; - *isoSeconds = tempSeconds; - -} // XMPDateToSeconds - -// ================================================================================================= -// ImportMVHDItems -// =============== - -static bool ImportMVHDItems ( MOOV_Manager::BoxInfo mvhdInfo, SXMPMeta * xmp ) -{ - XMP_Assert ( mvhdInfo.boxType == ISOMedia::k_mvhd ); - if ( mvhdInfo.contentSize < 4 ) return false; // Just enough to check the version/flags at first. - - XMP_Uns8 mvhdVersion = *mvhdInfo.content; - if ( mvhdVersion > 1 ) return false; - - XMP_Uns64 creationTime, modificationTime, duration; - XMP_Uns32 timescale; - - if ( mvhdVersion == 0 ) { - - if ( mvhdInfo.contentSize < sizeof ( MOOV_Manager::Content_mvhd_0 ) ) return false; - MOOV_Manager::Content_mvhd_0 * mvhdRaw_0 = (MOOV_Manager::Content_mvhd_0*) mvhdInfo.content; - - creationTime = (XMP_Uns64) GetUns32BE ( &mvhdRaw_0->creationTime ); - modificationTime = (XMP_Uns64) GetUns32BE ( &mvhdRaw_0->modificationTime ); - timescale = GetUns32BE ( &mvhdRaw_0->timescale ); - duration = (XMP_Uns64) GetUns32BE ( &mvhdRaw_0->duration ); - - } else { - - XMP_Assert ( mvhdVersion == 1 ); - if ( mvhdInfo.contentSize < sizeof ( MOOV_Manager::Content_mvhd_1 ) ) return false; - MOOV_Manager::Content_mvhd_1 * mvhdRaw_1 = (MOOV_Manager::Content_mvhd_1*) mvhdInfo.content; - - creationTime = GetUns64BE ( &mvhdRaw_1->creationTime ); - modificationTime = GetUns64BE ( &mvhdRaw_1->modificationTime ); - timescale = GetUns32BE ( &mvhdRaw_1->timescale ); - duration = GetUns64BE ( &mvhdRaw_1->duration ); - - } - - bool haveImports = false; - XMP_DateTime xmpDate; - - if ( (creationTime >> 32) < 0xFF ) { // Sanity check for bogus date info. - SecondsToXMPDate ( creationTime, &xmpDate ); - xmp->SetProperty_Date ( kXMP_NS_XMP, "CreateDate", xmpDate ); - haveImports = true; - } - - if ( (modificationTime >> 32) < 0xFF ) { // Sanity check for bogus date info. - SecondsToXMPDate ( modificationTime, &xmpDate ); - xmp->SetProperty_Date ( kXMP_NS_XMP, "ModifyDate", xmpDate ); - haveImports = true; - } - - if ( timescale != 0 ) { // Avoid 1/0 for the scale field. - char buffer [32]; // A 64-bit number is at most 20 digits. - xmp->DeleteProperty ( kXMP_NS_DM, "duration" ); // Delete the whole struct. - snprintf ( buffer, sizeof(buffer), "%llu", duration ); // AUDIT: The buffer is big enough. - xmp->SetStructField ( kXMP_NS_DM, "duration", kXMP_NS_DM, "value", &buffer[0] ); - snprintf ( buffer, sizeof(buffer), "1/%u", timescale ); // AUDIT: The buffer is big enough. - xmp->SetStructField ( kXMP_NS_DM, "duration", kXMP_NS_DM, "scale", &buffer[0] ); - haveImports = true; - } - - return haveImports; - -} // ImportMVHDItems - -// ================================================================================================= -// ExportMVHDItems -// =============== - -static void ExportMVHDItems ( const SXMPMeta & xmp, MOOV_Manager * moovMgr ) -{ - XMP_DateTime xmpDate; - bool createFound, modifyFound; - XMP_Uns64 createSeconds = 0, modifySeconds = 0; - - MOOV_Manager::BoxInfo mvhdInfo; - MOOV_Manager::BoxRef mvhdRef = moovMgr->GetBox ( "moov/mvhd", &mvhdInfo ); - if ( (mvhdRef == 0) || (mvhdInfo.contentSize < 4) ) return; - - XMP_Uns8 version = *mvhdInfo.content; - if ( version > 1 ) return; - - createFound = xmp.GetProperty_Date ( kXMP_NS_XMP, "CreateDate", &xmpDate, 0 ); - if ( createFound ) XMPDateToSeconds ( xmpDate, &createSeconds ); - - modifyFound = xmp.GetProperty_Date ( kXMP_NS_XMP, "ModifyDate", &xmpDate, 0 ); - if ( modifyFound ) XMPDateToSeconds ( xmpDate, &modifySeconds ); - - if ( version == 1 ) { - - // Modify the v1 box in-place. - - if ( mvhdInfo.contentSize < sizeof ( MOOV_Manager::Content_mvhd_1 ) ) return; - - XMP_Uns64 oldCreate = GetUns64BE ( mvhdInfo.content + 4 ); - XMP_Uns64 oldModify = GetUns64BE ( mvhdInfo.content + 12 ); - - if ( createFound ) { - if ( createSeconds != oldCreate ) PutUns64BE ( createSeconds, ((XMP_Uns8*)mvhdInfo.content + 4) ); - moovMgr->NoteChange(); - } - if ( modifyFound ) { - if ( modifySeconds != oldModify ) PutUns64BE ( modifySeconds, ((XMP_Uns8*)mvhdInfo.content + 12) ); - moovMgr->NoteChange(); - } - - } else if ( ((createSeconds >> 32) == 0) && ((modifySeconds >> 32) == 0) ) { - - // Modify the v0 box in-place. - - if ( mvhdInfo.contentSize < sizeof ( MOOV_Manager::Content_mvhd_0 ) ) return; - - XMP_Uns32 oldCreate = GetUns32BE ( mvhdInfo.content + 4 ); - XMP_Uns32 oldModify = GetUns32BE ( mvhdInfo.content + 8 ); - - if ( createFound ) { - if ( (XMP_Uns32)createSeconds != oldCreate ) PutUns32BE ( (XMP_Uns32)createSeconds, ((XMP_Uns8*)mvhdInfo.content + 4) ); - moovMgr->NoteChange(); - } - if ( modifyFound ) { - if ( (XMP_Uns32)modifySeconds != oldModify ) PutUns32BE ( (XMP_Uns32)modifySeconds, ((XMP_Uns8*)mvhdInfo.content + 8) ); - moovMgr->NoteChange(); - } - - } else { - - // Replace the v0 box with a v1 box. - - XMP_Assert ( createFound | modifyFound ); // One of them has high bits set. - if ( mvhdInfo.contentSize != sizeof ( MOOV_Manager::Content_mvhd_0 ) ) return; - - MOOV_Manager::Content_mvhd_0 * mvhdV0 = (MOOV_Manager::Content_mvhd_0*) mvhdInfo.content; - MOOV_Manager::Content_mvhd_1 mvhdV1; - - // Copy the unchanged fields directly. - - mvhdV1.timescale = mvhdV0->timescale; - mvhdV1.rate = mvhdV0->rate; - mvhdV1.volume = mvhdV0->volume; - mvhdV1.pad_1 = mvhdV0->pad_1; - mvhdV1.pad_2 = mvhdV0->pad_2; - mvhdV1.pad_3 = mvhdV0->pad_3; - for ( int i = 0; i < 9; ++i ) mvhdV1.matrix[i] = mvhdV0->matrix[i]; - for ( int i = 0; i < 6; ++i ) mvhdV1.preDef[i] = mvhdV0->preDef[i]; - mvhdV1.nextTrackID = mvhdV0->nextTrackID; - - // Set the fields that have changes. - - mvhdV1.vFlags = (1 << 24) | (mvhdV0->vFlags & 0xFFFFFF); - mvhdV1.duration = MakeUns64BE ( (XMP_Uns64) GetUns32BE ( &mvhdV0->duration ) ); - - XMP_Uns64 temp64; - - temp64 = (XMP_Uns64) GetUns32BE ( &mvhdV0->creationTime ); - if ( createFound ) temp64 = createSeconds; - mvhdV1.creationTime = MakeUns64BE ( temp64 ); - - temp64 = (XMP_Uns64) GetUns32BE ( &mvhdV0->modificationTime ); - if ( modifyFound ) temp64 = modifySeconds; - mvhdV1.modificationTime = MakeUns64BE ( temp64 ); - - moovMgr->SetBox ( mvhdRef, &mvhdV1, sizeof ( MOOV_Manager::Content_mvhd_1 ) ); - - } - -} // ExportMVHDItems - -// ================================================================================================= -// ImportISOCopyrights -// =================== -// -// The cached 'moov'/'udta'/'cprt' boxes are full boxes. The "real" content is a UInt16 packed 3 -// character language code and a UTF-8 or UTF-16 string. - -static bool ImportISOCopyrights ( const std::vector<MOOV_Manager::BoxInfo> & cprtBoxes, SXMPMeta * xmp ) -{ - bool haveImports = false; - - std::string tempStr; - char lang3 [4]; // The unpacked ISO-639-2/T language code with final null. - lang3[3] = 0; - - for ( size_t i = 0, limit = cprtBoxes.size(); i < limit; ++i ) { - - const MOOV_Manager::BoxInfo & currBox = cprtBoxes[i]; - if ( currBox.contentSize < 4+2+1 ) continue; // Want enough for a non-empty value. - if ( *currBox.content != 0 ) continue; // Only proceed for version 0, ignore the flags. - - XMP_Uns16 packedLang = GetUns16BE ( currBox.content + 4 ); - lang3[0] = (packedLang >> 10) + 0x60; - lang3[1] = ((packedLang >> 5) & 0x1F) + 0x60; - lang3[2] = (packedLang & 0x1F) + 0x60; - XMP_StringPtr xmpLang = Lookup2LetterLang ( lang3 ); - if ( *xmpLang == 0 ) continue; - - XMP_StringPtr textPtr = (XMP_StringPtr) (currBox.content + 6); - XMP_StringLen textLen = currBox.contentSize - 6; - - if ( (textLen >= 2) && (GetUns16BE(textPtr) == 0xFEFF) ) { - FromUTF16 ( (UTF16Unit*)textPtr, textLen/2, &tempStr, true /* big endian */ ); - textPtr = tempStr.c_str(); - } - - xmp->SetLocalizedText ( kXMP_NS_DC, "rights", xmpLang, xmpLang, textPtr ); - haveImports = true; - - } - - return haveImports; - -} // ImportISOCopyrights - -// ================================================================================================= -// ExportISOCopyrights -// =================== - -static void ExportISOCopyrights ( const SXMPMeta & xmp, MOOV_Manager * moovMgr ) -{ - bool haveMappings = false; // True if any ISO-XMP language mappings are found. - - // Go through the ISO 'cprt' items and look for a corresponding XMP item. Ignore the ISO item if - // there is no language mapping to XMP. Update the ISO item if the mappable XMP exists, delete - // the ISO item if the mappable XMP does not exist. Since the import side would have made sure - // the mappable XMP items existed, if they don't now they must have been deleted. - - MOOV_Manager::BoxInfo udtaInfo; - MOOV_Manager::BoxRef udtaRef = moovMgr->GetBox ( "moov/udta", &udtaInfo ); - if ( udtaRef == 0 ) return; - - std::string xmpPath, xmpValue, xmpLang, tempStr; - char lang3 [4]; // An unpacked ISO-639-2/T language code. - lang3[3] = 0; - - for ( XMP_Uns32 ordinal = udtaInfo.childCount; ordinal > 0; --ordinal ) { // ! Go backwards because of deletions. - - MOOV_Manager::BoxInfo cprtInfo; - MOOV_Manager::BoxRef cprtRef = moovMgr->GetNthChild ( udtaRef, ordinal-1, &cprtInfo ); - if ( (cprtRef == 0) ) break; // Sanity check, should not happen. - if ( (cprtInfo.boxType != ISOMedia::k_cprt) || (cprtInfo.contentSize < 6) ) continue; - if ( *cprtInfo.content != 0 ) continue; // Only accept version 0, ignore the flags. - - XMP_Uns16 packedLang = GetUns16BE ( cprtInfo.content + 4 ); - lang3[0] = (packedLang >> 10) + 0x60; - lang3[1] = ((packedLang >> 5) & 0x1F) + 0x60; - lang3[2] = (packedLang & 0x1F) + 0x60; - - XMP_StringPtr lang2 = Lookup2LetterLang ( lang3 ); - if ( *lang2 == 0 ) continue; // No language mapping to XMP. - haveMappings = true; - - bool xmpFound = xmp.GetLocalizedText ( kXMP_NS_DC, "rights", lang2, lang2, &xmpLang, &xmpValue, 0 ); - if ( xmpFound ) { - if ( (xmpLang.size() < 2) || - ( (xmpLang.size() == 2) && (xmpLang != lang2) ) || - ( (xmpLang.size() > 2) && ( (xmpLang[2] != '-') || (! XMP_LitNMatch ( xmpLang.c_str(), lang2, 2)) ) ) ) { - xmpFound = false; // The language does not match, the corresponding XMP does not exist. - } - } - - if ( ! xmpFound ) { - - // No XMP, delete the ISO item. - moovMgr->DeleteNthChild ( udtaRef, ordinal-1 ); - - } else { - - // Update the ISO item if necessary. - XMP_StringPtr isoStr = (XMP_StringPtr)cprtInfo.content + 6; - size_t rawLen = cprtInfo.contentSize - 6; - if ( (rawLen >= 8) && (GetUns16BE(isoStr) == 0xFEFF) ) { - FromUTF16 ( (UTF16Unit*)(isoStr+2), (rawLen-2)/2, &tempStr, true /* big endian */ ); - isoStr = tempStr.c_str(); - } - if ( xmpValue != isoStr ) { - std::string newContent = "vfffll"; - newContent += xmpValue; - memcpy ( (char*)newContent.c_str(), cprtInfo.content, 6 ); // Keep old version, flags, and language. - moovMgr->SetBox ( cprtRef, newContent.c_str(), (XMP_Uns32)(newContent.size() + 1) ); - } - - } - - } - - // Go through the XMP items and look for a corresponding ISO item. Skip if found (did it above), - // otherwise add a new ISO item. - - bool haveXDefault = false; - XMP_Index xmpCount = xmp.CountArrayItems ( kXMP_NS_DC, "rights" ); - - for ( XMP_Index xmpIndex = 1; xmpIndex <= xmpCount; ++xmpIndex ) { // ! The first XMP array index is 1. - - SXMPUtils::ComposeArrayItemPath ( kXMP_NS_DC, "rights", xmpIndex, &xmpPath ); - xmp.GetArrayItem ( kXMP_NS_DC, "rights", xmpIndex, &xmpValue, 0 ); - bool hasLang = xmp.GetQualifier ( kXMP_NS_DC, xmpPath.c_str(), kXMP_NS_XML, "lang", &xmpLang, 0 ); - if ( ! hasLang ) continue; // Sanity check. - if ( xmpLang == "x-default" ) { - haveXDefault = true; // See later special case. - continue; - } - - XMP_StringPtr isoLang = ""; - size_t rootLen = xmpLang.find ( '-' ); - if ( rootLen == std::string::npos ) rootLen = xmpLang.size(); - if ( rootLen == 2 ) { - xmpLang[2] = 0; - isoLang = Lookup3LetterLang ( xmpLang.c_str() ); - if ( *isoLang == 0 ) continue; - } else if ( rootLen == 3 ) { - xmpLang[3] = 0; - isoLang = xmpLang.c_str(); - } else { - continue; - } - haveMappings = true; - - bool isoFound = false; - XMP_Uns16 packedLang = ((isoLang[0] - 0x60) << 10) | ((isoLang[1] - 0x60) << 5) | (isoLang[2] - 0x60); - - for ( XMP_Uns32 isoIndex = 0; (isoIndex < udtaInfo.childCount) && (! isoFound); ++isoIndex ) { - - MOOV_Manager::BoxInfo cprtInfo; - MOOV_Manager::BoxRef cprtRef = moovMgr->GetNthChild ( udtaRef, isoIndex, &cprtInfo ); - if ( (cprtRef == 0) ) break; // Sanity check, should not happen. - if ( (cprtInfo.boxType != ISOMedia::k_cprt) || (cprtInfo.contentSize < 6) ) continue; - if ( *cprtInfo.content != 0 ) continue; // Only accept version 0, ignore the flags. - if ( packedLang != GetUns16BE ( cprtInfo.content + 4 ) ) continue; // Look for matching language. - - isoFound = true; // Found the language entry, whether or not we update it. - - } - - if ( ! isoFound ) { - - std::string newContent = "vfffll"; - newContent += xmpValue; - *((XMP_Uns32*)newContent.c_str()) = 0; // Set the version and flags to zero. - PutUns16BE ( packedLang, (char*)newContent.c_str() + 4 ); - moovMgr->AddChildBox ( udtaRef, ISOMedia::k_cprt, newContent.c_str(), (XMP_Uns32)(newContent.size() + 1) ); - - } - - } - - // If there were no mappings in the loops, export the XMP "x-default" value to the first ISO item. - - if ( ! haveMappings ) { - - MOOV_Manager::BoxInfo cprtInfo; - MOOV_Manager::BoxRef cprtRef = moovMgr->GetTypeChild ( udtaRef, ISOMedia::k_cprt, &cprtInfo ); - - if ( (cprtRef != 0) && (cprtInfo.contentSize >= 6) && (*cprtInfo.content == 0) ) { - - bool xmpFound = xmp.GetLocalizedText ( kXMP_NS_DC, "rights", "", "x-default", &xmpLang, &xmpValue, 0 ); - - if ( xmpFound && (xmpLang == "x-default") ) { - - // Update the ISO item if necessary. - XMP_StringPtr isoStr = (XMP_StringPtr)cprtInfo.content + 6; - size_t rawLen = cprtInfo.contentSize - 6; - if ( (rawLen >= 8) && (GetUns16BE(isoStr) == 0xFEFF) ) { - FromUTF16 ( (UTF16Unit*)(isoStr+2), (rawLen-2)/2, &tempStr, true /* big endian */ ); - isoStr = tempStr.c_str(); - } - if ( xmpValue != isoStr ) { - std::string newContent = "vfffll"; - newContent += xmpValue; - memcpy ( (char*)newContent.c_str(), cprtInfo.content, 6 ); // Keep old version, flags, and language. - moovMgr->SetBox ( cprtRef, newContent.c_str(), (XMP_Uns32)(newContent.size() + 1) ); - } - - } - - } - - } - -} // ExportISOCopyrights - -// ================================================================================================= -// ExportQuickTimeItems -// ==================== - -static void ExportQuickTimeItems ( const SXMPMeta & xmp, TradQT_Manager * qtMgr, MOOV_Manager * moovMgr ) -{ - - // The QuickTime 'udta' timecode items are done here for simplicity. - - #define createWithZeroLang true - - qtMgr->ExportSimpleXMP ( kQTilst_Reel, xmp, kXMP_NS_DM, "tapeName" ); - qtMgr->ExportSimpleXMP ( kQTilst_Timecode, xmp, kXMP_NS_DM, "startTimecode/xmpDM:timeValue", createWithZeroLang ); - qtMgr->ExportSimpleXMP ( kQTilst_TimeScale, xmp, kXMP_NS_DM, "startTimeScale", createWithZeroLang ); - qtMgr->ExportSimpleXMP ( kQTilst_TimeSize, xmp, kXMP_NS_DM, "startTimeSampleSize", createWithZeroLang ); - - qtMgr->UpdateChangedBoxes ( moovMgr ); - -} // ExportQuickTimeItems - -// ================================================================================================= -// SelectTimeFormat -// ================ - -static const char * SelectTimeFormat ( const MPEG4_MetaHandler::TimecodeTrackInfo & tmcdInfo ) -{ - const char * timeFormat = 0; - - float fltFPS = (float)tmcdInfo.timeScale / (float)tmcdInfo.frameDuration; - int intFPS = (int) (fltFPS + 0.5); - - switch ( intFPS ) { - - case 30: - if ( fltFPS >= 29.985 ) { - timeFormat = "30Timecode"; - } else if ( tmcdInfo.isDropFrame ) { - timeFormat = "2997DropTimecode"; - } else { - timeFormat = "2997NonDropTimecode"; - } - break; - - case 24: - if ( fltFPS >= 23.988 ) { - timeFormat = "24Timecode"; - } else { - timeFormat = "23976Timecode"; - } - break; - - case 25: - timeFormat = "25Timecode"; - break; - - case 50: - timeFormat = "50Timecode"; - break; - - case 60: - if ( fltFPS >= 59.97 ) { - timeFormat = "60Timecode"; - } else if ( tmcdInfo.isDropFrame ) { - timeFormat = "5994DropTimecode"; - } else { - timeFormat = "5994NonDropTimecode"; - } - break; - - } - - return timeFormat; - -} // SelectTimeFormat - -// ================================================================================================= -// SelectTimeFormat -// ================ - -static const char * SelectTimeFormat ( const SXMPMeta & xmp ) -{ - bool ok; - MPEG4_MetaHandler::TimecodeTrackInfo tmcdInfo; - - XMP_Int64 timeScale; - ok = xmp.GetProperty_Int64 ( kXMP_NS_DM, "startTimeScale", &timeScale, 0 ); - if ( ! ok ) return 0; - tmcdInfo.timeScale = (XMP_Uns32)timeScale; - - XMP_Int64 frameDuration; - ok = xmp.GetProperty_Int64 ( kXMP_NS_DM, "startTimeSampleSize", &frameDuration, 0 ); - if ( ! ok ) return 0; - tmcdInfo.frameDuration = (XMP_Uns32)frameDuration; - - std::string timecode; - ok = xmp.GetProperty ( kXMP_NS_DM, "startTimecode/xmpDM:timeValue", &timecode, 0 ); - if ( ! ok ) return 0; - if ( (timecode.size() == 11) && (timecode[8] == ';') ) tmcdInfo.isDropFrame = true; - - return SelectTimeFormat ( tmcdInfo ); - -} // SelectTimeFormat - -// ================================================================================================= -// ComposeTimecode -// =============== - -static const char * kDecDigits = "0123456789"; -#define TwoDigits(val,str) (str)[0] = kDecDigits[(val)/10]; (str)[1] = kDecDigits[(val)%10] - -static bool ComposeTimecode ( const MPEG4_MetaHandler::TimecodeTrackInfo & tmcdInfo, std::string * strValue ) -{ - float fltFPS = (float)tmcdInfo.timeScale / (float)tmcdInfo.frameDuration; - int intFPS = (int) (fltFPS + 0.5); - if ( (intFPS != 30) && (intFPS != 24) && (intFPS != 25) && (intFPS != 50) && (intFPS != 60) ) return false; - - XMP_Uns32 framesPerDay = intFPS * 24*60*60; - XMP_Uns32 dropLimit = 2; // Used in the drop-frame correction. - - if ( tmcdInfo.isDropFrame ) { - if ( intFPS == 30 ) { - framesPerDay = 2589408; // = 29.97 * 24*60*60 - } else if ( intFPS == 60 ) { - framesPerDay = 5178816; // = 59.94 * 24*60*60 - dropLimit = 4; - } else { - strValue->erase(); - return false; // Dropframe can only apply to 29.97 and 59.94. - } - } - - XMP_Uns32 framesPerHour = framesPerDay / 24; - XMP_Uns32 framesPerTenMinutes = framesPerHour / 6; - XMP_Uns32 framesPerMinute = framesPerTenMinutes / 10; - - XMP_Uns32 frameCount = tmcdInfo.timecodeSample; - while (frameCount >= framesPerDay ) frameCount -= framesPerDay; // Normalize to be within 24 hours. - - XMP_Uns32 hours, minHigh, minLow, seconds; - - hours = frameCount / framesPerHour; - frameCount -= (hours * framesPerHour); - - minHigh = frameCount / framesPerTenMinutes; - frameCount -= (minHigh * framesPerTenMinutes); - - minLow = frameCount / framesPerMinute; - frameCount -= (minLow * framesPerMinute); - - // Do some drop-frame corrections at this point: If this is drop-frame and the units of minutes - // is non-zero, and the seconds are zero, and the frames are zero or one, the time is illegal. - // Perform correction by subtracting 1 from the units of minutes and adding 1798 to the frames.Ê - // For example, 1:00:00 becomes 59:28, and 1:00:01 becomes 59:29. A special case can occur for - // when the frameCount just before the minHigh calculation is less than framesPerTenMinutes but - // more than 10*framesPerMinute. This happens because of roundoff, and will result in a minHigh - // of 0 and a minLow of 10.ÊThe drop frame correction mustÊalso be performed for this case. - - if ( tmcdInfo.isDropFrame ) { - if ( (minLow == 10) || ((minLow != 0) && (frameCount < dropLimit)) ) { - minLow -= 1; - frameCount += framesPerMinute; - } - } - - seconds = frameCount / intFPS; - frameCount -= (seconds * intFPS); - - if ( tmcdInfo.isDropFrame ) { - *strValue = "hh;mm;ss;ff"; - } else { - *strValue = "hh:mm:ss:ff"; - } - - char * str = (char*)strValue->c_str(); - TwoDigits ( hours, str ); - str[3] = kDecDigits[minHigh]; str[4] = kDecDigits[minLow]; - TwoDigits ( seconds, str+6 ); - TwoDigits ( frameCount, str+9 ); - - return true; - -} // ComposeTimecode - -// ================================================================================================= -// DecomposeTimecode -// ================= - -static bool DecomposeTimecode ( const char * strValue, MPEG4_MetaHandler::TimecodeTrackInfo * tmcdInfo ) -{ - float fltFPS = (float)tmcdInfo->timeScale / (float)tmcdInfo->frameDuration; - int intFPS = (int) (fltFPS + 0.5); - if ( (intFPS != 30) && (intFPS != 24) && (intFPS != 25) && (intFPS != 50) && (intFPS != 60) ) return false; - - XMP_Uns32 framesPerDay = intFPS * 24*60*60; - - int items, hours, minutes, seconds, frames; - - if ( ! tmcdInfo->isDropFrame ) { - items = sscanf ( strValue, "%d:%d:%d:%d", &hours, &minutes, &seconds, &frames ); - } else { - items = sscanf ( strValue, "%d;%d;%d;%d", &hours, &minutes, &seconds, &frames ); - if ( intFPS == 30 ) { - framesPerDay = 2589408; // = 29.97 * 24*60*60 - } else if ( intFPS == 60 ) { - framesPerDay = 5178816; // = 59.94 * 24*60*60 - } else { - return false; // Dropframe can only apply to 29.97 and 59.94. - } - } - - if ( items != 4 ) return false; - int minHigh = minutes / 10; - int minLow = minutes % 10; - - XMP_Uns32 framesPerHour = framesPerDay / 24; - XMP_Uns32 framesPerTenMinutes = framesPerHour / 6; - XMP_Uns32 framesPerMinute = framesPerTenMinutes / 10; - - tmcdInfo->timecodeSample = (hours * framesPerHour) + (minHigh * framesPerTenMinutes) + - (minLow * framesPerMinute) + (seconds * intFPS) + frames; - - return true; - -} // DecomposeTimecode - -// ================================================================================================= -// FindTimecodeTrack -// ================= -// -// Look for a well-formed timecode track, return the .../mdia/minf/stbl box ref. - -static MOOV_Manager::BoxRef FindTimecodeTrack ( const MOOV_Manager & moovMgr ) -{ - - // Find a 'trak' box with a handler type of 'tmcd'. - - MOOV_Manager::BoxInfo moovInfo; - MOOV_Manager::BoxRef moovRef = moovMgr.GetBox ( "moov", &moovInfo ); - XMP_Assert ( moovRef != 0 ); - - MOOV_Manager::BoxInfo trakInfo; - MOOV_Manager::BoxRef trakRef; - - size_t i = 0; - for ( ; i < moovInfo.childCount; ++i ) { - - trakRef = moovMgr.GetNthChild ( moovRef, i, &trakInfo ); - if ( trakRef == 0 ) return 0; // Sanity check, should not happen. - if ( trakInfo.boxType != ISOMedia::k_trak ) continue; - - MOOV_Manager::BoxRef innerRef; - MOOV_Manager::BoxInfo innerInfo; - - innerRef = moovMgr.GetTypeChild ( trakRef, ISOMedia::k_mdia, &innerInfo ); - if ( innerRef == 0 ) continue; - - innerRef = moovMgr.GetTypeChild ( innerRef, ISOMedia::k_hdlr, &innerInfo ); - if ( (innerRef == 0) || (innerInfo.contentSize < sizeof ( MOOV_Manager::Content_hdlr )) ) continue; - - const MOOV_Manager::Content_hdlr * hdlr = (MOOV_Manager::Content_hdlr*) innerInfo.content; - if ( hdlr->versionFlags != 0 ) continue; - if ( GetUns32BE ( &hdlr->handlerType ) == ISOMedia::k_tmcd ) break; - - } - if ( i == moovInfo.childCount ) return 0; - - // Find the .../mdia/minf/stbl box. - - MOOV_Manager::BoxInfo tempInfo; - MOOV_Manager::BoxRef tempRef, stblRef; - - tempRef = moovMgr.GetTypeChild ( trakRef, ISOMedia::k_mdia, &tempInfo ); - if ( tempRef == 0 ) return 0; - - tempRef = moovMgr.GetTypeChild ( tempRef, ISOMedia::k_minf, &tempInfo ); - if ( tempRef == 0 ) return 0; - - stblRef = moovMgr.GetTypeChild ( tempRef, ISOMedia::k_stbl, &tempInfo ); - return stblRef; - -} // FindTimecodeTrack - -// ================================================================================================= -// ImportTimecodeItems -// =================== - -static bool ImportTimecodeItems ( const MPEG4_MetaHandler::TimecodeTrackInfo & tmcdInfo, - const TradQT_Manager & qtInfo, SXMPMeta * xmp ) -{ - std::string xmpValue; - bool haveItem; - bool haveImports = false; - - // The QT user data item '©REL' goes into xmpDM:tapeName, and the 'name' box at the end of the - // timecode sample description goes into xmpDM:altTapeName. - haveImports |= qtInfo.ImportSimpleXMP ( kQTilst_Reel, xmp, kXMP_NS_DM, "tapeName" ); - if ( ! tmcdInfo.macName.empty() ) { - haveItem = ConvertFromMacLang ( tmcdInfo.macName, tmcdInfo.macLang, &xmpValue ); - if ( haveItem ) { - xmp->SetProperty ( kXMP_NS_DM, "altTapeName", xmpValue.c_str() ); - haveImports = true; - } - } - - // The QT user data item '©TSC' goes into xmpDM:startTimeScale. If that isn't present, then - // the timecode sample description's timeScale is used. - haveItem = qtInfo.ImportSimpleXMP ( kQTilst_TimeScale, xmp, kXMP_NS_DM, "startTimeScale" ); - if ( tmcdInfo.stsdBoxFound & (! haveItem) ) { - xmp->SetProperty_Int64 ( kXMP_NS_DM, "startTimeScale", tmcdInfo.timeScale ); - haveItem = true; - } - haveImports |= haveItem; - - // The QT user data item '©TSZ' goes into xmpDM:startTimeSampleSize. If that isn't present, then - // the timecode sample description's frameDuration is used. - haveItem = qtInfo.ImportSimpleXMP ( kQTilst_TimeSize, xmp, kXMP_NS_DM, "startTimeSampleSize" ); - if ( tmcdInfo.stsdBoxFound & (! haveItem) ) { - xmp->SetProperty_Int64 ( kXMP_NS_DM, "startTimeSampleSize", tmcdInfo.frameDuration ); - haveItem = true; - } - haveImports |= haveItem; - - const char * timeFormat; - - // The Timecode struct type is used for xmpDM:startTimecode and xmpDM:altTimecode. For both, only - // the xmpDM:timeValue and xmpDM:timeFormat fields are set. - - // The QT user data item '©TIM' goes into xmpDM:startTimecode/xmpDM:timeValue. This is an already - // formatted timecode string. The XMP values of xmpDM:startTimeScale, xmpDM:startTimeSampleSize, - // and xmpDM:startTimecode/xmpDM:timeValue are used to select the timeFormat value. - haveImports |= qtInfo.ImportSimpleXMP ( kQTilst_Timecode, xmp, kXMP_NS_DM, "startTimecode/xmpDM:timeValue" ); - timeFormat = SelectTimeFormat ( *xmp ); - if ( timeFormat != 0 ) { - xmp->SetProperty ( kXMP_NS_DM, "startTimecode/xmpDM:timeFormat", timeFormat ); - haveImports = true; - } - - if ( tmcdInfo.stsdBoxFound ) { - - haveItem = ComposeTimecode ( tmcdInfo, &xmpValue ); - if ( haveItem ) { - xmp->SetProperty ( kXMP_NS_DM, "altTimecode/xmpDM:timeValue", xmpValue.c_str() ); - haveImports = true; - } - - timeFormat = SelectTimeFormat ( tmcdInfo ); - if ( timeFormat != 0 ) { - xmp->SetProperty ( kXMP_NS_DM, "altTimecode/xmpDM:timeFormat", timeFormat ); - haveImports = true; - } - - } - - return haveImports; - -} // ImportTimecodeItems - -// ================================================================================================= -// ExportTimecodeItems -// =================== - -static void ExportTimecodeItems ( const SXMPMeta & xmp, MPEG4_MetaHandler::TimecodeTrackInfo * tmcdInfo, - TradQT_Manager * qtMgr, MOOV_Manager * moovMgr ) -{ - // Export the items that go into the timecode track: - // - the timescale and frame duration in the first 'stsd' table entry - // - the 'name' box appended to the first 'stsd' table entry - // - the first timecode sample - // ! The QuickTime 'udta' timecode items are handled in ExportQuickTimeItems. - - if ( ! tmcdInfo->stsdBoxFound ) return; // Don't make changes unless there is a well-formed timecode track. - - MOOV_Manager::BoxRef stblRef = FindTimecodeTrack ( *moovMgr ); - if ( stblRef == 0 ) return; - - MOOV_Manager::BoxInfo stsdInfo; - MOOV_Manager::BoxRef stsdRef; - - stsdRef = moovMgr->GetTypeChild ( stblRef, ISOMedia::k_stsd, &stsdInfo ); - if ( stsdRef == 0 ) return; - if ( stsdInfo.contentSize < (8 + sizeof ( MOOV_Manager::Content_stsd_entry )) ) return; - if ( GetUns32BE ( stsdInfo.content + 4 ) == 0 ) return; // Make sure the entry count is non-zero. - - const MOOV_Manager::Content_stsd_entry * stsdRawEntry = (MOOV_Manager::Content_stsd_entry*) (stsdInfo.content + 8); - - XMP_Uns32 stsdEntrySize = GetUns32BE ( &stsdRawEntry->entrySize ); - if ( stsdEntrySize > (stsdInfo.contentSize - 4) ) stsdEntrySize = stsdInfo.contentSize - 4; - if ( stsdEntrySize < sizeof ( MOOV_Manager::Content_stsd_entry ) ) return; - - bool ok; - std::string xmpValue; - XMP_Int64 int64; // Used to allow UInt32 values, GetProperty_Int is SInt32. - - // The tmcdInfo timeScale field is set from xmpDM:startTimeScale. - ok = xmp.GetProperty_Int64 ( kXMP_NS_DM, "startTimeScale", &int64, 0 ); - if ( ok && (int64 <= 0xFFFFFFFF) && ((XMP_Uns32)int64 != tmcdInfo->timeScale) ) { - tmcdInfo->timeScale = (XMP_Uns32)int64; - PutUns32BE ( tmcdInfo->timeScale, (void*)&stsdRawEntry->timeScale ); - moovMgr->NoteChange(); - } - - // The tmcdInfo frameDuration field is set from xmpDM:startTimeSampleSize. - ok = xmp.GetProperty_Int64 ( kXMP_NS_DM, "startTimeSampleSize", &int64, 0 ); - if ( ok && (int64 <= 0xFFFFFFFF) && ((XMP_Uns32)int64 != tmcdInfo->frameDuration) ) { - tmcdInfo->frameDuration = (XMP_Uns32)int64; - PutUns32BE ( tmcdInfo->frameDuration, (void*)&stsdRawEntry->frameDuration ); - moovMgr->NoteChange(); - } - - // The tmcdInfo isDropFrame flag is set from xmpDM:altTimecode/xmpDM:timeValue. The timeScale - // and frameDuration must be updated first, they are used by DecomposeTimecode. Compute the new - // UInt32 timecode sample, but it gets written to the file later by UpdateFile. - - ok = xmp.GetProperty ( kXMP_NS_DM, "altTimecode/xmpDM:timeValue", &xmpValue, 0 ); - if ( ok && (xmpValue.size() == 11) ) { - - bool oldDropFrame = tmcdInfo->isDropFrame; - tmcdInfo->isDropFrame = false; - if ( xmpValue[8] == ';' ) tmcdInfo->isDropFrame = true; - if ( oldDropFrame != tmcdInfo->isDropFrame ) { - XMP_Uns32 flags = GetUns32BE ( &stsdRawEntry->flags ); - flags = (flags & 0xFFFFFFFE) | (XMP_Uns32)tmcdInfo->isDropFrame; - PutUns32BE ( flags, (void*)&stsdRawEntry->flags ); - moovMgr->NoteChange(); - } - - XMP_Uns32 oldSample = tmcdInfo->timecodeSample; - ok = DecomposeTimecode ( xmpValue.c_str(), tmcdInfo ); - if ( ok && (oldSample != tmcdInfo->timecodeSample) ) moovMgr->NoteChange(); - - } - - // The 'name' box attached to the first 'stsd' table entry is set from xmpDM:altTapeName. - - bool replaceNameBox = false; - - ok = xmp.GetProperty ( kXMP_NS_DM, "altTapeName", &xmpValue, 0 ); - if ( (! ok) || xmpValue.empty() ) { - if ( tmcdInfo->nameOffset != 0 ) replaceNameBox = true; // No XMP, get rid of existing name. - } else { - std::string macValue; - ok = ConvertToMacLang ( xmpValue, tmcdInfo->macLang, &macValue ); - if ( ok && (macValue != tmcdInfo->macName) ) { - tmcdInfo->macName = macValue; - replaceNameBox = true; // Write changed name. - } - } - - if ( replaceNameBox ) { - - // To replace the 'name' box we have to create an entire new 'stsd' box, and attach the - // new name to the first 'stsd' table entry. The 'name' box content is a UInt16 text length, - // UInt16 language code, and Mac encoded text with no nul termination. - - if ( tmcdInfo->macName.size() > 0xFFFF ) tmcdInfo->macName.erase ( 0xFFFF ); - - ISOMedia::BoxInfo oldNameInfo; - XMP_Assert ( (oldNameInfo.headerSize == 0) && (oldNameInfo.contentSize == 0) ); - if ( tmcdInfo->nameOffset != 0 ) { - const XMP_Uns8 * oldNamePtr = stsdInfo.content + tmcdInfo->nameOffset; - const XMP_Uns8 * oldNameLimit = stsdInfo.content + stsdInfo.contentSize; - (void) ISOMedia::GetBoxInfo ( oldNamePtr, oldNameLimit, &oldNameInfo ); - } - - XMP_Uns32 oldNameBoxSize = (XMP_Uns32)oldNameInfo.headerSize + (XMP_Uns32)oldNameInfo.contentSize; - XMP_Uns32 newNameBoxSize = 0; - if ( ! tmcdInfo->macName.empty() ) newNameBoxSize = 4+4 + 2+2 + (XMP_Uns32)tmcdInfo->macName.size(); - - XMP_Uns32 stsdNewContentSize = stsdInfo.contentSize - oldNameBoxSize + newNameBoxSize; - RawDataBlock stsdNewContent; - stsdNewContent.assign ( stsdNewContentSize, 0 ); // Get the space allocated, direct fill below. - - XMP_Uns32 stsdPrefixSize = tmcdInfo->nameOffset; - if ( tmcdInfo->nameOffset == 0 ) stsdPrefixSize = 4+4 + sizeof ( MOOV_Manager::Content_stsd_entry ); - - XMP_Uns32 oldSuffixOffset = stsdPrefixSize + oldNameBoxSize; - XMP_Uns32 newSuffixOffset = stsdPrefixSize + newNameBoxSize; - XMP_Uns32 stsdSuffixSize = stsdInfo.contentSize - oldSuffixOffset; - - memcpy ( &stsdNewContent[0], stsdInfo.content, stsdPrefixSize ); - if ( stsdSuffixSize != 0 ) memcpy ( &stsdNewContent[newSuffixOffset], (stsdInfo.content + oldSuffixOffset), stsdSuffixSize ); - - XMP_Uns32 newEntrySize = stsdEntrySize - oldNameBoxSize + newNameBoxSize; - MOOV_Manager::Content_stsd_entry * stsdNewEntry = (MOOV_Manager::Content_stsd_entry*) (&stsdNewContent[0] + 8); - PutUns32BE ( newEntrySize, &stsdNewEntry->entrySize ); - - if ( newNameBoxSize != 0 ) { - PutUns32BE ( newNameBoxSize, &stsdNewContent[stsdPrefixSize] ); - PutUns32BE ( ISOMedia::k_name, &stsdNewContent[stsdPrefixSize+4] ); - PutUns16BE ( (XMP_Uns16)tmcdInfo->macName.size(), &stsdNewContent[stsdPrefixSize+8] ); - PutUns16BE ( tmcdInfo->macLang, &stsdNewContent[stsdPrefixSize+10] ); - memcpy ( &stsdNewContent[stsdPrefixSize+12], tmcdInfo->macName.c_str(), tmcdInfo->macName.size() ); - } - - moovMgr->SetBox ( stsdRef, &stsdNewContent[0], stsdNewContentSize ); - - } - -} // ExportTimecodeItems - -// ================================================================================================= -// ImportCr8rItems -// =============== - -#pragma pack ( push, 1 ) - -struct PrmLBoxContent { - XMP_Uns32 magic; - XMP_Uns32 size; - XMP_Uns16 verAPI; - XMP_Uns16 verCode; - XMP_Uns32 exportType; - XMP_Uns16 MacVRefNum; - XMP_Uns32 MacParID; - char filePath[260]; -}; - -enum { kExportTypeMovie = 0, kExportTypeStill = 1, kExportTypeAudio = 2, kExportTypeCustom = 3 }; - -struct Cr8rBoxContent { - XMP_Uns32 magic; - XMP_Uns32 size; - XMP_Uns16 majorVer; - XMP_Uns16 minorVer; - XMP_Uns32 creatorCode; - XMP_Uns32 appleEvent; - char fileExt[16]; - char appOptions[16]; - char appName[32]; -}; - -#pragma pack ( pop ) - -// ------------------------------------------------------------------------------------------------- - -static bool ImportCr8rItems ( const MOOV_Manager & moovMgr, SXMPMeta * xmp ) -{ - bool haveXMP = false; - - MOOV_Manager::BoxInfo infoPrmL, infoCr8r; - MOOV_Manager::BoxRef refPrmL = moovMgr.GetBox ( "moov/udta/PrmL", &infoPrmL ); - MOOV_Manager::BoxRef refCr8r = moovMgr.GetBox ( "moov/udta/Cr8r", &infoCr8r ); - - bool havePrmL = ( (refPrmL != 0) && (infoPrmL.contentSize == sizeof ( PrmLBoxContent )) ); - bool haveCr8r = ( (refCr8r != 0) && (infoCr8r.contentSize == sizeof ( Cr8rBoxContent )) ); - - if ( havePrmL ) { - - PrmLBoxContent rawPrmL; - XMP_Assert ( sizeof ( rawPrmL ) == 282 ); - XMP_Assert ( sizeof ( rawPrmL.filePath ) == 260 ); - memcpy ( &rawPrmL, infoPrmL.content, sizeof ( rawPrmL ) ); - if ( rawPrmL.magic != 0xBEEFCAFE ) { - Flip4 ( &rawPrmL.exportType ); // The only numeric field that we care about. - } - - rawPrmL.filePath[259] = 0; // Ensure a terminating nul. - if ( rawPrmL.filePath[0] != 0 ) { - if ( rawPrmL.filePath[0] == '/' ) { - haveXMP = true; - xmp->SetStructField ( kXMP_NS_CreatorAtom, "macAtom", - kXMP_NS_CreatorAtom, "posixProjectPath", rawPrmL.filePath ); - } else if ( XMP_LitNMatch ( rawPrmL.filePath, "\\\\?\\", 4 ) ) { - haveXMP = true; - xmp->SetStructField ( kXMP_NS_CreatorAtom, "windowsAtom", - kXMP_NS_CreatorAtom, "uncProjectPath", rawPrmL.filePath ); - } - } - - const char * exportStr = 0; - switch ( rawPrmL.exportType ) { - case kExportTypeMovie : exportStr = "movie"; break; - case kExportTypeStill : exportStr = "still"; break; - case kExportTypeAudio : exportStr = "audio"; break; - case kExportTypeCustom : exportStr = "custom"; break; - } - if ( exportStr != 0 ) { - haveXMP = true; - xmp->SetStructField ( kXMP_NS_DM, "projectRef", kXMP_NS_DM, "type", exportStr ); - } - - } - - if ( haveCr8r ) { - - Cr8rBoxContent rawCr8r; - XMP_Assert ( sizeof ( rawCr8r ) == 84 ); - XMP_Assert ( sizeof ( rawCr8r.fileExt ) == 16 ); - XMP_Assert ( sizeof ( rawCr8r.appOptions ) == 16 ); - XMP_Assert ( sizeof ( rawCr8r.appName ) == 32 ); - memcpy ( &rawCr8r, infoCr8r.content, sizeof ( rawCr8r ) ); - if ( rawCr8r.magic != 0xBEEFCAFE ) { - Flip4 ( &rawCr8r.creatorCode ); // The only numeric fields that we care about. - Flip4 ( &rawCr8r.appleEvent ); - } - - std::string fieldPath; - - SXMPUtils::ComposeStructFieldPath ( kXMP_NS_CreatorAtom, "macAtom", kXMP_NS_CreatorAtom, "applicationCode", &fieldPath ); - if ( rawCr8r.creatorCode != 0 ) { - haveXMP = true; - xmp->SetProperty_Int64 ( kXMP_NS_CreatorAtom, fieldPath.c_str(), (XMP_Int64)rawCr8r.creatorCode ); // ! Unsigned trickery. - } - - SXMPUtils::ComposeStructFieldPath ( kXMP_NS_CreatorAtom, "macAtom", kXMP_NS_CreatorAtom, "invocationAppleEvent", &fieldPath ); - if ( rawCr8r.appleEvent != 0 ) { - haveXMP = true; - xmp->SetProperty_Int64 ( kXMP_NS_CreatorAtom, fieldPath.c_str(), (XMP_Int64)rawCr8r.appleEvent ); // ! Unsigned trickery. - } - - rawCr8r.fileExt[15] = 0; // Ensure a terminating nul. - if ( rawCr8r.fileExt[0] != 0 ) { - haveXMP = true; - xmp->SetStructField ( kXMP_NS_CreatorAtom, "windowsAtom", kXMP_NS_CreatorAtom, "extension", rawCr8r.fileExt ); - } - - rawCr8r.appOptions[15] = 0; // Ensure a terminating nul. - if ( rawCr8r.appOptions[0] != 0 ) { - haveXMP = true; - xmp->SetStructField ( kXMP_NS_CreatorAtom, "windowsAtom", kXMP_NS_CreatorAtom, "invocationFlags", rawCr8r.appOptions ); - } - - rawCr8r.appName[31] = 0; // Ensure a terminating nul. - if ( rawCr8r.appName[0] != 0 ) { - haveXMP = true; - xmp->SetProperty ( kXMP_NS_XMP, "CreatorTool", rawCr8r.appName ); - } - - } - - return haveXMP; - -} // ImportCr8rItems - -// ================================================================================================= -// ExportCr8rItems -// =============== - -static inline void SetBufferedString ( char * dest, const std::string source, size_t limit ) -{ - memset ( dest, 0, limit ); - size_t count = source.size(); - if ( count >= limit ) count = limit - 1; // Ensure a terminating nul. - memcpy ( dest, source.c_str(), count ); -} - -// ------------------------------------------------------------------------------------------------- - -static void ExportCr8rItems ( const SXMPMeta & xmp, MOOV_Manager * moovMgr ) -{ - bool haveNewCr8r = false; - std::string creatorCode, appleEvent, fileExt, appOptions, appName; - - haveNewCr8r |= xmp.GetStructField ( kXMP_NS_CreatorAtom, "macAtom", kXMP_NS_CreatorAtom, "applicationCode", &creatorCode, 0 ); - haveNewCr8r |= xmp.GetStructField ( kXMP_NS_CreatorAtom, "macAtom", kXMP_NS_CreatorAtom, "invocationAppleEvent", &appleEvent, 0 ); - haveNewCr8r |= xmp.GetStructField ( kXMP_NS_CreatorAtom, "windowsAtom", kXMP_NS_CreatorAtom, "extension", &fileExt, 0 ); - haveNewCr8r |= xmp.GetStructField ( kXMP_NS_CreatorAtom, "windowsAtom", kXMP_NS_CreatorAtom, "invocationFlags", &appOptions, 0 ); - haveNewCr8r |= xmp.GetProperty ( kXMP_NS_XMP, "CreatorTool", &appName, 0 ); - - MOOV_Manager::BoxInfo infoCr8r; - MOOV_Manager::BoxRef refCr8r = moovMgr->GetBox ( "moov/udta/Cr8r", &infoCr8r ); - bool haveOldCr8r = ( (refCr8r != 0) && (infoCr8r.contentSize == sizeof ( Cr8rBoxContent )) ); - - if ( ! haveNewCr8r ) { - if ( haveOldCr8r ) { - MOOV_Manager::BoxRef udtaRef = moovMgr->GetBox ( "moov/udta", 0 ); - moovMgr->DeleteTypeChild ( udtaRef, 0x43723872 /* 'Cr8r' */ ); - } - return; - } - - Cr8rBoxContent newCr8r; - const Cr8rBoxContent * oldCr8r = (Cr8rBoxContent*) infoCr8r.content; - - if ( ! haveOldCr8r ) { - - memset ( &newCr8r, 0, sizeof(newCr8r) ); - newCr8r.magic = MakeUns32BE ( 0xBEEFCAFE ); - newCr8r.size = MakeUns32BE ( sizeof ( newCr8r ) ); - newCr8r.majorVer = MakeUns16BE ( 1 ); - - } else { - - memcpy ( &newCr8r, oldCr8r, sizeof(newCr8r) ); - if ( GetUns32BE ( &newCr8r.magic ) != 0xBEEFCAFE ) { // Make sure we write BE numbers. - Flip4 ( &newCr8r.magic ); - Flip4 ( &newCr8r.size ); - Flip2 ( &newCr8r.majorVer ); - Flip2 ( &newCr8r.minorVer ); - Flip4 ( &newCr8r.creatorCode ); - Flip4 ( &newCr8r.appleEvent ); - } - - } - - if ( ! creatorCode.empty() ) { - newCr8r.creatorCode = MakeUns32BE ( (XMP_Uns32) strtoul ( creatorCode.c_str(), 0, 0 ) ); - } - - if ( ! appleEvent.empty() ) { - newCr8r.appleEvent = MakeUns32BE ( (XMP_Uns32) strtoul ( appleEvent.c_str(), 0, 0 ) ); - } - - if ( ! fileExt.empty() ) SetBufferedString ( newCr8r.fileExt, fileExt, sizeof ( newCr8r.fileExt ) ); - if ( ! appOptions.empty() ) SetBufferedString ( newCr8r.appOptions, appOptions, sizeof ( newCr8r.appOptions ) ); - if ( ! appName.empty() ) SetBufferedString ( newCr8r.appName, appName, sizeof ( newCr8r.appName ) ); - - moovMgr->SetBox ( "moov/udta/Cr8r", &newCr8r, sizeof(newCr8r) ); - -} // ExportCr8rItems - -// ================================================================================================= -// GetAtomInfo -// =========== - -struct AtomInfo { - XMP_Int64 atomSize; - XMP_Uns32 atomType; - bool hasLargeSize; -}; - -enum { // ! Do not rearrange, code depends on this order. - kBadQT_NoError = 0, // No errors. - kBadQT_SmallInner = 1, // An extra 1..7 bytes at the end of an inner span. - kBadQT_LargeInner = 2, // More serious inner garbage, found as invalid atom length. - kBadQT_SmallOuter = 3, // An extra 1..7 bytes at the end of the file. - kBadQT_LargeOuter = 4 // More serious EOF garbage, found as invalid atom length. -}; -typedef XMP_Uns8 QTErrorMode; - -static QTErrorMode GetAtomInfo ( const LFA_FileRef qtFile, XMP_Int64 spanSize, int nesting, AtomInfo * info ) -{ - QTErrorMode status = kBadQT_NoError; - XMP_Uns8 buffer [8]; - - info->hasLargeSize = false; - - LFA_Read ( qtFile, buffer, 8, kLFA_RequireAll ); // Will throw if 8 bytes aren't available. - info->atomSize = GetUns32BE ( &buffer[0] ); // ! Yes, the initial size is big endian UInt32. - info->atomType = GetUns32BE ( &buffer[4] ); - - if ( info->atomSize == 0 ) { // Does the atom extend to EOF? - - if ( nesting != 0 ) return kBadQT_LargeInner; - info->atomSize = spanSize; // This outer atom goes to EOF. - - } else if ( info->atomSize == 1 ) { // Does the atom have a 64-bit size? - - if ( spanSize < 16 ) { // Is there room in the span for the 16 byte header? - status = kBadQT_LargeInner; - if ( nesting == 0 ) status += 2; // Convert to "outer". - return status; - } - - LFA_Read ( qtFile, buffer, 8, kLFA_RequireAll ); - info->atomSize = (XMP_Int64) GetUns64BE ( &buffer[0] ); - info->hasLargeSize = true; - - } - - XMP_Assert ( status == kBadQT_NoError ); - return status; - -} // GetAtomInfo - -// ================================================================================================= -// CheckAtomList -// ============= -// -// Check that a sequence of atoms fills a given span. The I/O position must be at the start of the -// span, it is left just past the span on success. Recursive checks are done for top level 'moov' -// atoms, and second level 'udta' atoms ('udta' inside 'moov'). -// -// Checking continues for "small inner" errors. They will be reported if no other kinds of errors -// are found, otherwise the other error is reported. Checking is immediately aborted for any "large" -// error. The rationale is that QuickTime can apparently handle small inner errors. They might be -// arise from updates that shorten an atom by less than 8 bytes. Larger shrinkage should introduce a -// 'free' atom. - -static QTErrorMode CheckAtomList ( const LFA_FileRef qtFile, XMP_Int64 spanSize, int nesting ) -{ - QTErrorMode status = kBadQT_NoError; - AtomInfo info; - - const static XMP_Uns32 moovAtomType = 0x6D6F6F76; // ! Don't use MakeUns32BE, already big endian. - const static XMP_Uns32 udtaAtomType = 0x75647461; - - for ( ; spanSize >= 8; spanSize -= info.atomSize ) { - - QTErrorMode atomStatus = GetAtomInfo ( qtFile, spanSize, nesting, &info ); - if ( atomStatus != kBadQT_NoError ) return atomStatus; - - XMP_Int64 headerSize = 8; - if ( info.hasLargeSize ) headerSize = 16; - - if ( (info.atomSize < headerSize) || (info.atomSize > spanSize) ) { - status = kBadQT_LargeInner; - if ( nesting == 0 ) status += 2; // Convert to "outer". - return status; - } - - bool doChildren = false; - if ( (nesting == 0) && (info.atomType == moovAtomType) ) doChildren = true; - if ( (nesting == 1) && (info.atomType == udtaAtomType) ) doChildren = true; - - XMP_Int64 dataSize = info.atomSize - headerSize; - - if ( ! doChildren ) { - LFA_Seek ( qtFile, dataSize, SEEK_CUR ); - } else { - QTErrorMode innerStatus = CheckAtomList ( qtFile, dataSize, nesting+1 ); - if ( innerStatus > kBadQT_SmallInner ) return innerStatus; // Quit for serious errors. - if ( status == kBadQT_NoError ) status = innerStatus; // Remember small inner errors. - } - - } - - XMP_Assert ( status <= kBadQT_SmallInner ); // Else already returned. - // ! Make sure inner kBadQT_SmallInner is propagated if this span is OK. - - if ( spanSize != 0 ) { - LFA_Seek ( qtFile, spanSize, SEEK_CUR ); // ! Skip the trailing garbage of this span. - status = kBadQT_SmallInner; - if ( spanSize >= 8 ) status = kBadQT_LargeInner; - if ( nesting == 0 ) status += 2; // Convert to "outer". - } - - return status; - -} // CheckAtomList - -// ================================================================================================= -// AttemptFileRepair -// ================= - -static void AttemptFileRepair ( LFA_FileRef qtFile, XMP_Int64 fileSpace, QTErrorMode status ) -{ - - switch ( status ) { - case kBadQT_NoError : return; // Sanity check. - case kBadQT_SmallInner : return; // Fixed in normal update code for the 'udta' box. - case kBadQT_LargeInner : XMP_Throw ( "Can't repair QuickTime file", kXMPErr_BadFileFormat ); - case kBadQT_SmallOuter : break; // Truncate file below. - case kBadQT_LargeOuter : break; // Truncate file below. - default : XMP_Throw ( "Invalid QuickTime error mode", kXMPErr_InternalFailure ); - } - - AtomInfo info; - XMP_Int64 headerSize; - - // Process the top level atoms until an error is found. - - LFA_Seek ( qtFile, 0, SEEK_SET ); - - for ( ; fileSpace >= 8; fileSpace -= info.atomSize ) { - - QTErrorMode atomStatus = GetAtomInfo ( qtFile, fileSpace, 0, &info ); - - headerSize = 8; // ! Set this before checking atomStatus, used after the loop. - if ( info.hasLargeSize ) headerSize = 16; - - if ( atomStatus != kBadQT_NoError ) break; - if ( (info.atomSize < headerSize) || (info.atomSize > fileSpace) ) break; - - XMP_Int64 dataSize = info.atomSize - headerSize; - LFA_Seek ( qtFile, dataSize, SEEK_CUR ); - - } - - // Truncate the file. If fileSpace >= 8 then the loop exited early due to a bad atom, seek back - // to the atom's start. Otherwise, the loop exited because no more atoms are possible, no seek. - - if ( fileSpace >= 8 ) LFA_Seek ( qtFile, -headerSize, SEEK_CUR ); - XMP_Int64 currPos = LFA_Tell ( qtFile ); - LFA_Truncate ( qtFile, currPos ); - -} // AttemptFileRepair - -// ================================================================================================= -// CheckQTFileStructure -// ==================== - -static void CheckQTFileStructure ( XMPFileHandler * thiz, bool doRepair ) -{ - XMPFiles * parent = thiz->parent; - LFA_FileRef fileRef = parent->fileRef; - XMP_Int64 fileSize = LFA_Measure ( fileRef ); - - // Check the basic file structure and try to repair if asked. - - LFA_Seek ( fileRef, 0, SEEK_SET ); - QTErrorMode status = CheckAtomList ( fileRef, fileSize, 0 ); - - if ( status != kBadQT_NoError ) { - if ( doRepair || (status == kBadQT_SmallInner) || (status == kBadQT_SmallOuter) ) { - AttemptFileRepair ( fileRef, fileSize, status ); // Will throw if the attempt fails. - } else if ( status != kBadQT_SmallInner ) { - XMP_Throw ( "Ill-formed QuickTime file", kXMPErr_BadFileFormat ); - } else { - return; // ! Ignore these, QT seems to be able to handle them. - // *** Might want to throw for check-only, ignore when repairing. - } - } - -} // CheckQTFileStructure; - -// ================================================================================================= -// CheckFinalBox -// ============= -// -// Before appending anything new, check if the final top level box has a "to EoF" length. If so, fix -// it to have an explicit length. - -static void CheckFinalBox ( LFA_FileRef fileRef, XMP_AbortProc abortProc, void * abortArg ) -{ - const bool checkAbort = (abortProc != 0); - - XMP_Uns64 fileSize = LFA_Measure ( fileRef ); - - // Find the last 2 boxes in the file. Need the previous to last in case it is an Apple 'wide' box. - - XMP_Uns64 prevPos, lastPos, nextPos; - ISOMedia::BoxInfo prevBox, lastBox; - XMP_Uns8 buffer [16]; // Enough to create an extended header. - - memset ( &prevBox, 0, sizeof(prevBox) ); // AUDIT: Using sizeof(prevBox) is safe. - memset ( &lastBox, 0, sizeof(lastBox) ); // AUDIT: Using sizeof(lastBox) is safe. - prevPos = lastPos = nextPos = 0; - while ( nextPos != fileSize ) { - if ( checkAbort && abortProc(abortArg) ) { - XMP_Throw ( "MPEG4_MetaHandler::CheckFinalBox - User abort", kXMPErr_UserAbort ); - } - prevBox = lastBox; - prevPos = lastPos; - lastPos = nextPos; - nextPos = ISOMedia::GetBoxInfo ( fileRef, lastPos, fileSize, &lastBox, true /* throw errors */ ); - } - - // See if the last box is valid and has a "to EoF" size. - - if ( lastBox.headerSize < 8 ) XMP_Throw ( "MPEG-4 final box is invalid", kXMPErr_EnforceFailure ); - LFA_Seek ( fileRef, lastPos, SEEK_SET ); - LFA_Read ( fileRef, buffer, 4 ); - XMP_Uns64 lastSize = GetUns32BE ( &buffer[0] ); // ! Yes, the file has a 32-bit value. - if ( lastSize != 0 ) return; - - // Have a final "to EoF" box, try to write the explicit size. - - lastSize = lastBox.headerSize + lastBox.contentSize; - if ( lastSize <= 0xFFFFFFFFUL ) { - - // Fill in the 32-bit exact size. - PutUns32BE ( (XMP_Uns32)lastSize, &buffer[0] ); - LFA_Seek ( fileRef, lastPos, SEEK_SET ); - LFA_Write ( fileRef, buffer, 4 ); - - } else { - - // Try to convert to using an extended header. - - if ( (prevBox.boxType != ISOMedia::k_wide) || (prevBox.headerSize != 8) || (prevBox.contentSize != 0) ) { - XMP_Throw ( "Can't expand final box header", kXMPErr_EnforceFailure ); - } - XMP_Assert ( prevPos == (lastPos - 8) ); - - PutUns32BE ( 1, &buffer[0] ); - PutUns32BE ( lastBox.boxType, &buffer[4] ); - PutUns64BE ( lastSize, &buffer[8] ); - LFA_Seek ( fileRef, prevPos, SEEK_SET ); - LFA_Write ( fileRef, buffer, 16 ); - - } - -} // CheckFinalBox - -// ================================================================================================= -// WriteBoxHeader -// ============== - -static void WriteBoxHeader ( LFA_FileRef fileRef, XMP_Uns32 boxType, XMP_Uns64 boxSize ) -{ - XMP_Uns32 u32; - XMP_Uns64 u64; - XMP_Enforce ( boxSize >= 8 ); // The size must be the full size, not just the content. - - if ( boxSize <= 0xFFFFFFFF ) { - - u32 = MakeUns32BE ( (XMP_Uns32)boxSize ); - LFA_Write ( fileRef, &u32, 4 ); - u32 = MakeUns32BE ( boxType ); - LFA_Write ( fileRef, &u32, 4 ); - - } else { - - u32 = MakeUns32BE ( 1 ); - LFA_Write ( fileRef, &u32, 4 ); - u32 = MakeUns32BE ( boxType ); - LFA_Write ( fileRef, &u32, 4 ); - u64 = MakeUns64BE ( boxSize ); - LFA_Write ( fileRef, &u64, 8 ); - - } - -} // WriteBoxHeader - -// ================================================================================================= -// WipeBoxFree -// =========== -// -// Change the box's type to 'free' (or create a 'free' box) and zero the content. - -static XMP_Uns8 kZeroes [64*1024]; // C semantics guarantee zero initialization. - -static void WipeBoxFree ( LFA_FileRef fileRef, XMP_Uns64 boxOffset, XMP_Uns32 boxSize ) -{ - if ( boxSize == 0 ) return; - XMP_Enforce ( boxSize >= 8 ); - - LFA_Seek ( fileRef, boxOffset, SEEK_SET ); - XMP_Uns32 u32; - u32 = MakeUns32BE ( boxSize ); // ! The actual size should not change, but might have had a long header. - LFA_Write ( fileRef, &u32, 4 ); - u32 = MakeUns32BE ( ISOMedia::k_free ); - LFA_Write ( fileRef, &u32, 4 ); - - XMP_Uns32 ioCount = sizeof ( kZeroes ); - for ( boxSize -= 8; boxSize > 0; boxSize -= ioCount ) { - if ( ioCount > boxSize ) ioCount = boxSize; - LFA_Write ( fileRef, &kZeroes[0], ioCount ); - } - -} // WipeBoxFree - -// ================================================================================================= -// CreateFreeSpaceList -// =================== - -struct SpaceInfo { - XMP_Uns64 offset, size; - SpaceInfo() : offset(0), size(0) {}; - SpaceInfo ( XMP_Uns64 _offset, XMP_Uns64 _size ) : offset(_offset), size(_size) {}; -}; - -typedef std::vector<SpaceInfo> FreeSpaceList; - -static void CreateFreeSpaceList ( LFA_FileRef fileRef, XMP_Uns64 fileSize, - XMP_Uns64 oldOffset, XMP_Uns32 oldSize, FreeSpaceList * spaceList ) -{ - XMP_Uns64 boxPos, boxNext, adjacentFree; - ISOMedia::BoxInfo currBox; - - LFA_Seek ( fileRef, 0, SEEK_SET ); - spaceList->clear(); - - for ( boxPos = 0; boxPos < fileSize; boxPos = boxNext ) { - - boxNext = ISOMedia::GetBoxInfo ( fileRef, boxPos, fileSize, &currBox, true /* throw errors */ ); - XMP_Uns64 currSize = currBox.headerSize + currBox.contentSize; - - if ( (currBox.boxType == ISOMedia::k_free) || - (currBox.boxType == ISOMedia::k_skip) || - ((boxPos == oldOffset) && (currSize == oldSize)) ) { - - if ( spaceList->empty() || (boxPos != adjacentFree) ) { - spaceList->push_back ( SpaceInfo ( boxPos, currSize ) ); - adjacentFree = boxPos + currSize; - } else { - SpaceInfo * lastSpace = &spaceList->back(); - lastSpace->size += currSize; - } - - } - - } - -} // CreateFreeSpaceList - -// ================================================================================================= -// MPEG4_MetaHandler::CacheFileData -// ================================ -// -// There are 3 file variants: normal ISO Base Media, modern QuickTime, and classic QuickTime. The -// XMP is placed differently between the ISO and two QuickTime forms, and there is different but not -// colliding native metadata. The entire 'moov' subtree is cached, along with the top level 'uuid' -// box of XMP if present. - -void MPEG4_MetaHandler::CacheFileData() -{ - XMP_Assert ( ! this->containsXMP ); - - XMPFiles * parent = this->parent; - XMP_OptionBits openFlags = parent->openFlags; - - LFA_FileRef fileRef = parent->fileRef; - - XMP_AbortProc abortProc = parent->abortProc; - void * abortArg = parent->abortArg; - const bool checkAbort = (abortProc != 0); - - // First do some special case repair to QuickTime files, based on bad files in the wild. - - const bool isUpdate = XMP_OptionIsSet ( openFlags, kXMPFiles_OpenForUpdate ); - const bool doRepair = XMP_OptionIsSet ( openFlags, kXMPFiles_OpenRepairFile ); - - if ( isUpdate && (parent->format == kXMP_MOVFile) ) { - CheckQTFileStructure ( this, doRepair ); // Will throw for failure. - } - - // Cache the top level 'moov' and 'uuid'/XMP boxes. - - XMP_Uns64 fileSize = LFA_Measure ( fileRef ); - - XMP_Uns64 boxPos, boxNext; - ISOMedia::BoxInfo currBox; - - bool xmpOnly = XMP_OptionIsSet ( openFlags, kXMPFiles_OpenOnlyXMP ); - bool haveISOFile = (this->fileMode == MOOV_Manager::kFileIsNormalISO); - - bool uuidFound = (! haveISOFile); // Ignore the XMP 'uuid' box for QuickTime files. - bool moovIgnored = (xmpOnly & haveISOFile); // Ignore the 'moov' box for XMP-only ISO files. - bool moovFound = moovIgnored; - - for ( boxPos = 0; boxPos < fileSize; boxPos = boxNext ) { - - if ( checkAbort && abortProc(abortArg) ) { - XMP_Throw ( "MPEG4_MetaHandler::CacheFileData - User abort", kXMPErr_UserAbort ); - } - - - boxNext = ISOMedia::GetBoxInfo ( fileRef, boxPos, fileSize, &currBox ); - - if ( (! moovFound) && (currBox.boxType == ISOMedia::k_moov) ) { - - XMP_Uns64 fullMoovSize = currBox.headerSize + currBox.contentSize; - if ( fullMoovSize > moovBoxSizeLimit ) { // From here on we know 32-bit offsets are safe. - XMP_Throw ( "Oversize 'moov' box", kXMPErr_EnforceFailure ); - } - - this->moovMgr.fullSubtree.assign ( (XMP_Uns32)fullMoovSize, 0 ); - LFA_Seek ( fileRef, boxPos, SEEK_SET ); - LFA_Read ( fileRef, &this->moovMgr.fullSubtree[0], (XMP_Uns32)fullMoovSize ); - - this->moovBoxPos = boxPos; - this->moovBoxSize = (XMP_Uns32)fullMoovSize; - moovFound = true; - if ( uuidFound ) break; // Exit the loop when both are found. - - } else if ( (! uuidFound) && (currBox.boxType == ISOMedia::k_uuid) ) { - - if ( currBox.contentSize < 16 ) continue; - - XMP_Uns8 uuid [16]; - LFA_Read ( fileRef, uuid, 16, kLFA_RequireAll ); - if ( memcmp ( uuid, ISOMedia::k_xmpUUID, 16 ) != 0 ) continue; // Check for the XMP GUID. - - XMP_Uns64 fullUuidSize = currBox.headerSize + currBox.contentSize; - if ( fullUuidSize > moovBoxSizeLimit ) { // From here on we know 32-bit offsets are safe. - XMP_Throw ( "Oversize XMP 'uuid' box", kXMPErr_EnforceFailure ); - } - - this->packetInfo.offset = boxPos + currBox.headerSize + 16; // The 16 is for the UUID. - this->packetInfo.length = (XMP_Int32) (currBox.contentSize - 16); - - this->xmpPacket.assign ( this->packetInfo.length, ' ' ); - LFA_Read ( fileRef, (void*)this->xmpPacket.data(), this->packetInfo.length, kLFA_RequireAll ); - - this->xmpBoxPos = boxPos; - this->xmpBoxSize = (XMP_Uns32)fullUuidSize; - uuidFound = true; - if ( moovFound ) break; // Exit the loop when both are found. - - } - - } - - if ( (! moovFound) && (! moovIgnored) ) XMP_Throw ( "No 'moov' box", kXMPErr_BadFileFormat ); - -} // MPEG4_MetaHandler::CacheFileData - -// ================================================================================================= -// MPEG4_MetaHandler::ProcessXMP -// ============================= - -void MPEG4_MetaHandler::ProcessXMP() -{ - if ( this->processedXMP ) return; - this->processedXMP = true; // Make sure only called once. - - XMPFiles * parent = this->parent; - XMP_OptionBits openFlags = parent->openFlags; - - bool xmpOnly = XMP_OptionIsSet ( openFlags, kXMPFiles_OpenOnlyXMP ); - bool haveISOFile = (this->fileMode == MOOV_Manager::kFileIsNormalISO); - - // Process the cached XMP (from the 'uuid' box) if that is all we want and this is an ISO file. - - if ( xmpOnly & haveISOFile ) { - - this->containsXMP = this->havePreferredXMP = (this->packetInfo.length != 0); - - if ( this->containsXMP ) { - FillPacketInfo ( this->xmpPacket, &this->packetInfo ); - this->xmpObj.ParseFromBuffer ( this->xmpPacket.c_str(), (XMP_StringLen)this->xmpPacket.size() ); - this->xmpObj.DeleteProperty ( kXMP_NS_XMP, "NativeDigests" ); // No longer used. - } - - return; - - } - - // Parse the cached 'moov' subtree, parse the preferred XMP. - - if ( this->moovMgr.fullSubtree.empty() ) XMP_Throw ( "No 'moov' box", kXMPErr_BadFileFormat ); - this->moovMgr.ParseMemoryTree ( this->fileMode ); - - if ( (this->xmpBoxPos == 0) || (! haveISOFile) ) { - - // Look for the QuickTime moov/uuid/XMP_ box. - - MOOV_Manager::BoxInfo xmpInfo; - MOOV_Manager::BoxRef xmpRef = this->moovMgr.GetBox ( "moov/udta/XMP_", &xmpInfo ); - - if ( (xmpRef != 0) && (xmpInfo.contentSize != 0) ) { - - this->xmpBoxPos = this->moovBoxPos + this->moovMgr.GetParsedOffset ( xmpRef ); - this->packetInfo.offset = this->xmpBoxPos + this->moovMgr.GetHeaderSize ( xmpRef ); - this->packetInfo.length = xmpInfo.contentSize; - - this->xmpPacket.assign ( (char*)xmpInfo.content, this->packetInfo.length ); - this->havePreferredXMP = (! haveISOFile); - - } - - } - - if ( this->xmpBoxPos != 0 ) { - this->containsXMP = true; - FillPacketInfo ( this->xmpPacket, &this->packetInfo ); - this->xmpObj.ParseFromBuffer ( this->xmpPacket.c_str(), (XMP_StringLen)this->xmpPacket.size() ); - this->xmpObj.DeleteProperty ( kXMP_NS_XMP, "NativeDigests" ); // No longer used. - } - - // Import the non-XMP items. Do the imports in reverse priority order, last import wins! - - MOOV_Manager::BoxInfo mvhdInfo; - MOOV_Manager::BoxRef mvhdRef = this->moovMgr.GetBox ( "moov/mvhd", &mvhdInfo ); - bool mvhdFound = ((mvhdRef != 0) && (mvhdInfo.contentSize != 0)); - - MOOV_Manager::BoxInfo udtaInfo; - MOOV_Manager::BoxRef udtaRef = this->moovMgr.GetBox ( "moov/udta", &udtaInfo ); - std::vector<MOOV_Manager::BoxInfo> cprtBoxes; - - if ( udtaRef != 0 ) { - for ( XMP_Uns32 i = 0; i < udtaInfo.childCount; ++i ) { - MOOV_Manager::BoxInfo currInfo; - MOOV_Manager::BoxRef currRef = this->moovMgr.GetNthChild ( udtaRef, i, &currInfo ); - if ( currRef == 0 ) break; // Sanity check, should not happen. - if ( currInfo.boxType != ISOMedia::k_cprt ) continue; - cprtBoxes.push_back ( currInfo ); - } - } - bool cprtFound = (! cprtBoxes.empty()); - - bool tradQTFound = this->tradQTMgr.ParseCachedBoxes ( this->moovMgr ); - bool tmcdFound = this->ParseTimecodeTrack(); - - if ( this->fileMode == MOOV_Manager::kFileIsNormalISO ) { - - if ( mvhdFound ) this->containsXMP |= ImportMVHDItems ( mvhdInfo, &this->xmpObj ); - if ( cprtFound ) this->containsXMP |= ImportISOCopyrights ( cprtBoxes, &this->xmpObj ); - } else { // This is a QuickTime file, either traditional or modern. - - if ( mvhdFound ) this->containsXMP |= ImportMVHDItems ( mvhdInfo, &this->xmpObj ); - if ( cprtFound ) this->containsXMP |= ImportISOCopyrights ( cprtBoxes, &this->xmpObj ); - if ( tmcdFound | tradQTFound ) { - // Some of the timecode items are in the .../udta/©... set but handled by ImportTimecodeItems. - this->containsXMP |= ImportTimecodeItems ( this->tmcdInfo, this->tradQTMgr, &this->xmpObj ); - } - - this->containsXMP |= ImportCr8rItems ( this->moovMgr, &this->xmpObj ); - - } - -} // MPEG4_MetaHandler::ProcessXMP - -// ================================================================================================= -// MPEG4_MetaHandler::ParseTimecodeTrack -// ===================================== - -bool MPEG4_MetaHandler::ParseTimecodeTrack() -{ - MOOV_Manager::BoxRef stblRef = FindTimecodeTrack ( this->moovMgr ); - if ( stblRef == 0 ) return false; - - // Find the .../stbl/stsd box and process the first table entry. - - MOOV_Manager::BoxInfo stsdInfo; - MOOV_Manager::BoxRef stsdRef; - - stsdRef = this->moovMgr.GetTypeChild ( stblRef, ISOMedia::k_stsd, &stsdInfo ); - if ( stsdRef == 0 ) return false; - if ( stsdInfo.contentSize < (8 + sizeof ( MOOV_Manager::Content_stsd_entry )) ) return false; - if ( GetUns32BE ( stsdInfo.content + 4 ) == 0 ) return false; // Make sure the entry count is non-zero. - - const MOOV_Manager::Content_stsd_entry * stsdRawEntry = (MOOV_Manager::Content_stsd_entry*) (stsdInfo.content + 8); - - XMP_Uns32 stsdEntrySize = GetUns32BE ( &stsdRawEntry->entrySize ); - if ( stsdEntrySize > (stsdInfo.contentSize - 4) ) stsdEntrySize = stsdInfo.contentSize - 4; - if ( stsdEntrySize < sizeof ( MOOV_Manager::Content_stsd_entry ) ) return false; - - XMP_Uns32 stsdEntryFormat = GetUns32BE ( &stsdRawEntry->format ); - if ( stsdEntryFormat != ISOMedia::k_tmcd ) return false; - - this->tmcdInfo.timeScale = GetUns32BE ( &stsdRawEntry->timeScale ); - this->tmcdInfo.frameDuration = GetUns32BE ( &stsdRawEntry->frameDuration ); - - XMP_Uns32 flags = GetUns32BE ( &stsdRawEntry->flags ); - this->tmcdInfo.isDropFrame = flags & 0x1; - - // Look for a trailing 'name' box on the first stsd table entry. - - XMP_Uns32 stsdTrailerSize = stsdEntrySize - sizeof ( MOOV_Manager::Content_stsd_entry ); - if ( stsdTrailerSize > 8 ) { // Room for a non-empty 'name' box? - - const XMP_Uns8 * trailerStart = stsdInfo.content + 8 + sizeof ( MOOV_Manager::Content_stsd_entry ); - const XMP_Uns8 * trailerLimit = trailerStart + stsdTrailerSize; - const XMP_Uns8 * trailerPos; - const XMP_Uns8 * trailerNext; - ISOMedia::BoxInfo trailerInfo; - - for ( trailerPos = trailerStart; trailerPos < trailerLimit; trailerPos = trailerNext ) { - - trailerNext = ISOMedia::GetBoxInfo ( trailerPos, trailerLimit, &trailerInfo ); - - if ( trailerInfo.boxType == ISOMedia::k_name ) { - - this->tmcdInfo.nameOffset = (XMP_Uns32) (trailerPos - stsdInfo.content); - - if ( trailerInfo.contentSize > 4 ) { - - XMP_Uns16 textLen = GetUns16BE ( trailerPos + trailerInfo.headerSize ); - this->tmcdInfo.macLang = GetUns16BE ( trailerPos + trailerInfo.headerSize + 2 ); - - if ( trailerInfo.contentSize >= (XMP_Uns64)(textLen + 4) ) { - const char * textPtr = (char*) (trailerPos + trailerInfo.headerSize + 4); - this->tmcdInfo.macName.assign ( textPtr, textLen ); - } - - } - - break; // Done after finding the first 'name' box. - - } - - } - - } - - // Find the timecode sample. - - XMP_Uns64 sampleOffset = 0; - MOOV_Manager::BoxInfo tempInfo; - MOOV_Manager::BoxRef tempRef; - - tempRef = this->moovMgr.GetTypeChild ( stblRef, ISOMedia::k_stsc, &tempInfo ); - if ( tempRef == 0 ) return false; - if ( tempInfo.contentSize < (8 + sizeof ( MOOV_Manager::Content_stsc_entry )) ) return false; - if ( GetUns32BE ( tempInfo.content + 4 ) == 0 ) return false; // Make sure the entry count is non-zero. - - XMP_Uns32 firstChunkNumber = GetUns32BE ( tempInfo.content + 8 ); // Want first field of first entry. - - tempRef = this->moovMgr.GetTypeChild ( stblRef, ISOMedia::k_stco, &tempInfo ); - - if ( tempRef != 0 ) { - - if ( tempInfo.contentSize < (8 + 4) ) return false; - XMP_Uns32 stcoCount = GetUns32BE ( tempInfo.content + 4 ); - if ( stcoCount < firstChunkNumber ) return false; - XMP_Uns32 * stcoPtr = (XMP_Uns32*) (tempInfo.content + 8); - sampleOffset = GetUns32BE ( &stcoPtr[firstChunkNumber-1] ); // ! Chunk number is 1-based. - - } else { - - tempRef = this->moovMgr.GetTypeChild ( stblRef, ISOMedia::k_co64, &tempInfo ); - if ( (tempRef == 0) || (tempInfo.contentSize < (8 + 8)) ) return false; - XMP_Uns32 co64Count = GetUns32BE ( tempInfo.content + 4 ); - if ( co64Count < firstChunkNumber ) return false; - XMP_Uns64 * co64Ptr = (XMP_Uns64*) (tempInfo.content + 8); - sampleOffset = GetUns64BE ( &co64Ptr[firstChunkNumber-1] ); // ! Chunk number is 1-based. - - } - - if ( sampleOffset != 0 ) { - - // Read the timecode sample. Need to reopen the file if the XMPFile was open for read-only, - // normally all I/O is done within CacheFileData. - - bool openForRead = XMP_OptionIsSet ( this->parent->openFlags, kXMPFiles_OpenForRead); - - LFA_FileRef fileRef = this->parent->fileRef; - if ( openForRead ) { - XMP_Assert ( fileRef == 0 ); - fileRef = LFA_Open ( this->parent->filePath.c_str(), 'r' ); - } - - if ( fileRef != 0 ) { // The reopen might have failed. - LFA_Seek ( fileRef, sampleOffset, SEEK_SET ); - LFA_Read ( fileRef, &this->tmcdInfo.timecodeSample, 4, kLFA_RequireAll ); - this->tmcdInfo.timecodeSample = MakeUns32BE ( this->tmcdInfo.timecodeSample ); - if ( openForRead ) LFA_Close ( fileRef ); - } - - } - - // Finally update this->tmcdInfo to remember (for update) that there is an OK timecode track. - - this->tmcdInfo.stsdBoxFound = true; - this->tmcdInfo.sampleOffset = sampleOffset; - return true; - -} // MPEG4_MetaHandler::ParseTimecodeTrack - -// ================================================================================================= -// MPEG4_MetaHandler::UpdateTopLevelBox -// ==================================== - -void MPEG4_MetaHandler::UpdateTopLevelBox ( XMP_Uns64 oldOffset, XMP_Uns32 oldSize, - const XMP_Uns8 * newBox, XMP_Uns32 newSize ) -{ - if ( (oldSize == 0) && (newSize == 0) ) return; // Sanity check, should not happen. - - LFA_FileRef fileRef = this->parent->fileRef; - XMP_Uns64 oldFileSize = LFA_Measure ( fileRef ); - - XMP_AbortProc abortProc = this->parent->abortProc; - void * abortArg = this->parent->abortArg; - - if ( newSize == oldSize ) { - - // Trivial case, update the existing box in-place. - LFA_Seek ( fileRef, oldOffset, SEEK_SET ); - LFA_Write ( fileRef, newBox, oldSize ); - - } else if ( (oldOffset + oldSize) == oldFileSize ) { - - // The old box was at the end, write the new and truncate the file if necessary. - LFA_Seek ( fileRef, oldOffset, SEEK_SET ); - LFA_Write ( fileRef, newBox, newSize ); - LFA_Truncate ( fileRef, (oldOffset + newSize) ); // Does nothing if new size is bigger. - - } else if ( (newSize < oldSize) && ((oldSize - newSize) >= 8) ) { - - // The new size is smaller and there is enough room to create a free box. - LFA_Seek ( fileRef, oldOffset, SEEK_SET ); - LFA_Write ( fileRef, newBox, newSize ); - WipeBoxFree ( fileRef, (oldOffset + newSize), (oldSize - newSize) ); - - } else { - - // Look for a trailing free box with enough space. If not found, consider any free space. - // If still not found, append the new box and make the old one free. - - ISOMedia::BoxInfo nextBoxInfo; - (void) ISOMedia::GetBoxInfo ( fileRef, (oldOffset + oldSize), oldFileSize, &nextBoxInfo, true /* throw errors */ ); - - XMP_Uns64 totalRoom = oldSize + nextBoxInfo.headerSize + nextBoxInfo.contentSize; - - bool nextIsFree = (nextBoxInfo.boxType == ISOMedia::k_free) || (nextBoxInfo.boxType == ISOMedia::k_skip); - bool haveEnoughRoom = (newSize == totalRoom) || - ( (newSize < totalRoom) && ((totalRoom - newSize) >= 8) ); - - if ( nextIsFree & haveEnoughRoom ) { - - LFA_Seek ( fileRef, oldOffset, SEEK_SET ); - LFA_Write ( fileRef, newBox, newSize ); - - if ( newSize < totalRoom ) { - // Don't wipe, at most 7 old bytes left, it will be covered by the free header. - WriteBoxHeader ( fileRef, ISOMedia::k_free, (totalRoom - newSize) ); - } - - } else { - - // Create a list of all top level free space, including the old space as free. Use the - // earliest space that fits. If none, append. - - FreeSpaceList spaceList; - CreateFreeSpaceList ( fileRef, oldFileSize, oldOffset, oldSize, &spaceList ); - - size_t freeSlot, limit; - for ( freeSlot = 0, limit = spaceList.size(); freeSlot < limit; ++freeSlot ) { - XMP_Uns64 freeSize = spaceList[freeSlot].size; - if ( (newSize == freeSize) || ( (newSize < freeSize) && ((freeSize - newSize) >= 8) ) ) break; - } - - if ( freeSlot == spaceList.size() ) { - - // No available free space, append the new box. - CheckFinalBox ( fileRef, abortProc, abortArg ); - LFA_Seek ( fileRef, 0, SEEK_END ); - LFA_Write ( fileRef, newBox, newSize ); - WipeBoxFree ( fileRef, oldOffset, oldSize ); - - } else { - - // Use the available free space. Wipe non-overlapping parts of the old box. The old - // box is either included in the new space, or is fully disjoint. - - SpaceInfo & newSpace = spaceList[freeSlot]; - - bool oldIsDisjoint = ((oldOffset + oldSize) <= newSpace.offset) || // Old is in front. - ((newSpace.offset + newSpace.size) <= oldOffset); // Old is behind. - - XMP_Assert ( (newSize == newSpace.size) || - ( (newSize < newSpace.size) && ((newSpace.size - newSize) >= 8) ) ); - - XMP_Assert ( oldIsDisjoint || - ( (newSpace.offset <= oldOffset) && - ((oldOffset + oldSize) <= (newSpace.offset + newSpace.size)) ) /* old is included */ ); - - XMP_Uns64 newFreeOffset = newSpace.offset + newSize; - XMP_Uns64 newFreeSize = newSpace.size - newSize; - - LFA_Seek ( fileRef, newSpace.offset, SEEK_SET ); - LFA_Write ( fileRef, newBox, newSize ); - - if ( newFreeSize > 0 ) WriteBoxHeader ( fileRef, ISOMedia::k_free, newFreeSize ); - - if ( oldIsDisjoint ) { - - WipeBoxFree ( fileRef, oldOffset, oldSize ); - - } else { - - // Clear the exposed portion of the old box. - - XMP_Uns64 zeroStart = newFreeOffset + 8; - if ( newFreeSize > 0xFFFFFFFF ) zeroStart += 8; - if ( oldOffset > zeroStart ) zeroStart = oldOffset; - XMP_Uns64 zeroEnd = newFreeOffset + newFreeSize; - if ( (oldOffset + oldSize) < zeroEnd ) zeroEnd = oldOffset + oldSize; - - if ( zeroStart < zeroEnd ) { // The new box might cover the old. - XMP_Assert ( (zeroEnd - zeroStart) <= (XMP_Uns64)oldSize ); - XMP_Uns32 zeroSize = (XMP_Uns32) (zeroEnd - zeroStart); - LFA_Seek ( fileRef, zeroStart, SEEK_SET ); - for ( XMP_Uns32 ioCount = sizeof ( kZeroes ); zeroSize > 0; zeroSize -= ioCount ) { - if ( ioCount > zeroSize ) ioCount = zeroSize; - LFA_Write ( fileRef, &kZeroes[0], ioCount ); - } - } - - } - - } - - } - - } - -} // MPEG4_MetaHandler::UpdateTopLevelBox - -// ================================================================================================= -// MPEG4_MetaHandler::UpdateFile -// ============================= -// -// Revamp notes: -// The 'moov' subtree and possibly the XMP 'uuid' box get updated. Compose the new copy of each and -// see if it fits in existing space, incorporating adjacent 'free' boxes if necessary. If that won't -// work, look for a sufficient 'free' box anywhere in the file. As a last resort, append the new copy. -// Assume no location sensitive data within 'moov', i.e. no offsets into it. This lets it be moved -// and its children freely rearranged. - -void MPEG4_MetaHandler::UpdateFile ( bool doSafeUpdate ) -{ - if ( ! this->needsUpdate ) { // If needsUpdate is set then at least the XMP changed. - return; - } - - this->needsUpdate = false; // Make sure only called once. - XMP_Assert ( ! doSafeUpdate ); // This should only be called for "unsafe" updates. - - XMP_AbortProc abortProc = this->parent->abortProc; - void * abortArg = this->parent->abortArg; - const bool checkAbort = (abortProc != 0); - - LFA_FileRef fileRef = this->parent->fileRef; - XMP_Uns64 fileSize = LFA_Measure ( fileRef ); - - bool haveISOFile = (this->fileMode == MOOV_Manager::kFileIsNormalISO); - - // Update the 'moov' subtree with exports from the XMP, but not the XMP itself (for QT files). - - ExportMVHDItems ( this->xmpObj, &this->moovMgr ); - ExportISOCopyrights ( this->xmpObj, &this->moovMgr ); - ExportQuickTimeItems ( this->xmpObj, &this->tradQTMgr, &this->moovMgr ); - ExportTimecodeItems ( this->xmpObj, &this->tmcdInfo, &this->tradQTMgr, &this->moovMgr ); - - if ( ! haveISOFile ) ExportCr8rItems ( this->xmpObj, &this->moovMgr ); - - // Try to update the XMP in-place if that is all that changed, or if it is in a preferred 'uuid' box. - // The XMP has already been serialized by common code to the appropriate length. Otherwise, update - // the 'moov'/'udta'/'XMP_' box in the MOOV_Manager, or the 'uuid' XMP box in the file. - - bool useUuidXMP = (this->fileMode == MOOV_Manager::kFileIsNormalISO); - - if ( (this->xmpPacket.size() == (size_t)this->packetInfo.length) && - ( (useUuidXMP & this->havePreferredXMP) || (! this->moovMgr.IsChanged()) ) ) { - - // Update the existing XMP in-place. - LFA_Seek ( fileRef, this->packetInfo.offset, SEEK_SET ); - LFA_Write ( fileRef, this->xmpPacket.c_str(), (XMP_Int32)this->xmpPacket.size() ); - - } else if ( ! useUuidXMP ) { - - // Don't leave an old uuid XMP around (if we know about it). - if ( (! havePreferredXMP) && (this->xmpBoxSize != 0) ) { - WipeBoxFree ( fileRef, this->xmpBoxPos, this->xmpBoxSize ); - } - - // The udta form of XMP has just the XMP packet. - this->moovMgr.SetBox ( "moov/udta/XMP_", this->xmpPacket.c_str(), (XMP_Uns32)this->xmpPacket.size() ); - - } else { - - // Don't leave an old 'moov'/'udta'/'XMP_' box around. - MOOV_Manager::BoxRef udtaRef = this->moovMgr.GetBox ( "moov/udta", 0 ); - if ( udtaRef != 0 ) this->moovMgr.DeleteTypeChild ( udtaRef, ISOMedia::k_XMP_ ); - - // The uuid form of XMP has the 16-byte UUID in front of the XMP packet. Form the complete - // box (including size/type header) for UpdateTopLevelBox. - RawDataBlock uuidBox; - XMP_Uns32 uuidSize = 4+4 + 16 + (XMP_Uns32)this->xmpPacket.size(); - uuidBox.assign ( uuidSize, 0 ); - PutUns32BE ( uuidSize, &uuidBox[0] ); - PutUns32BE ( ISOMedia::k_uuid, &uuidBox[4] ); - memcpy ( &uuidBox[8], ISOMedia::k_xmpUUID, 16 ); - memcpy ( &uuidBox[24], this->xmpPacket.c_str(), this->xmpPacket.size() ); - this->UpdateTopLevelBox ( this->xmpBoxPos, this->xmpBoxSize, &uuidBox[0], uuidSize ); - - } - - // Update the 'moov' subtree if necessary, and finally update the timecode sample. - - if ( this->moovMgr.IsChanged() ) { - this->moovMgr.UpdateMemoryTree(); - this->UpdateTopLevelBox ( moovBoxPos, moovBoxSize, &this->moovMgr.fullSubtree[0], (XMP_Uns32)this->moovMgr.fullSubtree.size() ); - } - - if ( this->tmcdInfo.sampleOffset != 0 ) { - LFA_Seek ( fileRef, this->tmcdInfo.sampleOffset, SEEK_SET ); - XMP_Uns32 sample = MakeUns32BE ( this->tmcdInfo.timecodeSample ); - LFA_Write ( fileRef, &sample, 4 ); - } - -} // MPEG4_MetaHandler::UpdateFile - -// ================================================================================================= -// MPEG4_MetaHandler::WriteFile -// ============================ -// -// Since the XMP and legacy is probably a miniscule part of the entire file, and since we can't -// change the offset of most of the boxes, just copy the entire source file to the dest file, then -// do an in-place update to the destination file. - -void MPEG4_MetaHandler::WriteFile ( LFA_FileRef sourceRef, const std::string & sourcePath ) -{ - XMP_Assert ( this->needsUpdate ); - - LFA_FileRef destRef = this->parent->fileRef; - - LFA_Seek ( sourceRef, 0, SEEK_SET ); - LFA_Seek ( destRef, 0, SEEK_SET ); - LFA_Copy ( sourceRef, destRef, LFA_Measure ( sourceRef ), - this->parent->abortProc, this->parent->abortArg ); - - this->UpdateFile ( false ); - -} // MPEG4_MetaHandler::WriteFile - -// ================================================================================================= diff --git a/source/XMPFiles/FileHandlers/MPEG4_Handler.hpp b/source/XMPFiles/FileHandlers/MPEG4_Handler.hpp deleted file mode 100644 index 9d00781..0000000 --- a/source/XMPFiles/FileHandlers/MPEG4_Handler.hpp +++ /dev/null @@ -1,94 +0,0 @@ -#ifndef __MPEG4_Handler_hpp__ -#define __MPEG4_Handler_hpp__ 1 - -// ================================================================================================= -// ADOBE SYSTEMS INCORPORATED -// Copyright 2006 Adobe Systems Incorporated -// 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 "XMPFiles_Impl.hpp" - -#include "MOOV_Support.hpp" -#include "QuickTime_Support.hpp" - -// ================================================================================================ -/// \file MPEG4_Handler.hpp -/// \brief File format handler for MPEG-4. -/// -/// This header ... -/// -// ================================================================================================ - -extern XMPFileHandler * MPEG4_MetaHandlerCTor ( XMPFiles * parent ); - -extern bool MPEG4_CheckFormat ( XMP_FileFormat format, - XMP_StringPtr filePath, - LFA_FileRef fileRef, - XMPFiles * parent ); - -static const XMP_OptionBits kMPEG4_HandlerFlags = ( kXMPFiles_CanInjectXMP | - kXMPFiles_CanExpand | - kXMPFiles_CanRewrite | - kXMPFiles_PrefersInPlace | - kXMPFiles_CanReconcile | - kXMPFiles_AllowsOnlyXMP | - kXMPFiles_ReturnsRawPacket | - kXMPFiles_AllowsSafeUpdate - ); - -class MPEG4_MetaHandler : public XMPFileHandler -{ -public: - - void CacheFileData(); - void ProcessXMP(); - - void UpdateFile ( bool doSafeUpdate ); - void WriteFile ( LFA_FileRef sourceRef, const std::string & sourcePath ); - - MPEG4_MetaHandler ( XMPFiles * _parent ); - virtual ~MPEG4_MetaHandler(); - - struct TimecodeTrackInfo { // Info about a QuickTime timecode track. - bool stsdBoxFound, isDropFrame; - XMP_Uns32 timeScale; - XMP_Uns32 frameDuration; - XMP_Uns32 timecodeSample; - XMP_Uns64 sampleOffset; // Absolute file offset of the timecode sample, 0 if none. - XMP_Uns32 nameOffset; // The offset of the 'name' box relative to the 'stsd' box content. - XMP_Uns16 macLang; // The Mac language code of the trailing 'name' box. - std::string macName; // The text part of the trailing 'name' box, in macLang encoding. - TimecodeTrackInfo() - : stsdBoxFound(false), isDropFrame(false), timeScale(0), frameDuration(0), - timecodeSample(0), sampleOffset(0), nameOffset(0), macLang(0) {}; - }; - -private: - - MPEG4_MetaHandler() : fileMode(0), havePreferredXMP(false), - xmpBoxPos(0), moovBoxPos(0), xmpBoxSize(0), moovBoxSize(0) {}; // Hidden on purpose. - - bool ParseTimecodeTrack(); - - void UpdateTopLevelBox ( XMP_Uns64 oldOffset, XMP_Uns32 oldSize, const XMP_Uns8 * newBox, XMP_Uns32 newSize ); - - XMP_Uns8 fileMode; - bool havePreferredXMP; - XMP_Uns64 xmpBoxPos; // The file offset of the XMP box (the size field, not the content). - XMP_Uns64 moovBoxPos; // The file offset of the 'moov' box (the size field, not the content). - XMP_Uns32 xmpBoxSize, moovBoxSize; // The full size of the boxes, not just the content. - - MOOV_Manager moovMgr; - TradQT_Manager tradQTMgr; - - TimecodeTrackInfo tmcdInfo; - -}; // MPEG4_MetaHandler - -// ================================================================================================= - -#endif // __MPEG4_Handler_hpp__ diff --git a/source/XMPFiles/FileHandlers/P2_Handler.cpp b/source/XMPFiles/FileHandlers/P2_Handler.cpp deleted file mode 100644 index 5e3af85..0000000 --- a/source/XMPFiles/FileHandlers/P2_Handler.cpp +++ /dev/null @@ -1,1363 +0,0 @@ -// ================================================================================================= -// ADOBE SYSTEMS INCORPORATED -// Copyright 2007 Adobe Systems Incorporated -// 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 "P2_Handler.hpp" - -#include "MD5.h" -#include <cmath> - -using namespace std; - -// ================================================================================================= -/// \file P2_Handler.cpp -/// \brief Folder format handler for P2. -/// -/// This handler is for the P2 video format. This is a pseudo-package, visible files but with a very -/// well-defined layout and naming rules. A typical P2 example looks like: -/// -/// .../MyMovie -/// CONTENTS/ -/// CLIP/ -/// 0001AB.XML -/// 0001AB.XMP -/// 0002CD.XML -/// 0002CD.XMP -/// VIDEO/ -/// 0001AB.MXF -/// 0002CD.MXF -/// AUDIO/ -/// 0001AB00.MXF -/// 0001AB01.MXF -/// 0002CD00.MXF -/// 0002CD01.MXF -/// ICON/ -/// 0001AB.BMP -/// 0002CD.BMP -/// VOICE/ -/// 0001AB.WAV -/// 0002CD.WAV -/// PROXY/ -/// 0001AB.MP4 -/// 0002CD.MP4 -/// -/// From the user's point of view, .../MyMovie contains P2 stuff, in this case 2 clips whose raw -/// names are 0001AB and 0002CD. There may be mapping information for nicer clip names to the raw -/// names, but that can be ignored for now. Each clip is stored as a collection of files, each file -/// holding some specific aspect of the clip's data. -/// -/// The P2 handler operates on clips. The path from the client of XMPFiles can be either a logical -/// clip path, like ".../MyMovie/0001AB", or a full path to one of the files. In the latter case the -/// handler must figure out the intended clip, it must not blindly use the named file. -/// -/// Once the P2 structure and intended clip are identified, the handler only deals with the .XMP and -/// .XML files in the CLIP folder. The .XMP file, if present, contains the XMP for the clip. The .XML -/// file must be present to define the existance of the clip. It contains a variety of information -/// about the clip, including some legacy metadata. -/// -// ================================================================================================= - -static const char * kContentFolderNames[] = { "CLIP", "VIDEO", "AUDIO", "ICON", "VOICE", "PROXY", 0 }; -static int kNumRequiredContentFolders = 6; // All 6 of the above. - -static inline bool CheckContentFolderName ( const std::string & folderName ) -{ - for ( int i = 0; kContentFolderNames[i] != 0; ++i ) { - if ( folderName == kContentFolderNames[i] ) return true; - } - return false; -} - -// ================================================================================================= -// InternalMakeClipFilePath -// ======================== -// -// P2_CheckFormat can't use the member function. - -static void InternalMakeClipFilePath ( std::string * path, - const std::string & rootPath, - const std::string & clipName, - XMP_StringPtr suffix ) -{ - - *path = rootPath; - *path += kDirChar; - *path += "CONTENTS"; - *path += kDirChar; - *path += "CLIP"; - *path += kDirChar; - *path += clipName; - *path += suffix; - -} // InternalMakeClipFilePath - -// ================================================================================================= -// P2_CheckFormat -// ============== -// -// This version does fairly simple checks. The top level folder (.../MyMovie) must a child folder -// called CONTENTS. This must have a subfolder called CLIP. It may also have subfolders called -// VIDEO, AUDIO, ICON, VOICE, and PROXY. Any mixture of these additional folders is allowed, but no -// other children are allowed in CONTENTS. The CLIP folder must contain a .XML file for the desired -// clip. The name checks are case insensitive. -// -// The state of the string parameters depends on the form of the path passed by the client. If the -// client passed a logical clip path, like ".../MyMovie/0001AB", the parameters are: -// rootPath - ".../MyMovie" -// gpName - empty -// parentName - empty -// leafName - "0001AB" -// If the client passed a full file path, like ".../MyMovie/CONTENTS/VOICE/0001AB.WAV", they are: -// rootPath - ".../MyMovie" -// gpName - "CONTENTS" -// parentName - "VOICE" -// leafName - "0001AB" -// -// For most of the content files the base file name is the raw clip name. Files in the AUDIO and -// VOICE folders have an extra 2 digits appended to the raw clip name. These must be trimmed. - -// ! The common code has shifted the gpName, parentName, and leafName strings to upper case. It has -// ! also made sure that for a logical clip path the rootPath is an existing folder, and that the -// ! file exists for a full file path. - -bool P2_CheckFormat ( XMP_FileFormat format, - const std::string & rootPath, - const std::string & gpName, - const std::string & parentName, - const std::string & leafName, - XMPFiles * parent ) -{ - XMP_FolderInfo folderInfo; - std::string tempPath, childName; - - std::string clipName = leafName; - - // Do some basic checks on the gpName and parentName. - - if ( gpName.empty() != parentName.empty() ) return false; // Must be both empty or both non-empty. - - if ( ! gpName.empty() ) { - - if ( gpName != "CONTENTS" ) return false; - if ( ! CheckContentFolderName ( parentName ) ) return false; - - if ( (parentName == "AUDIO") | (parentName == "VOICE") ) { - if ( clipName.size() < 3 ) return false; - clipName.erase ( clipName.size() - 2 ); - } - - } - - tempPath = rootPath; - tempPath += kDirChar; - tempPath += "CONTENTS"; - if ( GetFileMode ( tempPath.c_str() ) != kFMode_IsFolder ) return false; - - folderInfo.Open ( tempPath.c_str() ); - int numChildrenFound = 0; - while ( ( folderInfo.GetNextChild ( &childName ) && ( numChildrenFound < kNumRequiredContentFolders ) ) ) { // Make sure the children of CONTENTS are legit. - if ( CheckContentFolderName ( childName ) ) { - folderInfo.GetFolderPath ( &tempPath ); - tempPath += kDirChar; - tempPath += childName; - if ( GetFileMode ( tempPath.c_str() ) != kFMode_IsFolder ) return false; - ++numChildrenFound; - } - } - folderInfo.Close(); - - // Make sure the clip's .XML file exists. - - InternalMakeClipFilePath ( &tempPath, rootPath, clipName, ".XML" ); - if ( GetFileMode ( tempPath.c_str() ) != kFMode_IsFile ) return false; - - // Make a bogus path to pass the root path and clip name to the handler. A bit of a hack, but - // the only way to get info from here to there. - - - tempPath = rootPath; - tempPath += kDirChar; - tempPath += clipName; - - size_t pathLen = tempPath.size() + 1; // Include a terminating nul. - parent->tempPtr = malloc ( pathLen ); - if ( parent->tempPtr == 0 ) XMP_Throw ( "No memory for P2 clip path", kXMPErr_NoMemory ); - memcpy ( parent->tempPtr, tempPath.c_str(), pathLen ); // AUDIT: Safe, allocated above. - - return true; - -} // P2_CheckFormat - -// ================================================================================================= -// P2_MetaHandlerCTor -// ================== - -XMPFileHandler * P2_MetaHandlerCTor ( XMPFiles * parent ) -{ - return new P2_MetaHandler ( parent ); - -} // P2_MetaHandlerCTor - -// ================================================================================================= -// P2_MetaHandler::P2_MetaHandler -// ============================== - -P2_MetaHandler::P2_MetaHandler ( XMPFiles * _parent ) : expat(0), clipMetadata(0), clipContent(0) -{ - - this->parent = _parent; // Inherited, can't set in the prefix. - this->handlerFlags = kP2_HandlerFlags; - this->stdCharForm = kXMP_Char8Bit; - - // Extract the root path and clip name from tempPtr. - - XMP_Assert ( this->parent->tempPtr != 0 ); - this->rootPath = (char*)this->parent->tempPtr; - free ( this->parent->tempPtr ); - this->parent->tempPtr = 0; - - SplitLeafName ( &this->rootPath, &this->clipName ); - -} // P2_MetaHandler::P2_MetaHandler - -// ================================================================================================= -// P2_MetaHandler::~P2_MetaHandler -// =============================== - -P2_MetaHandler::~P2_MetaHandler() -{ - - this->CleanupLegacyXML(); - if ( this->parent->tempPtr != 0 ) { - free ( this->parent->tempPtr ); - this->parent->tempPtr = 0; - } - -} // P2_MetaHandler::~P2_MetaHandler - -// ================================================================================================= -// P2_MetaHandler::MakeClipFilePath -// ================================ - -void P2_MetaHandler::MakeClipFilePath ( std::string * path, XMP_StringPtr suffix ) -{ - - InternalMakeClipFilePath ( path, this->rootPath, this->clipName, suffix ); - -} // P2_MetaHandler::MakeClipFilePath - -// ================================================================================================= -// P2_MetaHandler::CleanupLegacyXML -// ================================ - -void P2_MetaHandler::CleanupLegacyXML() -{ - - if ( this->expat != 0 ) { delete ( this->expat ); this->expat = 0; } - - clipMetadata = 0; // ! Was a pointer into the expat tree. - clipContent = 0; // ! Was a pointer into the expat tree. - -} // P2_MetaHandler::CleanupLegacyXML - -// ================================================================================================= -// P2_MetaHandler::DigestLegacyItem -// ================================ - -void P2_MetaHandler::DigestLegacyItem ( MD5_CTX & md5Context, XML_NodePtr legacyContext, XMP_StringPtr legacyPropName ) -{ - XML_NodePtr legacyProp = legacyContext->GetNamedElement ( this->p2NS.c_str(), legacyPropName ); - - if ( (legacyProp != 0) && legacyProp->IsLeafContentNode() && (! legacyProp->content.empty()) ) { - const XML_Node * xmlValue = legacyProp->content[0]; - MD5Update ( &md5Context, (XMP_Uns8*)xmlValue->value.c_str(), (unsigned int)xmlValue->value.size() ); - } - -} // P2_MetaHandler::DigestLegacyItem - -// ================================================================================================= -// P2_MetaHandler::DigestLegacyRelations -// ===================================== - -void P2_MetaHandler::DigestLegacyRelations ( MD5_CTX & md5Context ) -{ - XMP_StringPtr p2NS = this->p2NS.c_str(); - XML_Node * legacyContext = this->clipContent->GetNamedElement ( p2NS, "Relation" ); - - if ( legacyContext != 0 ) { - - this->DigestLegacyItem ( md5Context, legacyContext, "GlobalShotID" ); - XML_Node * legacyConnectionContext = legacyContext = this->clipContent->GetNamedElement ( p2NS, "Connection" ); - - if ( legacyConnectionContext != 0 ) { - - legacyContext = legacyConnectionContext->GetNamedElement ( p2NS, "Top" ); - - if ( legacyContext != 0 ) { - this->DigestLegacyItem ( md5Context, legacyContext, "GlobalClipID" ); - } - - legacyContext = legacyConnectionContext->GetNamedElement ( p2NS, "Previous" ); - - if ( legacyContext != 0 ) { - this->DigestLegacyItem ( md5Context, legacyContext, "GlobalClipID" ); - } - - legacyContext = legacyConnectionContext->GetNamedElement ( p2NS, "Next" ); - - if ( legacyContext != 0 ) { - this->DigestLegacyItem ( md5Context, legacyContext, "GlobalClipID" ); - } - - } - - } - -} // P2_MetaHandler::DigestLegacyRelations - -// ================================================================================================= -// P2_MetaHandler::SetXMPPropertyFromLegacyXML -// =========================================== - -void P2_MetaHandler::SetXMPPropertyFromLegacyXML ( bool digestFound, - XML_NodePtr legacyContext, - XMP_StringPtr schemaNS, - XMP_StringPtr propName, - XMP_StringPtr legacyPropName, - bool isLocalized ) -{ - - if ( digestFound || (! this->xmpObj.DoesPropertyExist ( schemaNS, propName )) ) { - - XMP_StringPtr p2NS = this->p2NS.c_str(); - XML_NodePtr legacyProp = legacyContext->GetNamedElement ( p2NS, legacyPropName ); - - if ( (legacyProp != 0) && legacyProp->IsLeafContentNode() ) { - if ( isLocalized ) { - this->xmpObj.SetLocalizedText ( schemaNS, propName, "", "x-default", legacyProp->GetLeafContentValue(), kXMP_DeleteExisting ); - } else { - this->xmpObj.SetProperty ( schemaNS, propName, legacyProp->GetLeafContentValue(), kXMP_DeleteExisting ); - } - this->containsXMP = true; - } - - } - -} // P2_MetaHandler::SetXMPPropertyFromLegacyXML - -// ================================================================================================= -// P2_MetaHandler::SetRelationsFromLegacyXML -// ========================================= - -void P2_MetaHandler::SetRelationsFromLegacyXML ( bool digestFound ) -{ - XMP_StringPtr p2NS = this->p2NS.c_str(); - XML_NodePtr legacyRelationContext = this->clipContent->GetNamedElement ( p2NS, "Relation" ); - - // P2 Relation blocks are optional -- they're only present when a clip is part of a multi-clip shot. - - if ( legacyRelationContext != 0 ) { - - if ( digestFound || (! this->xmpObj.DoesPropertyExist ( kXMP_NS_DC, "relation" )) ) { - - XML_NodePtr legacyProp = legacyRelationContext->GetNamedElement ( p2NS, "GlobalShotID" ); - std::string relationString; - - if ( (legacyProp != 0) && legacyProp->IsLeafContentNode() ) { - - this->xmpObj.DeleteProperty ( kXMP_NS_DC, "relation" ); - relationString = std::string("globalShotID:") + legacyProp->GetLeafContentValue(); - this->xmpObj.AppendArrayItem ( kXMP_NS_DC, "relation", kXMP_PropArrayIsUnordered, relationString ); - this->containsXMP = true; - - XML_NodePtr legacyConnectionContext = legacyRelationContext->GetNamedElement ( p2NS, "Connection" ); - - if ( legacyConnectionContext != 0 ) { - - XML_NodePtr legacyContext = legacyConnectionContext->GetNamedElement ( p2NS, "Top" ); - - if ( legacyContext != 0 ) { - legacyProp = legacyContext->GetNamedElement ( p2NS, "GlobalClipID" ); - - if ( (legacyProp != 0) && legacyProp->IsLeafContentNode() ) { - relationString = std::string("topGlobalClipID:") + legacyProp->GetLeafContentValue(); - this->xmpObj.AppendArrayItem ( kXMP_NS_DC, "relation", kXMP_PropArrayIsUnordered, relationString ); - } - } - - legacyContext = legacyConnectionContext->GetNamedElement ( p2NS, "Previous" ); - - if ( legacyContext != 0 ) { - legacyProp = legacyContext->GetNamedElement ( p2NS, "GlobalClipID" ); - - if ( (legacyProp != 0) && legacyProp->IsLeafContentNode() ) { - relationString = std::string("previousGlobalClipID:") + legacyProp->GetLeafContentValue(); - this->xmpObj.AppendArrayItem ( kXMP_NS_DC, "relation", kXMP_PropArrayIsUnordered, relationString ); - } - } - - legacyContext = legacyConnectionContext->GetNamedElement ( p2NS, "Next" ); - - if ( legacyContext != 0 ) { - legacyProp = legacyContext->GetNamedElement ( p2NS, "GlobalClipID" ); - - if ( (legacyProp != 0) && legacyProp->IsLeafContentNode() ) { - relationString = std::string("nextGlobalClipID:") + legacyProp->GetLeafContentValue(); - this->xmpObj.AppendArrayItem ( kXMP_NS_DC, "relation", kXMP_PropArrayIsUnordered, relationString ); - } - } - - } - - } - - } - - } - -} // P2_MetaHandler::SetRelationsFromLegacyXML - -// ================================================================================================= -// P2_MetaHandler::SetAudioInfoFromLegacyXML -// ========================================= - -void P2_MetaHandler::SetAudioInfoFromLegacyXML ( bool digestFound ) -{ - XMP_StringPtr p2NS = this->p2NS.c_str(); - XML_NodePtr legacyAudioContext = this->clipContent->GetNamedElement ( p2NS, "EssenceList" ); - - if ( legacyAudioContext != 0 ) { - - legacyAudioContext = legacyAudioContext->GetNamedElement ( p2NS, "Audio" ); - - if ( legacyAudioContext != 0 ) { - - this->SetXMPPropertyFromLegacyXML ( digestFound, legacyAudioContext, kXMP_NS_DM, "audioSampleRate", "SamplingRate", false ); - - if ( digestFound || (! this->xmpObj.DoesPropertyExist ( kXMP_NS_DM, "audioSampleType" )) ) { - XML_NodePtr legacyProp = legacyAudioContext->GetNamedElement ( p2NS, "BitsPerSample" ); - - if ( (legacyProp != 0) && legacyProp->IsLeafContentNode() ) { - - const std::string p2BitsPerSample = legacyProp->GetLeafContentValue(); - std::string dmSampleType; - - if ( p2BitsPerSample == "16" ) { - dmSampleType = "16Int"; - } else if ( p2BitsPerSample == "24" ) { - dmSampleType = "32Int"; - } - - if ( ! dmSampleType.empty() ) { - this->xmpObj.SetProperty ( kXMP_NS_DM, "audioSampleType", dmSampleType, kXMP_DeleteExisting ); - this->containsXMP = true; - } - - } - - } - - } - - } - -} // P2_MetaHandler::SetAudioInfoFromLegacyXML - -// ================================================================================================= -// P2_MetaHandler::SetVideoInfoFromLegacyXML -// ========================================= - -void P2_MetaHandler::SetVideoInfoFromLegacyXML ( bool digestFound ) -{ - XMP_StringPtr p2NS = this->p2NS.c_str(); - XML_NodePtr legacyVideoContext = this->clipContent->GetNamedElement ( p2NS, "EssenceList" ); - - if ( legacyVideoContext != 0 ) { - - legacyVideoContext = legacyVideoContext->GetNamedElement ( p2NS, "Video" ); - - if ( legacyVideoContext != 0 ) { - this->SetVideoFrameInfoFromLegacyXML ( legacyVideoContext, digestFound ); - this->SetStartTimecodeFromLegacyXML ( legacyVideoContext, digestFound ); - this->SetXMPPropertyFromLegacyXML ( digestFound, legacyVideoContext, kXMP_NS_DM, "videoFrameRate", "FrameRate", false ); - } - - } - -} // P2_MetaHandler::SetVideoInfoFromLegacyXML - -// ================================================================================================= -// P2_MetaHandler::SetDurationFromLegacyXML -// ======================================== - -void P2_MetaHandler::SetDurationFromLegacyXML ( bool digestFound ) -{ - - if ( digestFound || (! this->xmpObj.DoesPropertyExist ( kXMP_NS_DM, "duration" )) ) { - - XMP_StringPtr p2NS = this->p2NS.c_str(); - XML_NodePtr legacyDurationProp = this->clipContent->GetNamedElement ( p2NS, "Duration" ); - XML_NodePtr legacyEditUnitProp = this->clipContent->GetNamedElement ( p2NS, "EditUnit" ); - - if ( (legacyDurationProp != 0) && ( legacyEditUnitProp != 0 ) && - legacyDurationProp->IsLeafContentNode() && legacyEditUnitProp->IsLeafContentNode() ) { - - this->xmpObj.DeleteProperty ( kXMP_NS_DM, "duration" ); - this->xmpObj.SetStructField ( kXMP_NS_DM, "duration", - kXMP_NS_DM, "value", legacyDurationProp->GetLeafContentValue() ); - - this->xmpObj.SetStructField ( kXMP_NS_DM, "duration", - kXMP_NS_DM, "scale", legacyEditUnitProp->GetLeafContentValue() ); - this->containsXMP = true; - - } - - } - -} // P2_MetaHandler::SetDurationFromLegacyXML - -// ================================================================================================= -// P2_MetaHandler::SetVideoFrameInfoFromLegacyXML -// ============================================== - -void P2_MetaHandler::SetVideoFrameInfoFromLegacyXML ( XML_NodePtr legacyVideoContext, bool digestFound ) -{ - - // Map the P2 Codec field to various dynamic media schema fields. - if ( digestFound || (! this->xmpObj.DoesPropertyExist ( kXMP_NS_DM, "videoFrameSize" )) ) { - - XMP_StringPtr p2NS = this->p2NS.c_str(); - XML_NodePtr legacyProp = legacyVideoContext->GetNamedElement ( p2NS, "Codec" ); - - if ( (legacyProp != 0) && legacyProp->IsLeafContentNode() ) { - - const std::string p2Codec = legacyProp->GetLeafContentValue(); - std::string dmPixelAspectRatio, dmVideoCompressor, dmWidth, dmHeight; - - if ( p2Codec == "DV25_411" ) { - dmWidth = "720"; - dmVideoCompressor = "DV25 4:1:1"; - } else if ( p2Codec == "DV25_420" ) { - dmWidth = "720"; - dmVideoCompressor = "DV25 4:2:0"; - } else if ( p2Codec == "DV50_422" ) { - dmWidth = "720"; - dmVideoCompressor = "DV50 4:2:2"; - } else if ( ( p2Codec == "DV100_1080/59.94i" ) || ( p2Codec == "DV100_1080/50i" ) ) { - dmVideoCompressor = "DV100"; - dmHeight = "1080"; - - if ( p2Codec == "DV100_1080/59.94i" ) { - dmWidth = "1280"; - dmPixelAspectRatio = "3/2"; - } else { - dmWidth = "1440"; - dmPixelAspectRatio = "1920/1440"; - } - } else if ( ( p2Codec == "DV100_720/59.94p" ) || ( p2Codec == "DV100_720/50p" ) ) { - dmVideoCompressor = "DV100"; - dmHeight = "720"; - dmWidth = "960"; - dmPixelAspectRatio = "1920/1440"; - } else if ( ( p2Codec.compare ( 0, 6, "AVC-I_" ) == 0 ) ) { - - // This is AVC-Intra footage. The framerate and PAR depend on the "class" attribute in the P2 XML. - const XMP_StringPtr codecClass = legacyProp->GetAttrValue( "Class" ); - - if ( XMP_LitMatch ( codecClass, "100" ) ) { - - dmVideoCompressor = "AVC-Intra 100"; - dmPixelAspectRatio = "1/1"; - - if ( p2Codec.compare ( 6, 4, "1080" ) == 0 ) { - dmHeight = "1080"; - dmWidth = "1920"; - } else if ( p2Codec.compare ( 6, 3, "720" ) == 0 ) { - dmHeight = "720"; - dmWidth = "1280"; - } - - } else if ( XMP_LitMatch ( codecClass, "50" ) ) { - - dmVideoCompressor = "AVC-Intra 50"; - dmPixelAspectRatio = "1920/1440"; - - if ( p2Codec.compare ( 6, 4, "1080" ) == 0 ) { - dmHeight = "1080"; - dmWidth = "1440"; - } else if ( p2Codec.compare ( 6, 3, "720" ) == 0 ) { - dmHeight = "720"; - dmWidth = "960"; - } - - } else { - // Unknown codec class -- we don't have enough info to determine the - // codec, PAR, or aspect ratio - dmVideoCompressor = "AVC-Intra"; - } - } - - if ( dmWidth == "720" ) { - - // This is SD footage -- calculate the frame height and pixel aspect ratio using the legacy P2 - // FrameRate and AspectRatio fields. - - legacyProp = legacyVideoContext->GetNamedElement ( p2NS, "FrameRate" ); - if ( (legacyProp != 0) && legacyProp->IsLeafContentNode() ) { - - const std::string p2FrameRate = legacyProp->GetLeafContentValue(); - - legacyProp = legacyVideoContext->GetNamedElement ( p2NS, "AspectRatio" ); - - if ( (legacyProp != 0) && legacyProp->IsLeafContentNode() ) { - const std::string p2AspectRatio = legacyProp->GetLeafContentValue(); - - if ( p2FrameRate == "50i" ) { - // Standard Definition PAL. - dmHeight = "576"; - if ( p2AspectRatio == "4:3" ) { - dmPixelAspectRatio = "768/702"; - } else if ( p2AspectRatio == "16:9" ) { - dmPixelAspectRatio = "1024/702"; - } - } else if ( p2FrameRate == "59.94i" ) { - // Standard Definition NTSC. - dmHeight = "480"; - if ( p2AspectRatio == "4:3" ) { - dmPixelAspectRatio = "10/11"; - } else if ( p2AspectRatio == "16:9" ) { - dmPixelAspectRatio = "40/33"; - } - } - - } - } - } - - if ( ! dmPixelAspectRatio.empty() ) { - this->xmpObj.SetProperty ( kXMP_NS_DM, "videoPixelAspectRatio", dmPixelAspectRatio, kXMP_DeleteExisting ); - this->containsXMP = true; - } - - if ( ! dmVideoCompressor.empty() ) { - this->xmpObj.SetProperty ( kXMP_NS_DM, "videoCompressor", dmVideoCompressor, kXMP_DeleteExisting ); - this->containsXMP = true; - } - - if ( ( ! dmWidth.empty() ) && ( ! dmHeight.empty() ) ) { - this->xmpObj.SetStructField ( kXMP_NS_DM, "videoFrameSize", kXMP_NS_XMP_Dimensions, "w", dmWidth, 0 ); - this->xmpObj.SetStructField ( kXMP_NS_DM, "videoFrameSize", kXMP_NS_XMP_Dimensions, "h", dmHeight, 0 ); - this->xmpObj.SetStructField ( kXMP_NS_DM, "videoFrameSize", kXMP_NS_XMP_Dimensions, "unit", "pixel", 0 ); - this->containsXMP = true; - } - - } - - } - -} // P2_MetaHandler::SetVideoFrameInfoFromLegacyXML - -// ================================================================================================= -// P2_MetaHandler::SetStartTimecodeFromLegacyXML -// ============================================= - -void P2_MetaHandler::SetStartTimecodeFromLegacyXML ( XML_NodePtr legacyVideoContext, bool digestFound ) -{ - - // Translate start timecode to the format specified by the dynamic media schema. - if ( digestFound || (! this->xmpObj.DoesPropertyExist ( kXMP_NS_DM, "startTimecode" )) ) { - - XMP_StringPtr p2NS = this->p2NS.c_str(); - XML_NodePtr legacyProp = legacyVideoContext->GetNamedElement ( p2NS, "StartTimecode" ); - - if ( (legacyProp != 0) && legacyProp->IsLeafContentNode() ) { - - std::string p2StartTimecode = legacyProp->GetLeafContentValue(); - - legacyProp = legacyVideoContext->GetNamedElement ( p2NS, "FrameRate" ); - - if ( (legacyProp != 0) && legacyProp->IsLeafContentNode() ) { - - const std::string p2FrameRate = legacyProp->GetLeafContentValue(); - XMP_StringPtr p2DropFrameFlag = legacyProp->GetAttrValue ( "DropFrameFlag" ); - if ( p2DropFrameFlag == 0 ) p2DropFrameFlag = ""; // Make tests easier. - std::string dmTimeFormat; - - if ( ( p2FrameRate == "50i" ) || ( p2FrameRate == "25p" ) ) { - - dmTimeFormat = "25Timecode"; - - } else if ( p2FrameRate == "23.98p" ) { - - dmTimeFormat = "23976Timecode"; - - } else if ( p2FrameRate == "50p" ) { - - dmTimeFormat = "50Timecode"; - - } else if ( p2FrameRate == "59.94p" ) { - - if ( XMP_LitMatch ( p2DropFrameFlag, "true" ) ) { - dmTimeFormat = "5994DropTimecode"; - } else if ( XMP_LitMatch ( p2DropFrameFlag, "false" ) ) { - dmTimeFormat = "5994NonDropTimecode"; - } - - } else if ( (p2FrameRate == "59.94i") || (p2FrameRate == "29.97p") ) { - - if ( p2DropFrameFlag != 0 ) { - - if ( XMP_LitMatch ( p2DropFrameFlag, "false" ) ) { - - dmTimeFormat = "2997NonDropTimecode"; - - } else if ( XMP_LitMatch ( p2DropFrameFlag, "true" ) ) { - - // Drop frame NTSC timecode uses semicolons instead of colons as separators. - std::string::iterator currCharIt = p2StartTimecode.begin(); - const std::string::iterator charsEndIt = p2StartTimecode.end(); - - for ( ; currCharIt != charsEndIt; ++currCharIt ) { - if ( *currCharIt == ':' ) *currCharIt = ';'; - } - - dmTimeFormat = "2997DropTimecode"; - - } - - } - - } - - if ( ( ! p2StartTimecode.empty() ) && ( ! dmTimeFormat.empty() ) ) { - this->xmpObj.SetStructField ( kXMP_NS_DM, "startTimecode", kXMP_NS_DM, "timeValue", p2StartTimecode, 0 ); - this->xmpObj.SetStructField ( kXMP_NS_DM, "startTimecode", kXMP_NS_DM, "timeFormat", dmTimeFormat, 0 ); - this->containsXMP = true; - } - - } - - } - - } - -} // P2_MetaHandler::SetStartTimecodeFromLegacyXML - - -// ================================================================================================= -// P2_MetaHandler::SetGPSPropertyFromLegacyXML -// =========================================== - -void P2_MetaHandler::SetGPSPropertyFromLegacyXML ( XML_NodePtr legacyLocationContext, bool digestFound, XMP_StringPtr propName, XMP_StringPtr legacyPropName ) -{ - - if ( digestFound || (! this->xmpObj.DoesPropertyExist ( kXMP_NS_EXIF, propName )) ) { - - XMP_StringPtr p2NS = this->p2NS.c_str(); - XML_NodePtr legacyGPSProp = legacyLocationContext->GetNamedElement ( p2NS, legacyPropName ); - - if ( ( legacyGPSProp != 0 ) && legacyGPSProp->IsLeafContentNode() ) { - - this->xmpObj.DeleteProperty ( kXMP_NS_EXIF, propName ); - - const std::string legacyGPSValue = legacyGPSProp->GetLeafContentValue(); - - if ( ! legacyGPSValue.empty() ) { - - // Convert from decimal to sexagesimal GPS coordinates - char direction = '\0'; - double degrees = 0.0; - const int numFieldsRead = sscanf ( legacyGPSValue.c_str(), "%c%lf", &direction, °rees ); - - if ( numFieldsRead == 2 ) { - double wholeDegrees = 0.0; - const double fractionalDegrees = modf ( degrees, &wholeDegrees ); - const double minutes = fractionalDegrees * 60.0; - char xmpValue [128]; - - sprintf ( xmpValue, "%d,%.5lf%c", static_cast<int>(wholeDegrees), minutes, direction ); - this->xmpObj.SetProperty ( kXMP_NS_EXIF, propName, xmpValue ); - this->containsXMP = true; - - } - - } - - } - - } - -} // P2_MetaHandler::SetGPSPropertyFromLegacyXML - -// ================================================================================================= -// P2_MetaHandler::SetAltitudeFromLegacyXML -// ======================================== - -void P2_MetaHandler::SetAltitudeFromLegacyXML ( XML_NodePtr legacyLocationContext, bool digestFound ) -{ - - if ( digestFound || (! this->xmpObj.DoesPropertyExist ( kXMP_NS_EXIF, "GPSAltitude" )) ) { - - XMP_StringPtr p2NS = this->p2NS.c_str(); - XML_NodePtr legacyAltitudeProp = legacyLocationContext->GetNamedElement ( p2NS, "Altitude" ); - - if ( ( legacyAltitudeProp != 0 ) && legacyAltitudeProp->IsLeafContentNode() ) { - - this->xmpObj.DeleteProperty ( kXMP_NS_EXIF, "GPSAltitude" ); - - const std::string legacyGPSValue = legacyAltitudeProp->GetLeafContentValue(); - - if ( ! legacyGPSValue.empty() ) { - - int altitude = 0; - - if ( sscanf ( legacyGPSValue.c_str(), "%d", &altitude ) == 1) { - - if ( altitude >= 0 ) { - // At or above sea level. - this->xmpObj.SetProperty ( kXMP_NS_EXIF, "GPSAltitudeRef", "0" ); - } else { - // Below sea level. - altitude = -altitude; - this->xmpObj.SetProperty ( kXMP_NS_EXIF, "GPSAltitudeRef", "1" ); - } - - char xmpValue [128]; - - sprintf ( xmpValue, "%d/1", altitude ); - this->xmpObj.SetProperty ( kXMP_NS_EXIF, "GPSAltitude", xmpValue ); - this->containsXMP = true; - - } - - } - - } - - } - -} // P2_MetaHandler::SetAltitudeFromLegacyXML - -// ================================================================================================= -// P2_MetaHandler::ForceChildElement -// ================================= - -XML_Node * P2_MetaHandler::ForceChildElement ( XML_Node * parent, XMP_StringPtr localName, int indent /* = 0 */ ) -{ - XML_Node * wsNode; - XML_Node * childNode = parent->GetNamedElement ( this->p2NS.c_str(), localName ); - - if ( childNode == 0 ) { - - // The indenting is a hack, assuming existing 2 spaces per level. - - wsNode = new XML_Node ( parent, "", kCDataNode ); - wsNode->value = " "; // Add 2 spaces to the existing WS before the parent's close tag. - parent->content.push_back ( wsNode ); - - childNode = new XML_Node ( parent, localName, kElemNode ); - childNode->ns = parent->ns; - childNode->nsPrefixLen = parent->nsPrefixLen; - childNode->name.insert ( 0, parent->name, 0, parent->nsPrefixLen ); - parent->content.push_back ( childNode ); - - wsNode = new XML_Node ( parent, "", kCDataNode ); - wsNode->value = '\n'; - for ( ; indent > 1; --indent ) wsNode->value += " "; // Indent less 1, to "outdent" the parent's close. - parent->content.push_back ( wsNode ); - - } - - return childNode; - -} // P2_MetaHandler::ForceChildElement - -// ================================================================================================= -// P2_MetaHandler::MakeLegacyDigest -// ================================= - -// *** Early hack version. - -#define kHexDigits "0123456789ABCDEF" - -void P2_MetaHandler::MakeLegacyDigest ( std::string * digestStr ) -{ - digestStr->erase(); - if ( this->clipMetadata == 0 ) return; // Bail if we don't have any legacy XML. - XMP_Assert ( this->expat != 0 ); - - XMP_StringPtr p2NS = this->p2NS.c_str(); - XML_NodePtr legacyContext; - MD5_CTX md5Context; - unsigned char digestBin [16]; - MD5Init ( &md5Context ); - - legacyContext = this->clipContent; - this->DigestLegacyItem ( md5Context, legacyContext, "ClipName" ); - this->DigestLegacyItem ( md5Context, legacyContext, "GlobalClipID" ); - this->DigestLegacyItem ( md5Context, legacyContext, "Duration" ); - this->DigestLegacyItem ( md5Context, legacyContext, "EditUnit" ); - this->DigestLegacyRelations ( md5Context ); - - legacyContext = this->clipContent->GetNamedElement ( p2NS, "EssenceList" ); - - if ( legacyContext != 0 ) { - - XML_NodePtr videoContext = legacyContext->GetNamedElement ( p2NS, "Video" ); - - if ( videoContext != 0 ) { - this->DigestLegacyItem ( md5Context, videoContext, "AspectRatio" ); - this->DigestLegacyItem ( md5Context, videoContext, "Codec" ); - this->DigestLegacyItem ( md5Context, videoContext, "FrameRate" ); - this->DigestLegacyItem ( md5Context, videoContext, "StartTimecode" ); - } - - XML_NodePtr audioContext = legacyContext->GetNamedElement ( p2NS, "Audio" ); - - if ( audioContext != 0 ) { - this->DigestLegacyItem ( md5Context, audioContext, "SamplingRate" ); - this->DigestLegacyItem ( md5Context, audioContext, "BitsPerSample" ); - } - - } - - legacyContext = this->clipMetadata; - this->DigestLegacyItem ( md5Context, legacyContext, "UserClipName" ); - this->DigestLegacyItem ( md5Context, legacyContext, "ShotMark" ); - - legacyContext = this->clipMetadata->GetNamedElement ( p2NS, "Access" ); - /* Rather return than create the digest because the "Access" element is listed as "required" in the P2 spec. - So a P2 file without an "Access" element does not follow the spec and might be corrupt.*/ - if ( legacyContext == 0 ) return; - - this->DigestLegacyItem ( md5Context, legacyContext, "Creator" ); - this->DigestLegacyItem ( md5Context, legacyContext, "CreationDate" ); - this->DigestLegacyItem ( md5Context, legacyContext, "LastUpdateDate" ); - - legacyContext = this->clipMetadata->GetNamedElement ( p2NS, "Shoot" ); - - if ( legacyContext != 0 ) { - this->DigestLegacyItem ( md5Context, legacyContext, "Shooter" ); - - legacyContext = legacyContext->GetNamedElement ( p2NS, "Location" ); - - if ( legacyContext != 0 ) { - this->DigestLegacyItem ( md5Context, legacyContext, "PlaceName" ); - this->DigestLegacyItem ( md5Context, legacyContext, "Longitude" ); - this->DigestLegacyItem ( md5Context, legacyContext, "Latitude" ); - this->DigestLegacyItem ( md5Context, legacyContext, "Altitude" ); - } - } - - legacyContext = this->clipMetadata->GetNamedElement ( p2NS, "Scenario" ); - - if ( legacyContext != 0 ) { - this->DigestLegacyItem ( md5Context, legacyContext, "SceneNo." ); - this->DigestLegacyItem ( md5Context, legacyContext, "TakeNo." ); - } - - legacyContext = this->clipMetadata->GetNamedElement ( p2NS, "Device" ); - - if ( legacyContext != 0 ) { - this->DigestLegacyItem ( md5Context, legacyContext, "Manufacturer" ); - this->DigestLegacyItem ( md5Context, legacyContext, "SerialNo." ); - this->DigestLegacyItem ( md5Context, legacyContext, "ModelName" ); - } - - MD5Final ( digestBin, &md5Context ); - - char buffer [40]; - for ( int in = 0, out = 0; in < 16; in += 1, out += 2 ) { - XMP_Uns8 byte = digestBin[in]; - buffer[out] = kHexDigits [ byte >> 4 ]; - buffer[out+1] = kHexDigits [ byte & 0xF ]; - } - buffer[32] = 0; - digestStr->append ( buffer ); - -} // P2_MetaHandler::MakeLegacyDigest - -// ================================================================================================= -// P2_MetaHandler::CacheFileData -// ============================= - -void P2_MetaHandler::CacheFileData() -{ - XMP_Assert ( ! this->containsXMP ); - - // Make sure the clip's .XMP file exists. - - std::string xmpPath; - this->MakeClipFilePath ( &xmpPath, ".XMP" ); - - if ( GetFileMode ( xmpPath.c_str() ) != kFMode_IsFile ) return; // No XMP. - - // Read the entire .XMP file. - - bool openForUpdate = XMP_OptionIsSet ( this->parent->openFlags, kXMPFiles_OpenForUpdate ); - char openMode = 'r'; - if ( openForUpdate ) openMode = 'w'; - - LFA_FileRef xmpFile = LFA_Open ( xmpPath.c_str(), openMode ); - if ( xmpFile == 0 ) return; // The open failed. - - XMP_Int64 xmpLen = LFA_Measure ( xmpFile ); - if ( xmpLen > 100*1024*1024 ) { - XMP_Throw ( "P2 XMP is outrageously large", kXMPErr_InternalFailure ); // Sanity check. - } - - this->xmpPacket.erase(); - this->xmpPacket.reserve ( (size_t)xmpLen ); - this->xmpPacket.append ( (size_t)xmpLen, ' ' ); - - XMP_Int32 ioCount = LFA_Read ( xmpFile, (void*)this->xmpPacket.data(), (XMP_Int32)xmpLen, kLFA_RequireAll ); - XMP_Assert ( ioCount == xmpLen ); - - this->packetInfo.offset = 0; - this->packetInfo.length = (XMP_Int32)xmpLen; - FillPacketInfo ( this->xmpPacket, &this->packetInfo ); - - XMP_Assert ( this->parent->fileRef == 0 ); - if ( openMode == 'r' ) { - LFA_Close ( xmpFile ); - } else { - this->parent->fileRef = xmpFile; - } - - this->containsXMP = true; - -} // P2_MetaHandler::CacheFileData - -// ================================================================================================= -// P2_MetaHandler::ProcessXMP -// ========================== - -void P2_MetaHandler::ProcessXMP() -{ - - // Some versions of gcc can't tolerate goto's across declarations. - // *** Better yet, avoid this cruft with self-cleaning objects. - #define CleanupAndExit \ - { \ - bool openForUpdate = XMP_OptionIsSet ( this->parent->openFlags, kXMPFiles_OpenForUpdate ); \ - if ( ! openForUpdate ) this->CleanupLegacyXML(); \ - return; \ - } - - if ( this->processedXMP ) return; - this->processedXMP = true; // Make sure only called once. - - if ( this->containsXMP ) { - this->xmpObj.ParseFromBuffer ( this->xmpPacket.c_str(), (XMP_StringLen)this->xmpPacket.size() ); - } - - std::string xmlPath; - this->MakeClipFilePath ( &xmlPath, ".XML" ); - - AutoFile xmlFile; - xmlFile.fileRef = LFA_Open ( xmlPath.c_str(), 'r' ); - if ( xmlFile.fileRef == 0 ) return; // The open failed. - - this->expat = XMP_NewExpatAdapter ( ExpatAdapter::kUseLocalNamespaces ); - if ( this->expat == 0 ) XMP_Throw ( "P2_MetaHandler: Can't create Expat adapter", kXMPErr_NoMemory ); - - XMP_Uns8 buffer [64*1024]; - while ( true ) { - XMP_Int32 ioCount = LFA_Read ( xmlFile.fileRef, buffer, sizeof(buffer) ); - if ( ioCount == 0 ) break; - this->expat->ParseBuffer ( buffer, ioCount, false /* not the end */ ); - } - this->expat->ParseBuffer ( 0, 0, true ); // End the parse. - - LFA_Close ( xmlFile.fileRef ); - xmlFile.fileRef = 0; - - // The root element should be P2Main in some namespace. At least 2 different namespaces are in - // use (ending in "v3.0" and "v3.1"). Take whatever this file uses. - - XML_Node & xmlTree = this->expat->tree; - XML_NodePtr rootElem = 0; - - for ( size_t i = 0, limit = xmlTree.content.size(); i < limit; ++i ) { - if ( xmlTree.content[i]->kind == kElemNode ) { - rootElem = xmlTree.content[i]; - } - } - - if ( rootElem == 0 ) CleanupAndExit - XMP_StringPtr rootLocalName = rootElem->name.c_str() + rootElem->nsPrefixLen; - if ( ! XMP_LitMatch ( rootLocalName, "P2Main" ) ) CleanupAndExit - - this->p2NS = rootElem->ns; - - // Now find ClipMetadata element and check the legacy digest. - - XMP_StringPtr p2NS = this->p2NS.c_str(); - XML_NodePtr legacyContext, legacyProp; - - legacyContext = rootElem->GetNamedElement ( p2NS, "ClipContent" ); - if ( legacyContext == 0 ) CleanupAndExit - - this->clipContent = legacyContext; // ! Save the ClipContext pointer for other use. - - legacyContext = legacyContext->GetNamedElement ( p2NS, "ClipMetadata" ); - if ( legacyContext == 0 ) CleanupAndExit - - this->clipMetadata = legacyContext; // ! Save the ClipMetadata pointer for other use. - - std::string oldDigest, newDigest; - bool digestFound = this->xmpObj.GetStructField ( kXMP_NS_XMP, "NativeDigests", kXMP_NS_XMP, "P2", &oldDigest, 0 ); - if ( digestFound ) { - this->MakeLegacyDigest ( &newDigest ); - if ( oldDigest == newDigest ) CleanupAndExit - } - - // If we get here we need find and import the actual legacy elements using the current namespace. - // Either there is no old digest in the XMP, or the digests differ. In the former case keep any - // existing XMP, in the latter case take new legacy values. - this->SetXMPPropertyFromLegacyXML ( digestFound, this->clipContent, kXMP_NS_DC, "title", "ClipName", true ); - this->SetXMPPropertyFromLegacyXML ( digestFound, this->clipContent, kXMP_NS_DC, "identifier", "GlobalClipID", false ); - this->SetDurationFromLegacyXML (digestFound ); - this->SetRelationsFromLegacyXML ( digestFound ); - this->SetXMPPropertyFromLegacyXML ( digestFound, this->clipMetadata, kXMP_NS_DM, "shotName", "UserClipName", false ); - this->SetAudioInfoFromLegacyXML ( digestFound ); - this->SetVideoInfoFromLegacyXML ( digestFound ); - - legacyContext = this->clipMetadata->GetNamedElement ( p2NS, "Access" ); - if ( legacyContext == 0 ) CleanupAndExit - - if ( digestFound || (! this->xmpObj.DoesPropertyExist ( kXMP_NS_DC, "creator" )) ) { - legacyProp = legacyContext->GetNamedElement ( p2NS, "Creator" ); - if ( (legacyProp != 0) && legacyProp->IsLeafContentNode() ) { - this->xmpObj.DeleteProperty ( kXMP_NS_DC, "creator" ); - this->xmpObj.AppendArrayItem ( kXMP_NS_DC, "creator", kXMP_PropArrayIsOrdered, - legacyProp->GetLeafContentValue() ); - this->containsXMP = true; - } - } - - this->SetXMPPropertyFromLegacyXML ( digestFound, legacyContext, kXMP_NS_XMP, "CreateDate", "CreationDate", false ); - this->SetXMPPropertyFromLegacyXML ( digestFound, legacyContext, kXMP_NS_XMP, "ModifyDate", "LastUpdateDate", false ); - - if ( digestFound || (! this->xmpObj.DoesPropertyExist ( kXMP_NS_DM, "good" )) ) { - legacyProp = this->clipMetadata->GetNamedElement ( p2NS, "ShotMark" ); - if ( (legacyProp == 0) || (! legacyProp->IsLeafContentNode()) ) { - this->xmpObj.DeleteProperty ( kXMP_NS_DM, "good" ); - } else { - XMP_StringPtr markValue = legacyProp->GetLeafContentValue(); - if ( markValue == 0 ) { - this->xmpObj.DeleteProperty ( kXMP_NS_DM, "good" ); - } else if ( XMP_LitMatch ( markValue, "true" ) || XMP_LitMatch ( markValue, "1" ) ) { - this->xmpObj.SetProperty_Bool ( kXMP_NS_DM, "good", true, kXMP_DeleteExisting ); - this->containsXMP = true; - } else if ( XMP_LitMatch ( markValue, "false" ) || XMP_LitMatch ( markValue, "0" ) ) { - this->xmpObj.SetProperty_Bool ( kXMP_NS_DM, "good", false, kXMP_DeleteExisting ); - this->containsXMP = true; - } - } - } - - legacyContext = this->clipMetadata->GetNamedElement ( p2NS, "Shoot" ); - if ( legacyContext != 0 ) { - this->SetXMPPropertyFromLegacyXML ( digestFound, legacyContext, kXMP_NS_TIFF, "Artist", "Shooter", false ); - legacyContext = legacyContext->GetNamedElement ( p2NS, "Location" ); - } - - if ( legacyContext != 0 ) { - this->SetXMPPropertyFromLegacyXML ( digestFound, legacyContext, kXMP_NS_DM, "shotLocation", "PlaceName", false ); - this->SetGPSPropertyFromLegacyXML ( legacyContext, digestFound, "GPSLongitude", "Longitude" ); - this->SetGPSPropertyFromLegacyXML ( legacyContext, digestFound, "GPSLatitude", "Latitude" ); - this->SetAltitudeFromLegacyXML ( legacyContext, digestFound ); - } - - legacyContext = this->clipMetadata->GetNamedElement ( p2NS, "Device" ); - if ( legacyContext != 0 ) { - this->SetXMPPropertyFromLegacyXML ( digestFound, legacyContext, kXMP_NS_TIFF, "Make", "Manufacturer", false ); - this->SetXMPPropertyFromLegacyXML ( digestFound, legacyContext, kXMP_NS_EXIF_Aux, "SerialNumber", "SerialNo.", false ); - this->SetXMPPropertyFromLegacyXML ( digestFound, legacyContext, kXMP_NS_TIFF, "Model", "ModelName", false ); - } - - legacyContext = this->clipMetadata->GetNamedElement ( p2NS, "Scenario" ); - if ( legacyContext != 0 ) { - this->SetXMPPropertyFromLegacyXML ( digestFound, legacyContext, kXMP_NS_DM, "scene", "SceneNo.", false ); - this->SetXMPPropertyFromLegacyXML ( digestFound, legacyContext, kXMP_NS_DM, "takeNumber", "TakeNo.", false ); - } - - CleanupAndExit - #undef CleanupAndExit - -} // P2_MetaHandler::ProcessXMP - -// ================================================================================================= -// P2_MetaHandler::UpdateFile -// ========================== -// -// Note that UpdateFile is only called from XMPFiles::CloseFile, so it is OK to close the file here. - -void P2_MetaHandler::UpdateFile ( bool doSafeUpdate ) -{ - if ( ! this->needsUpdate ) return; - this->needsUpdate = false; // Make sure only called once. - - LFA_FileRef oldFile = 0; - std::string filePath, tempPath; - - // Update the internal legacy XML tree if we have one, and set the digest in the XMP. - // *** This is just a minimal prototype. - - bool updateLegacyXML = false; - - if ( this->clipMetadata != 0 ) { - - XMP_Assert ( this->expat != 0 ); - - bool xmpFound; - std::string xmpValue; - XML_Node * xmlNode; - - xmpFound = this->xmpObj.GetLocalizedText ( kXMP_NS_DC, "title", "", "x-default", 0, &xmpValue, 0 ); - - if ( xmpFound ) { - - xmlNode = this->ForceChildElement ( this->clipContent, "ClipName", 3 ); - - if ( xmpValue != xmlNode->GetLeafContentValue() ) { - xmlNode->SetLeafContentValue ( xmpValue.c_str() ); - updateLegacyXML = true; - } - - } - - xmpFound = this->xmpObj.GetArrayItem ( kXMP_NS_DC, "creator", 1, &xmpValue, 0 ); - - if ( xmpFound ) { - xmlNode = this->ForceChildElement ( this->clipMetadata, "Access", 3 ); - xmlNode = this->ForceChildElement ( xmlNode, "Creator", 4 ); - if ( xmpValue != xmlNode->GetLeafContentValue() ) { - xmlNode->SetLeafContentValue ( xmpValue.c_str() ); - updateLegacyXML = true; - } - } - - } - - std::string newDigest; - this->MakeLegacyDigest ( &newDigest ); - this->xmpObj.SetStructField ( kXMP_NS_XMP, "NativeDigests", kXMP_NS_XMP, "P2", newDigest.c_str(), kXMP_DeleteExisting ); - - this->xmpObj.SerializeToBuffer ( &this->xmpPacket, this->GetSerializeOptions() ); - - // Update the legacy XML file if necessary. - - if ( updateLegacyXML ) { - - std::string legacyXML; - this->expat->tree.Serialize ( &legacyXML ); - - this->MakeClipFilePath ( &filePath, ".XML" ); - oldFile = LFA_Open ( filePath.c_str(), 'w' ); - - if ( oldFile == 0 ) { - - // The XML does not exist yet. - - this->MakeClipFilePath ( &filePath, ".XML" ); - oldFile = LFA_Create ( filePath.c_str() ); - if ( oldFile == 0 ) XMP_Throw ( "Failure creating P2 legacy XML file", kXMPErr_ExternalFailure ); - LFA_Write ( oldFile, legacyXML.data(), (XMP_StringLen)legacyXML.size() ); - LFA_Close ( oldFile ); - - } else if ( ! doSafeUpdate ) { - - // Over write the existing XML file. - - LFA_Seek ( oldFile, 0, SEEK_SET ); - LFA_Truncate ( oldFile, 0 ); - LFA_Write ( oldFile, legacyXML.data(), (XMP_StringLen)legacyXML.size() ); - LFA_Close ( oldFile ); - - } else { - - // Do a safe update. - - // *** We really need an LFA_SwapFiles utility. - - this->MakeClipFilePath ( &filePath, ".XML" ); - - CreateTempFile ( filePath, &tempPath ); - LFA_FileRef tempFile = LFA_Open ( tempPath.c_str(), 'w' ); - LFA_Write ( tempFile, legacyXML.data(), (XMP_StringLen)legacyXML.size() ); - LFA_Close ( tempFile ); - - LFA_Close ( oldFile ); - LFA_Delete ( filePath.c_str() ); - LFA_Rename ( tempPath.c_str(), filePath.c_str() ); - - } - - } - - // Update the XMP file. - - oldFile = this->parent->fileRef; - - if ( oldFile == 0 ) { - - // The XMP does not exist yet. - - this->MakeClipFilePath ( &filePath, ".XMP" ); - oldFile = LFA_Create ( filePath.c_str() ); - if ( oldFile == 0 ) XMP_Throw ( "Failure creating P2 XMP file", kXMPErr_ExternalFailure ); - LFA_Write ( oldFile, this->xmpPacket.data(), (XMP_StringLen)this->xmpPacket.size() ); - LFA_Close ( oldFile ); - - } else if ( ! doSafeUpdate ) { - - // Over write the existing XMP file. - - LFA_Seek ( oldFile, 0, SEEK_SET ); - LFA_Truncate ( oldFile, 0 ); - LFA_Write ( oldFile, this->xmpPacket.data(), (XMP_StringLen)this->xmpPacket.size() ); - LFA_Close ( oldFile ); - - } else { - - // Do a safe update. - - // *** We really need an LFA_SwapFiles utility. - - this->MakeClipFilePath ( &filePath, ".XMP" ); - - CreateTempFile ( filePath, &tempPath ); - LFA_FileRef tempFile = LFA_Open ( tempPath.c_str(), 'w' ); - LFA_Write ( tempFile, this->xmpPacket.data(), (XMP_StringLen)this->xmpPacket.size() ); - LFA_Close ( tempFile ); - - LFA_Close ( oldFile ); - LFA_Delete ( filePath.c_str() ); - LFA_Rename ( tempPath.c_str(), filePath.c_str() ); - - } - - this->parent->fileRef = 0; - -} // P2_MetaHandler::UpdateFile - -// ================================================================================================= -// P2_MetaHandler::WriteFile -// ========================= - -void P2_MetaHandler::WriteFile ( LFA_FileRef sourceRef, const std::string & sourcePath ) -{ - - // ! WriteFile is not supposed to be called for handlers that own the file. - XMP_Throw ( "P2_MetaHandler::WriteFile should not be called", kXMPErr_InternalFailure ); - -} // P2_MetaHandler::WriteFile - -// ================================================================================================= diff --git a/source/XMPFiles/FileHandlers/P2_Handler.hpp b/source/XMPFiles/FileHandlers/P2_Handler.hpp deleted file mode 100644 index 3d0cd96..0000000 --- a/source/XMPFiles/FileHandlers/P2_Handler.hpp +++ /dev/null @@ -1,108 +0,0 @@ -#ifndef __P2_Handler_hpp__ -#define __P2_Handler_hpp__ 1 - -// ================================================================================================= -// ADOBE SYSTEMS INCORPORATED -// Copyright 2007 Adobe Systems Incorporated -// 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 "XMP_Environment.h" // ! This must be the first include. - -#include "XMPFiles_Impl.hpp" - -#include "ExpatAdapter.hpp" - -#include "MD5.h" - -// ================================================================================================= -/// \file P2_Handler.hpp -/// \brief Folder format handler for P2. -/// -/// This header ... -/// -// ================================================================================================= - -// *** Could derive from Basic_Handler - buffer file tail in a temp file. - -extern XMPFileHandler * P2_MetaHandlerCTor ( XMPFiles * parent ); - -extern bool P2_CheckFormat ( XMP_FileFormat format, - const std::string & rootPath, - const std::string & gpName, - const std::string & parentName, - const std::string & leafName, - XMPFiles * parent ); - -static const XMP_OptionBits kP2_HandlerFlags = (kXMPFiles_CanInjectXMP | - kXMPFiles_CanExpand | - kXMPFiles_CanRewrite | - kXMPFiles_PrefersInPlace | - kXMPFiles_CanReconcile | - kXMPFiles_AllowsOnlyXMP | - kXMPFiles_ReturnsRawPacket | - kXMPFiles_HandlerOwnsFile | - kXMPFiles_AllowsSafeUpdate | - kXMPFiles_UsesSidecarXMP | - kXMPFiles_FolderBasedFormat); - -class P2_MetaHandler : public XMPFileHandler -{ -public: - - void CacheFileData(); - void ProcessXMP(); - - XMP_OptionBits GetSerializeOptions() // *** These should be standard for standalone XMP files. - { return (kXMP_UseCompactFormat | kXMP_OmitPacketWrapper); }; - - void UpdateFile ( bool doSafeUpdate ); - void WriteFile ( LFA_FileRef sourceRef, const std::string & sourcePath ); - - P2_MetaHandler ( XMPFiles * _parent ); - virtual ~P2_MetaHandler(); - -private: - - P2_MetaHandler() : expat(0), clipMetadata(0), clipContent(0) {}; // Hidden on purpose. - - void MakeClipFilePath ( std::string * path, XMP_StringPtr suffix ); - void MakeLegacyDigest ( std::string * digestStr ); - void CleanupLegacyXML(); - - void DigestLegacyItem ( MD5_CTX & md5Context, XML_NodePtr legacyContext, XMP_StringPtr legacyPropName ); - void DigestLegacyRelations ( MD5_CTX & md5Context ); - - void SetXMPPropertyFromLegacyXML ( bool digestFound, - XML_NodePtr legacyContext, - XMP_StringPtr schemaNS, - XMP_StringPtr propName, - XMP_StringPtr legacyPropName, - bool isLocalized ); - - void SetRelationsFromLegacyXML ( bool digestFound ); - void SetAudioInfoFromLegacyXML ( bool digestFound ); - void SetVideoInfoFromLegacyXML ( bool digestFound ); - void SetDurationFromLegacyXML ( bool digestFound ); - - void SetVideoFrameInfoFromLegacyXML ( XML_NodePtr legacyVideoContext, bool digestFound ); - void SetStartTimecodeFromLegacyXML ( XML_NodePtr legacyVideoContext, bool digestFound ); - void SetGPSPropertyFromLegacyXML ( XML_NodePtr legacyLocationContext, bool digestFound, XMP_StringPtr propName, XMP_StringPtr legacyPropName ); - void SetAltitudeFromLegacyXML ( XML_NodePtr legacyLocationContext, bool digestFound ); - - XML_Node * ForceChildElement ( XML_Node * parent, XMP_StringPtr localName, int indent = 0 ); - - std::string rootPath, clipName, p2NS; - - ExpatAdapter * expat; - XML_Node * clipMetadata; // ! Don't delete, points into the Expat tree. - XML_Node * clipContent; // ! Don't delete, points into the Expat tree. - -}; // P2_MetaHandler - -// ================================================================================================= - -#endif /* __P2_Handler_hpp__ */ diff --git a/source/XMPFiles/FileHandlers/PNG_Handler.cpp b/source/XMPFiles/FileHandlers/PNG_Handler.cpp deleted file mode 100644 index 0185fce..0000000 --- a/source/XMPFiles/FileHandlers/PNG_Handler.cpp +++ /dev/null @@ -1,271 +0,0 @@ -// ================================================================================================= -// ADOBE SYSTEMS INCORPORATED -// Copyright 2006 Adobe Systems Incorporated -// 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 "PNG_Handler.hpp" - -#include "PNG_Support.hpp" - -using namespace std; - -// ================================================================================================= -/// \file PNG_Handler.hpp -/// \brief File format handler for PNG. -/// -/// This handler ... -/// -// ================================================================================================= - -// ================================================================================================= -// PNG_MetaHandlerCTor -// ==================== - -XMPFileHandler * PNG_MetaHandlerCTor ( XMPFiles * parent ) -{ - return new PNG_MetaHandler ( parent ); - -} // PNG_MetaHandlerCTor - -// ================================================================================================= -// PNG_CheckFormat -// =============== - -bool PNG_CheckFormat ( XMP_FileFormat format, - XMP_StringPtr filePath, - LFA_FileRef fileRef, - XMPFiles * parent ) -{ - IgnoreParam(format); IgnoreParam(fileRef); IgnoreParam(parent); - XMP_Assert ( format == kXMP_PNGFile ); - - IOBuffer ioBuf; - - LFA_Seek ( fileRef, 0, SEEK_SET ); - if ( ! CheckFileSpace ( fileRef, &ioBuf, PNG_SIGNATURE_LEN ) ) return false; // We need at least 8, the buffer is filled anyway. - - if ( ! CheckBytes ( ioBuf.ptr, PNG_SIGNATURE_DATA, PNG_SIGNATURE_LEN ) ) return false; - - return true; - -} // PNG_CheckFormat - -// ================================================================================================= -// PNG_MetaHandler::PNG_MetaHandler -// ================================== - -PNG_MetaHandler::PNG_MetaHandler ( XMPFiles * _parent ) -{ - this->parent = _parent; - this->handlerFlags = kPNG_HandlerFlags; - this->stdCharForm = kXMP_Char8Bit; - -} - -// ================================================================================================= -// PNG_MetaHandler::~PNG_MetaHandler -// =================================== - -PNG_MetaHandler::~PNG_MetaHandler() -{ -} - -// ================================================================================================= -// PNG_MetaHandler::CacheFileData -// =============================== - -void PNG_MetaHandler::CacheFileData() -{ - - this->containsXMP = false; - - LFA_FileRef fileRef ( this->parent->fileRef ); - if ( fileRef == 0) return; - - PNG_Support::ChunkState chunkState; - long numChunks = PNG_Support::OpenPNG ( fileRef, chunkState ); - if ( numChunks == 0 ) return; - - if (chunkState.xmpLen != 0) - { - // XMP present - - this->xmpPacket.reserve(chunkState.xmpLen); - this->xmpPacket.assign(chunkState.xmpLen, ' '); - - if (PNG_Support::ReadBuffer ( fileRef, chunkState.xmpPos, chunkState.xmpLen, const_cast<char *>(this->xmpPacket.data()) )) - { - this->packetInfo.offset = chunkState.xmpPos; - this->packetInfo.length = chunkState.xmpLen; - this->containsXMP = true; - } - } - else - { - // no XMP - } - -} // PNG_MetaHandler::CacheFileData - -// ================================================================================================= -// PNG_MetaHandler::ProcessXMP -// ============================ -// -// Process the raw XMP and legacy metadata that was previously cached. - -void PNG_MetaHandler::ProcessXMP() -{ - this->processedXMP = true; // Make sure we only come through here once. - - // Process the XMP packet. - - if ( ! this->xmpPacket.empty() ) { - - XMP_Assert ( this->containsXMP ); - XMP_StringPtr packetStr = this->xmpPacket.c_str(); - XMP_StringLen packetLen = (XMP_StringLen)this->xmpPacket.size(); - - this->xmpObj.ParseFromBuffer ( packetStr, packetLen ); - - this->containsXMP = true; - - } - -} // PNG_MetaHandler::ProcessXMP - -// ================================================================================================= -// PNG_MetaHandler::UpdateFile -// ============================ - -void PNG_MetaHandler::UpdateFile ( bool doSafeUpdate ) -{ - bool updated = false; - - if ( ! this->needsUpdate ) return; - if ( doSafeUpdate ) XMP_Throw ( "PNG_MetaHandler::UpdateFile: Safe update not supported", kXMPErr_Unavailable ); - - XMP_StringPtr packetStr = xmpPacket.c_str(); - XMP_StringLen packetLen = (XMP_StringLen)xmpPacket.size(); - if ( packetLen == 0 ) return; - - LFA_FileRef fileRef(this->parent->fileRef); - if ( fileRef == 0 ) return; - - PNG_Support::ChunkState chunkState; - long numChunks = PNG_Support::OpenPNG ( fileRef, chunkState ); - if ( numChunks == 0 ) return; - - // write/update chunk - if (chunkState.xmpLen == 0) - { - // no current chunk -> inject - updated = SafeWriteFile(); - } - else if (chunkState.xmpLen >= packetLen ) - { - // current chunk size is sufficient -> write and update CRC (in place update) - updated = PNG_Support::WriteBuffer(fileRef, chunkState.xmpPos, packetLen, packetStr ); - PNG_Support::UpdateChunkCRC(fileRef, chunkState.xmpChunk ); - } - else if (chunkState.xmpLen < packetLen) - { - // XMP is too large for current chunk -> expand - updated = SafeWriteFile(); - } - - if ( ! updated )return; // If there's an error writing the chunk, bail. - - this->needsUpdate = false; - -} // PNG_MetaHandler::UpdateFile - -// ================================================================================================= -// PNG_MetaHandler::WriteFile -// =========================== - -void PNG_MetaHandler::WriteFile ( LFA_FileRef sourceRef, const std::string & sourcePath ) -{ - LFA_FileRef destRef = this->parent->fileRef; - - PNG_Support::ChunkState chunkState; - long numChunks = PNG_Support::OpenPNG ( sourceRef, chunkState ); - if ( numChunks == 0 ) return; - - LFA_Truncate(destRef, 0); - LFA_Write(destRef, PNG_SIGNATURE_DATA, PNG_SIGNATURE_LEN); - - PNG_Support::ChunkIterator curPos = chunkState.chunks.begin(); - PNG_Support::ChunkIterator endPos = chunkState.chunks.end(); - - for (; (curPos != endPos); ++curPos) - { - PNG_Support::ChunkData chunk = *curPos; - - // discard existing XMP chunk - if (chunk.xmp) - continue; - - // copy any other chunk - PNG_Support::CopyChunk(sourceRef, destRef, chunk); - - // place XMP chunk immediately after IHDR-chunk - if (PNG_Support::CheckIHDRChunkHeader(chunk)) - { - XMP_StringPtr packetStr = xmpPacket.c_str(); - XMP_StringLen packetLen = (XMP_StringLen)xmpPacket.size(); - - PNG_Support::WriteXMPChunk(destRef, packetLen, packetStr ); - } - } - -} // PNG_MetaHandler::WriteFile - -// ================================================================================================= -// PNG_MetaHandler::SafeWriteFile -// =========================== - -bool PNG_MetaHandler::SafeWriteFile () -{ - bool ret = false; - - std::string origPath = this->parent->filePath; - LFA_FileRef origRef = this->parent->fileRef; - - std::string updatePath; - LFA_FileRef updateRef = 0; - - CreateTempFile ( origPath, &updatePath, kCopyMacRsrc ); - updateRef = LFA_Open ( updatePath.c_str(), 'w' ); - - this->parent->filePath = updatePath; - this->parent->fileRef = updateRef; - - try { - this->WriteFile ( origRef, origPath ); - ret = true; - } catch ( ... ) { - LFA_Close ( updateRef ); - LFA_Delete ( updatePath.c_str() ); - this->parent->filePath = origPath; - this->parent->fileRef = origRef; - throw; - } - - LFA_Close ( origRef ); - LFA_Delete ( origPath.c_str() ); - - LFA_Close ( updateRef ); - LFA_Rename ( updatePath.c_str(), origPath.c_str() ); - this->parent->filePath = origPath; - - this->parent->fileRef = 0; - - return ret; - -} // PNG_MetaHandler::SafeWriteFile - -// ================================================================================================= diff --git a/source/XMPFiles/FileHandlers/PNG_Handler.hpp b/source/XMPFiles/FileHandlers/PNG_Handler.hpp deleted file mode 100644 index cc978c3..0000000 --- a/source/XMPFiles/FileHandlers/PNG_Handler.hpp +++ /dev/null @@ -1,58 +0,0 @@ -#ifndef __PNG_Handler_hpp__ -#define __PNG_Handler_hpp__ 1 - -// ================================================================================================= -// ADOBE SYSTEMS INCORPORATED -// Copyright 2006 Adobe Systems Incorporated -// 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 "PNG_Support.hpp" - -// ================================================================================================= -/// \file PNG_Handler.hpp -/// \brief File format handler for PNG. -/// -/// This header ... -/// -// ================================================================================================= - -// *** Could derive from Basic_Handler - buffer file tail in a temp file. - -extern XMPFileHandler* PNG_MetaHandlerCTor ( XMPFiles* parent ); - -extern bool PNG_CheckFormat ( XMP_FileFormat format, - XMP_StringPtr filePath, - LFA_FileRef fileRef, - XMPFiles * parent ); - -static const XMP_OptionBits kPNG_HandlerFlags = ( kXMPFiles_CanInjectXMP | - kXMPFiles_CanExpand | - kXMPFiles_PrefersInPlace | - kXMPFiles_AllowsOnlyXMP | - kXMPFiles_ReturnsRawPacket | - kXMPFiles_NeedsReadOnlyPacket ); - -class PNG_MetaHandler : public XMPFileHandler -{ -public: - - void CacheFileData(); - void ProcessXMP(); - - void UpdateFile ( bool doSafeUpdate ); - void WriteFile ( LFA_FileRef sourceRef, const std::string& sourcePath ); - - bool SafeWriteFile (); - - PNG_MetaHandler ( XMPFiles* parent ); - virtual ~PNG_MetaHandler(); - -}; // PNG_MetaHandler - -// ================================================================================================= - -#endif /* __PNG_Handler_hpp__ */ diff --git a/source/XMPFiles/FileHandlers/PSD_Handler.cpp b/source/XMPFiles/FileHandlers/PSD_Handler.cpp deleted file mode 100644 index b81ef0c..0000000 --- a/source/XMPFiles/FileHandlers/PSD_Handler.cpp +++ /dev/null @@ -1,428 +0,0 @@ -// ================================================================================================= -// ADOBE SYSTEMS INCORPORATED -// Copyright 2006 Adobe Systems Incorporated -// 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 "PSD_Handler.hpp" - -#include "TIFF_Support.hpp" -#include "PSIR_Support.hpp" -#include "IPTC_Support.hpp" -#include "ReconcileLegacy.hpp" -#include "Reconcile_Impl.hpp" - -#include "MD5.h" - -using namespace std; - -// ================================================================================================= -/// \file PSD_Handler.cpp -/// \brief File format handler for PSD (Photoshop). -/// -/// This handler ... -/// -// ================================================================================================= - -// ================================================================================================= -// PSD_CheckFormat -// =============== - -// For PSD we just check the "8BPS" signature, the following version, and that the file is at least -// 34 bytes long. This covers the 26 byte header, the 4 byte color mode section length (which might -// be 0), and the 4 byte image resource section length (which might be 0). The parsing logic in -// CacheFileData will do further checks that the image resources actually exist. Those checks are -// not needed to decide if this is a PSD file though, instead they decide if this is valid PSD. - -// ! The CheckXyzFormat routines don't track the filePos, that is left to ScanXyzFile. - -bool PSD_CheckFormat ( XMP_FileFormat format, - XMP_StringPtr filePath, - LFA_FileRef fileRef, - XMPFiles * parent ) -{ - IgnoreParam(format); IgnoreParam(filePath); IgnoreParam(parent); - XMP_Assert ( format == kXMP_PhotoshopFile ); - - IOBuffer ioBuf; - - LFA_Seek ( fileRef, 0, SEEK_SET ); - if ( ! CheckFileSpace ( fileRef, &ioBuf, 34 ) ) return false; // 34 = header plus 2 lengths - - if ( ! CheckBytes ( ioBuf.ptr, "8BPS", 4 ) ) return false; - ioBuf.ptr += 4; // Move to the version. - XMP_Uns16 version = GetUns16BE ( ioBuf.ptr ); - if ( (version != 1) && (version != 2) ) return false; - - return true; - -} // PSD_CheckFormat - -// ================================================================================================= -// PSD_MetaHandlerCTor -// =================== - -XMPFileHandler * PSD_MetaHandlerCTor ( XMPFiles * parent ) -{ - return new PSD_MetaHandler ( parent ); - -} // PSD_MetaHandlerCTor - -// ================================================================================================= -// PSD_MetaHandler::PSD_MetaHandler -// ================================ - -PSD_MetaHandler::PSD_MetaHandler ( XMPFiles * _parent ) : iptcMgr(0), exifMgr(0), skipReconcile(false) -{ - this->parent = _parent; - this->handlerFlags = kPSD_HandlerFlags; - this->stdCharForm = kXMP_Char8Bit; - -} // PSD_MetaHandler::PSD_MetaHandler - -// ================================================================================================= -// PSD_MetaHandler::~PSD_MetaHandler -// ================================= - -PSD_MetaHandler::~PSD_MetaHandler() -{ - - if ( this->iptcMgr != 0 ) delete ( this->iptcMgr ); - if ( this->exifMgr != 0 ) delete ( this->exifMgr ); - -} // PSD_MetaHandler::~PSD_MetaHandler - -// ================================================================================================= -// PSD_MetaHandler::CacheFileData -// ============================== -// -// Find and parse the image resource section, everything we want is in there. Don't simply capture -// the whole section, there could be lots of stuff we don't care about. - -// *** This implementation simply returns when an invalid file is encountered. Should we throw instead? - -void PSD_MetaHandler::CacheFileData() -{ - LFA_FileRef fileRef = this->parent->fileRef; - XMP_PacketInfo & packetInfo = this->packetInfo; - - XMP_AbortProc abortProc = this->parent->abortProc; - void * abortArg = this->parent->abortArg; - const bool checkAbort = (abortProc != 0); - - XMP_Assert ( ! this->containsXMP ); - // Set containsXMP to true here only if the XMP image resource is found. - - if ( checkAbort && abortProc(abortArg) ) { - XMP_Throw ( "PSD_MetaHandler::CacheFileData - User abort", kXMPErr_UserAbort ); - } - - XMP_Uns8 psdHeader[30]; - XMP_Int64 filePos; - XMP_Uns32 ioLen, cmLen, psirLen; - - filePos = LFA_Seek ( fileRef, 0, SEEK_SET ); - - ioLen = LFA_Read ( fileRef, psdHeader, 30 ); - if ( ioLen != 30 ) return; // Throw? - - this->imageHeight = GetUns32BE ( &psdHeader[14] ); - this->imageWidth = GetUns32BE ( &psdHeader[18] ); - - cmLen = GetUns32BE ( &psdHeader[26] ); - - XMP_Int64 psirOrigin = 26 + 4 + cmLen; - - filePos = LFA_Seek ( fileRef, psirOrigin, SEEK_SET ); - if ( filePos != psirOrigin ) return; // Throw? - - ioLen = LFA_Read ( fileRef, psdHeader, 4 ); - if ( ioLen != 4 ) return; // Throw? - - psirLen = GetUns32BE ( &psdHeader[0] ); - - this->psirMgr.ParseFileResources ( fileRef, psirLen ); - - PSIR_Manager::ImgRsrcInfo xmpInfo; - bool found = this->psirMgr.GetImgRsrc ( kPSIR_XMP, &xmpInfo ); - - if ( found ) { - - // printf ( "PSD_MetaHandler::CacheFileData - XMP packet offset %d (0x%X), size %d\n", - // xmpInfo.origOffset, xmpInfo.origOffset, xmpInfo.dataLen ); - this->packetInfo.offset = xmpInfo.origOffset; - this->packetInfo.length = xmpInfo.dataLen; - this->packetInfo.padSize = 0; // Assume for now, set these properly in ProcessXMP. - this->packetInfo.charForm = kXMP_CharUnknown; - this->packetInfo.writeable = true; - - this->xmpPacket.assign ( (XMP_StringPtr)xmpInfo.dataPtr, xmpInfo.dataLen ); - - this->containsXMP = true; - - } - -} // PSD_MetaHandler::CacheFileData - -// ================================================================================================= -// PSD_MetaHandler::ProcessXMP -// =========================== -// -// Process the raw XMP and legacy metadata that was previously cached. - -void PSD_MetaHandler::ProcessXMP() -{ - - this->processedXMP = true; // Make sure we only come through here once. - - // Set up everything for the legacy import, but don't do it yet. This lets us do a forced legacy - // import if the XMP packet gets parsing errors. - - bool readOnly = ((this->parent->openFlags & kXMPFiles_OpenForUpdate) == 0); - - if ( readOnly ) { - this->iptcMgr = new IPTC_Reader(); - this->exifMgr = new TIFF_MemoryReader(); - } else { - this->iptcMgr = new IPTC_Writer(); // ! Parse it later. - this->exifMgr = new TIFF_FileWriter(); - } - - PSIR_Manager & psir = this->psirMgr; // Give the compiler help in recognizing non-aliases. - IPTC_Manager & iptc = *this->iptcMgr; - TIFF_Manager & exif = *this->exifMgr; - - PSIR_Manager::ImgRsrcInfo iptcInfo, exifInfo; - bool haveIPTC = psir.GetImgRsrc ( kPSIR_IPTC, &iptcInfo ); - bool haveExif = psir.GetImgRsrc ( kPSIR_Exif, &exifInfo ); - int iptcDigestState = kDigestMatches; - - if ( haveExif ) exif.ParseMemoryStream ( exifInfo.dataPtr, exifInfo.dataLen ); - - if ( haveIPTC ) { - - bool haveDigest = false; - PSIR_Manager::ImgRsrcInfo digestInfo; - haveDigest = psir.GetImgRsrc ( kPSIR_IPTCDigest, &digestInfo ); - if ( digestInfo.dataLen != 16 ) haveDigest = false; - - if ( ! haveDigest ) { - iptcDigestState = kDigestMissing; - } else { - iptcDigestState = PhotoDataUtils::CheckIPTCDigest ( iptcInfo.dataPtr, iptcInfo.dataLen, digestInfo.dataPtr ); - } - - } - - XMP_OptionBits options = 0; - if ( this->containsXMP ) options |= k2XMP_FileHadXMP; - if ( haveIPTC ) options |= k2XMP_FileHadIPTC; - if ( haveExif ) options |= k2XMP_FileHadExif; - - // Process the XMP packet. If it fails to parse, do a forced legacy import but still throw an - // exception. This tells the caller that an error happened, but gives them recovered legacy - // should they want to proceed with that. - - bool haveXMP = false; - - if ( ! this->xmpPacket.empty() ) { - XMP_Assert ( this->containsXMP ); - // Common code takes care of packetInfo.charForm, .padSize, and .writeable. - XMP_StringPtr packetStr = this->xmpPacket.c_str(); - XMP_StringLen packetLen = (XMP_StringLen)this->xmpPacket.size(); - try { - this->xmpObj.ParseFromBuffer ( packetStr, packetLen ); - haveXMP = true; - } catch ( ... ) { - XMP_ClearOption ( options, k2XMP_FileHadXMP ); - if ( haveIPTC ) iptc.ParseMemoryDataSets ( iptcInfo.dataPtr, iptcInfo.dataLen ); - if ( iptcDigestState == kDigestMatches ) iptcDigestState = kDigestMissing; - ImportPhotoData ( exif, iptc, psir, iptcDigestState, &this->xmpObj, options ); - throw; // ! Rethrow the exception, don't absorb it. - } - } - - // Process the legacy metadata. - - if ( haveIPTC && (! haveXMP) && (iptcDigestState == kDigestMatches) ) iptcDigestState = kDigestMissing; - bool parseIPTC = (iptcDigestState != kDigestMatches) || (! readOnly); - if ( parseIPTC ) iptc.ParseMemoryDataSets ( iptcInfo.dataPtr, iptcInfo.dataLen ); - ImportPhotoData ( exif, iptc, psir, iptcDigestState, &this->xmpObj, options ); - this->containsXMP = true; // Assume we now have something in the XMP. - -} // PSD_MetaHandler::ProcessXMP - -// ================================================================================================= -// PSD_MetaHandler::UpdateFile -// =========================== - -void PSD_MetaHandler::UpdateFile ( bool doSafeUpdate ) -{ - XMP_Assert ( ! doSafeUpdate ); // This should only be called for "unsafe" updates. - - XMP_Int64 oldPacketOffset = this->packetInfo.offset; - XMP_Int32 oldPacketLength = this->packetInfo.length; - - if ( oldPacketOffset == kXMPFiles_UnknownOffset ) oldPacketOffset = 0; // ! Simplify checks. - if ( oldPacketLength == kXMPFiles_UnknownLength ) oldPacketLength = 0; - - bool fileHadXMP = ((oldPacketOffset != 0) && (oldPacketLength != 0)); - - // Update the IPTC-IIM and native TIFF/Exif metadata. ExportPhotoData also trips the tiff: and - // exif: copies from the XMP, so reserialize the now final XMP packet. - - ExportPhotoData ( kXMP_PhotoshopFile, &this->xmpObj, this->exifMgr, this->iptcMgr, &this->psirMgr ); - - try { - XMP_OptionBits options = kXMP_UseCompactFormat; - if ( fileHadXMP ) options |= kXMP_ExactPacketLength; - this->xmpObj.SerializeToBuffer ( &this->xmpPacket, options, oldPacketLength ); - } catch ( ... ) { - this->xmpObj.SerializeToBuffer ( &this->xmpPacket, kXMP_UseCompactFormat ); - } - - // Decide whether to do an in-place update. This can only happen if all of the following are true: - // - There is an XMP packet in the file. - // - The are no changes to the legacy image resources. (The IPTC and EXIF are in the PSIR.) - // - The new XMP can fit in the old space. - - bool doInPlace = (fileHadXMP && (this->xmpPacket.size() <= (size_t)oldPacketLength)); - if ( this->psirMgr.IsLegacyChanged() ) doInPlace = false; - - if ( doInPlace ) { - - #if GatherPerformanceData - sAPIPerf->back().extraInfo += ", PSD in-place update"; - #endif - - if ( this->xmpPacket.size() < (size_t)this->packetInfo.length ) { - // They ought to match, cheap to be sure. - size_t extraSpace = (size_t)this->packetInfo.length - this->xmpPacket.size(); - this->xmpPacket.append ( extraSpace, ' ' ); - } - - LFA_FileRef liveFile = this->parent->fileRef; - - XMP_Assert ( this->xmpPacket.size() == (size_t)oldPacketLength ); // ! Done by common PutXMP logic. - - // printf ( "PSD_MetaHandler::UpdateFile - XMP in-place packet offset %lld (0x%llX), size %d\n", - // oldPacketOffset, oldPacketOffset, this->xmpPacket.size() ); - LFA_Seek ( liveFile, oldPacketOffset, SEEK_SET ); - LFA_Write ( liveFile, this->xmpPacket.c_str(), (XMP_StringLen)this->xmpPacket.size() ); - - } else { - - #if GatherPerformanceData - sAPIPerf->back().extraInfo += ", PSD copy update"; - #endif - - std::string origPath = this->parent->filePath; - LFA_FileRef origRef = this->parent->fileRef; - - std::string updatePath; - LFA_FileRef updateRef = 0; - - CreateTempFile ( origPath, &updatePath, kCopyMacRsrc ); - updateRef = LFA_Open ( updatePath.c_str(), 'w' ); - - this->parent->filePath = updatePath; - this->parent->fileRef = updateRef; - - try { - XMP_Assert ( ! this->skipReconcile ); - this->skipReconcile = true; - this->WriteFile ( origRef, origPath ); - this->skipReconcile = false; - } catch ( ... ) { - this->skipReconcile = false; - LFA_Close ( updateRef ); - LFA_Delete ( updatePath.c_str() ); - this->parent->filePath = origPath; - this->parent->fileRef = origRef; - throw; - } - - LFA_Close ( origRef ); - LFA_Delete ( origPath.c_str() ); - - LFA_Close ( updateRef ); - LFA_Rename ( updatePath.c_str(), origPath.c_str() ); - this->parent->filePath = origPath; - this->parent->fileRef = 0; - - } - - this->needsUpdate = false; - -} // PSD_MetaHandler::UpdateFile - -// ================================================================================================= -// PSD_MetaHandler::WriteFile -// ========================== - -// The metadata parts of a Photoshop file are all in the image resources. The PSIR_Manager's -// UpdateFileResources method will take care of the image resource portion of the file, updating -// those resources that have changed and preserving those that have not. - -void PSD_MetaHandler::WriteFile ( LFA_FileRef sourceRef, const std::string & sourcePath ) -{ - LFA_FileRef destRef = this->parent->fileRef; - - XMP_AbortProc abortProc = this->parent->abortProc; - void * abortArg = this->parent->abortArg; - const bool checkAbort = (abortProc != 0); - - XMP_Uns64 sourceLen = LFA_Measure ( sourceRef ); - if ( sourceLen == 0 ) return; // Tolerate empty files. - - // Reconcile the legacy metadata, unless this is called from UpdateFile. Reserialize the XMP to - // get standard padding, PutXMP has probably done an in-place serialize. Set the XMP image resource. - - if ( ! skipReconcile ) { - // Update the IPTC-IIM and native TIFF/Exif metadata, and reserialize the now final XMP packet. - ExportPhotoData ( kXMP_JPEGFile, &this->xmpObj, this->exifMgr, this->iptcMgr, &this->psirMgr ); - this->xmpObj.SerializeToBuffer ( &this->xmpPacket, kXMP_UseCompactFormat ); - } - - this->xmpObj.SerializeToBuffer ( &this->xmpPacket, kXMP_UseCompactFormat ); - this->packetInfo.offset = kXMPFiles_UnknownOffset; - this->packetInfo.length = (XMP_StringLen)this->xmpPacket.size(); - FillPacketInfo ( this->xmpPacket, &this->packetInfo ); - - this->psirMgr.SetImgRsrc ( kPSIR_XMP, this->xmpPacket.c_str(), (XMP_StringLen)this->xmpPacket.size() ); - - // Copy the file header and color mode section, then write the updated image resource section, - // and copy the tail of the source file (layer and mask section to EOF). - - LFA_Seek ( sourceRef, 0, SEEK_SET ); - LFA_Truncate (destRef, 0 ); - - LFA_Copy ( sourceRef, destRef, 26 ); // Copy the file header. - - XMP_Uns32 cmLen; - LFA_Read ( sourceRef, &cmLen, 4 ); - LFA_Write ( destRef, &cmLen, 4 ); // Copy the color mode section length. - cmLen = GetUns32BE ( &cmLen ); - LFA_Copy ( sourceRef, destRef, cmLen ); // Copy the color mode section contents. - - XMP_Uns32 irLen; - LFA_Read ( sourceRef, &irLen, 4 ); // Get the source image resource section length. - irLen = GetUns32BE ( &irLen ); - - this->psirMgr.UpdateFileResources ( sourceRef, destRef, 0, abortProc, abortArg ); - - XMP_Uns64 tailOffset = 26 + 4 + cmLen + 4 + irLen; - XMP_Uns64 tailLength = sourceLen - tailOffset; - - LFA_Seek ( sourceRef, tailOffset, SEEK_SET ); - LFA_Seek ( destRef, 0, SEEK_END ); - LFA_Copy ( sourceRef, destRef, tailLength ); // Copy the tail of the file. - - this->needsUpdate = false; - -} // PSD_MetaHandler::WriteFile - -// ================================================================================================= diff --git a/source/XMPFiles/FileHandlers/PSD_Handler.hpp b/source/XMPFiles/FileHandlers/PSD_Handler.hpp deleted file mode 100644 index 7ac56b5..0000000 --- a/source/XMPFiles/FileHandlers/PSD_Handler.hpp +++ /dev/null @@ -1,72 +0,0 @@ -#ifndef __PSD_Handler_hpp__ -#define __PSD_Handler_hpp__ 1 - -// ================================================================================================= -// ADOBE SYSTEMS INCORPORATED -// Copyright 2006 Adobe Systems Incorporated -// 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 "TIFF_Support.hpp" -#include "PSIR_Support.hpp" -#include "IPTC_Support.hpp" - -// ================================================================================================= -/// \file PSD_Handler.hpp -/// \brief File format handler for PSD (Photoshop). -/// -/// This header ... -/// -// ================================================================================================= - -// *** Could derive from Basic_Handler - buffer file tail in a temp file. - -extern XMPFileHandler * PSD_MetaHandlerCTor ( XMPFiles * parent ); - -extern bool PSD_CheckFormat ( XMP_FileFormat format, - XMP_StringPtr filePath, - LFA_FileRef fileRef, - XMPFiles * parent ); - -static const XMP_OptionBits kPSD_HandlerFlags = (kXMPFiles_CanInjectXMP | - kXMPFiles_CanExpand | - kXMPFiles_CanRewrite | - kXMPFiles_PrefersInPlace | - kXMPFiles_CanReconcile | - kXMPFiles_AllowsOnlyXMP | - kXMPFiles_ReturnsRawPacket | - kXMPFiles_AllowsSafeUpdate); - -class PSD_MetaHandler : public XMPFileHandler -{ -public: - - void CacheFileData(); - void ProcessXMP(); - - void UpdateFile ( bool doSafeUpdate ); - void WriteFile ( LFA_FileRef sourceRef, const std::string & sourcePath ); - - bool skipReconcile; // ! Used between UpdateFile and WriteFile. - - PSD_MetaHandler ( XMPFiles * parent ); - virtual ~PSD_MetaHandler(); - -private: - - PSD_MetaHandler() : iptcMgr(0), exifMgr(0), skipReconcile(false) {}; // Hidden on purpose. - - PSIR_FileWriter psirMgr; // Don't need a pointer, the PSIR part is always file-based. - IPTC_Manager * iptcMgr; // Need to use pointers so we can properly select between read-only - TIFF_Manager * exifMgr; // and read-write modes of usage. - - XMP_Uns32 imageWidth, imageHeight; // Pixel dimensions, used with thumbnail info. - -}; // PSD_MetaHandler - -// ================================================================================================= - -#endif /* __PSD_Handler_hpp__ */ diff --git a/source/XMPFiles/FileHandlers/PostScript_Handler.cpp b/source/XMPFiles/FileHandlers/PostScript_Handler.cpp deleted file mode 100644 index e925833..0000000 --- a/source/XMPFiles/FileHandlers/PostScript_Handler.cpp +++ /dev/null @@ -1,574 +0,0 @@ -// ================================================================================================= -// ADOBE SYSTEMS INCORPORATED -// Copyright 2004 Adobe Systems Incorporated -// 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 "XMPScanner.hpp" - -#include "Scanner_Handler.hpp" -#include "PostScript_Handler.hpp" - -using namespace std; - -// ================================================================================================= -/// \file PostScript_Handler.cpp -/// \brief File format handler for PostScript and EPS files. -/// -/// This header ... -/// -// ================================================================================================= - -static const char * kPSFileTag = "%!PS-Adobe-"; -static const size_t kPSFileTagLen = strlen ( kPSFileTag ); - -// ================================================================================================= -// PostScript_MetaHandlerCTor -// ========================== - -XMPFileHandler * PostScript_MetaHandlerCTor ( XMPFiles * parent ) -{ - XMPFileHandler * newHandler = new PostScript_MetaHandler ( parent ); - - return newHandler; - -} // PostScript_MetaHandlerCTor - -// ================================================================================================= -// PostScript_CheckFormat -// ====================== - -bool PostScript_CheckFormat ( XMP_FileFormat format, - XMP_StringPtr filePath, - LFA_FileRef fileRef, - XMPFiles * parent ) -{ - IgnoreParam(filePath); IgnoreParam(parent); - XMP_Assert ( (format == kXMP_EPSFile) || (format == kXMP_PostScriptFile) ); - - IOBuffer ioBuf; - - XMP_Int64 psOffset; - size_t psLength; - XMP_Uns32 temp1, temp2; - - // Check for the binary EPSF preview header. - - LFA_Seek ( fileRef, 0, SEEK_SET ); - if ( ! CheckFileSpace ( fileRef, &ioBuf, 4 ) ) return false; - temp1 = GetUns32BE ( ioBuf.ptr ); - - if ( temp1 == 0xC5D0D3C6 ) { - - if ( ! CheckFileSpace ( fileRef, &ioBuf, 30 ) ) return false; - - psOffset = GetUns32LE ( ioBuf.ptr+4 ); // PostScript offset. - psLength = GetUns32LE ( ioBuf.ptr+8 ); // PostScript length. - - bool ok; - LFA_Seek ( fileRef, psOffset, SEEK_SET, &ok ); - if ( ! ok ) return false; // Don't throw for a failure. - - ioBuf.ptr = ioBuf.limit; // Make sure RefillBuffer does a simple read. - RefillBuffer ( fileRef, &ioBuf ); - if ( (ioBuf.len < kIOBufferSize) && (ioBuf.len < psLength) ) return false; // Not enough PostScript. - - } - - // Check the start of the PostScript DSC header comment. - - if ( ! CheckFileSpace ( fileRef, &ioBuf, (kPSFileTagLen + 3 + 1) ) ) return false; - if ( ! CheckBytes ( ioBuf.ptr, Uns8Ptr(kPSFileTag), kPSFileTagLen ) ) return false; - ioBuf.ptr += kPSFileTagLen; - - // Check the PostScript DSC major version number. - - temp1 = 0; - while ( (ioBuf.ptr < ioBuf.limit) && ('0' <= *ioBuf.ptr) && (*ioBuf.ptr <= '9') ) { - temp1 = (temp1 * 10) + (*ioBuf.ptr - '0'); - if ( temp1 > 1000 ) return false; // Overflow. - ioBuf.ptr += 1; - } - if ( temp1 < 3 ) return false; // The version must be at least 3.0. - - if ( ! CheckFileSpace ( fileRef, &ioBuf, 3 ) ) return false; - if ( *ioBuf.ptr != '.' ) return false; // No minor number. - ioBuf.ptr += 1; - - // Check the PostScript DSC minor version number. - - temp2 = 0; - while ( (ioBuf.ptr < ioBuf.limit) && ('0' <= *ioBuf.ptr) && (*ioBuf.ptr <= '9') ) { - temp2 = (temp2 * 10) + (*ioBuf.ptr - '0'); - if ( temp2 > 1000 ) return false; // Overflow. - ioBuf.ptr += 1; - } - // We don't care about the actual minor version number. - - if ( format == kXMP_PostScriptFile ) { - - // Almost done for plain PostScript, check for whitespace. - - if ( ! CheckFileSpace ( fileRef, &ioBuf, 1 ) ) return false; - if ( (*ioBuf.ptr != ' ') && (*ioBuf.ptr != kLF) && (*ioBuf.ptr != kCR) ) return false; - ioBuf.ptr += 1; - - } else { - - // Check for the EPSF keyword on the header comment. - - if ( ! CheckFileSpace ( fileRef, &ioBuf, 6+3+1 ) ) return false; - if ( ! CheckBytes ( ioBuf.ptr, Uns8Ptr(" EPSF-"), 6 ) ) return false; - ioBuf.ptr += 6; - - // Check the EPS major version number. - - temp1 = 0; - while ( (ioBuf.ptr < ioBuf.limit) && ('0' <= *ioBuf.ptr) && (*ioBuf.ptr <= '9') ) { - temp1 = (temp1 * 10) + (*ioBuf.ptr - '0'); - if ( temp1 > 1000 ) return false; // Overflow. - ioBuf.ptr += 1; - } - if ( temp1 < 3 ) return false; // The version must be at least 3.0. - - if ( ! CheckFileSpace ( fileRef, &ioBuf, 3 ) ) return false; - if ( *ioBuf.ptr != '.' ) return false; // No minor number. - ioBuf.ptr += 1; - - // Check the EPS minor version number. - - temp2 = 0; - while ( (ioBuf.ptr < ioBuf.limit) && ('0' <= *ioBuf.ptr) && (*ioBuf.ptr <= '9') ) { - temp2 = (temp2 * 10) + (*ioBuf.ptr - '0'); - if ( temp2 > 1000 ) return false; // Overflow. - ioBuf.ptr += 1; - } - // We don't care about the actual minor version number. - - if ( ! CheckFileSpace ( fileRef, &ioBuf, 1 ) ) return false; - if ( (*ioBuf.ptr != kLF) && (*ioBuf.ptr != kCR) ) return false; - ioBuf.ptr += 1; - - } - - return true; - -} // PostScript_CheckFormat - -// ================================================================================================= -// PostScript_MetaHandler::PostScript_MetaHandler -// ============================================== - -PostScript_MetaHandler::PostScript_MetaHandler ( XMPFiles * _parent ) -{ - this->parent = _parent; - this->handlerFlags = kPostScript_HandlerFlags; - this->stdCharForm = kXMP_Char8Bit; - this->psHint = kPSHint_NoMarker; - -} // PostScript_MetaHandler::PostScript_MetaHandler - -// ================================================================================================= -// PostScript_MetaHandler::~PostScript_MetaHandler -// =============================================== - -PostScript_MetaHandler::~PostScript_MetaHandler() -{ - // ! Inherit the base cleanup. - -} // PostScript_MetaHandler::~PostScript_MetaHandler - -// ================================================================================================= -// PostScript_MetaHandler::FindPostScriptHint -// ========================================== -// -// Search for "%ADO_ContainsXMP:" at the beginning of a line, it must be before "%%EndComments". If -// the XMP marker is found, look for the MainFirst/MainLast/NoMain options. - -static const char * kPSContainsXMPString = "%ADO_ContainsXMP:"; -static const size_t kPSContainsXMPLength = strlen ( kPSContainsXMPString ); - -static const char * kPSEndCommentString = "%%EndComments"; // ! Assumed shorter than kPSContainsXMPString. -static const size_t kPSEndCommentLength = strlen ( kPSEndCommentString ); - -int PostScript_MetaHandler::FindPostScriptHint() -{ - bool found = false; - IOBuffer ioBuf; - XMP_Uns8 ch; - - LFA_FileRef fileRef = this->parent->fileRef; - - XMP_AbortProc abortProc = this->parent->abortProc; - void * abortArg = this->parent->abortArg; - const bool checkAbort = (abortProc != 0); - - // Check for the binary EPSF preview header. - - LFA_Seek ( fileRef, 0, SEEK_SET ); - if ( ! CheckFileSpace ( fileRef, &ioBuf, 4 ) ) return false; - XMP_Uns32 temp1 = GetUns32BE ( ioBuf.ptr ); - - if ( temp1 == 0xC5D0D3C6 ) { - - if ( ! CheckFileSpace ( fileRef, &ioBuf, 30 ) ) return false; - - size_t psOffset = GetUns32LE ( ioBuf.ptr+4 ); // PostScript offset. - size_t psLength = GetUns32LE ( ioBuf.ptr+8 ); // PostScript length. - - bool ok; - LFA_Seek ( fileRef, psOffset, SEEK_SET, &ok ); - if ( ! ok ) return false; // Don't throw for a failure. - - ioBuf.ptr = ioBuf.limit; // Force the next check to refill the buffer. - - } - - // Look for the ContainsXMP comment. - - while ( true ) { - - if ( checkAbort && abortProc(abortArg) ) { - XMP_Throw ( "PostScript_MetaHandler::FindPostScriptHint - User abort", kXMPErr_UserAbort ); - } - - if ( ! CheckFileSpace ( fileRef, &ioBuf, kPSContainsXMPLength ) ) return kPSHint_NoMarker; - - if ( CheckBytes ( ioBuf.ptr, Uns8Ptr(kPSEndCommentString), kPSEndCommentLength ) ) { - - // Found "%%EndComments", don't look any further. - return kPSHint_NoMarker; - - } else if ( ! CheckBytes ( ioBuf.ptr, Uns8Ptr(kPSContainsXMPString), kPSContainsXMPLength ) ) { - - // Not "%%EndComments" or "%ADO_ContainsXMP:", skip past the end of this line. - do { - if ( ! CheckFileSpace ( fileRef, &ioBuf, 1 ) ) return kPSHint_NoMarker; - ch = *ioBuf.ptr; - ++ioBuf.ptr; - } while ( ! IsNewline ( ch ) ); - - } else { - - // Found "%ADO_ContainsXMP:", look for the main packet location option. - - ioBuf.ptr += kPSContainsXMPLength; - int xmpHint = kPSHint_NoMain; // ! From here on, a failure means "no main", not "no marker". - if ( ! CheckFileSpace ( fileRef, &ioBuf, 1 ) ) return kPSHint_NoMain; - if ( ! IsSpaceOrTab ( *ioBuf.ptr ) ) return kPSHint_NoMain; - - while ( true ) { - - while ( true ) { // Skip leading spaces and tabs. - if ( ! CheckFileSpace ( fileRef, &ioBuf, 1 ) ) return kPSHint_NoMain; - if ( ! IsSpaceOrTab ( *ioBuf.ptr ) ) break; - ++ioBuf.ptr; - } - if ( IsNewline ( *ioBuf.ptr ) ) return kPSHint_NoMain; // Reached the end of the ContainsXMP comment. - - if ( ! CheckFileSpace ( fileRef, &ioBuf, 6 ) ) return kPSHint_NoMain; - - if ( CheckBytes ( ioBuf.ptr, Uns8Ptr("NoMain"), 6 ) ) { - - ioBuf.ptr += 6; - xmpHint = kPSHint_NoMain; - break; - - } else if ( CheckBytes ( ioBuf.ptr, Uns8Ptr("MainFi"), 6 ) ) { - - ioBuf.ptr += 6; - if ( ! CheckFileSpace ( fileRef, &ioBuf, 3 ) ) return kPSHint_NoMain; - if ( CheckBytes ( ioBuf.ptr, Uns8Ptr("rst"), 3 ) ) { - ioBuf.ptr += 3; - xmpHint = kPSHint_MainFirst; - } - break; - - } else if ( CheckBytes ( ioBuf.ptr, Uns8Ptr("MainLa"), 6 ) ) { - - ioBuf.ptr += 6; - if ( ! CheckFileSpace ( fileRef, &ioBuf, 2 ) ) return kPSHint_NoMain; - if ( CheckBytes ( ioBuf.ptr, Uns8Ptr("st"), 2 ) ) { - ioBuf.ptr += 2; - xmpHint = kPSHint_MainLast; - } - break; - - } else { - - while ( true ) { // Skip until whitespace. - if ( ! CheckFileSpace ( fileRef, &ioBuf, 1 ) ) return kPSHint_NoMain; - if ( IsWhitespace ( *ioBuf.ptr ) ) break; - ++ioBuf.ptr; - } - - } - - } // Look for the main packet location option. - - // Make sure we found exactly a known option. - if ( ! CheckFileSpace ( fileRef, &ioBuf, 1 ) ) return kPSHint_NoMain; - if ( ! IsWhitespace ( *ioBuf.ptr ) ) return kPSHint_NoMain; - return xmpHint; - - } // Found "%ADO_ContainsXMP:". - - } // Outer marker loop. - - return kPSHint_NoMarker; // Should never reach here. - -} // PostScript_MetaHandler::FindPostScriptHint - - -// ================================================================================================= -// PostScript_MetaHandler::FindFirstPacket -// ======================================= -// -// Run the packet scanner until we find a valid packet. The first one is the main. For simplicity, -// the state of all snips is checked after each buffer is read. In theory only the last of the -// previous snips might change from partial to valid, but then we would have to special case the -// first pass when there is no previous set of snips. Since we have to get a full report to look at -// the last snip anyway, it costs virtually nothing extra to recheck all of the snips. - -bool PostScript_MetaHandler::FindFirstPacket() -{ - int snipCount; - bool found = false; - size_t bufPos, bufLen; - - LFA_FileRef fileRef = this->parent->fileRef; - XMP_Int64 fileLen = LFA_Measure ( fileRef ); - XMP_PacketInfo & packetInfo = this->packetInfo; - - XMPScanner scanner ( fileLen ); - XMPScanner::SnipInfoVector snips; - - enum { kBufferSize = 64*1024 }; - XMP_Uns8 buffer [kBufferSize]; - - XMP_AbortProc abortProc = this->parent->abortProc; - void * abortArg = this->parent->abortArg; - const bool checkAbort = (abortProc != 0); - - bufPos = 0; - bufLen = 0; - - LFA_Seek ( fileRef, 0, SEEK_SET ); // Seek back to the beginning of the file. - - while ( true ) { - - if ( checkAbort && abortProc(abortArg) ) { - XMP_Throw ( "PostScript_MetaHandler::FindFirstPacket - User abort", kXMPErr_UserAbort ); - } - - bufPos += bufLen; - bufLen = LFA_Read ( fileRef, buffer, kBufferSize ); - if ( bufLen == 0 ) return false; // Must be at EoF, no packets found. - - scanner.Scan ( buffer, bufPos, bufLen ); - snipCount = scanner.GetSnipCount(); - scanner.Report ( snips ); - - for ( int i = 0; i < snipCount; ++i ) { - if ( snips[i].fState == XMPScanner::eValidPacketSnip ) { - if ( snips[i].fLength > 0x7FFFFFFF ) XMP_Throw ( "PostScript_MetaHandler::FindFirstPacket: Oversize packet", kXMPErr_BadXMP ); - packetInfo.offset = snips[i].fOffset; - packetInfo.length = (XMP_Int32)snips[i].fLength; - packetInfo.charForm = snips[i].fCharForm; - packetInfo.writeable = (snips[i].fAccess == 'w'); - return true; - } - } - - } - - return false; - -} // FindFirstPacket - - -// ================================================================================================= -// PostScript_MetaHandler::FindLastPacket -// ====================================== -// -// Run the packet scanner backwards until we find the start of a packet, or a valid packet. If we -// found a packet start, resume forward scanning to see if it is a valid packet. For simplicity, all -// of the snips are checked on each pass, for much the same reasons as in FindFirstPacket. - -#if 1 - -// *** Doing this right (as described above) requires out of order scanning support which isn't -// *** implemented yet. For now we scan the whole file and pick the last valid packet. - -bool PostScript_MetaHandler::FindLastPacket() -{ - int pkt; - size_t bufPos, bufLen; - - LFA_FileRef fileRef = this->parent->fileRef; - XMP_Int64 fileLen = LFA_Measure ( fileRef ); - XMP_PacketInfo & packetInfo = this->packetInfo; - - // ------------------------------------------------------ - // Scan the entire file to find all of the valid packets. - - XMPScanner scanner ( fileLen ); - - enum { kBufferSize = 64*1024 }; - XMP_Uns8 buffer [kBufferSize]; - - XMP_AbortProc abortProc = this->parent->abortProc; - void * abortArg = this->parent->abortArg; - const bool checkAbort = (abortProc != 0); - - LFA_Seek ( fileRef, 0, SEEK_SET ); // Seek back to the beginning of the file. - - for ( bufPos = 0; bufPos < (size_t)fileLen; bufPos += bufLen ) { - if ( checkAbort && abortProc(abortArg) ) { - XMP_Throw ( "PostScript_MetaHandler::FindLastPacket - User abort", kXMPErr_UserAbort ); - } - bufLen = LFA_Read ( fileRef, buffer, kBufferSize ); - if ( bufLen == 0 ) XMP_Throw ( "PostScript_MetaHandler::FindLastPacket: Read failure", kXMPErr_ExternalFailure ); - scanner.Scan ( buffer, bufPos, bufLen ); - } - - // ------------------------------- - // Pick the last the valid packet. - - int snipCount = scanner.GetSnipCount(); - - XMPScanner::SnipInfoVector snips ( snipCount ); - scanner.Report ( snips ); - - for ( pkt = snipCount-1; pkt >= 0; --pkt ) { - if ( snips[pkt].fState == XMPScanner::eValidPacketSnip ) break; - } - - if ( pkt >= 0 ) { - if ( snips[pkt].fLength > 0x7FFFFFFF ) XMP_Throw ( "PostScript_MetaHandler::FindLastPacket: Oversize packet", kXMPErr_BadXMP ); - packetInfo.offset = snips[pkt].fOffset; - packetInfo.length = (XMP_Int32)snips[pkt].fLength; - packetInfo.charForm = snips[pkt].fCharForm; - packetInfo.writeable = (snips[pkt].fAccess == 'w'); - return true; - } - - return false; - -} // PostScript_MetaHandler::FindLastPacket - -#else - -bool PostScript_MetaHandler::FindLastPacket() -{ - int err, snipCount; - bool found = false; - XMP_Int64 backPos, backLen; - size_t ioCount; - - LFA_FileRef fileRef = this->parent->fileRef; - XMP_Int64 fileLen = LFA_Measure ( fileRef ); - XMP_PacketInfo & packetInfo = this->packetInfo; - - XMPScanner scanner ( fileLen ); - XMPScanner::SnipInfoVector snips; - - enum { kBufferSize = 64*1024 }; - XMP_Uns8 buffer [kBufferSize]; - - XMP_AbortProc abortProc = this->parent->abortProc; - void * abortArg = this->parent->abortArg; - const bool checkAbort = (abortProc != 0); - - backPos = fileLen; - backLen = 0; - - while ( true ) { - - if ( checkAbort && abortProc(abortArg) ) { - XMP_Throw ( "PostScript_MetaHandler::FindLastPacket - User abort", kXMPErr_UserAbort ); - } - - backLen = kBufferSize; - if ( backPos < kBufferSize ) backLen = backPos; - if ( backLen == 0 ) return false; // Must be at BoF, no packets found. - - backPos -= backLen; - LFA_Seek ( fileRef, backPos, SEEK_SET ); // Seek back to the start of the next buffer. - - #error "ioCount is 32 bits, backLen is 64" - ioCount = LFA_Read ( fileRef, buffer, backLen ); - if ( ioCount != backLen ) XMP_Throw ( "PostScript_MetaHandler::FindLastPacket: Read failure", kXMPErr_ExternalFailure ); - - scanner.Scan ( buffer, backPos, backLen ); - snipCount = scanner.GetSnipCount(); - scanner.Report ( snips ); - - for ( int i = snipCount-1; i >= 0; --i ) { - - if ( snips[i].fState == XMPScanner::eValidPacketSnip ) { - - return VerifyMainPacket ( fileRef, snips[i].fOffset, snips[i].fLength, format, beLenient, mainInfo ); - - } else if ( snips[i].fState == XMPScanner::ePartialPacketSnip ) { - - // This part is a tad tricky. We have a partial packet, so we need to scan - // forward from its ending to see if it is a valid packet. Snips won't recombine, - // the partial snip will change state. Be careful with the I/O to not clobber the - // backward scan positions, so that it can be resumed if necessary. - - size_t fwdPos = snips[i].fOffset + snips[i].fLength; - LFA_Seek ( fileRef, fwdPos, SEEK_SET ); // Seek to the end of the partial snip. - - while ( (fwdPos < fileLen) && (snips[i].fState == XMPScanner::ePartialPacketSnip) ) { - ioCount = LFA_Read ( fileRef, buffer, kBufferSize ); - if ( ioCount == 0 ) XMP_Throw ( "PostScript_MetaHandler::FindLastPacket: Read failure", kXMPErr_ExternalFailure ); - scanner.Scan ( buffer, fwdPos, ioCount ); - scanner.Report ( snips ); - fwdPos += ioCount; - } - - if ( snips[i].fState == XMPScanner::eValidPacketSnip ) { - if ( snips[i].fLength > 0x7FFFFFFF ) XMP_Throw ( "PostScript_MetaHandler::FindLastPacket: Oversize packet", kXMPErr_BadXMP ); - packetInfo.offset = snips[i].fOffset; - packetInfo.length = (XMP_Int32)snips[i].fLength; - packetInfo.charForm = snips[i].fCharForm; - packetInfo.writeable = (snips[i].fAccess == 'w'); - return true; - } - - } - - } // Backwards snip loop. - - } // Backwards read loop. - - return false; // Should never get here. - -} // PostScript_MetaHandler::FindLastPacket - -#endif - -// ================================================================================================= -// PostScript_MetaHandler::CacheFileData -// ===================================== - -void PostScript_MetaHandler::CacheFileData() -{ - this->containsXMP = false; - this->psHint = FindPostScriptHint(); - - if ( this->psHint == kPSHint_MainFirst ) { - this->containsXMP = FindFirstPacket(); - } else if ( this->psHint == kPSHint_MainLast ) { - this->containsXMP = FindLastPacket(); - } - - if ( this->containsXMP ) ReadXMPPacket ( this ); - -} // PostScript_MetaHandler::CacheFileData - -// ================================================================================================= diff --git a/source/XMPFiles/FileHandlers/PostScript_Handler.hpp b/source/XMPFiles/FileHandlers/PostScript_Handler.hpp deleted file mode 100644 index c0d4e37..0000000 --- a/source/XMPFiles/FileHandlers/PostScript_Handler.hpp +++ /dev/null @@ -1,63 +0,0 @@ -#ifndef __PostScript_Handler_hpp__ -#define __PostScript_Handler_hpp__ 1 - -// ================================================================================================= -// ADOBE SYSTEMS INCORPORATED -// Copyright 2004 Adobe Systems Incorporated -// 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 "Trivial_Handler.hpp" - -// ================================================================================================= -/// \file PostScript_Handler.hpp -/// \brief File format handler for PostScript and EPS files. -/// -/// This header ... -/// -// ================================================================================================= - -// *** This probably could be derived from Basic_Handler, buffer the file tail in a temp file. - -extern XMPFileHandler * PostScript_MetaHandlerCTor ( XMPFiles * parent ); - -extern bool PostScript_CheckFormat ( XMP_FileFormat format, - XMP_StringPtr filePath, - LFA_FileRef fileRef, - XMPFiles * parent ); - -static const XMP_OptionBits kPostScript_HandlerFlags = kTrivial_HandlerFlags; - -enum { - kPSHint_NoMarker = 0, - kPSHint_NoMain = 1, - kPSHint_MainFirst = 2, - kPSHint_MainLast = 3 -}; - -class PostScript_MetaHandler : public Trivial_MetaHandler -{ -public: - - PostScript_MetaHandler ( XMPFiles * parent ); - ~PostScript_MetaHandler(); - - void CacheFileData(); - - int psHint; - -protected: - - int FindPostScriptHint(); - - bool FindFirstPacket(); - bool FindLastPacket(); - -}; // PostScript_MetaHandler - -// ================================================================================================= - -#endif /* __PostScript_Handler_hpp__ */ diff --git a/source/XMPFiles/FileHandlers/RIFF_Handler.cpp b/source/XMPFiles/FileHandlers/RIFF_Handler.cpp deleted file mode 100644 index daa4455..0000000 --- a/source/XMPFiles/FileHandlers/RIFF_Handler.cpp +++ /dev/null @@ -1,347 +0,0 @@ -// ================================================================================================= -// ADOBE SYSTEMS INCORPORATED -// Copyright 2009 Adobe Systems Incorporated -// 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 "RIFF.hpp" -#include "RIFF_Handler.hpp" - -using namespace std; - -// ================================================================================================= -/// \file RIFF_Handler.cpp -/// \brief File format handler for RIFF. -// ================================================================================================= - -// ================================================================================================= -// RIFF_MetaHandlerCTor -// ==================== - -XMPFileHandler * RIFF_MetaHandlerCTor ( XMPFiles * parent ) -{ - return new RIFF_MetaHandler ( parent ); -} - -// ================================================================================================= -// RIFF_CheckFormat -// =============== -// -// An RIFF file must begin with "RIFF", a 4 byte length, then the chunkType (AVI or WAV) -// The specified length MUST (in practice: SHOULD) match fileSize-8, but we don't bother checking this here. - -bool RIFF_CheckFormat ( XMP_FileFormat format, - XMP_StringPtr filePath, - LFA_FileRef file, - XMPFiles* parent ) -{ - IgnoreParam(format); IgnoreParam(parent); - XMP_Assert ( (format == kXMP_AVIFile) || (format == kXMP_WAVFile) ); - LFA_Rewind( file ); - - XMP_Uns8 chunkID[12]; - LFA_Read( file, chunkID, 12, true ); - if ( ! CheckBytes( &chunkID[0], "RIFF", 4 )) return false; - - if ( CheckBytes(&chunkID[8],"AVI ",4) && format == kXMP_AVIFile ) return true; - if ( CheckBytes(&chunkID[8],"WAVE",4) && format == kXMP_WAVFile ) return true; - - return false; -} // RIFF_CheckFormat - -// ================================================================================================= -// RIFF_MetaHandler::RIFF_MetaHandler -// ================================ - -RIFF_MetaHandler::RIFF_MetaHandler ( XMPFiles * _parent ) -{ - this->parent = _parent; - this->handlerFlags = kRIFF_HandlerFlags; - this->stdCharForm = kXMP_Char8Bit; - - this->oldFileSize = this->newFileSize = this->trailingGarbageSize = 0; - this->level = 0; - this->listInfoChunk = this->listTdatChunk = 0; - this->dispChunk = this->bextChunk = this->cr8rChunk = this->prmlChunk = 0; - this->xmpChunk = 0; - this->lastChunk = 0; - this->hasListInfoINAM = false; -} - -// ================================================================================================= -// RIFF_MetaHandler::~RIFF_MetaHandler -// ================================= - -RIFF_MetaHandler::~RIFF_MetaHandler() -{ - while ( ! this->riffChunks.empty() ) - { - delete this->riffChunks.back(); - this->riffChunks.pop_back(); - } -} - -// ================================================================================================= -// RIFF_MetaHandler::CacheFileData -// ============================== - -void RIFF_MetaHandler::CacheFileData() -{ - this->containsXMP = false; //assume for now - - LFA_FileRef file = this->parent->fileRef; - this->oldFileSize = LFA_Measure( file ); - if ( (this->parent->format == kXMP_WAVFile) && (this->oldFileSize > 0xFFFFFFFF) ) - XMP_Throw ( "RIFF_MetaHandler::CacheFileData: WAV Files larger 4GB not supported", kXMPErr_Unimplemented ); - - LFA_Rewind( file ); - this->level = 0; - - // parse top-level chunks (most likely just one, except large avi files) - XMP_Int64 filePos = 0; - while ( filePos < this->oldFileSize ) - { - - this->riffChunks.push_back( (RIFF::ContainerChunk*) RIFF::getChunk( NULL, this ) ); - - // Tolerate limited forms of trailing garbage in a file. Some apps append private data. - - filePos = LFA_Tell( file ); - XMP_Int64 fileTail = this->oldFileSize - filePos; - - if ( fileTail != 0 ) { - - if ( fileTail < 12 ) { - - this->oldFileSize = filePos; // Pretend the file is smaller. - this->trailingGarbageSize = fileTail; - - } else if ( this->parent->format == kXMP_WAVFile ) { - - if ( fileTail < 1024*1024 ) { - this->oldFileSize = filePos; // Pretend the file is smaller. - this->trailingGarbageSize = fileTail; - } else { - XMP_Throw ( "Excessive garbage at end of file", kXMPErr_BadFileFormat ) - } - - } else { - - XMP_Int32 chunkInfo [3]; - LFA_Read ( file, &chunkInfo, 12, kLFA_RequireAll ); - LFA_Seek ( file, -12, SEEK_CUR ); - if ( (GetUns32LE ( &chunkInfo[0] ) != RIFF::kChunk_RIFF) || (GetUns32LE ( &chunkInfo[2] ) != RIFF::kType_AVIX) ) { - if ( fileTail < 1024*1024 ) { - this->oldFileSize = filePos; // Pretend the file is smaller. - this->trailingGarbageSize = fileTail; - } else { - XMP_Throw ( "Excessive garbage at end of file", kXMPErr_BadFileFormat ) - } - } - - } - - } - - } - - // covered before => internal error if it occurs - XMP_Validate( LFA_Tell( file ) == this->oldFileSize, - "RIFF_MetaHandler::CacheFileData: unknown data at end of file", - kXMPErr_InternalFailure ); - -} // RIFF_MetaHandler::CacheFileData - -// ================================================================================================= -// RIFF_MetaHandler::ProcessXMP -// ============================ - -void RIFF_MetaHandler::ProcessXMP() -{ - SXMPUtils::RemoveProperties ( &this->xmpObj, 0, 0, kXMPUtil_DoAllProperties ); - // process physical packet first - if ( this->containsXMP ) this->xmpObj.ParseFromBuffer ( this->xmpPacket.c_str(), (XMP_StringLen)this->xmpPacket.size() ); - // then import native properties: - RIFF::importProperties( this ); - this->processedXMP = true; -} - -// ================================================================================================= -// RIFF_MetaHandler::UpdateFile -// =========================== - -void RIFF_MetaHandler::UpdateFile ( bool doSafeUpdate ) -{ - XMP_Validate( this->needsUpdate, "nothing to update", kXMPErr_InternalFailure ); - - //////////////////////////////////////////////////////////////////////////////////////// - //////////// PASS 1: basics, exports, packet reserialze - LFA_FileRef file = this->parent->fileRef; - RIFF::containerVect *rc = &this->riffChunks; - - //temptemp - //printf( "BEFORE:\n%s\n", rc->at(0)->toString().c_str() ); - - XMP_Enforce( rc->size() >= 1); - RIFF::ContainerChunk* mainChunk = rc->at(0); - this->lastChunk = rc->at( rc->size() - 1 ); // (may or may not coincide with mainChunk: ) - XMP_Enforce( mainChunk != NULL ); - - RIFF::relocateWronglyPlacedXMPChunk( this ); - // [2435625] lifted disablement for AVI - RIFF::exportAndRemoveProperties( this ); - - // always rewrite both LISTs (implicit size changes, e.g. through 0-term corrections may - // have very well led to size changes...) - // set XMP packet info, re-serialize - this->packetInfo.charForm = stdCharForm; - this->packetInfo.writeable = true; - this->packetInfo.offset = kXMPFiles_UnknownOffset; - this->packetInfo.length = kXMPFiles_UnknownLength; - - // re-serialization ( needed because of above exportAndRemoveProperties() ) - try { - if ( this->xmpChunk == 0 ) // new chunk? pad with 2K - this->xmpObj.SerializeToBuffer ( &this->xmpPacket, kXMP_NoOptions , 2048 ); - else // otherwise try to match former size - this->xmpObj.SerializeToBuffer ( &this->xmpPacket, kXMP_ExactPacketLength , (XMP_Uns32) this->xmpChunk->oldSize-8 ); - } catch ( ... ) { // if that fails, be happy with whatever. - this->xmpObj.SerializeToBuffer ( &this->xmpPacket, kXMP_NoOptions ); - } - - if ( (this->xmpPacket.size() & 1) == 1 ) this->xmpPacket += ' '; // Force the XMP to have an even size. - - // if missing, add xmp packet at end: - if( this->xmpChunk == 0 ) - this->xmpChunk = new RIFF::XMPChunk( this->lastChunk ); - // * parenting happens within this call. - // * size computation will happen in XMPChunk::changesAndSize() - // * actual data will be set in XMPChunk::write() - - //////////////////////////////////////////////////////////////////////////////////////// - // PASS 2: compute sizes, optimize container structure (no writing yet) - { - this->newFileSize = 0; - - // note: going through forward (not vice versa) is an important condition, - // so that parking LIST:Tdat,Cr8r, PrmL to last chunk is doable - // when encountered en route - for ( XMP_Uns32 chunkNo = 0; chunkNo < rc->size(); chunkNo++ ) - { - RIFF::Chunk* cur = rc->at(chunkNo); - cur->changesAndSize( this ); - this->newFileSize += cur->newSize; - if ( this->newFileSize % 2 == 1 ) this->newFileSize++; // pad byte - } - this->newFileSize += this->trailingGarbageSize; - } // PASS2 - - //////////////////////////////////////////////////////////////////////////////////////// - // PASS 2a: verify no chunk violates 2GB boundaries - switch( this->parent->format ) - { - // NB: <4GB for ALL chunks asserted before in ContainerChunk::changesAndSize() - - case kXMP_AVIFile: - // ensure no chunk (single or multi, no matter) crosses 2 GB ... - for ( XMP_Int32 chunkNo = 0; chunkNo < (XMP_Int32)rc->size(); chunkNo++ ) - { - if ( rc->at(chunkNo)->oldSize <= 0x80000000LL ) // ... if <2GB before - XMP_Validate( rc->at(chunkNo)->newSize <= 0x80000000LL, - "Chunk grew beyond 2 GB", kXMPErr_Unimplemented ); - } - - // compatibility: if single-chunk AND below <1GB, ensure <1GB - if ( ( rc->size() > 1 ) && ( rc->at(0)->oldSize < 0x40000000 ) ) - { - XMP_Validate( rc->at(0)->newSize < 0x40000000LL, "compatibility: mainChunk must remain < 1GB" , kXMPErr_Unimplemented ); - } - - // [2473381] compatibility: if single-chunk AND >2GB,<4GB, ensure <4GB - if ( ( rc->size() > 1 ) && - ( rc->at(0)->oldSize > 0x80000000LL ) && // 2GB - ( rc->at(0)->oldSize < 0x100000000LL ) ) // 4GB - { - XMP_Validate( rc->at(0)->newSize < 0x100000000LL, "compatibility: mainChunk must remain < 4GB" , kXMPErr_Unimplemented ); - } - - break; - - case kXMP_WAVFile: - XMP_Validate( 1 == rc->size(), "WAV must be single-chunk", kXMPErr_InternalFailure ); - XMP_Validate( rc->at(0)->newSize <= 0xFFFFFFFFLL, "WAV above 4 GB not supported", kXMPErr_Unimplemented ); - break; - - default: - XMP_Throw( "unknown format", kXMPErr_InternalFailure ); - } - - //////////////////////////////////////////////////////////////////////////////////////// - // PASS 3: write avix chunk(s) if applicable (shrinks or stays) - // and main chunk. -- operation order depends on mainHasShrunk. - { - // if needed, extend file beforehand - if ( this->newFileSize > this->oldFileSize ) LFA_Extend( file, newFileSize ); - - RIFF::Chunk* mainChunk = rc->at(0); - - XMP_Int64 mainGrowth = mainChunk->newSize - mainChunk->oldSize; - XMP_Enforce( mainGrowth >= 0 ); // main always stays or grows - - //temptemp - //printf( "AFTER:\n%s\n", rc->at(0)->toString().c_str() ); - - if ( rc->size() > 1 ) // [2457482] - XMP_Validate( mainGrowth == 0, "mainChunk must not grow, if multiple RIFF chunks", kXMPErr_InternalFailure ); - - // back to front: - - XMP_Int64 avixStart = newFileSize; // count from the back - - if ( this->trailingGarbageSize != 0 ) { - XMP_Int64 goodDataEnd = this->newFileSize - this->trailingGarbageSize; - LFA_Move ( file, this->oldFileSize, file, goodDataEnd, this->trailingGarbageSize ); - avixStart = goodDataEnd; - } - - for ( XMP_Int32 chunkNo = ((XMP_Int32)rc->size()) -1; chunkNo >= 0; chunkNo-- ) - { - RIFF::Chunk* cur = rc->at(chunkNo); - - avixStart -= cur->newSize; - if ( avixStart % 2 == 1 ) // rewind one more - avixStart -= 1; - - LFA_Seek( file, avixStart , SEEK_SET ); - - if ( cur->hasChange ) // need explicit write-out ? - cur->write( this, file, chunkNo == 0 ); - else // or will a simple move do? - { - XMP_Enforce( cur->oldSize == cur->newSize ); - if ( cur->oldPos != avixStart ) // important optimization: only move if there's a need to - LFA_Move( file, cur->oldPos, file, avixStart, cur->newSize ); - } - } - - // if needed, shrink file afterwards - if ( this->newFileSize < this->oldFileSize ) LFA_Truncate( file, this->newFileSize ); - } // PASS 3 - - this->needsUpdate = false; //do last for safety -} // RIFF_MetaHandler::UpdateFile - -// ================================================================================================= -// RIFF_MetaHandler::WriteFile -// ========================== - -void RIFF_MetaHandler::WriteFile ( LFA_FileRef sourceRef, - const std::string & sourcePath ) -{ - IgnoreParam(sourceRef); IgnoreParam(sourcePath); - XMP_Throw ( "RIFF_MetaHandler::WriteFile: Not supported (must go through UpdateFile", kXMPErr_Unavailable ); -} - diff --git a/source/XMPFiles/FileHandlers/RIFF_Handler.hpp b/source/XMPFiles/FileHandlers/RIFF_Handler.hpp deleted file mode 100644 index 093fd63..0000000 --- a/source/XMPFiles/FileHandlers/RIFF_Handler.hpp +++ /dev/null @@ -1,70 +0,0 @@ -// ================================================================================================= -// ADOBE SYSTEMS INCORPORATED -// Copyright 2009 Adobe Systems Incorporated -// 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. -// ================================================================================================= -#ifndef __RIFF_Handler_hpp__ -#define __RIFF_Handler_hpp__ 1 - -#include "XMP_Const.h" -#include "XMPFiles_Impl.hpp" -#include "RIFF_Support.hpp" - -// ================================================================================================= -/// \file RIFF_Handler.hpp -/// \brief File format handler for RIFF (AVI, WAV). -// ================================================================================================= - -extern XMPFileHandler * RIFF_MetaHandlerCTor ( XMPFiles * parent ); - -extern bool RIFF_CheckFormat ( XMP_FileFormat format, - XMP_StringPtr filePath, - LFA_FileRef fileRef, - XMPFiles * parent ); - -static const XMP_OptionBits kRIFF_HandlerFlags = (kXMPFiles_CanInjectXMP | - kXMPFiles_CanExpand | - kXMPFiles_PrefersInPlace | - kXMPFiles_AllowsOnlyXMP | - kXMPFiles_ReturnsRawPacket | - kXMPFiles_CanReconcile - ); - -class RIFF_MetaHandler : public XMPFileHandler -{ -public: - RIFF_MetaHandler ( XMPFiles* parent ); - ~RIFF_MetaHandler(); - - void CacheFileData(); - void ProcessXMP(); - - void UpdateFile ( bool doSafeUpdate ); - void WriteFile ( LFA_FileRef sourceRef, const std::string & sourcePath ); - - //////////////////////////////////////////////////////////////////////////////////// - // instance vars - // most often just one RIFF:* (except for AVI,[AVIX] >1 GB) - std::vector<RIFF::ContainerChunk*> riffChunks; - XMP_Int64 oldFileSize, newFileSize, trailingGarbageSize; - - // state variables, needed during parsing - XMP_Uns8 level; - - RIFF::ContainerChunk *listInfoChunk, *listTdatChunk; - RIFF::ValueChunk* dispChunk; - RIFF::ValueChunk* bextChunk; - RIFF::ValueChunk* cr8rChunk; - RIFF::ValueChunk* prmlChunk; - RIFF::XMPChunk* xmpChunk; - RIFF::ContainerChunk* lastChunk; - bool hasListInfoINAM; // needs to be known for the special 3-way merge around dc:title - -}; // RIFF_MetaHandler - -// ================================================================================================= - -#endif /* __RIFF_Handler_hpp__ */ diff --git a/source/XMPFiles/FileHandlers/SWF_Handler.cpp b/source/XMPFiles/FileHandlers/SWF_Handler.cpp deleted file mode 100644 index 9ae2b25..0000000 --- a/source/XMPFiles/FileHandlers/SWF_Handler.cpp +++ /dev/null @@ -1,386 +0,0 @@ -// ================================================================================================= -// ADOBE SYSTEMS INCORPORATED -// Copyright 2006 Adobe Systems Incorporated -// 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 "SWF_Handler.hpp" - -#include "SWF_Support.hpp" - - -using namespace std; - -// ================================================================================================= -/// \file SWF_Handler.hpp -/// \brief File format handler for SWF. -/// -/// This handler ... -/// -// ================================================================================================= - -// ================================================================================================= -// SWF_MetaHandlerCTor -// ==================== - -XMPFileHandler * SWF_MetaHandlerCTor ( XMPFiles * parent ) -{ - return new SWF_MetaHandler ( parent ); - -} // SWF_MetaHandlerCTor - -// ================================================================================================= -// SWF_CheckFormat -// =============== - -bool SWF_CheckFormat ( XMP_FileFormat format, - XMP_StringPtr filePath, - LFA_FileRef fileRef, - XMPFiles * parent ) -{ - IgnoreParam(format); IgnoreParam(fileRef); IgnoreParam(parent); - XMP_Assert ( format == kXMP_SWFFile ); - - IOBuffer ioBuf; - - LFA_Seek ( fileRef, 0, SEEK_SET ); - if ( ! CheckFileSpace ( fileRef, &ioBuf, SWF_SIGNATURE_LEN ) ) return false; - - if ( !(CheckBytes ( ioBuf.ptr, SWF_F_SIGNATURE_DATA, SWF_SIGNATURE_LEN ) || - CheckBytes(ioBuf.ptr, SWF_C_SIGNATURE_DATA, SWF_SIGNATURE_LEN)) ) - return false; - - return true; - -} // SWF_CheckFormat - -// ================================================================================================= -// SWF_MetaHandler::SWF_MetaHandler -// ================================== - -SWF_MetaHandler::SWF_MetaHandler ( XMPFiles * _parent ) -{ - this->parent = _parent; - this->handlerFlags = kSWF_HandlerFlags; - this->stdCharForm = kXMP_Char8Bit; - -} - -// ================================================================================================= -// SWF_MetaHandler::~SWF_MetaHandler -// =================================== - -SWF_MetaHandler::~SWF_MetaHandler() -{ -} - -// ================================================================================================= -// SWF_MetaHandler::CacheFileData -// =============================== - -void SWF_MetaHandler::CacheFileData() -{ - - this->containsXMP = false; - - LFA_FileRef fileRef ( this->parent->fileRef ); - if ( fileRef == 0) return; - - SWF_Support::FileInfo fileInfo(fileRef, this->parent->filePath); - IO::InputStream * is = NULL; - if(fileInfo.IsCompressed()) - { - XMP_Uns32 fileSize = fileInfo.GetSize(); - is = new IO::ZIP::DeflateInputStream(fileRef, fileSize); - - IO::ZIP::DeflateInputStream * in = dynamic_cast<IO::ZIP::DeflateInputStream*>(is); - in->Skip(SWF_COMPRESSION_BEGIN, IO::ZIP::DEFLATE_NO); - } - else - { - is = new IO::FileInputStream(fileRef); - is->Skip(SWF_COMPRESSION_BEGIN); - } - - SWF_Support::TagState tagState; - //important flag to stop iteration over all tags when xmp flag within FileAttributeTag isn't set - tagState.cachingFile = true; - - long numTags = SWF_Support::OpenSWF ( is, tagState ); - - is->Close(); - delete is; - - if ( numTags == 0 ) return; - - if (tagState.hasXMP && tagState.xmpLen != 0) - { - this->xmpPacket.assign(tagState.xmpPacket); - this->containsXMP = true; - } - else - { - // no XMP - } - - -} // SWF_MetaHandler::CacheFileData - -// ================================================================================================= -// SWF_MetaHandler::ProcessXMP -// ============================ -// -// Process the raw XMP and legacy metadata that was previously cached. - -void SWF_MetaHandler::ProcessXMP() -{ - - this->processedXMP = true; // Make sure we only come through here once. - - // Process the XMP packet. - - if ( ! this->xmpPacket.empty() ) { - - XMP_Assert ( this->containsXMP ); - XMP_StringPtr packetStr = this->xmpPacket.c_str(); - XMP_StringLen packetLen = (XMP_StringLen)this->xmpPacket.size(); - - this->xmpObj.ParseFromBuffer ( packetStr, packetLen ); - - this->containsXMP = true; - - } - -} // SWF_MetaHandler::ProcessXMP - - -// ================================================================================================= -// XMPFileHandler::GetSerializeOptions -// =================================== -// -// Override default implementation to ensure omitting xmp wrapper -// -XMP_OptionBits SWF_MetaHandler::GetSerializeOptions() -{ - return (kXMP_OmitPacketWrapper | kXMP_OmitAllFormatting | kXMP_OmitXMPMetaElement); -} // XMPFileHandler::GetSerializeOptions - - -// ================================================================================================= -// SWF_MetaHandler::UpdateFile -// ============================ - -void SWF_MetaHandler::UpdateFile ( bool doSafeUpdate ) -{ - bool updated = false; - - if ( ! this->needsUpdate ) return; - if ( doSafeUpdate ) XMP_Throw ( "SWF_MetaHandler::UpdateFile: Safe update not supported", kXMPErr_Unavailable ); - - LFA_FileRef sourceRef = this->parent->fileRef; - std::string sourcePath = this->parent->filePath; - - SWF_Support::FileInfo fileInfo(sourceRef, sourcePath); - - if(fileInfo.IsCompressed()) - sourceRef = fileInfo.Decompress(); - - IO::InputStream * is = new IO::FileInputStream(sourceRef); - //processing SWF starts after byte SWF_COMPRESSION_BEGIN - currently 8 bytes - is->Skip(SWF_COMPRESSION_BEGIN); - - SWF_Support::TagState tagState; - - long numTags = SWF_Support::OpenSWF ( is, tagState ); - - //clean objects - is->Close(); - delete is; - - bool foundTag = false; - SWF_Support::TailBufferDef tailBuffer; - - - //find end position to measure tail buffer - tailBuffer.tailEndPosition = LFA_Seek(sourceRef, 0, SEEK_END); - - SWF_Support::TagIterator curPos = tagState.tags.begin(); - SWF_Support::TagIterator endPos = tagState.tags.end(); - - for( ;(curPos != endPos) && ! foundTag; ++curPos) - { - SWF_Support::TagData tag = *curPos; - - //write XMP Tag at the beginning of the file - if(! tagState.hasXMP && ! tagState.hasFileAttrTag) - { - tailBuffer.tailStartPosition = tag.pos; - tailBuffer.writePosition = tag.pos; - foundTag = true; - } - //update existing XMP Tag - if(tagState.hasXMP && (tagState.xmpTag.pos == tag.pos)) - { - ++curPos; - SWF_Support::TagData nextTag = *curPos; - tailBuffer.tailStartPosition = nextTag.pos; - tailBuffer.writePosition = tagState.xmpTag.pos; - foundTag = true; - } - //write XMP Tag after FileAttribute Tag - else if(! tagState.hasXMP && (tag.id == SWF_TAG_ID_FILEATTRIBUTES)) - { - ++curPos; - SWF_Support::TagData nextTag = *curPos; - tailBuffer.tailStartPosition = nextTag.pos; - tailBuffer.writePosition = nextTag.pos; - foundTag = true; - } - } - - //cache tail of file - XMP_Assert(tailBuffer.tailEndPosition > tailBuffer.tailStartPosition); - XMP_Uns32 tailSize = tailBuffer.GetTailSize(); - XMP_Uns8 * buffer = new XMP_Uns8[tailSize]; - SWF_Support::ReadBuffer(sourceRef, tailBuffer.tailStartPosition, tailSize, buffer); - - //write new XMP packet - XMP_StringPtr packetStr = xmpPacket.c_str(); - XMP_StringLen packetLen = (XMP_StringLen)xmpPacket.size(); - - LFA_Seek(sourceRef, tailBuffer.writePosition, SEEK_SET); - SWF_Support::WriteXMPTag(sourceRef, packetLen, packetStr); - - // truncate to minimal size - LFA_Truncate(sourceRef, LFA_Tell(sourceRef)); - - //move tail of the file - LFA_Write(sourceRef, buffer, tailSize); - - //cleaning buffer - delete [] buffer; - - //update FileAttribute Tag if exists - if(tagState.hasFileAttrTag) - SWF_Support::UpdateFileAttrTag(sourceRef, tagState.fileAttrTag, tagState); - - - //update File Size - SWF_Support::UpdateHeader(sourceRef); - - //compress file after writing XMP - if(fileInfo.IsCompressed()) - { - fileInfo.Compress(sourceRef, this->parent->fileRef); - fileInfo.Clean(); - } - - - if ( ! updated )return; // If there's an error writing the chunk, bail. - - this->needsUpdate = false; - -} // SWF_MetaHandler::UpdateFile - -// ================================================================================================= -// SWF_MetaHandler::WriteFile -// =========================== - -void SWF_MetaHandler::WriteFile ( LFA_FileRef sourceRef, const std::string & sourcePath ) -{ - - LFA_FileRef destRef = this->parent->fileRef; - - SWF_Support::TagState tagState; - - LFA_FileRef origRef(destRef); - std::string updatePath; - - - SWF_Support::FileInfo fileInfo(sourceRef, sourcePath); - - if(fileInfo.IsCompressed()) - { - sourceRef = fileInfo.Decompress(); - CreateTempFile ( sourcePath, &updatePath, kCopyMacRsrc ); - destRef = LFA_Open ( updatePath.c_str(), 'w' ); - } - - IO::InputStream * is = NULL; - - is = new IO::FileInputStream(sourceRef); - is->Skip(SWF_COMPRESSION_BEGIN); - - long numTags = SWF_Support::OpenSWF( is, tagState ); - - is->Close(); - delete is; - - if ( numTags == 0 ) return; - - LFA_Truncate(destRef, 0); - SWF_Support::CopyHeader(sourceRef, destRef, tagState); - - SWF_Support::TagIterator curPos = tagState.tags.begin(); - SWF_Support::TagIterator endPos = tagState.tags.end(); - - XMP_StringPtr packetStr = xmpPacket.c_str(); - XMP_StringLen packetLen = (XMP_StringLen)xmpPacket.size(); - - bool copying = true; - bool isXMPTagWritten = false; - - for (; (curPos != endPos); ++curPos) - { - SWF_Support::TagData tag = *curPos; - - // write XMP tag right after FileAttribute Tag if no XMP tag exists - if (! tagState.hasXMP && (tag.id == SWF_TAG_ID_FILEATTRIBUTES)) - SWF_Support::WriteXMPTag(destRef, packetLen, packetStr ); - - //no FileAttribute Tag and no XMP tag write XMP Tag at the beginning of the file - if(!tagState.hasXMP && !tagState.hasFileAttrTag && ! isXMPTagWritten) - { - isXMPTagWritten = true; - SWF_Support::WriteXMPTag(destRef, packetLen, packetStr ); - } - - // write XMP tag where old XMP exists - if(tagState.hasXMP && (tag.pos == tagState.xmpTag.pos)) - { - copying = false; - SWF_Support::WriteXMPTag(destRef, packetLen, packetStr ); - } - - // copy any other chunk - if(copying) - SWF_Support::CopyTag(sourceRef, destRef, tag); - - copying = true; - } - - // update FileAttribute Tag in new file - if(tagState.hasFileAttrTag) - SWF_Support::UpdateFileAttrTag(destRef, tagState.fileAttrTag, tagState); - - - // update file header by measuring new file size - SWF_Support::UpdateHeader(origRef); - - //compress re-written file - if(fileInfo.IsCompressed()) - { - fileInfo.Compress(destRef, origRef); - fileInfo.Clean(); - LFA_Close(destRef); - LFA_Delete(updatePath.c_str()); - } - - - - -} // SWF_MetaHandler::WriteFile - diff --git a/source/XMPFiles/FileHandlers/SWF_Handler.hpp b/source/XMPFiles/FileHandlers/SWF_Handler.hpp deleted file mode 100644 index 91ad760..0000000 --- a/source/XMPFiles/FileHandlers/SWF_Handler.hpp +++ /dev/null @@ -1,61 +0,0 @@ -#ifndef __SWF_Handler_hpp__ -#define __SWF_Handler_hpp__ 1 - -// ================================================================================================= -// ADOBE SYSTEMS INCORPORATED -// Copyright 2006 Adobe Systems Incorporated -// 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 "XMP_Environment.h" // ! This must be the first include. - -#include "XMPFiles_Impl.hpp" - -//#include "SWF_Support.hpp" - -// ================================================================================================= -/// \file SWF_Handler.hpp -/// \brief File format handler for SWF. -/// -/// This header ... -/// -// ================================================================================================= - -// *** Could derive from Basic_Handler - buffer file tail in a temp file. - -extern XMPFileHandler* SWF_MetaHandlerCTor ( XMPFiles* parent ); - -extern bool SWF_CheckFormat ( XMP_FileFormat format, - XMP_StringPtr filePath, - LFA_FileRef fileRef, - XMPFiles * parent ); - -static const XMP_OptionBits kSWF_HandlerFlags = ( kXMPFiles_CanInjectXMP | - kXMPFiles_CanExpand | - kXMPFiles_PrefersInPlace | - kXMPFiles_AllowsOnlyXMP | - kXMPFiles_ReturnsRawPacket ); - -class SWF_MetaHandler : public XMPFileHandler -{ -public: - - void CacheFileData(); - void ProcessXMP(); - - XMP_OptionBits GetSerializeOptions(); - - void UpdateFile ( bool doSafeUpdate ); - void WriteFile ( LFA_FileRef sourceRef, const std::string& sourcePath ); - - SWF_MetaHandler ( XMPFiles* parent ); - virtual ~SWF_MetaHandler(); - -}; // SWF_MetaHandler - -// ================================================================================================= - -#endif /* __SWF_Handler_hpp__ */ diff --git a/source/XMPFiles/FileHandlers/Scanner_Handler.cpp b/source/XMPFiles/FileHandlers/Scanner_Handler.cpp deleted file mode 100644 index 1af7326..0000000 --- a/source/XMPFiles/FileHandlers/Scanner_Handler.cpp +++ /dev/null @@ -1,335 +0,0 @@ -// ================================================================================================= -// ADOBE SYSTEMS INCORPORATED -// Copyright 2004 Adobe Systems Incorporated -// 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 <vector> - -#include "XMPScanner.hpp" - -#include "Scanner_Handler.hpp" - -using namespace std; - -// ================================================================================================= -/// \file Scanner_Handler.cpp -/// \brief File format handler for packet scanning. -/// -/// This header ... -/// -// ================================================================================================= - -struct CandidateInfo { - XMP_PacketInfo packetInfo; - std::string xmpPacket; - SXMPMeta * xmpObj; -}; - -// ================================================================================================= -// Scanner_MetaHandlerCTor -// ======================= - -XMPFileHandler * Scanner_MetaHandlerCTor ( XMPFiles * parent ) -{ - return new Scanner_MetaHandler ( parent ); - -} // Scanner_MetaHandlerCTor - -// ================================================================================================= -// Scanner_MetaHandler::Scanner_MetaHandler -// ======================================== - -Scanner_MetaHandler::Scanner_MetaHandler ( XMPFiles * _parent ) -{ - this->parent = _parent; - this->handlerFlags = kScanner_HandlerFlags; - -} // Scanner_MetaHandler::Scanner_MetaHandler - -// ================================================================================================= -// Scanner_MetaHandler::~Scanner_MetaHandler -// ========================================= - -Scanner_MetaHandler::~Scanner_MetaHandler() -{ - // ! Inherit the base cleanup. - -} // Scanner_MetaHandler::~Scanner_MetaHandler - -// ================================================================================================= -// PickMainPacket -// ============== -// -// Pick the main packet from the vector of candidates. The rules: -// 1. Use the manifest find containment. Prune contained packets. -// 2. Use the metadata date to pick the most recent. -// 3. if lenient, pick the last writeable packet, or the last if all are read only. - -static int -PickMainPacket ( std::vector<CandidateInfo>& candidates, bool beLenient ) -{ - int pkt; // ! Must be signed. - int main = -1; // Assume the worst. - XMP_OptionBits options; - - int metaCount = (int)candidates.size(); - if ( metaCount == 0 ) return -1; - if ( metaCount == 1 ) return 0; - - // --------------------------------------------------------------------------------------------- - // 1. Look at each packet to see if it has a manifest. If it does, prune all of the others that - // this one says it contains. Hopefully we'll end up with just one packet. Note that we have to - // mark all the children first, then prune. Pruning on the fly means that we won't do a proper - // tree discovery if we prune a parent before a child. This would happen if we happened to visit - // a grandparent first. - - int child; - - std::vector<bool> pruned ( metaCount, false ); - - for ( pkt = 0; pkt < (int)candidates.size(); ++pkt ) { - - // First see if this candidate has a manifest. - - try { - std::string voidValue; - bool found = candidates[pkt].xmpObj->GetProperty ( kXMP_NS_XMP_MM, "Manifest", &voidValue, &options ); - if ( (! found) || (! XMP_PropIsArray ( options )) ) continue; // No manifest, or not an array. - } catch ( ... ) { - continue; // No manifest. - }; - - // Mark all other candidates that are referred to in this manifest. - - for ( child = 0; child < (int)candidates.size(); ++child ) { - if ( pruned[child] || (child == pkt) ) continue; // Skip already pruned ones and self. - } - - } - - // Go ahead and actually remove the marked packets. - - for ( pkt = 0; pkt < (int)candidates.size(); ++pkt ) { - if ( pruned[pkt] ) { - delete candidates[pkt].xmpObj; - candidates[pkt].xmpObj = 0; - metaCount -= 1; - } - } - - // We're done if the containment pruning left us with 0 or 1 candidate. - - if ( metaCount == 0 ) { - XMP_Throw ( "GetMainPacket/PickMainPacket: Recursive containment", kXMPErr_BadXMP ); - } else if ( metaCount == 1 ) { - for ( pkt = 0; pkt < (int)candidates.size(); ++pkt ) { - if ( candidates[pkt].xmpObj != 0 ) { - main = pkt; - break; - } - } - } - - if ( main != -1 ) return main; // We found the main. - - // ------------------------------------------------------------------------------------------- - // 2. Pick the packet with the most recent metadata date. If we are being lenient then missing - // dates are older than any real date, and equal dates pick the last packet. If we are being - // strict then any missing or equal dates mean we can't pick. - - XMP_DateTime latestTime, currTime; - - for ( pkt = 0; pkt < (int)candidates.size(); ++pkt ) { - - if ( candidates[pkt].xmpObj == 0 ) continue; // This was pruned in the manifest stage. - - bool haveDate = candidates[pkt].xmpObj->GetProperty_Date ( kXMP_NS_XMP, "MetadataDate", &currTime, &options ); - - if ( ! haveDate ) { - - if ( ! beLenient ) return -1; - if ( main == -1 ) { - main = pkt; - memset ( &latestTime, 0, sizeof(latestTime) ); - } - - } else if ( main == -1 ) { - - main = pkt; - latestTime = currTime; - - } else { - - int timeOp = SXMPUtils::CompareDateTime ( currTime, latestTime ); - - if ( timeOp > 0 ) { - main = pkt; - latestTime = currTime; - } else if ( timeOp == 0 ) { - if ( ! beLenient ) return -1; - main = pkt; - latestTime = currTime; - } - - } - - } - - if ( main != -1 ) return main; // We found the main. - - // -------------------------------------------------------------------------------------------- - // 3. If we're being lenient, pick the last writeable packet, or the last if all are read only. - - if ( beLenient ) { - - for ( pkt = (int)candidates.size()-1; pkt >= 0; --pkt ) { - if ( candidates[pkt].xmpObj == 0 ) continue; // This was pruned in the manifest stage. - if ( candidates[pkt].packetInfo.writeable ) { - main = pkt; - break; - } - } - - if ( main == -1 ) { - for ( pkt = (int)candidates.size()-1; pkt >= 0; --pkt ) { - if ( candidates[pkt].xmpObj != 0 ) { - main = pkt; - break; - } - } - } - - } - - return main; - -} // PickMainPacket - -// ================================================================================================= -// Scanner_MetaHandler::CacheFileData -// ================================== - -void Scanner_MetaHandler::CacheFileData() -{ - LFA_FileRef fileRef = this->parent->fileRef; - bool beLenient = XMP_OptionIsClear ( this->parent->openFlags, kXMPFiles_OpenStrictly ); - - int pkt; - XMP_Int64 bufPos; - size_t bufLen; - SXMPMeta * newMeta; - - XMP_AbortProc abortProc = this->parent->abortProc; - void * abortArg = this->parent->abortArg; - const bool checkAbort = (abortProc != 0); - - std::vector<CandidateInfo> candidates; // ! These have SXMPMeta* fields, don't leak on exceptions. - - this->containsXMP = false; - - try { - - // ------------------------------------------------------ - // Scan the entire file to find all of the valid packets. - - XMP_Int64 fileLen = LFA_Measure ( fileRef ); - XMPScanner scanner ( fileLen ); - - enum { kBufferSize = 64*1024 }; - XMP_Uns8 buffer [kBufferSize]; - - LFA_Seek ( fileRef, 0, SEEK_SET ); - - for ( bufPos = 0; bufPos < fileLen; bufPos += bufLen ) { - if ( checkAbort && abortProc(abortArg) ) { - XMP_Throw ( "Scanner_MetaHandler::LocateXMP - User abort", kXMPErr_UserAbort ); - } - bufLen = LFA_Read ( fileRef, buffer, kBufferSize ); - if ( bufLen == 0 ) XMP_Throw ( "Scanner_MetaHandler::LocateXMP: Read failure", kXMPErr_ExternalFailure ); - scanner.Scan ( buffer, bufPos, bufLen ); - } - - // -------------------------------------------------------------- - // Parse the valid packet snips, building a vector of candidates. - - long snipCount = scanner.GetSnipCount(); - - XMPScanner::SnipInfoVector snips ( snipCount ); - scanner.Report ( snips ); - - for ( pkt = 0; pkt < snipCount; ++pkt ) { - - if ( checkAbort && abortProc(abortArg) ) { - XMP_Throw ( "Scanner_MetaHandler::LocateXMP - User abort", kXMPErr_UserAbort ); - } - - // Seek to the packet then try to parse it. - - if ( snips[pkt].fState != XMPScanner::eValidPacketSnip ) continue; - LFA_Seek ( fileRef, snips[pkt].fOffset, SEEK_SET ); - newMeta = new SXMPMeta(); - std::string xmpPacket; - xmpPacket.reserve ( (size_t)snips[pkt].fLength ); - - try { - for ( bufPos = 0; bufPos < snips[pkt].fLength; bufPos += bufLen ) { - bufLen = kBufferSize; - if ( (bufPos + bufLen) > (size_t)snips[pkt].fLength ) bufLen = size_t ( snips[pkt].fLength - bufPos ); - (void) LFA_Read ( fileRef, buffer, (XMP_Int32)bufLen, kLFA_RequireAll ); - xmpPacket.append ( (const char *)buffer, bufLen ); - newMeta->ParseFromBuffer ( (char *)buffer, (XMP_StringLen)bufLen, kXMP_ParseMoreBuffers ); - } - newMeta->ParseFromBuffer ( 0, 0, kXMP_NoOptions ); - } catch ( ... ) { - delete newMeta; - if ( beLenient ) continue; // Skip if we're being lenient, else rethrow. - throw; - } - - // It parsed OK, add it to the array of candidates. - - candidates.push_back ( CandidateInfo() ); - CandidateInfo & newInfo = candidates.back(); - newInfo.xmpObj = newMeta; - newInfo.xmpPacket.swap ( xmpPacket ); - newInfo.packetInfo.offset = snips[pkt].fOffset; - newInfo.packetInfo.length = (XMP_Int32)snips[pkt].fLength; - newInfo.packetInfo.charForm = snips[pkt].fCharForm; - newInfo.packetInfo.writeable = (snips[pkt].fAccess == 'w'); - - } - - // ---------------------------------------- - // Figure out which packet is the main one. - - int main = PickMainPacket ( candidates, beLenient ); - - if ( main != -1 ) { - this->packetInfo = candidates[main].packetInfo; - this->xmpPacket.swap ( candidates[main].xmpPacket ); - this->xmpObj = *candidates[main].xmpObj; - this->containsXMP = true; - this->processedXMP = true; - } - - for ( pkt = 0; pkt < (int)candidates.size(); ++pkt ) { - if ( candidates[pkt].xmpObj != 0 ) delete candidates[pkt].xmpObj; - } - - } catch ( ... ) { - - // Clean up the SXMPMeta* fields from the vector of candidates. - for ( pkt = 0; pkt < (int)candidates.size(); ++pkt ) { - if ( candidates[pkt].xmpObj != 0 ) delete candidates[pkt].xmpObj; - } - throw; - - } - -} // Scanner_MetaHandler::CacheFileData - -// ================================================================================================= diff --git a/source/XMPFiles/FileHandlers/Scanner_Handler.hpp b/source/XMPFiles/FileHandlers/Scanner_Handler.hpp deleted file mode 100644 index a6f23b5..0000000 --- a/source/XMPFiles/FileHandlers/Scanner_Handler.hpp +++ /dev/null @@ -1,42 +0,0 @@ -#ifndef __Scanner_Handler_hpp__ -#define __Scanner_Handler_hpp__ 1 - -// ================================================================================================= -// ADOBE SYSTEMS INCORPORATED -// Copyright 2004 Adobe Systems Incorporated -// 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 "Trivial_Handler.hpp" - -// ================================================================================================= -/// \file Scanner_Handler.hpp -/// \brief File format handler for packet scanning. -/// -/// This header ... -/// -// ================================================================================================= - -extern XMPFileHandler * Scanner_MetaHandlerCTor ( XMPFiles * parent ); - -static const XMP_OptionBits kScanner_HandlerFlags = kTrivial_HandlerFlags; - -class Scanner_MetaHandler : public Trivial_MetaHandler -{ -public: - - Scanner_MetaHandler () {}; - Scanner_MetaHandler ( XMPFiles * parent ); - - ~Scanner_MetaHandler(); - - void CacheFileData(); - -}; // Scanner_MetaHandler - -// ================================================================================================= - -#endif /* __Scanner_Handler_hpp__ */ diff --git a/source/XMPFiles/FileHandlers/SonyHDV_Handler.cpp b/source/XMPFiles/FileHandlers/SonyHDV_Handler.cpp deleted file mode 100644 index d3758da..0000000 --- a/source/XMPFiles/FileHandlers/SonyHDV_Handler.cpp +++ /dev/null @@ -1,782 +0,0 @@ -// ================================================================================================= -// ADOBE SYSTEMS INCORPORATED -// Copyright 2007 Adobe Systems Incorporated -// 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 "SonyHDV_Handler.hpp" - -#include "MD5.h" - -#if XMP_WinBuild - #pragma warning ( disable : 4996 ) // '...' was declared deprecated -#endif - -using namespace std; - -// ================================================================================================= -/// \file SonyHDV_Handler.cpp -/// \brief Folder format handler for Sony HDV. -/// -/// This handler is for the Sony HDV video format. This is a pseudo-package, visible files but with -/// a very well-defined layout and naming rules. -/// -/// A typical Sony HDV layout looks like: -/// -/// .../MyMovie/ -/// VIDEO/ -/// HDV/ -/// 00_0001_2007-08-06_165555.IDX -/// 00_0001_2007-08-06_165555.M2T -/// 00_0001_2007-08-06_171740.M2T -/// 00_0001_2007-08-06_171740.M2T.ese -/// tracks.dat -/// -/// The logical clip name can be "00_0001" or "00_0001_" plus anything. We'll find the .IDX file, -/// which defines the existence of the clip. Full file names as input will pull out the camera/clip -/// parts and match in the same way. The .XMP file will use the date/time suffix from the .IDX file. -// ================================================================================================= - -// ================================================================================================= -// SonyHDV_CheckFormat -// =================== -// -// This version does fairly simple checks. The top level folder (.../MyMovie) must contain the -// VIDEO/HVR subtree. The HVR folder must contain a .IDX file for the desired clip. The name checks -// are case insensitive. -// -// The state of the string parameters depends on the form of the path passed by the client. If the -// client passed a logical clip path, like ".../MyMovie/00_0001", the parameters are: -// rootPath - ".../MyMovie" -// gpName - empty -// parentName - empty -// leafName - "00_0001" -// -// If the client passed a full file path, like ".../MyMovie/VIDEO/HVR/00_0001_2007-08-06_165555.M2T", -// they are: -// rootPath - ".../MyMovie" -// gpName - "VIDEO" -// parentName - "HVR" -// leafName - "00_0001_2007-08-06_165555.M2T" -// -// The logical clip name can be short like "00_0001", or long like "00_0001_2007-08-06_165555". We -// only key off of the portion before a second underscore. - -// ! The common code has shifted the gpName, parentName, and leafName strings to upper case. It has -// ! also made sure that for a logical clip path the rootPath is an existing folder, and that the -// ! file exists for a full file path. - -bool SonyHDV_CheckFormat ( XMP_FileFormat format, - const std::string & rootPath, - const std::string & gpName, - const std::string & parentName, - const std::string & leafName, - XMPFiles * parent ) -{ - // Do some basic checks on the root path and component names. - - if ( gpName.empty() != parentName.empty() ) return false; // Must be both empty or both non-empty. - - std::string tempPath = rootPath; - tempPath += kDirChar; - tempPath += "VIDEO"; - - if ( gpName.empty() ) { - // This is the logical clip path case. Look for VIDEO/HVR subtree. - if ( GetChildMode ( tempPath, "HVR" ) != kFMode_IsFolder ) return false; - } else { - // This is the existing file case. Check the parent and grandparent names. - if ( (gpName != "VIDEO") || (parentName != "HVR") ) return false; - } - - // Look for the clip's .IDX file. If found use that as the full clip name. - - tempPath += kDirChar; - tempPath += "HVR"; - - std::string clipName = leafName; - int usCount = 0; - size_t i, limit = leafName.size(); - for ( i = 0; i < limit; ++i ) { - if ( clipName[i] == '_' ) { - ++usCount; - if ( usCount == 2 ) break; - } - } - if ( i < limit ) clipName.erase ( i ); - clipName += '_'; // Make sure a final '_' is there for the search comparisons. - - XMP_FolderInfo folderInfo; - std::string childName; - bool found = false; - - folderInfo.Open ( tempPath.c_str() ); - while ( (! found) && folderInfo.GetNextChild ( &childName ) ) { - size_t childLen = childName.size(); - if ( childLen < 4 ) continue; - MakeUpperCase ( &childName ); - if ( childName.compare ( childLen-4, 4, ".IDX" ) != 0 ) continue; - if ( childName.compare ( 0, clipName.size(), clipName ) == 0 ) { - found = true; - clipName = childName; - clipName.erase ( childLen-4 ); - } - } - if ( ! found ) return false; - - - // Disabled until Sony HDV clip spanning is supported. - // Since segments of spanned clips are currently considered separate entities, - // information such as frame count needs to be considered on a per segment basis. JPM - clipName = leafName; - - // Set tempPath to <root>/<clip-name>, e.g. ".../MyMovie/00_0001_2007-08-06_165555". This is - // passed to the handler via the tempPtr hackery. - - tempPath = rootPath; - tempPath += kDirChar; - tempPath += clipName; - - size_t pathLen = tempPath.size() + 1; // Include a terminating nul. - parent->tempPtr = malloc ( pathLen ); - if ( parent->tempPtr == 0 ) XMP_Throw ( "No memory for SonyHDV clip info", kXMPErr_NoMemory ); - memcpy ( parent->tempPtr, tempPath.c_str(), pathLen ); // AUDIT: Safe, allocated above. - - return true; - -} // SonyHDV_CheckFormat - -// ================================================================================================= -// ReadIDXFile -// =========== - -#define ExtractTimeCodeByte(ch,mask) ( (((ch & mask) >> 4) * 10) + (ch & 0xF) ) - -static bool ReadIDXFile ( const std::string& idxPath, - const std::string& clipName, - SXMPMeta* xmpObj, - bool& containsXMP, - MD5_CTX* md5Context, - bool digestFound ) -{ - bool result = true; - containsXMP = false; - - if ( clipName.size() != 25 ) return false; - - try { - - - AutoFile idxFile; - idxFile.fileRef = LFA_Open ( idxPath.c_str(), 'r' ); - if ( idxFile.fileRef == 0 ) return false; // The open failed. - - struct SHDV_HeaderBlock - { - char mHeader[8]; - unsigned char mValidFlag; - unsigned char mReserved; - unsigned char mECCTB; - unsigned char mSignalMode; - unsigned char mFileThousands; - unsigned char mFileHundreds; - unsigned char mFileTens; - unsigned char mFileUnits; - }; - - SHDV_HeaderBlock hdvHeaderBlock; - memset ( &hdvHeaderBlock, 0, sizeof(SHDV_HeaderBlock) ); - - LFA_Read ( idxFile.fileRef, hdvHeaderBlock.mHeader, 8 ); - LFA_Read ( idxFile.fileRef, &hdvHeaderBlock.mValidFlag, 1 ); - LFA_Read ( idxFile.fileRef, &hdvHeaderBlock.mReserved, 1 ); - LFA_Read ( idxFile.fileRef, &hdvHeaderBlock.mECCTB, 1 ); - LFA_Read ( idxFile.fileRef, &hdvHeaderBlock.mSignalMode, 1 ); - LFA_Read ( idxFile.fileRef, &hdvHeaderBlock.mFileThousands, 1 ); - LFA_Read ( idxFile.fileRef, &hdvHeaderBlock.mFileHundreds, 1 ); - LFA_Read ( idxFile.fileRef, &hdvHeaderBlock.mFileTens, 1 ); - LFA_Read ( idxFile.fileRef, &hdvHeaderBlock.mFileUnits, 1 ); - - const int fileCount = (hdvHeaderBlock.mFileThousands - '0') * 1000 + - (hdvHeaderBlock.mFileHundreds - '0') * 100 + - (hdvHeaderBlock.mFileTens - '0') * 10 + - (hdvHeaderBlock.mFileUnits - '0'); - - // Read file info block. - struct SHDV_FileBlock - { - char mDT[2]; - unsigned char mFileNameYear; - unsigned char mFileNameMonth; - unsigned char mFileNameDay; - unsigned char mFileNameHour; - unsigned char mFileNameMinute; - unsigned char mFileNameSecond; - unsigned char mStartTimeCode[4]; - unsigned char mTotalFrame[4]; - }; - - SHDV_FileBlock hdvFileBlock; - memset ( &hdvFileBlock, 0, sizeof(SHDV_FileBlock) ); - - char filenameBuffer[256]; - std::string fileDateAndTime = clipName.substr(8); - - bool foundFileBlock = false; - - for ( int i=0; ((i < fileCount) && (! foundFileBlock)); ++i ) { - - LFA_Read ( idxFile.fileRef, hdvFileBlock.mDT, 2 ); - LFA_Read ( idxFile.fileRef, &hdvFileBlock.mFileNameYear, 1 ); - LFA_Read ( idxFile.fileRef, &hdvFileBlock.mFileNameMonth, 1 ); - LFA_Read ( idxFile.fileRef, &hdvFileBlock.mFileNameDay, 1 ); - LFA_Read ( idxFile.fileRef, &hdvFileBlock.mFileNameHour, 1 ); - LFA_Read ( idxFile.fileRef, &hdvFileBlock.mFileNameMinute, 1 ); - LFA_Read ( idxFile.fileRef, &hdvFileBlock.mFileNameSecond, 1 ); - LFA_Read ( idxFile.fileRef, &hdvFileBlock.mStartTimeCode, 4 ); - LFA_Read ( idxFile.fileRef, &hdvFileBlock.mTotalFrame, 4 ); - - // Compose file name we expect from file contents and break out on match. - sprintf ( filenameBuffer, "%02d-%02d-%02d_%02d%02d%02d", - hdvFileBlock.mFileNameYear + 2000, - hdvFileBlock.mFileNameMonth, - hdvFileBlock.mFileNameDay, - hdvFileBlock.mFileNameHour, - hdvFileBlock.mFileNameMinute, - hdvFileBlock.mFileNameSecond ); - - foundFileBlock = (fileDateAndTime==filenameBuffer); - - } - - LFA_Close ( idxFile.fileRef ); - idxFile.fileRef = 0; - - if ( ! foundFileBlock ) return false; - - // If digest calculation requested, calculate it and return. - if ( md5Context != 0 ) { - MD5Update ( md5Context, (XMP_Uns8*)(&hdvHeaderBlock), sizeof(SHDV_HeaderBlock) ); - MD5Update ( md5Context, (XMP_Uns8*)(&hdvFileBlock), sizeof(SHDV_FileBlock) ); - } - - // The xmpObj parameter must be provided in order to extract XMP - if ( xmpObj == 0 ) return (md5Context != 0); - - // Standard def? - const bool isSD = ((hdvHeaderBlock.mSignalMode == 0x80) || (hdvHeaderBlock.mSignalMode == 0)); - - // Progressive vs interlaced extracted from high bit of ECCTB byte - const bool clipIsProgressive = ((hdvHeaderBlock.mECCTB & 0x80) != 0); - - // Lowest three bits contain frame rate information - const int sfr = (hdvHeaderBlock.mECCTB & 7) + (clipIsProgressive ? 0 : 8); - - // Sample scale and sample size. - int clipSampleScale = 0; - int clipSampleSize = 0; - std::string frameRate; - - // Frame rate - switch ( sfr ) { - case 0 : break; // Not valid in spec, but it's happening in test files. - case 1 : clipSampleScale = 24000; clipSampleSize = 1001; frameRate = "23.98p"; break; - case 3 : clipSampleScale = 25; clipSampleSize = 1; frameRate = "25p"; break; - case 4 : clipSampleScale = 30000; clipSampleSize = 1001; frameRate = "29.97p"; break; - case 11 : clipSampleScale = 25; clipSampleSize = 1; frameRate = "50i"; break; - case 12 : clipSampleScale = 30000; clipSampleSize = 1001; frameRate = "59.94i"; break; - } - - containsXMP = true; - - // Frame size and PAR for HD (not clear on SD yet). - std::string xmpString; - XMP_StringPtr xmpValue = 0; - - if ( ! isSD ) { - - if ( digestFound || (! xmpObj->DoesPropertyExist ( kXMP_NS_DM, "videoFrameSize" )) ) { - - xmpValue = "1440"; - xmpObj->GetStructField ( kXMP_NS_DM, "videoFrameSize", kXMP_NS_DM, "w", &xmpString, 0 ); - if ( xmpString != xmpValue ) { - xmpObj->SetStructField ( kXMP_NS_DM, "videoFrameSize", kXMP_NS_XMP_Dimensions, "w", xmpValue, 0 ); - } - - xmpValue = "1080"; - xmpObj->GetStructField ( kXMP_NS_DM, "videoFrameSize", kXMP_NS_DM, "h", &xmpString, 0 ); - if ( xmpString != xmpValue ) { - xmpObj->SetStructField ( kXMP_NS_DM, "videoFrameSize", kXMP_NS_XMP_Dimensions, "h", xmpValue, 0 ); - } - - xmpValue = "pixels"; - xmpObj->GetStructField ( kXMP_NS_DM, "videoFrameSize", kXMP_NS_DM, "unit", &xmpString, 0 ); - if ( xmpString != xmpValue ) { - xmpObj->SetStructField ( kXMP_NS_DM, "videoFrameSize", kXMP_NS_XMP_Dimensions, "unit", xmpValue, 0 ); - } - } - - xmpValue = "4/3"; - if ( digestFound || (! xmpObj->DoesPropertyExist ( kXMP_NS_DM, "videoPixelAspectRatio" )) ) { - xmpObj->SetProperty ( kXMP_NS_DM, "videoPixelAspectRatio", xmpValue, kXMP_DeleteExisting ); - } - - } - - // Sample size and scale. - if ( clipSampleScale != 0 ) { - - char buffer[255]; - - if ( digestFound || (! xmpObj->DoesPropertyExist ( kXMP_NS_DM, "startTimeScale" )) ) { - sprintf(buffer, "%d", clipSampleScale); - xmpValue = buffer; - xmpObj->SetProperty ( kXMP_NS_DM, "startTimeScale", xmpValue, kXMP_DeleteExisting ); - } - - if ( digestFound || (! xmpObj->DoesPropertyExist ( kXMP_NS_DM, "startTimeSampleSize" )) ) { - sprintf(buffer, "%d", clipSampleSize); - xmpValue = buffer; - xmpObj->SetProperty ( kXMP_NS_DM, "startTimeSampleSize", xmpValue, kXMP_DeleteExisting ); - } - - if ( digestFound || (! xmpObj->DoesPropertyExist ( kXMP_NS_DM, "duration" )) ) { - - const int frameCount = (hdvFileBlock.mTotalFrame[0] << 24) + (hdvFileBlock.mTotalFrame[1] << 16) + - (hdvFileBlock.mTotalFrame[2] << 8) + hdvFileBlock.mTotalFrame[3]; - - sprintf ( buffer, "%d", frameCount ); - xmpValue = buffer; - xmpObj->SetStructField ( kXMP_NS_DM, "duration", kXMP_NS_DM, "value", xmpValue, 0 ); - - sprintf ( buffer, "%d/%d", clipSampleSize, clipSampleScale ); - xmpValue = buffer; - xmpObj->SetStructField ( kXMP_NS_DM, "duration", kXMP_NS_DM, "scale", xmpValue, 0 ); - - } - - } - - // Time Code. - if ( digestFound || (! xmpObj->DoesPropertyExist ( kXMP_NS_DM, "startTimecode" )) ) { - - if ( (clipSampleScale != 0) && (clipSampleSize != 0) ) { - - const bool dropFrame = ( (0x40 & hdvFileBlock.mStartTimeCode[0]) != 0 ) && ( sfr == 4 || sfr == 12 ); - const char chDF = dropFrame ? ';' : ':'; - const int tcFrames = ExtractTimeCodeByte ( hdvFileBlock.mStartTimeCode[0], 0x30 ); - const int tcSeconds = ExtractTimeCodeByte ( hdvFileBlock.mStartTimeCode[1], 0x70 ); - const int tcMinutes = ExtractTimeCodeByte ( hdvFileBlock.mStartTimeCode[2], 0x70 ); - const int tcHours = ExtractTimeCodeByte ( hdvFileBlock.mStartTimeCode[3], 0x30 ); - - // HH:MM:SS:FF or HH;MM;SS;FF - char timecode[256]; - sprintf ( timecode, "%02d%c%02d%c%02d%c%02d", tcHours, chDF, tcMinutes, chDF, tcSeconds, chDF, tcFrames ); - std::string sonyTimeString = timecode; - - xmpObj->GetStructField ( kXMP_NS_DM, "startTimecode", kXMP_NS_DM, "timeValue", &xmpString, 0 ); - if ( xmpString != sonyTimeString ) { - - xmpObj->SetStructField ( kXMP_NS_DM, "startTimecode", kXMP_NS_DM, "timeValue", sonyTimeString, 0 ); - - std::string timeFormat; - if ( clipSampleSize == 1 ) { - - // 24, 25, 40, 50, 60 - switch ( clipSampleScale ) { - case 24 : timeFormat = "24"; break; - case 25 : timeFormat = "25"; break; - case 50 : timeFormat = "50"; break; - default : XMP_Assert ( false ); - } - - timeFormat += "Timecode"; - - } else { - - // 23.976, 29.97, 59.94 - XMP_Assert ( clipSampleSize == 1001 ); - switch ( clipSampleScale ) { - case 24000 : timeFormat = "23976"; break; - case 30000 : timeFormat = "2997"; break; - case 60000 : timeFormat = "5994"; break; - default : XMP_Assert( false ); break; - } - - timeFormat += dropFrame ? "DropTimecode" : "NonDropTimecode"; - - } - - xmpObj->SetStructField ( kXMP_NS_DM, "startTimecode", kXMP_NS_DM, "timeFormat", timeFormat, 0 ); - - } - - } - - } - - if ( digestFound || (! xmpObj->DoesPropertyExist ( kXMP_NS_DM, "CreateDate" )) ) { - - // Clip has date and time in the case of DT (otherwise date and time haven't been set). - bool clipHasDate = ((hdvFileBlock.mDT[0] == 'D') && (hdvFileBlock.mDT[1] == 'T')); - - // Creation date - if ( clipHasDate ) { - - // YYYY-MM-DDThh:mm:ssZ - char date[256]; - sprintf ( date, "%4d-%02d-%02dT%02d:%02d:%02dZ", - hdvFileBlock.mFileNameYear + 2000, - hdvFileBlock.mFileNameMonth, - hdvFileBlock.mFileNameDay, - hdvFileBlock.mFileNameHour, - hdvFileBlock.mFileNameMinute, - hdvFileBlock.mFileNameSecond ); - - XMP_StringPtr xmpDate = date; - xmpObj->SetProperty ( kXMP_NS_XMP, "CreateDate", xmpDate, kXMP_DeleteExisting ); - - } - - } - - // Frame rate. - if ( digestFound || (! xmpObj->DoesPropertyExist ( kXMP_NS_DM, "videoFrameRate" )) ) { - - if ( frameRate.size() != 0 ) { - xmpString = frameRate; - xmpObj->SetProperty ( kXMP_NS_DM, "videoFrameRate", xmpString, kXMP_DeleteExisting ); - } - - } - - } catch ( ... ) { - - result = false; - - } - - return result; - -} // ReadIDXFile - -// ================================================================================================= -// SonyHDV_MetaHandlerCTor -// ======================= - -XMPFileHandler * SonyHDV_MetaHandlerCTor ( XMPFiles * parent ) -{ - return new SonyHDV_MetaHandler ( parent ); - -} // SonyHDV_MetaHandlerCTor - -// ================================================================================================= -// SonyHDV_MetaHandler::SonyHDV_MetaHandler -// ======================================== - -SonyHDV_MetaHandler::SonyHDV_MetaHandler ( XMPFiles * _parent ) -{ - - this->parent = _parent; // Inherited, can't set in the prefix. - this->handlerFlags = kSonyHDV_HandlerFlags; - this->stdCharForm = kXMP_Char8Bit; - - // Extract the root path and clip name. - - XMP_Assert ( this->parent->tempPtr != 0 ); - - this->rootPath.assign ( (char*) this->parent->tempPtr ); - free ( this->parent->tempPtr ); - this->parent->tempPtr = 0; - - SplitLeafName ( &this->rootPath, &this->clipName ); - -} // SonyHDV_MetaHandler::SonyHDV_MetaHandler - -// ================================================================================================= -// SonyHDV_MetaHandler::~SonyHDV_MetaHandler -// ========================================= - -SonyHDV_MetaHandler::~SonyHDV_MetaHandler() -{ - - if ( this->parent->tempPtr != 0 ) { - free ( this->parent->tempPtr ); - this->parent->tempPtr = 0; - } - -} // SonyHDV_MetaHandler::~SonyHDV_MetaHandler - -// ================================================================================================= -// SonyHDV_MetaHandler::MakeClipFilePath -// ===================================== - -void SonyHDV_MetaHandler::MakeClipFilePath ( std::string * path, XMP_StringPtr suffix ) -{ - - *path = this->rootPath; - *path += kDirChar; - *path += "VIDEO"; - *path += kDirChar; - *path += "HVR"; - *path += kDirChar; - *path += this->clipName; - *path += suffix; - -} // SonyHDV_MetaHandler::MakeClipFilePath - -// ================================================================================================= -// SonyHDV_MetaHandler::MakeIndexFilePath -// ====================================== - -bool SonyHDV_MetaHandler::MakeIndexFilePath ( std::string& idxPath, const std::string& rootPath, const std::string& leafName ) -{ - std::string tempPath; - tempPath = rootPath; - tempPath += kDirChar; - tempPath += "VIDEO"; - tempPath += kDirChar; - tempPath += "HVR"; - - idxPath = tempPath; - idxPath += kDirChar; - idxPath += leafName; - idxPath += ".IDX"; - - // Default case - if ( GetFileMode ( idxPath.c_str() ) == kFMode_IsFile ) return true; - - // Spanned clip case - - // Scanning code taken from SonyHDV_CheckFormat - // Can be isolated to a separate function. - - std::string clipName = leafName; - int usCount = 0; - size_t i, limit = leafName.size(); - - for ( i = 0; i < limit; ++i ) { - if ( clipName[i] == '_' ) { - ++usCount; - if ( usCount == 2 ) break; - } - } - - if ( i < limit ) clipName.erase ( i ); - clipName += '_'; // Make sure a final '_' is there for the search comparisons. - - XMP_FolderInfo folderInfo; - std::string childName; - bool found = false; - - folderInfo.Open ( tempPath.c_str() ); - while ( (! found) && folderInfo.GetNextChild ( &childName ) ) { - size_t childLen = childName.size(); - if ( childLen < 4 ) continue; - MakeUpperCase ( &childName ); - if ( childName.compare ( childLen-4, 4, ".IDX" ) != 0 ) continue; - if ( childName.compare ( 0, clipName.size(), clipName ) == 0 ) { - found = true; - clipName = childName; - clipName.erase ( childLen-4 ); - } - } - if ( ! found ) return false; - - idxPath = tempPath; - idxPath += kDirChar; - idxPath += clipName; - idxPath += ".IDX"; - - return true; -} - -// ================================================================================================= -// SonyHDV_MetaHandler::MakeLegacyDigest -// ===================================== - -#define kHexDigits "0123456789ABCDEF" - -void SonyHDV_MetaHandler::MakeLegacyDigest ( std::string * digestStr ) -{ - std::string idxPath; - if ( ! this->MakeIndexFilePath ( idxPath, this->rootPath, this->clipName ) ) return; - - MD5_CTX context; - unsigned char digestBin [16]; - bool dummy = false; - MD5Init ( &context ); - ReadIDXFile ( idxPath, this->clipName, 0, dummy, &context, false ); - MD5Final ( digestBin, &context ); - - char buffer [40]; - for ( int in = 0, out = 0; in < 16; in += 1, out += 2 ) { - XMP_Uns8 byte = digestBin[in]; - buffer[out] = kHexDigits [ byte >> 4 ]; - buffer[out+1] = kHexDigits [ byte & 0xF ]; - } - buffer[32] = 0; - digestStr->erase(); - digestStr->append ( buffer, 32 ); - -} // MakeLegacyDigest - -// ================================================================================================= -// SonyHDV_MetaHandler::CacheFileData -// ================================== - -void SonyHDV_MetaHandler::CacheFileData() -{ - XMP_Assert ( ! this->containsXMP ); - - // See if the clip's .XMP file exists. - - std::string xmpPath; - this->MakeClipFilePath ( &xmpPath, ".XMP" ); - if ( GetFileMode ( xmpPath.c_str() ) != kFMode_IsFile ) return; // No XMP. - - // Read the entire .XMP file. - - char openMode = 'r'; - if ( this->parent->openFlags & kXMPFiles_OpenForUpdate ) openMode = 'w'; - - LFA_FileRef xmpFile = LFA_Open ( xmpPath.c_str(), openMode ); - if ( xmpFile == 0 ) return; // The open failed. - - XMP_Int64 xmpLen = LFA_Measure ( xmpFile ); - if ( xmpLen > 100*1024*1024 ) { - XMP_Throw ( "SonyHDV XMP is outrageously large", kXMPErr_InternalFailure ); // Sanity check. - } - - this->xmpPacket.erase(); - this->xmpPacket.reserve ( (size_t)xmpLen ); - this->xmpPacket.append ( (size_t)xmpLen, ' ' ); - - XMP_Int32 ioCount = LFA_Read ( xmpFile, (void*)this->xmpPacket.data(), (XMP_Int32)xmpLen, kLFA_RequireAll ); - XMP_Assert ( ioCount == xmpLen ); - - this->packetInfo.offset = 0; - this->packetInfo.length = (XMP_Int32)xmpLen; - FillPacketInfo ( this->xmpPacket, &this->packetInfo ); - - XMP_Assert ( this->parent->fileRef == 0 ); - if ( openMode == 'r' ) { - LFA_Close ( xmpFile ); - } else { - this->parent->fileRef = xmpFile; - } - - this->containsXMP = true; - -} // SonyHDV_MetaHandler::CacheFileData - -// ================================================================================================= -// SonyHDV_MetaHandler::ProcessXMP -// =============================== - -void SonyHDV_MetaHandler::ProcessXMP() -{ - if ( this->processedXMP ) return; - this->processedXMP = true; // Make sure only called once. - - if ( this->containsXMP ) { - this->xmpObj.ParseFromBuffer ( this->xmpPacket.c_str(), (XMP_StringLen)this->xmpPacket.size() ); - } - - // Check the legacy digest. - std::string oldDigest, newDigest; - bool digestFound; - digestFound = this->xmpObj.GetStructField ( kXMP_NS_XMP, "NativeDigests", kXMP_NS_XMP, "SonyHDV", &oldDigest, 0 ); - if ( digestFound ) { - this->MakeLegacyDigest ( &newDigest ); - if ( oldDigest == newDigest ) return; - } - - // Read the IDX legacy. - std::string idxPath; - if ( ! this->MakeIndexFilePath ( idxPath, this->rootPath, this->clipName ) ) return; - ReadIDXFile ( idxPath, this->clipName, &this->xmpObj, this->containsXMP, 0, digestFound ); - -} // SonyHDV_MetaHandler::ProcessXMP - -// ================================================================================================= -// SonyHDV_MetaHandler::UpdateFile -// =============================== -// -// Note that UpdateFile is only called from XMPFiles::CloseFile, so it is OK to close the file here. - -void SonyHDV_MetaHandler::UpdateFile ( bool doSafeUpdate ) -{ - if ( ! this->needsUpdate ) return; - this->needsUpdate = false; // Make sure only called once. - - std::string newDigest; - this->MakeLegacyDigest ( &newDigest ); - this->xmpObj.SetStructField ( kXMP_NS_XMP, "NativeDigests", kXMP_NS_XMP, "SonyHDV", newDigest.c_str(), kXMP_DeleteExisting ); - - LFA_FileRef oldFile = this->parent->fileRef; - - this->xmpObj.SerializeToBuffer ( &this->xmpPacket, this->GetSerializeOptions() ); - - if ( oldFile == 0 ) { - - // The XMP does not exist yet. - - std::string xmpPath; - this->MakeClipFilePath ( &xmpPath, ".XMP" ); - - LFA_FileRef xmpFile = LFA_Create ( xmpPath.c_str() ); - if ( xmpFile == 0 ) XMP_Throw ( "Failure creating SonyHDV XMP file", kXMPErr_ExternalFailure ); - LFA_Write ( xmpFile, this->xmpPacket.data(), (XMP_StringLen)this->xmpPacket.size() ); - LFA_Close ( xmpFile ); - - } else if ( ! doSafeUpdate ) { - - // Over write the existing XMP file. - - LFA_Seek ( oldFile, 0, SEEK_SET ); - LFA_Truncate ( oldFile, 0 ); - LFA_Write ( oldFile, this->xmpPacket.data(), (XMP_StringLen)this->xmpPacket.size() ); - LFA_Close ( oldFile ); - - } else { - - // Do a safe update. - - // *** We really need an LFA_SwapFiles utility. - - std::string xmpPath, tempPath; - - this->MakeClipFilePath ( &xmpPath, ".XMP" ); - - CreateTempFile ( xmpPath, &tempPath ); - LFA_FileRef tempFile = LFA_Open ( tempPath.c_str(), 'w' ); - LFA_Write ( tempFile, this->xmpPacket.data(), (XMP_StringLen)this->xmpPacket.size() ); - LFA_Close ( tempFile ); - - LFA_Close ( oldFile ); - LFA_Delete ( xmpPath.c_str() ); - LFA_Rename ( tempPath.c_str(), xmpPath.c_str() ); - - } - - this->parent->fileRef = 0; - -} // SonyHDV_MetaHandler::UpdateFile - -// ================================================================================================= -// SonyHDV_MetaHandler::WriteFile -// ============================== - -void SonyHDV_MetaHandler::WriteFile ( LFA_FileRef sourceRef, const std::string & sourcePath ) -{ - - // ! WriteFile is not supposed to be called for handlers that own the file. - XMP_Throw ( "SonyHDV_MetaHandler::WriteFile should not be called", kXMPErr_InternalFailure ); - -} // SonyHDV_MetaHandler::WriteFile - -// ================================================================================================= diff --git a/source/XMPFiles/FileHandlers/SonyHDV_Handler.hpp b/source/XMPFiles/FileHandlers/SonyHDV_Handler.hpp deleted file mode 100644 index 2af4ffc..0000000 --- a/source/XMPFiles/FileHandlers/SonyHDV_Handler.hpp +++ /dev/null @@ -1,77 +0,0 @@ -#ifndef __SonyHDV_Handler_hpp__ -#define __SonyHDV_Handler_hpp__ 1 - -// ================================================================================================= -// ADOBE SYSTEMS INCORPORATED -// Copyright 2007 Adobe Systems Incorporated -// 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 "XMP_Environment.h" // ! This must be the first include. - -#include "XMPFiles_Impl.hpp" - -#include "ExpatAdapter.hpp" - -// ================================================================================================= -/// \file SonyHDV_Handler.hpp -/// \brief Folder format handler for SonyHDV. -/// -/// This header ... -/// -// ================================================================================================= - -extern XMPFileHandler * SonyHDV_MetaHandlerCTor ( XMPFiles * parent ); - -extern bool SonyHDV_CheckFormat ( XMP_FileFormat format, - const std::string & rootPath, - const std::string & gpName, - const std::string & parentName, - const std::string & leafName, - XMPFiles * parent ); - -static const XMP_OptionBits kSonyHDV_HandlerFlags = (kXMPFiles_CanInjectXMP | - kXMPFiles_CanExpand | - kXMPFiles_CanRewrite | - kXMPFiles_PrefersInPlace | - kXMPFiles_CanReconcile | - kXMPFiles_AllowsOnlyXMP | - kXMPFiles_ReturnsRawPacket | - kXMPFiles_HandlerOwnsFile | - kXMPFiles_AllowsSafeUpdate | - kXMPFiles_FolderBasedFormat); - -class SonyHDV_MetaHandler : public XMPFileHandler -{ -public: - - void CacheFileData(); - void ProcessXMP(); - - XMP_OptionBits GetSerializeOptions() // *** These should be standard for standalone XMP files. - { return (kXMP_UseCompactFormat | kXMP_OmitPacketWrapper); }; - - void UpdateFile ( bool doSafeUpdate ); - void WriteFile ( LFA_FileRef sourceRef, const std::string & sourcePath ); - - SonyHDV_MetaHandler ( XMPFiles * _parent ); - virtual ~SonyHDV_MetaHandler(); - -private: - - SonyHDV_MetaHandler() {}; // Hidden on purpose. - - void MakeClipFilePath ( std::string * path, XMP_StringPtr suffix ); - bool MakeIndexFilePath ( std::string& idxPath, const std::string& rootPath, const std::string& leafName ); - void MakeLegacyDigest ( std::string * digestStr ); - - std::string rootPath, clipName; - -}; // SonyHDV_MetaHandler - -// ================================================================================================= - -#endif /* __SonyHDV_Handler_hpp__ */ diff --git a/source/XMPFiles/FileHandlers/TIFF_Handler.cpp b/source/XMPFiles/FileHandlers/TIFF_Handler.cpp deleted file mode 100644 index 15332e6..0000000 --- a/source/XMPFiles/FileHandlers/TIFF_Handler.cpp +++ /dev/null @@ -1,388 +0,0 @@ -// ================================================================================================= -// ADOBE SYSTEMS INCORPORATED -// Copyright 2006 Adobe Systems Incorporated -// 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 "TIFF_Handler.hpp" - -#include "TIFF_Support.hpp" -#include "PSIR_Support.hpp" -#include "IPTC_Support.hpp" -#include "ReconcileLegacy.hpp" -#include "Reconcile_Impl.hpp" - -#include "MD5.h" - -using namespace std; - -// ================================================================================================= -/// \file TIFF_Handler.cpp -/// \brief File format handler for TIFF. -/// -/// This handler ... -/// -// ================================================================================================= - -// ================================================================================================= -// TIFF_CheckFormat -// ================ - -// For TIFF we just check for the II/42 or MM/42 in the first 4 bytes and that there are at least -// 26 bytes of data (4+4+2+12+4). -// -// ! The CheckXyzFormat routines don't track the filePos, that is left to ScanXyzFile. - -bool TIFF_CheckFormat ( XMP_FileFormat format, - XMP_StringPtr filePath, - LFA_FileRef fileRef, - XMPFiles * parent ) -{ - IgnoreParam(format); IgnoreParam(filePath); IgnoreParam(parent); - XMP_Assert ( format == kXMP_TIFFFile ); - - enum { kMinimalTIFFSize = 4+4+2+12+4 }; // Header plus IFD with 1 entry. - - IOBuffer ioBuf; - - LFA_Seek ( fileRef, 0, SEEK_SET ); - if ( ! CheckFileSpace ( fileRef, &ioBuf, kMinimalTIFFSize ) ) return false; - - bool leTIFF = CheckBytes ( ioBuf.ptr, "\x49\x49\x2A\x00", 4 ); - bool beTIFF = CheckBytes ( ioBuf.ptr, "\x4D\x4D\x00\x2A", 4 ); - - return (leTIFF | beTIFF); - -} // TIFF_CheckFormat - -// ================================================================================================= -// TIFF_MetaHandlerCTor -// ==================== - -XMPFileHandler * TIFF_MetaHandlerCTor ( XMPFiles * parent ) -{ - return new TIFF_MetaHandler ( parent ); - -} // TIFF_MetaHandlerCTor - -// ================================================================================================= -// TIFF_MetaHandler::TIFF_MetaHandler -// ================================== - -TIFF_MetaHandler::TIFF_MetaHandler ( XMPFiles * _parent ) : psirMgr(0), iptcMgr(0) -{ - this->parent = _parent; - this->handlerFlags = kTIFF_HandlerFlags; - this->stdCharForm = kXMP_Char8Bit; - -} // TIFF_MetaHandler::TIFF_MetaHandler - -// ================================================================================================= -// TIFF_MetaHandler::~TIFF_MetaHandler -// =================================== - -TIFF_MetaHandler::~TIFF_MetaHandler() -{ - - if ( this->psirMgr != 0 ) delete ( this->psirMgr ); - if ( this->iptcMgr != 0 ) delete ( this->iptcMgr ); - -} // TIFF_MetaHandler::~TIFF_MetaHandler - -// ================================================================================================= -// TIFF_MetaHandler::CacheFileData -// =============================== -// -// The data caching for TIFF is easy to explain and implement, but does more processing than one -// might at first expect. This seems unavoidable given the need to close the disk file after calling -// CacheFileData. We parse the TIFF stream and cache the values for all tags of interest, and note -// whether XMP is present. We do not parse the XMP, Photoshop image resources, or IPTC datasets. - -// *** This implementation simply returns when invalid TIFF is encountered. Should we throw instead? - -void TIFF_MetaHandler::CacheFileData() -{ - LFA_FileRef fileRef = this->parent->fileRef; - XMP_PacketInfo & packetInfo = this->packetInfo; - - XMP_AbortProc abortProc = this->parent->abortProc; - void * abortArg = this->parent->abortArg; - const bool checkAbort = (abortProc != 0); - - XMP_Assert ( ! this->containsXMP ); - // Set containsXMP to true here only if the XMP tag is found. - - if ( checkAbort && abortProc(abortArg) ) { - XMP_Throw ( "TIFF_MetaHandler::CacheFileData - User abort", kXMPErr_UserAbort ); - } - - this->tiffMgr.ParseFileStream ( fileRef ); - - TIFF_Manager::TagInfo dngInfo; - if ( this->tiffMgr.GetTag ( kTIFF_PrimaryIFD, kTIFF_DNGVersion, &dngInfo ) ) { - - // Reject DNG files that are version 2.0 or beyond, this is being written at the time of - // DNG version 1.2. The DNG team says it is OK to use 2.0, not strictly 1.2. Use the - // DNGBackwardVersion if it is present, else the DNGVersion. Note that the version value is - // supposed to be type BYTE, so the file order is always essentially big endian. - - XMP_Uns8 majorVersion = *((XMP_Uns8*)dngInfo.dataPtr); // Start with DNGVersion. - if ( this->tiffMgr.GetTag ( kTIFF_PrimaryIFD, kTIFF_DNGBackwardVersion, &dngInfo ) ) { - majorVersion = *((XMP_Uns8*)dngInfo.dataPtr); // Use DNGBackwardVersion if possible. - } - if ( majorVersion > 1 ) XMP_Throw ( "DNG version beyond 1.x", kXMPErr_BadTIFF ); - - } - - TIFF_Manager::TagInfo xmpInfo; - bool found = this->tiffMgr.GetTag ( kTIFF_PrimaryIFD, kTIFF_XMP, &xmpInfo ); - - if ( found ) { - - this->packetInfo.offset = this->tiffMgr.GetValueOffset ( kTIFF_PrimaryIFD, kTIFF_XMP ); - this->packetInfo.length = xmpInfo.dataLen; - this->packetInfo.padSize = 0; // Assume for now, set these properly in ProcessXMP. - this->packetInfo.charForm = kXMP_CharUnknown; - this->packetInfo.writeable = true; - - this->xmpPacket.assign ( (XMP_StringPtr)xmpInfo.dataPtr, xmpInfo.dataLen ); - - this->containsXMP = true; - - } - -} // TIFF_MetaHandler::CacheFileData - -// ================================================================================================= -// TIFF_MetaHandler::ProcessXMP -// ============================ -// -// Process the raw XMP and legacy metadata that was previously cached. The legacy metadata in TIFF -// is messy because there are 2 copies of the IPTC and because of a Photoshop 6 bug/quirk in the way -// Exif metadata is saved. - -void TIFF_MetaHandler::ProcessXMP() -{ - - this->processedXMP = true; // Make sure we only come through here once. - - // Set up everything for the legacy import, but don't do it yet. This lets us do a forced legacy - // import if the XMP packet gets parsing errors. - - // ! Photoshop 6 wrote annoyingly wacky TIFF files. It buried a lot of the Exif metadata inside - // ! image resource 1058, itself inside of tag 34377 in the 0th IFD. Take care of this before - // ! doing any of the legacy metadata presence or priority analysis. Delete image resource 1058 - // ! to get rid of the buried Exif, but don't mark the XMPFiles object as changed. This change - // ! should not trigger an update, but should be included as part of a normal update. - - bool found; - bool readOnly = ((this->parent->openFlags & kXMPFiles_OpenForUpdate) == 0); - - if ( readOnly ) { - this->psirMgr = new PSIR_MemoryReader(); - this->iptcMgr = new IPTC_Reader(); - } else { - this->psirMgr = new PSIR_FileWriter(); - this->iptcMgr = new IPTC_Writer(); // ! Parse it later. - } - - TIFF_Manager & tiff = this->tiffMgr; // Give the compiler help in recognizing non-aliases. - PSIR_Manager & psir = *this->psirMgr; - IPTC_Manager & iptc = *this->iptcMgr; - - TIFF_Manager::TagInfo psirInfo; - bool havePSIR = tiff.GetTag ( kTIFF_PrimaryIFD, kTIFF_PSIR, &psirInfo ); - - if ( havePSIR ) { // ! Do the Photoshop 6 integration before other legacy analysis. - psir.ParseMemoryResources ( psirInfo.dataPtr, psirInfo.dataLen ); - PSIR_Manager::ImgRsrcInfo buriedExif; - found = psir.GetImgRsrc ( kPSIR_Exif, &buriedExif ); - if ( found ) { - tiff.IntegrateFromPShop6 ( buriedExif.dataPtr, buriedExif.dataLen ); - if ( ! readOnly ) psir.DeleteImgRsrc ( kPSIR_Exif ); - } - } - - TIFF_Manager::TagInfo iptcInfo; - bool haveIPTC = tiff.GetTag ( kTIFF_PrimaryIFD, kTIFF_IPTC, &iptcInfo ); // The TIFF IPTC tag. - int iptcDigestState = kDigestMatches; - - if ( haveIPTC ) { - - bool haveDigest = false; - PSIR_Manager::ImgRsrcInfo digestInfo; - if ( havePSIR ) haveDigest = psir.GetImgRsrc ( kPSIR_IPTCDigest, &digestInfo ); - if ( digestInfo.dataLen != 16 ) haveDigest = false; - - if ( ! haveDigest ) { - - iptcDigestState = kDigestMissing; - - } else { - - // Older versions of Photoshop wrote tag 33723 with type LONG, but ignored the trailing - // zero padding for the IPTC digest. If the full digest differs, recheck without the padding. - - iptcDigestState = PhotoDataUtils::CheckIPTCDigest ( iptcInfo.dataPtr, iptcInfo.dataLen, digestInfo.dataPtr ); - - if ( (iptcDigestState == kDigestDiffers) && (kTIFF_TypeSizes[iptcInfo.type] > 1) ) { - XMP_Uns8 * endPtr = (XMP_Uns8*)iptcInfo.dataPtr + iptcInfo.dataLen - 1; - XMP_Uns8 * minPtr = endPtr - kTIFF_TypeSizes[iptcInfo.type] + 1; - while ( (endPtr >= minPtr) && (*endPtr == 0) ) --endPtr; - XMP_Uns32 unpaddedLen = (XMP_Uns32) (endPtr - (XMP_Uns8*)iptcInfo.dataPtr + 1); - iptcDigestState = PhotoDataUtils::CheckIPTCDigest ( iptcInfo.dataPtr, unpaddedLen, digestInfo.dataPtr ); - } - - } - - } - - XMP_OptionBits options = k2XMP_FileHadExif; // TIFF files are presumed to have Exif legacy. - if ( haveIPTC ) options |= k2XMP_FileHadIPTC; - if ( this->containsXMP ) options |= k2XMP_FileHadXMP; - - // Process the XMP packet. If it fails to parse, do a forced legacy import but still throw an - // exception. This tells the caller that an error happened, but gives them recovered legacy - // should they want to proceed with that. - - bool haveXMP = false; - - if ( ! this->xmpPacket.empty() ) { - XMP_Assert ( this->containsXMP ); - // Common code takes care of packetInfo.charForm, .padSize, and .writeable. - XMP_StringPtr packetStr = this->xmpPacket.c_str(); - XMP_StringLen packetLen = (XMP_StringLen)this->xmpPacket.size(); - try { - this->xmpObj.ParseFromBuffer ( packetStr, packetLen ); - haveXMP = true; - } catch ( ... ) { - XMP_ClearOption ( options, k2XMP_FileHadXMP ); - if ( haveIPTC ) iptc.ParseMemoryDataSets ( iptcInfo.dataPtr, iptcInfo.dataLen ); - if ( iptcDigestState == kDigestMatches ) iptcDigestState = kDigestMissing; - ImportPhotoData ( tiff, iptc, psir, iptcDigestState, &this->xmpObj, options ); - throw; // ! Rethrow the exception, don't absorb it. - } - } - - // Process the legacy metadata. - - if ( haveIPTC && (! haveXMP) && (iptcDigestState == kDigestMatches) ) iptcDigestState = kDigestMissing; - bool parseIPTC = (iptcDigestState != kDigestMatches) || (! readOnly); - if ( parseIPTC ) iptc.ParseMemoryDataSets ( iptcInfo.dataPtr, iptcInfo.dataLen ); - ImportPhotoData ( tiff, iptc, psir, iptcDigestState, &this->xmpObj, options ); - - this->containsXMP = true; // Assume we now have something in the XMP. - -} // TIFF_MetaHandler::ProcessXMP - -// ================================================================================================= -// TIFF_MetaHandler::UpdateFile -// ============================ -// -// There is very little to do directly in UpdateFile. ExportXMPtoJTP takes care of setting all of -// the necessary TIFF tags, including things like the 2nd copy of the IPTC in the Photoshop image -// resources in tag 34377. TIFF_FileWriter::UpdateFileStream does all of the update-by-append I/O. - -// *** Need to pass the abort proc and arg to TIFF_FileWriter::UpdateFileStream. - -void TIFF_MetaHandler::UpdateFile ( bool doSafeUpdate ) -{ - XMP_Assert ( ! doSafeUpdate ); // This should only be called for "unsafe" updates. - - LFA_FileRef destRef = this->parent->fileRef; - XMP_AbortProc abortProc = this->parent->abortProc; - void * abortArg = this->parent->abortArg; - - XMP_Int64 oldPacketOffset = this->packetInfo.offset; - XMP_Int32 oldPacketLength = this->packetInfo.length; - - if ( oldPacketOffset == kXMPFiles_UnknownOffset ) oldPacketOffset = 0; // ! Simplify checks. - if ( oldPacketLength == kXMPFiles_UnknownLength ) oldPacketLength = 0; - - bool fileHadXMP = ((oldPacketOffset != 0) && (oldPacketLength != 0)); - - // Update the IPTC-IIM and native TIFF/Exif metadata. ExportPhotoData also trips the tiff: and - // exif: copies from the XMP, so reserialize the now final XMP packet. - - ExportPhotoData ( kXMP_TIFFFile, &this->xmpObj, &this->tiffMgr, this->iptcMgr, this->psirMgr ); - - try { - XMP_OptionBits options = kXMP_UseCompactFormat; - if ( fileHadXMP ) options |= kXMP_ExactPacketLength; - this->xmpObj.SerializeToBuffer ( &this->xmpPacket, options, oldPacketLength ); - } catch ( ... ) { - this->xmpObj.SerializeToBuffer ( &this->xmpPacket, kXMP_UseCompactFormat ); - } - - // Decide whether to do an in-place update. This can only happen if all of the following are true: - // - There is an XMP packet in the file. - // - The are no changes to the legacy tags. (The IPTC and PSIR are in the TIFF tags.) - // - The new XMP can fit in the old space. - - bool doInPlace = (fileHadXMP && (this->xmpPacket.size() <= (size_t)oldPacketLength)); - if ( this->tiffMgr.IsLegacyChanged() ) doInPlace = false; - - if ( ! doInPlace ) { - - #if GatherPerformanceData - sAPIPerf->back().extraInfo += ", TIFF append update"; - #endif - - this->tiffMgr.SetTag ( kTIFF_PrimaryIFD, kTIFF_XMP, kTIFF_UndefinedType, (XMP_Uns32)this->xmpPacket.size(), this->xmpPacket.c_str() ); - this->tiffMgr.UpdateFileStream ( destRef ); - - } else { - - #if GatherPerformanceData - sAPIPerf->back().extraInfo += ", TIFF in-place update"; - #endif - - if ( this->xmpPacket.size() < (size_t)this->packetInfo.length ) { - // They ought to match, cheap to be sure. - size_t extraSpace = (size_t)this->packetInfo.length - this->xmpPacket.size(); - this->xmpPacket.append ( extraSpace, ' ' ); - } - - LFA_FileRef liveFile = this->parent->fileRef; - - XMP_Assert ( this->xmpPacket.size() == (size_t)oldPacketLength ); // ! Done by common PutXMP logic. - - LFA_Seek ( liveFile, oldPacketOffset, SEEK_SET ); - LFA_Write ( liveFile, this->xmpPacket.c_str(), (XMP_Int32)this->xmpPacket.size() ); - - } - - this->needsUpdate = false; - -} // TIFF_MetaHandler::UpdateFile - -// ================================================================================================= -// TIFF_MetaHandler::WriteFile -// =========================== -// -// The structure of TIFF makes it hard to do a sequential source-to-dest copy with interleaved -// updates. So, copy the existing source to the destination and call UpdateFile. - -void TIFF_MetaHandler::WriteFile ( LFA_FileRef sourceRef, const std::string & sourcePath ) -{ - LFA_FileRef destRef = this->parent->fileRef; - XMP_AbortProc abortProc = this->parent->abortProc; - void * abortArg = this->parent->abortArg; - - XMP_Int64 fileLen = LFA_Measure ( sourceRef ); - if ( fileLen > 0xFFFFFFFFLL ) { // Check before making a copy of the file. - XMP_Throw ( "TIFF fles can't exceed 4GB", kXMPErr_BadTIFF ); - } - - LFA_Seek ( sourceRef, 0, SEEK_SET ); - LFA_Truncate ( destRef, 0 ); - LFA_Copy ( sourceRef, destRef, fileLen, abortProc, abortArg ); - - this->UpdateFile ( false ); - -} // TIFF_MetaHandler::WriteFile - -// ================================================================================================= diff --git a/source/XMPFiles/FileHandlers/TIFF_Handler.hpp b/source/XMPFiles/FileHandlers/TIFF_Handler.hpp deleted file mode 100644 index aabb509..0000000 --- a/source/XMPFiles/FileHandlers/TIFF_Handler.hpp +++ /dev/null @@ -1,68 +0,0 @@ -#ifndef __TIFF_Handler_hpp__ -#define __TIFF_Handler_hpp__ 1 - -// ================================================================================================= -// ADOBE SYSTEMS INCORPORATED -// Copyright 2006 Adobe Systems Incorporated -// 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 "TIFF_Support.hpp" -#include "PSIR_Support.hpp" -#include "IPTC_Support.hpp" - -// ================================================================================================= -/// \file TIFF_Handler.hpp -/// \brief File format handler for TIFF. -/// -/// This header ... -/// -// ================================================================================================= - -// *** Could derive from Basic_Handler - buffer file tail in a temp file. - -extern XMPFileHandler * TIFF_MetaHandlerCTor ( XMPFiles * parent ); - -extern bool TIFF_CheckFormat ( XMP_FileFormat format, - XMP_StringPtr filePath, - LFA_FileRef fileRef, - XMPFiles * parent ); - -static const XMP_OptionBits kTIFF_HandlerFlags = (kXMPFiles_CanInjectXMP | - kXMPFiles_CanExpand | - kXMPFiles_CanRewrite | - kXMPFiles_PrefersInPlace | - kXMPFiles_CanReconcile | - kXMPFiles_AllowsOnlyXMP | - kXMPFiles_ReturnsRawPacket | - kXMPFiles_AllowsSafeUpdate); - -class TIFF_MetaHandler : public XMPFileHandler -{ -public: - - void CacheFileData(); - void ProcessXMP(); - - void UpdateFile ( bool doSafeUpdate ); - void WriteFile ( LFA_FileRef sourceRef, const std::string & sourcePath ); - - TIFF_MetaHandler ( XMPFiles * parent ); - virtual ~TIFF_MetaHandler(); - -private: - - TIFF_MetaHandler() : psirMgr(0), iptcMgr(0) {}; // Hidden on purpose. - - TIFF_FileWriter tiffMgr; // The TIFF part is always file-based. - PSIR_Manager * psirMgr; // Need to use pointers so we can properly select between read-only and - IPTC_Manager * iptcMgr; // read-write modes of usage. - -}; // TIFF_MetaHandler - -// ================================================================================================= - -#endif /* __TIFF_Handler_hpp__ */ diff --git a/source/XMPFiles/FileHandlers/Trivial_Handler.cpp b/source/XMPFiles/FileHandlers/Trivial_Handler.cpp deleted file mode 100644 index 3ebdb67..0000000 --- a/source/XMPFiles/FileHandlers/Trivial_Handler.cpp +++ /dev/null @@ -1,66 +0,0 @@ -// ================================================================================================= -// ADOBE SYSTEMS INCORPORATED -// Copyright 2004 Adobe Systems Incorporated -// 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 "Trivial_Handler.hpp" - -using namespace std; - -// ================================================================================================= -/// \file Trivial_Handler.cpp -/// \brief Base class for trivial handlers that only process in-place XMP. -/// -/// This header ... -/// -// ================================================================================================= - -// ================================================================================================= -// Trivial_MetaHandler::~Trivial_MetaHandler -// ========================================= - -Trivial_MetaHandler::~Trivial_MetaHandler() -{ - // Nothing to do. - -} // Trivial_MetaHandler::~Trivial_MetaHandler - -// ================================================================================================= -// Trivial_MetaHandler::UpdateFile -// =============================== - -void Trivial_MetaHandler::UpdateFile ( bool doSafeUpdate ) -{ - IgnoreParam ( doSafeUpdate ); - XMP_Assert ( ! doSafeUpdate ); // Not supported at this level. - if ( ! this->needsUpdate ) return; - - LFA_FileRef fileRef = this->parent->fileRef; - XMP_PacketInfo & packetInfo = this->packetInfo; - std::string & xmpPacket = this->xmpPacket; - - LFA_Seek ( fileRef, packetInfo.offset, SEEK_SET ); - LFA_Write ( fileRef, xmpPacket.c_str(), packetInfo.length ); - XMP_Assert ( xmpPacket.size() == (size_t)packetInfo.length ); - - this->needsUpdate = false; - -} // Trivial_MetaHandler::UpdateFile - -// ================================================================================================= -// Trivial_MetaHandler::WriteFile -// ============================== - -void Trivial_MetaHandler::WriteFile ( LFA_FileRef sourceRef, const std::string & sourcePath ) -{ - IgnoreParam ( sourceRef ); IgnoreParam ( sourcePath ); - - XMP_Throw ( "Trivial_MetaHandler::WriteFile: Not supported", kXMPErr_Unavailable ); - -} // Trivial_MetaHandler::WriteFile - -// ================================================================================================= diff --git a/source/XMPFiles/FileHandlers/Trivial_Handler.hpp b/source/XMPFiles/FileHandlers/Trivial_Handler.hpp deleted file mode 100644 index 9db851c..0000000 --- a/source/XMPFiles/FileHandlers/Trivial_Handler.hpp +++ /dev/null @@ -1,47 +0,0 @@ -#ifndef __Trivial_Handler_hpp__ -#define __Trivial_Handler_hpp__ 1 - -// ================================================================================================= -// ADOBE SYSTEMS INCORPORATED -// Copyright 2004 Adobe Systems Incorporated -// 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 "XMPFiles_Impl.hpp" - -// ================================================================================================= -/// \file Trivial_Handler.hpp -/// \brief Base class for trivial handlers that only process in-place XMP. -/// -/// This header ... -/// -/// \note There is no general promise here about crash-safe I/O. An update to an existing file might -/// have invalid partial state while rewriting existing XMP in-place. Crash-safe updates are managed -/// at a higher level of XMPFiles, using a temporary file and final swap of file content. -/// -// ================================================================================================= - -static const XMP_OptionBits kTrivial_HandlerFlags = ( kXMPFiles_AllowsOnlyXMP | - kXMPFiles_ReturnsRawPacket | - kXMPFiles_AllowsSafeUpdate ); - -class Trivial_MetaHandler : public XMPFileHandler -{ -public: - - Trivial_MetaHandler() {}; - ~Trivial_MetaHandler(); - - virtual void CacheFileData() = 0; - - void UpdateFile ( bool doSafeUpdate ); - void WriteFile ( LFA_FileRef sourceRef, const std::string & sourcePath ); - -}; // Trivial_MetaHandler - -// ================================================================================================= - -#endif /* __Trivial_Handler_hpp__ */ diff --git a/source/XMPFiles/FileHandlers/UCF_Handler.cpp b/source/XMPFiles/FileHandlers/UCF_Handler.cpp deleted file mode 100644 index cea781e..0000000 --- a/source/XMPFiles/FileHandlers/UCF_Handler.cpp +++ /dev/null @@ -1,850 +0,0 @@ -// ================================================================================================= -// ADOBE SYSTEMS INCORPORATED -// Copyright 2007 Adobe Systems Incorporated -// 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. -// =============================================================================================== - -#if XMP_WinBuild - #pragma warning ( disable : 4996 ) // '...' was declared deprecated -#endif - -#include "UCF_Handler.hpp" - -#include "zlib.h" - -#include <time.h> - -#ifdef DYNAMIC_CRC_TABLE - #error "unexpectedly DYNAMIC_CRC_TABLE defined." - //Must implement get_crc_table prior to any multi-threading (see notes there) -#endif - -using namespace std; - -// ================================================================================================= -/// \file UCF_Handler.cpp -/// \brief UCF handler class -// ================================================================================================= -const XMP_Uns16 xmpFilenameLen = 21; -const char* xmpFilename = "META-INF/metadata.xml"; - -// ================================================================================================= -// UCF_MetaHandlerCTor -// ==================== -XMPFileHandler* UCF_MetaHandlerCTor ( XMPFiles * parent ) -{ - return new UCF_MetaHandler ( parent ); -} // UCF_MetaHandlerCTor - -// ================================================================================================= -// UCF_CheckFormat -// ================ -// * lenght must at least be 114 bytes -// * first bytes must be \x50\x4B\x03\x04 for *any* zip file -// * at offset 30 it must spell "mimetype" - -#define MIN_UCF_LENGTH 114 -// zip minimum considerations: -// the shortest legal zip is 100 byte: -// 30+1* bytes file header -//+ 0 byte content file (uncompressed) -//+ 46+1* bytes central directory file header -//+ 22 byte end of central directory record -//------- -//100 bytes -// -//1 byte is the shortest legal filename. anything below is no valid zip. -// -//==> the mandatory+first "mimetype" content file has a filename length of 8 bytes, -// thus even if empty (arguably incorrect but tolerable), -// the shortest legal UCF is 114 bytes (30 + 8 + 0 + 46 + 8 + 22 ) -// anything below is with certainty not a valid ucf. - -bool UCF_CheckFormat ( XMP_FileFormat format, - XMP_StringPtr filePath, - LFA_FileRef fileRef, - XMPFiles * parent ) -{ - // *not* using buffer functionality here, all we need - // to detect UCF securely is in the first 38 bytes... - IgnoreParam(filePath); IgnoreParam(parent); //suppress warnings - XMP_Assert ( format == kXMP_UCFFile ); //standard assert - - XMP_Uns8 buffer[MIN_UCF_LENGTH]; - - LFA_Seek ( fileRef, 0, SEEK_SET ); - if ( MIN_UCF_LENGTH != LFA_Read ( fileRef, buffer, MIN_UCF_LENGTH) ) //NO requireall (->no throw), just return false - return false; - if ( !CheckBytes ( &buffer[0], "\x50\x4B\x03\x04", 4 ) ) // "PK 03 04" - return false; - // UCF spec says: there must be a content file mimetype, and be first and be uncompressed... - if ( !CheckBytes ( &buffer[30], "mimetype", 8 ) ) - return false; - - ////////////////////////////////////////////////////////////////////////////// - //figure out mimetype, decide on writeability - // grab mimetype - LFA_Seek( fileRef, 18, SEEK_SET ); - XMP_Uns32 mimeLength = LFA_ReadUns32_LE ( fileRef ); - XMP_Uns32 mimeCompressedLength = LFA_ReadUns32_LE ( fileRef ); // must be same since uncompressed - - XMP_Validate( mimeLength == mimeCompressedLength, - "mimetype compressed and uncompressed length differ", - kXMPErr_BadFileFormat ); - - XMP_Validate( mimeLength != 0, "0-byte mimetype", kXMPErr_BadFileFormat ); - - // determine writability based on mimetype - LFA_Seek( fileRef, 30 + 8, SEEK_SET ); - char* mimetype = new char[ mimeLength + 1 ]; - LFA_Read( fileRef, mimetype, mimeLength, true ); - mimetype[mimeLength] = '\0'; - - bool okMimetype; - - // be lenient on extraneous CR (0xA) [non-XMP bug #16980028] - if ( mimeLength > 0 ) //avoid potential crash (will properly fail below anyhow) - if ( mimetype[mimeLength-1] == 0xA ) - mimetype[mimeLength-1] = '\0'; - - if ( - XMP_LitMatch( mimetype, "application/vnd.adobe.xfl" ) || //Flash Diesel team - XMP_LitMatch( mimetype, "application/vnd.adobe.xfl+zip") || //Flash Diesel team - XMP_LitMatch( mimetype, "application/vnd.adobe.x-mars" ) || //Mars plugin(labs only), Acrobat8 - XMP_LitMatch( mimetype, "application/vnd.adobe.pdfxml" ) || //Mars plugin(labs only), Acrobat 9 - XMP_LitMatch( mimetype, "vnd.adobe.x-asnd" ) || //Adobe Sound Document (Soundbooth Team) - XMP_LitMatch( mimetype, "application/vnd.adobe.indesign-idml-package" ) || //inCopy (inDesign) IDML Document - XMP_LitMatch( mimetype, "application/vnd.adobe.incopy-package" ) || // InDesign Document - XMP_LitMatch( mimetype, "application/vnd.adobe.indesign-package" ) || // InDesign Document - - false ) // "sentinel" - - // *** ==> unknown are also treated as not acceptable - okMimetype = true; - else - okMimetype = false; - - // not accepted (neither read nor write - //.air - Adobe Air Files - //application/vnd.adobe.air-application-installer-package+zip - //.airi - temporary Adobe Air Files - //application/vnd.adobe.air-application-intermediate-package+zip - - delete [] mimetype; - return okMimetype; - -} // UCF_CheckFormat - -// ================================================================================================= -// UCF_MetaHandler::UCF_MetaHandler -// ================================== - -UCF_MetaHandler::UCF_MetaHandler ( XMPFiles * _parent ) -{ - this->parent = _parent; - this->handlerFlags = kUCF_HandlerFlags; - this->stdCharForm = kXMP_Char8Bit; -} // UCF_MetaHandler::UCF_MetaHandler - -// ================================================================================================= -// UCF_MetaHandler::~UCF_MetaHandler -// ===================================== - -UCF_MetaHandler::~UCF_MetaHandler() -{ - // nothing -} - -// ================================================================================================= -// UCF_MetaHandler::CacheFileData -// =============================== -// -void UCF_MetaHandler::CacheFileData() -{ - //*** abort procedures - this->containsXMP = false; //assume no XMP for now (beware of exceptions...) - LFA_FileRef file = this->parent->fileRef; - XMP_PacketInfo &packetInfo = this->packetInfo; - - // clear file positioning info --------------------------------------------------- - b=0;b2=0;x=0;x2=0;cd=0;cd2=0;cdx=0;cdx2=0;h=0;h2=0,fl=0;f2l=0; - al=0;bl=0;xl=0;x2l=0;cdl=0;cd2l=0;cdxl=0;cdx2l=0;hl=0,z=0,z2=0,z2l=0; - numCF=0;numCF2=0; - wasCompressed = false; - - // ------------------------------------------------------------------------------- - fl=LFA_Measure( file ); - if ( fl < MIN_UCF_LENGTH ) XMP_Throw("file too short, can't be correct UCF",kXMPErr_Unimplemented); - - ////////////////////////////////////////////////////////////////////////////// - // find central directory before optional comment - // things have to go bottom-up, since description headers are allowed in UCF - // "scan backwards" until feasible field found (plus sig sanity check) - // OS buffering should be smart enough, so not doing anything on top - // plus almost all comments will be zero or rather short - - //no need to check anything but the 21 chars of "METADATA-INF/metadata.xml" - char filenameToTest[22]; - filenameToTest[21]='\0'; - - XMP_Int32 zipCommentLen = 0; - for ( ; zipCommentLen <= EndOfDirectory::COMMENT_MAX; zipCommentLen++ ) - { - LFA_Seek( file, -zipCommentLen -2, SEEK_END ); - if ( LFA_ReadUns16_LE( file ) == zipCommentLen ) //found it? - { - //double check, might just look like comment length (actually be 'evil' comment) - LFA_Seek( file , - EndOfDirectory::FIXED_SIZE, SEEK_CUR ); - if ( LFA_ReadUns32_LE( file ) == EndOfDirectory::ID ) break; //heureka, directory ID - // 'else': pretend nothing happended, just go on - } - } - //was it a break or just not found ? - if ( zipCommentLen > EndOfDirectory::COMMENT_MAX ) XMP_Throw( "zip broken near end or invalid comment" , kXMPErr_BadFileFormat ); - - //////////////////////////////////////////////////////////////////////////// - //read central directory - hl = zipCommentLen + EndOfDirectory::FIXED_SIZE; - h = fl - hl; - LFA_Seek( file , h , SEEK_SET ); - - if ( LFA_ReadUns32_LE( file ) != EndOfDirectory::ID ) - XMP_Throw("directory header id not found. or broken comment",kXMPErr_BadFileFormat); - if ( LFA_ReadUns16_LE( file ) != 0 ) - XMP_Throw("UCF must be 'first' zip volume",kXMPErr_BadFileFormat); - if ( LFA_ReadUns16_LE( file ) != 0 ) - XMP_Throw("UCF must be single-volume zip",kXMPErr_BadFileFormat); - - numCF = LFA_ReadUns16_LE( file ); //number of content files - if ( numCF != LFA_ReadUns16_LE( file ) ) - XMP_Throw( "per volume and total number of dirs differ" , kXMPErr_BadFileFormat ); - cdl = LFA_ReadUns32_LE( file ); - cd = LFA_ReadUns32_LE( file ); - LFA_Seek( file, 2,SEEK_CUR ); //skip comment len, needed since next LFA is SEEK_CUR ! - - ////////////////////////////////////////////////////////////////////////////// - // check for zip64-end-of-CD-locator/ zip64-end-of-CD - // to to central directory - if ( cd == 0xffffffff ) - { // deal with zip 64, otherwise continue - XMP_Int64 tmp = LFA_Seek( file, - - EndOfDirectory::FIXED_SIZE - Zip64Locator::TOTAL_SIZE, - SEEK_CUR ); //go to begining of zip64 locator - //relative movement , absolute would imho only require another -zipCommentLen - - if ( Zip64Locator::ID == LFA_ReadUns32_LE(file) ) // prevent 'coincidental length' ffffffff - { - XMP_Validate( 0 == LFA_ReadUns32_LE(file), - "zip64 CD disk must be 0", kXMPErr_BadFileFormat ); - - z = LFA_ReadUns64_LE(file); - XMP_Validate( z < 0xffffffffffffLL, "file in terrabyte range?", kXMPErr_BadFileFormat ); // 3* ffff, sanity test - - XMP_Uns32 totalNumOfDisks = LFA_ReadUns32_LE(file); - /* tolerated while pkglib bug #1742179 */ - XMP_Validate( totalNumOfDisks == 0 || totalNumOfDisks == 1, - "zip64 total num of disks must be 0", kXMPErr_BadFileFormat ); - - /////////////////////////////////////////////// - /// on to end-of-CD itself - LFA_Seek( file, z, SEEK_SET ); - XMP_Validate( Zip64EndOfDirectory::ID == LFA_ReadUns32_LE(file), - "invalid zip64 end of CD sig", kXMPErr_BadFileFormat ); - - XMP_Int64 sizeOfZip64EOD = LFA_ReadUns64_LE(file); - LFA_Seek(file, 12, SEEK_CUR ); - //yes twice "total" and "per disk" - XMP_Int64 tmp64 = LFA_ReadUns64_LE(file); - XMP_Validate( tmp64 == numCF, "num of content files differs to zip64 (1)", kXMPErr_BadFileFormat ); - tmp64 = LFA_ReadUns64_LE(file); - XMP_Validate( tmp64 == numCF, "num of content files differs to zip64 (2)", kXMPErr_BadFileFormat ); - // cd length verification - tmp64 = LFA_ReadUns64_LE(file); - XMP_Validate( tmp64 == cdl, "CD length differs in zip64", kXMPErr_BadFileFormat ); - - cd = LFA_ReadUns64_LE(file); // wipe out invalid 0xffffffff with the real thing - //ignoring "extensible data sector (would need fullLength - fixed length) for now - } - } // of zip64 fork - ///////////////////////////////////////////////////////////////////////////// - // parse central directory - // 'foundXMP' <=> cdx != 0 - - LFA_Seek( file, cd, SEEK_SET ); - XMP_Int64 cdx_suspect=0; - XMP_Int64 cdxl_suspect=0; - CDFileHeader curCDHeader; - - for ( XMP_Uns16 entryNum=1 ; entryNum <= numCF ; entryNum++ ) - { - cdx_suspect = LFA_Tell( file ); //just suspect for now - curCDHeader.read( file ); - - if ( GetUns32LE( &curCDHeader.fields[CDFileHeader::o_sig] ) != 0x02014b50 ) - XMP_Throw("&invalid file header",kXMPErr_BadFileFormat); - - cdxl_suspect = curCDHeader.FIXED_SIZE + - GetUns16LE(&curCDHeader.fields[CDFileHeader::o_fileNameLength]) + - GetUns16LE(&curCDHeader.fields[CDFileHeader::o_extraFieldLength]) + - GetUns16LE(&curCDHeader.fields[CDFileHeader::o_commentLength]); - - // we only look 21 characters, that's META-INF/metadata.xml, no \0 attached - if ( curCDHeader.filenameLen == xmpFilenameLen /*21*/ ) - if( XMP_LitNMatch( curCDHeader.filename , "META-INF/metadata.xml", 21 ) ) - { - cdx = cdx_suspect; - cdxl = cdxl_suspect; - break; - } - //hop to next - LFA_Seek( file, cdx_suspect + cdxl_suspect , SEEK_SET ); - } //for-loop, iterating *all* central directory headers (also beyond found) - - if ( !cdx ) // not found xmp - { - // b and bl remain 0, x and xl remain 0 - // ==> a is everything before directory - al = cd; - return; - } - - // from here is if-found-only - ////////////////////////////////////////////////////////////////////////////// - //CD values needed, most serve counter-validation purposes (below) only - // read whole object (incl. all 3 fields) again properly - // to get extra Fields, etc - LFA_Seek( file, cdx, SEEK_SET ); - xmpCDHeader.read( file ); - - XMP_Validate( xmpFilenameLen == GetUns16LE( &xmpCDHeader.fields[CDFileHeader::o_fileNameLength]), - "content file length not ok", kXMPErr_BadFileFormat ); - - XMP_Uns16 CD_compression = GetUns16LE( &xmpCDHeader.fields[CDFileHeader::o_compression] ); - XMP_Validate(( CD_compression == 0 || CD_compression == 0x08), - "illegal compression, must be flate or none", kXMPErr_BadFileFormat ); - XMP_Uns16 CD_flags = GetUns16LE( &xmpCDHeader.fields[CDFileHeader::o_flags] ); - XMP_Uns32 CD_crc = GetUns32LE( &xmpCDHeader.fields[CDFileHeader::o_crc32] ); - - // parse (actual, non-CD!) file header //////////////////////////////////////////////// - x = xmpCDHeader.offsetLocalHeader; - LFA_Seek( file , x ,SEEK_SET); - xmpFileHeader.read( file ); - xl = xmpFileHeader.sizeHeader() + xmpCDHeader.sizeCompressed; - - //values needed - XMP_Uns16 fileNameLength = GetUns16LE( &xmpFileHeader.fields[FileHeader::o_fileNameLength] ); - XMP_Uns16 extraFieldLength = GetUns16LE( &xmpFileHeader.fields[FileHeader::o_extraFieldLength] ); - XMP_Uns16 compression = GetUns16LE( &xmpFileHeader.fields[FileHeader::o_compression] ); - XMP_Uns32 sig = GetUns32LE( &xmpFileHeader.fields[FileHeader::o_sig] ); - XMP_Uns16 flags = GetUns16LE( &xmpFileHeader.fields[FileHeader::o_flags] ); - XMP_Uns32 sizeCompressed = GetUns32LE( &xmpFileHeader.fields[FileHeader::o_sizeCompressed] ); - XMP_Uns32 sizeUncompressed = GetUns32LE( &xmpFileHeader.fields[FileHeader::o_sizeUncompressed] ); - XMP_Uns32 crc = GetUns32LE( &xmpFileHeader.fields[FileHeader::o_crc32] ); - - // check filename - XMP_Validate( fileNameLength == 21, "filename size contradiction" , kXMPErr_BadFileFormat ); - XMP_Enforce ( xmpFileHeader.filename != 0 ); - XMP_Validate( !memcmp( "META-INF/metadata.xml", xmpFileHeader.filename , xmpFilenameLen ) , "filename is cf header is not META-INF/metadata.xml" , kXMPErr_BadFileFormat ); - - // deal with data descriptor if needed - if ( flags & FileHeader::kdataDescriptorFlag ) - { - if ( sizeCompressed!=0 || sizeUncompressed!=0 || crc!=0 ) XMP_Throw("data descriptor must mean 3x zero",kXMPErr_BadFileFormat); - LFA_Seek( file, xmpCDHeader.sizeCompressed + fileNameLength + xmpCDHeader.extraFieldLen, SEEK_CUR); //skip actual data to get to descriptor - crc = LFA_ReadUns32_LE( file ); - if ( crc == 0x08074b50 ) //data descriptor may or may not have signature (see spec) - { - crc = LFA_ReadUns32_LE( file ); //if it does, re-read - } - sizeCompressed = LFA_ReadUns32_LE( file ); - sizeUncompressed = LFA_ReadUns32_LE( file ); - // *** cater for zip64 plus 'streamed' data-descriptor stuff - } - - // more integrity checks (post data descriptor handling) - if ( sig != 0x04034b50 ) XMP_Throw("invalid content file header",kXMPErr_BadFileFormat); - if ( compression != CD_compression ) XMP_Throw("compression contradiction",kXMPErr_BadFileFormat); - if ( sizeUncompressed != xmpCDHeader.sizeUncompressed ) XMP_Throw("contradicting uncompressed lengths",kXMPErr_BadFileFormat); - if ( sizeCompressed != xmpCDHeader.sizeCompressed ) XMP_Throw("contradicting compressed lengths",kXMPErr_BadFileFormat); - if ( sizeUncompressed == 0 ) XMP_Throw("0-byte uncompressed size", kXMPErr_BadFileFormat ); - - //////////////////////////////////////////////////////////////////// - // packet Info - this->packetInfo.charForm = stdCharForm; - this->packetInfo.writeable = false; - this->packetInfo.offset = kXMPFiles_UnknownOffset; // checksum!, hide position to not give funny ideas - this->packetInfo.length = kXMPFiles_UnknownLength; - - //////////////////////////////////////////////////////////////////// - // prepare packet (compressed or not) - this->xmpPacket.erase(); - this->xmpPacket.reserve( sizeUncompressed ); - this->xmpPacket.append( sizeUncompressed, ' ' ); - XMP_StringPtr packetStr = XMP_StringPtr ( xmpPacket.c_str() ); // only set after reserving the space! - - // go to packet offset - LFA_Seek ( file, x + xmpFileHeader.FIXED_SIZE + fileNameLength + extraFieldLength , SEEK_SET); - - // compression fork -------------------------------------------------- - switch (compression) - { - case 0x8: // FLATE - { - wasCompressed = true; - XMP_Uns32 bytesRead = 0; - XMP_Uns32 bytesWritten = 0; // for writing into packetString - const unsigned int CHUNK = 16384; - - int ret; - unsigned int have; //added type - z_stream strm; - unsigned char in[CHUNK]; - unsigned char out[CHUNK]; - // does need this intermediate stage, no direct compressio to packetStr possible, - // since also partially filled buffers must be picked up. That's how it works. - // in addition: internal zlib variables might have 16 bit limits... - - /* allocate inflate state */ - strm.zalloc = Z_NULL; - strm.zfree = Z_NULL; - strm.opaque = Z_NULL; - strm.avail_in = 0; - strm.next_in = Z_NULL; - - /* must use windowBits = -15, for raw inflate, no zlib header */ - ret = inflateInit2(&strm,-MAX_WBITS); - - if (ret != Z_OK) - XMP_Throw("zlib error ",kXMPErr_ExternalFailure); - - /* decompress until deflate stream ends or end of file */ - do { - // must take care here not to read too much, thus whichever is smaller: - XMP_Int32 bytesRemaining = sizeCompressed - bytesRead; - if ( (XMP_Int32)CHUNK < bytesRemaining ) bytesRemaining = (XMP_Int32)CHUNK; - strm.avail_in=LFA_Read( file , in , bytesRemaining , true ); - bytesRead += strm.avail_in; // NB: avail_in is "unsigned_int", so might be 16 bit (not harmfull) - - if (strm.avail_in == 0) break; - strm.next_in = in; - - 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: - (void)inflateEnd(&strm); - XMP_Throw("zlib error: Z_NEED_DICT",kXMPErr_ExternalFailure); - case Z_DATA_ERROR: - (void)inflateEnd(&strm); - XMP_Throw("zlib error: Z_DATA_ERROR",kXMPErr_ExternalFailure); - case Z_MEM_ERROR: - (void)inflateEnd(&strm); - XMP_Throw("zlib error: Z_MEM_ERROR",kXMPErr_ExternalFailure); - } - - have = CHUNK - strm.avail_out; - memcpy( (unsigned char*) packetStr + bytesWritten , out , have ); - bytesWritten += have; - - } while (strm.avail_out == 0); - - /* it's done when inflate() says it's done */ - } while (ret != Z_STREAM_END); - - /* clean up and return */ - (void)inflateEnd(&strm); - if (ret != Z_STREAM_END) - XMP_Throw("zlib error ",kXMPErr_ExternalFailure); - break; - } - case 0x0: // no compression - read directly into the right place - { - wasCompressed = false; - XMP_Enforce( LFA_Read ( file, (char*)packetStr, sizeUncompressed, kLFA_RequireAll ) ); - break; - } - default: - { - XMP_Throw("illegal zip compression method (not none, not flate)",kXMPErr_BadFileFormat); - } - } - this->containsXMP = true; // do this last, after all possible failure/execptions -} - - -// ================================================================================================= -// UCF_MetaHandler::ProcessXMP -// ============================ - -void UCF_MetaHandler::ProcessXMP() -{ - // we have no legacy, CacheFileData did all that was needed - // ==> default implementation is fine - XMPFileHandler::ProcessXMP(); -} - -// ================================================================================================= -// UCF_MetaHandler::UpdateFile -// ============================= - -// TODO: xmp packet with data descriptor - -void UCF_MetaHandler::UpdateFile ( bool doSafeUpdate ) -{ - //sanity - XMP_Enforce( (x!=0) == (cdx!=0) ); - if (!cdx) - xmpCDHeader.setXMPFilename(); //if new, set filename (impacts length, thus before computation) - if ( ! this->needsUpdate ) - return; - - // *** - if ( doSafeUpdate ) XMP_Throw ( "UCF_MetaHandler::UpdateFile: Safe update not supported", kXMPErr_Unavailable ); - - LFA_FileRef file = this->parent->fileRef; - - // final may mean compressed or not, whatever is to-be-embedded - uncomprPacketStr = xmpPacket.c_str(); - uncomprPacketLen = (XMP_StringLen) xmpPacket.size(); - finalPacketStr = uncomprPacketStr; // will be overriden if compressedXMP==true - finalPacketLen = uncomprPacketLen; - std::string compressedPacket; // moot if non-compressed, still here for scope reasons (having to keep a .c_str() alive) - - if ( !x ) // if new XMP... - { - xmpFileHeader.clear(); - xmpFileHeader.setXMPFilename(); - // ZIP64 TODO: extra Fields, impact on cdxl2 and x2l - } - - //////////////////////////////////////////////////////////////////////////////////////////////// - // COMPRESSION DECISION - - // for large files compression is bad: - // a) size of XMP becomes irrelevant on large files ==> why worry over compression ? - // b) more importantly: no such thing as padding possible, compression == ever changing sizes - // => never in-place rewrites, *ugly* performance impact on large files - inPlacePossible = false; //assume for now - - if ( !x ) // no prior XMP? -> decide on filesize - compressXMP = ( fl > 1024*50 /* 100 kB */ ) ? false : true; - else - compressXMP = wasCompressed; // don't change a thing - - if ( !wasCompressed && !compressXMP && - ( GetUns32LE( &xmpFileHeader.fields[FileHeader::o_sizeUncompressed] ) == uncomprPacketLen )) - { - inPlacePossible = true; - } - //////////////////////////////////////////////////////////////////////////////////////////////// - // COMPRESS XMP - if ( compressXMP ) - { - const unsigned int CHUNK = 16384; - int ret, flush; - unsigned int have; - z_stream strm; - unsigned char out[CHUNK]; - - /* allocate deflate state */ - strm.zalloc = Z_NULL; strm.zfree = Z_NULL; strm.opaque = Z_NULL; - if ( deflateInit2(&strm, Z_DEFAULT_COMPRESSION, Z_DEFLATED, -MAX_WBITS, 8 /*memlevel*/, Z_DEFAULT_STRATEGY) ) - XMP_Throw("zlib error ",kXMPErr_ExternalFailure); - - //write at once, since we got it in mem anyway: - strm.avail_in = uncomprPacketLen; - flush = Z_FINISH; // that's all, folks - strm.next_in = (unsigned char*) uncomprPacketStr; - - do { - strm.avail_out = CHUNK; - strm.next_out = out; - - ret = deflate(&strm, flush); /* no bad return value (!=0 acceptable) */ - XMP_Enforce(ret != Z_STREAM_ERROR); /* state not clobbered */ - //fwrite(buffer,size,count,file) - have = CHUNK - strm.avail_out; - compressedPacket.append( (const char*) out, have); - } while (strm.avail_out == 0); - - if (ret != Z_STREAM_END) - XMP_Throw("zlib stream incomplete ",kXMPErr_ExternalFailure); - XMP_Enforce(strm.avail_in == 0); // all input will be used - (void)deflateEnd(&strm); //clean up (do prior to checks) - - finalPacketStr = compressedPacket.c_str(); - finalPacketLen = (XMP_StringLen)compressedPacket.size(); - } - - PutUns32LE ( uncomprPacketLen, &xmpFileHeader.fields[FileHeader::o_sizeUncompressed] ); - PutUns32LE ( finalPacketLen, &xmpFileHeader.fields[FileHeader::o_sizeCompressed] ); - PutUns16LE ( compressXMP ? 8:0, &xmpFileHeader.fields[FileHeader::o_compression] ); - - //////////////////////////////////////////////////////////////////////////////////////////////// - // CRC (always of uncompressed data) - XMP_Uns32 crc = crc32( 0 , (Bytef*)uncomprPacketStr, uncomprPacketLen ); - PutUns32LE( crc, &xmpFileHeader.fields[FileHeader::o_crc32] ); - - //////////////////////////////////////////////////////////////////////////////////////////////// - // TIME calculation for timestamp - // will be applied both to xmp content file and CD header - XMP_Uns16 lastModTime, lastModDate; - XMP_DateTime time; - SXMPUtils::CurrentDateTime( &time ); - - if ( (time.year - 1900) < 80) - { - lastModTime = 0; // 1.1.1980 00:00h - lastModDate = 21; - } - - // typedef unsigned short ush; //2 bytes - lastModDate = (XMP_Uns16) (((time.year) - 1980 ) << 9 | ((time.month) << 5) | time.day); - lastModTime = ((XMP_Uns16)time.hour << 11) | ((XMP_Uns16)time.minute << 5) | ((XMP_Uns16)time.second >> 1); - - PutUns16LE ( lastModDate, &xmpFileHeader.fields[FileHeader::o_lastmodDate] ); - PutUns16LE ( lastModTime, &xmpFileHeader.fields[FileHeader::o_lastmodTime] ); - - //////////////////////////////////////////////////////////////////////////////////////////////// - // adjustments depending on 4GB Border, - // decisions on in-place update - // so far only z, zl have been determined - - // Zip64 related assurances, see (15) - XMP_Enforce(!z2); - XMP_Enforce(h+hl == fl ); - - //////////////////////////////////////////////////////////////////////////////////////////////// - // COMPUTE MISSING VARIABLES - // A - based on xmp existence - // - // already known: x, xl, cd - // most left side vars, - // - // finalPacketStr, finalPacketLen - - if ( x ) // previous xmp? - { - al = x; - b = x + xl; - bl = cd - b; - } - else - { - al = cd; - //b,bl left at zero - } - - if ( inPlacePossible ) - { // leave xmp right after A - x2 = al; - x2l = xmpFileHeader.sizeTotalCF(); //COULDDO: assert (x2l == xl) - if (b) b2 = x2 + x2l; // b follows x as last content part - cd2 = b2 + bl; // CD follows B2 - } - else - { // move xmp to end - if (b) b2 = al; // b follows - // x follows as last content part (B existing or not) - x2 = al + bl; - x2l = xmpFileHeader.sizeTotalCF(); - cd2 = x2 + x2l; // CD follows X - } - - /// create new XMP header /////////////////////////////////////////////////// - // written into actual fields + generation of extraField at .write()-time... - // however has impact on .size() computation -- thus enter before cdx2l computation - xmpCDHeader.sizeUncompressed = uncomprPacketLen; - xmpCDHeader.sizeCompressed = finalPacketLen; - xmpCDHeader.offsetLocalHeader = x2; - PutUns32LE ( crc, &xmpCDHeader.fields[CDFileHeader::o_crc32] ); - PutUns16LE ( compressXMP ? 8:0, &xmpCDHeader.fields[CDFileHeader::o_compression] ); - PutUns16LE ( lastModDate, &xmpCDHeader.fields[CDFileHeader::o_lastmodDate] ); - PutUns16LE ( lastModTime, &xmpCDHeader.fields[CDFileHeader::o_lastmodTime] ); - - // for - if ( inPlacePossible ) - { - cdx2 = cdx; //same, same - writeOut( file, file, false, true ); - return; - } - - //////////////////////////////////////////////////////////////////////// - // temporarily store (those few, small) trailing things that might not survive the move around: - LFA_Seek(file, cd, SEEK_SET); // seek to central directory - cdEntries.clear(); //mac precaution - - ////////////////////////////////////////////////////////////////////////////// - // parse headers - // * stick together output header list - cd2l = 0; //sum up below - - CDFileHeader tempHeader; - for( XMP_Uns16 pos=1 ; pos <= numCF ; pos++ ) - { - if ( (cdx) && (LFA_Tell( file ) == cdx) ) - { - tempHeader.read( file ); //read, even if not use, to advance file pointer - } - else - { - tempHeader.read( file ); - // adjust b2 offset for files that were behind the xmp: - // may (if xmp moved to back) - // or may not (inPlace Update) make a difference - if ( (x) && ( tempHeader.offsetLocalHeader > x) ) // if xmp existed before and this was a file behind it - tempHeader.offsetLocalHeader += b2 - b; - cd2l += tempHeader.size(); // prior offset change might have impact - cdEntries.push_back( tempHeader ); - } - } - - //push in XMP packet as last one (new or not) - cdEntries.push_back( xmpCDHeader ); - cdx2l = xmpCDHeader.size(); - cd2l += cdx2l; // true, no matter which order - - //OLD cd2l = : cdl - cdxl + cdx2l; // (NB: cdxl might be 0) - numCF2 = numCF + ( (cdx)?0:1 ); //xmp packet for the first time? -> add one more CF - - XMP_Validate( numCF2 > 0, "no content files", kXMPErr_BadFileFormat ); - XMP_Validate( numCF2 <= 0xFFFE, "max number of 0xFFFE entries reached", kXMPErr_BadFileFormat ); - - cdx2 = cd2 + cd2l - cdx2l; // xmp content entry comes last (since beyond inPlace Update) - - // zip64 decision - if ( ( cd2 + cd2l + hl ) > 0xffffffff ) // predict non-zip size ==> do we need a zip-64? - { - z2 = cd2 + cd2l; - z2l = Zip64EndOfDirectory::FIXED_SIZE + Zip64Locator::TOTAL_SIZE; - } - - // header and output length, - h2 = cd2 + cd2l + z2l; // (z2l might be 0) - f2l = h2 + hl; - - //////////////////////////////////////////////////////////////////////////////////////////////// - // read H (endOfCD), correct offset - LFA_Seek(file, h, SEEK_SET); - - endOfCD.read( file ); - if ( cd2 <= 0xffffffff ) - PutUns32LE( (XMP_Int32) cd2 , &endOfCD.fields[ endOfCD.o_CdOffset ] ); - else - PutUns32LE( 0xffffffff , &endOfCD.fields[ endOfCD.o_CdOffset ] ); - PutUns16LE( numCF2, &endOfCD.fields[ endOfCD.o_CdNumEntriesDisk ] ); - PutUns16LE( numCF2, &endOfCD.fields[ endOfCD.o_CdNumEntriesTotal ] ); - - XMP_Enforce( cd2l <= 0xffffffff ); // _size_ of directory itself certainly under 4GB - PutUns32LE( (XMP_Uns32)cd2l, &endOfCD.fields[ endOfCD.o_CdSize ] ); - - //////////////////////////////////////////////////////////////////////////////////////////////// - // MOVING - writeOut( file, file, false, false ); - - this->needsUpdate = false; //do last for safety reasons -} // UCF_MetaHandler::UpdateFile - -// ================================================================================================= -// UCF_MetaHandler::WriteFile -// ============================ -void UCF_MetaHandler::WriteFile ( LFA_FileRef sourceRef, const std::string & sourcePath ) -{ - IgnoreParam ( sourcePath ); - XMP_Throw ( "UCF_MetaHandler::WriteFile: TO BE IMPLEMENTED", kXMPErr_Unimplemented ); -} - -// ================================================================================================= -// own approach to unify Update and WriteFile: -// ============================ - -void UCF_MetaHandler::writeOut( LFA_FileRef sourceFile, LFA_FileRef targetFile, bool isRewrite, bool isInPlace) -{ - // isInPlace is only possible when it's not a complete rewrite - XMP_Enforce( (!isInPlace) || (!isRewrite) ); - - ///////////////////////////////////////////////////////// - // A - if (isRewrite) //move over A block - LFA_Move( sourceFile , 0 , targetFile, 0 , al ); - - ///////////////////////////////////////////////////////// - // B / X (not necessarily in this order) - if ( !isInPlace ) // B does not change a thing (important optimization) - { - LFA_Seek( targetFile , b2 , SEEK_SET ); - LFA_Move( sourceFile , b , targetFile, b2 , bl ); - } - - LFA_Seek( targetFile , x2 , SEEK_SET ); - xmpFileHeader.write( targetFile ); - LFA_Write( targetFile, finalPacketStr, finalPacketLen ); - //TODO: cover reverse case / inplace ... - - ///////////////////////////////////////////////////////// - // CD - // No Seek here on purpose. - // This assert must still be valid - - // if inPlace, the only thing that needs still correction is the CRC in CDX: - if ( isInPlace ) - { - XMP_Uns32 crc; //TEMP, not actually needed - crc = GetUns32LE( &xmpFileHeader.fields[FileHeader::o_crc32] ); - - // go there, - // do the job (take value directly from (non-CD-)fileheader), - // end of story. - LFA_Seek( targetFile , cdx2 + CDFileHeader::o_crc32 , SEEK_SET ); - LFA_Write( targetFile, &xmpFileHeader.fields[FileHeader::o_crc32], 4); - - return; - } - - LFA_Seek( targetFile , cd2 , SEEK_SET ); - - std::vector<CDFileHeader>::iterator iter; - int tmptmp=1; - for( iter = cdEntries.begin(); iter != cdEntries.end(); iter++ ) { - CDFileHeader* p=&(*iter); - XMP_Int64 before = LFA_Tell(targetFile); - p->write( targetFile ); - XMP_Int64 total = LFA_Tell(targetFile) - before; - XMP_Int64 tmpSize = p->size(); - tmptmp++; - } - - ///////////////////////////////////////////////////////// - // Z - if ( z2 ) // yes, that simple - { - XMP_Assert( z2 == LFA_Tell(targetFile)); - LFA_Seek( targetFile , z2 , SEEK_SET ); - - //no use in copying, always construct from scratch - Zip64EndOfDirectory zip64EndOfDirectory( cd2, cd2l, numCF2) ; - Zip64Locator zip64Locator( z2 ); - - zip64EndOfDirectory.write( targetFile ); - zip64Locator.write( targetFile ); - } - - ///////////////////////////////////////////////////////// - // H - XMP_Assert( h2 == LFA_Tell(targetFile)); - endOfCD.write( targetFile ); - - XMP_Assert( f2l == LFA_Tell(targetFile)); - if ( f2l< fl) - LFA_Truncate(targetFile,f2l); //file may have shrunk -} - - diff --git a/source/XMPFiles/FileHandlers/UCF_Handler.hpp b/source/XMPFiles/FileHandlers/UCF_Handler.hpp deleted file mode 100644 index 2f9af5e..0000000 --- a/source/XMPFiles/FileHandlers/UCF_Handler.hpp +++ /dev/null @@ -1,716 +0,0 @@ -#ifndef __UCF_Handler_hpp__ -#define __UCF_Handler_hpp__ 1 - -// ================================================================================================= -// ADOBE SYSTEMS INCORPORATED -// Copyright 2007 Adobe Systems Incorporated -// 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 "XMPFiles_Impl.hpp" - -// ================================================================================================= -/// \file UCF_Handler.hpp -// -// underlying math: -// __ 0 ______ 0 ______ __ -// | A | | A | -// | | | | -// al | | (a2l)| | -// x |------| b2 |------| -// xl | X | | B |_ -// b |------| (b2l)| | | -// | B | x2 |------| | B2 could also be -// bl | | x2l | X2 | | _after_ X2 -// cd |------| cd2 |------|<' -// |//CD//| |//CD2/| -// cdx |------| cdx2 |------| -// cdxl|------| cdx2l|------| -// cdl|//////| cd2l|//////| -// z |------| z2|------| -// | | | | -// [zl]| Z | z2l | Z2 | -// h |------| h2 |------| -// fl | H | | H2 | f2l -// __ hl |______| (h2l)|______| __ -// -// fl file length pre (2 = post) -// numCf number of content files prior to injection -// numCf2 " post -// -// -// l length variable, all else offset -// [ ] variable is not needed -// ( ) variable is identical to left -// a content files prior to xmp (possibly: all) -// b content files behind xmp (possibly: 0) -// x xmp packet (possibly: 0) -// cd central directory -// h end of central directory -// -// z zip64 record and locator (if existing) -// -// general rules: -// the bigger A, the less rewrite effort. -// (also within the CD) -// putting XMP at the end maximizes A. -// -// bool previousXMP == x!=0 -// -// (x==0) == (cdx==0) == (xl==0) == (cdxl==0) -// -// std::vector<XMP_Uns32> cdOffsetsPre; -// -// ----------------- -// asserts: -//( 1) a == a2 == 0, making these variables obsolete -//( 2) a2l == al, this block is not touched -//( 3) b2 <= b, b is only moved closer to the beginning of file -//( 4) b2l == bl, b does not change in size -//( 5) x2 >= x, b is only moved further down in the file -// -//( 6) x != 0, x2l != 0, cd != 0, cdl != 0 -// none of these blocks is at the beginning ('mimetype' by spec), -// nor is any of them zero byte long -//( 7) h!=0, hl >= 22 header is not at the beginning, minimum size 22 -// -// file size computation: -//( 8) al + bl + xl +cdl +hl = fl -//( 9) al + bl + x2l+cd2l+hl = fl2 -// -//(10) ( x==0 ) <=> ( cdx == 0 ) -// if there's a packet in the pre-file, or there isn't -//(11) (x==0) => xl=0 -//(12) (cdx==0)=> cdx=0 -// -//(13) x==0 ==> b,bl,b2,b2l==0 -// if there is no pre-xmp, B does not exist -//(14) x!=0 ==> al:=x, b:=x+xl, bl:=cd-b -// -// zip 64: -//(15) zl and z2l are basically equal, except _one_ of them is 0 : -// -//(16) b2l is indeed never different t -// -// FIXED_SIZE means the fixed (minimal) portion of a struct -// TOTAL_SIZE indicates, that this struct indeed has a fixed, known total length -// -// ================================================================================================= - -extern XMPFileHandler* UCF_MetaHandlerCTor ( XMPFiles * parent ); - -extern bool UCF_CheckFormat ( XMP_FileFormat format, - XMP_StringPtr filePath, - LFA_FileRef fileRef, - XMPFiles * parent ); - -static const XMP_OptionBits kUCF_HandlerFlags = ( - kXMPFiles_CanInjectXMP | - kXMPFiles_CanExpand | - kXMPFiles_CanRewrite | - /* kXMPFiles_PrefersInPlace | removed, only reasonable for formats where difference is significant */ - kXMPFiles_AllowsOnlyXMP | - kXMPFiles_ReturnsRawPacket | - // *** kXMPFiles_AllowsSafeUpdate | - kXMPFiles_NeedsReadOnlyPacket //UCF/zip has checksums... - ); - -enum { // data descriptor - // may or may not have a signature: 0x08074b50 - kUCF_DD_crc32 = 0, - kUCF_DD_sizeCompressed = 4, - kUCF_DD_sizeUncompressed = 8, -}; - -class UCF_MetaHandler : public XMPFileHandler -{ -public: - UCF_MetaHandler ( XMPFiles * _parent ); - ~UCF_MetaHandler(); - - void CacheFileData(); - void ProcessXMP(); - - void UpdateFile ( bool doSafeUpdate ); - void WriteFile ( LFA_FileRef sourceRef, const std::string & sourcePath ); - -protected: - const static XMP_Uns16 xmpFilenameLen = 21; - const static char* xmpFilename; - -private: - class Zip64EndOfDirectory { - private: - - public: - const static XMP_Uns16 o_sig = 0; // 0x06064b50 - const static XMP_Uns16 o_size = 4; // of this, excluding leading 12 bytes - // == FIXED_SIZE -12, since we're never creating the extensible data sector... - const static XMP_Uns16 o_VersionMade = 12; - const static XMP_Uns16 o_VersionNeededExtr = 14; - const static XMP_Uns16 o_numDisk = 16; // force 0 - const static XMP_Uns16 o_numCDDisk = 20; // force 0 - const static XMP_Uns16 o_numCFsThisDisk = 24; - const static XMP_Uns16 o_numCFsTotal = 32; // force equal - const static XMP_Uns16 o_sizeOfCD = 40; // (regular one, not Z64) - const static XMP_Uns16 o_offsetCD = 48; // " - - const static XMP_Int32 FIXED_SIZE = 56; - char fields[FIXED_SIZE]; - - const static XMP_Uns32 ID = 0x06064b50; - - Zip64EndOfDirectory( XMP_Int64 offsetCD, XMP_Int64 sizeOfCD, XMP_Uns64 numCFs ) - { - memset(fields,'\0',FIXED_SIZE); - - PutUns32LE(ID ,&fields[o_sig] ); - PutUns64LE(FIXED_SIZE - 12, &fields[o_size] ); //see above - PutUns16LE( 45 ,&fields[o_VersionMade] ); - PutUns16LE( 45 ,&fields[o_VersionNeededExtr] ); - // fine at 0: o_numDisk - // fine at 0: o_numCDDisk - PutUns64LE( numCFs, &fields[o_numCFsThisDisk] ); - PutUns64LE( numCFs, &fields[o_numCFsTotal] ); - PutUns64LE( sizeOfCD, &fields[o_sizeOfCD] ); - PutUns64LE( offsetCD, &fields[o_offsetCD] ); - } - - void write(LFA_FileRef file) - { - XMP_Validate( ID == GetUns32LE( &this->fields[o_sig] ), "invalid header on write", kXMPErr_BadFileFormat ); - LFA_Write( file , fields , FIXED_SIZE ); - } - - }; - - class Zip64Locator { - public: - const static XMP_Uns16 o_sig = 0; // 0x07064b50 - const static XMP_Uns16 o_numDiskZ64CD = 4; // force 0 - const static XMP_Uns16 o_offsZ64EOD = 8; - const static XMP_Uns16 o_numDisks = 16; // set 1, tolerate 0 - - const static XMP_Int32 TOTAL_SIZE = 20; - char fields[TOTAL_SIZE]; - - const static XMP_Uns32 ID = 0x07064b50; - - Zip64Locator( XMP_Int64 offsetZ64EOD ) - { - memset(fields,'\0',TOTAL_SIZE); - PutUns32LE(ID, &fields[Zip64Locator::o_sig] ); - PutUns32LE(0, &fields[Zip64Locator::o_numDiskZ64CD] ); - PutUns64LE(offsetZ64EOD, &fields[Zip64Locator::o_offsZ64EOD] ); - PutUns32LE(1, &fields[Zip64Locator::o_numDisks] ); - } - - // writes structure to file (starting at current position) - void write(LFA_FileRef file) - { - XMP_Validate( ID == GetUns32LE( &this->fields[o_sig] ), "invalid header on write", kXMPErr_BadFileFormat ); - LFA_Write( file , fields , TOTAL_SIZE ); - } - }; - - struct EndOfDirectory { - public: - const static XMP_Int32 FIXED_SIZE = 22; //32 bit type is important to not overrun on maxcomment - const static XMP_Uns32 ID = 0x06054b50; - const static XMP_Int32 COMMENT_MAX = 0xFFFF; - //offsets - const static XMP_Int32 o_CentralDirectorySize = 12; - const static XMP_Int32 o_CentralDirectoryOffset = 16; - }; - - class FileHeader { - private: - //TODO intergrate in clear() - void release() // avoid terminus free() since subject to a #define (mem-leak-check) - { - if (filename) delete filename; - if (extraField) delete extraField; - filename=0; - extraField=0; - } - - public: - const static XMP_Uns32 SIG = 0x04034b50; - const static XMP_Uns16 kdataDescriptorFlag = 0x8; - - const static XMP_Uns16 o_sig = 0; - const static XMP_Uns16 o_extractVersion = 4; - const static XMP_Uns16 o_flags = 6; - const static XMP_Uns16 o_compression = 8; - const static XMP_Uns16 o_lastmodTime = 10; - const static XMP_Uns16 o_lastmodDate = 12; - const static XMP_Uns16 o_crc32 = 14; - const static XMP_Uns16 o_sizeCompressed = 18; - const static XMP_Uns16 o_sizeUncompressed = 22; - const static XMP_Uns16 o_fileNameLength = 26; - const static XMP_Uns16 o_extraFieldLength = 28; - // total 30 - - const static int FIXED_SIZE = 30; - char fields[FIXED_SIZE]; - - char* filename; - char* extraField; - XMP_Uns16 filenameLen; - XMP_Uns16 extraFieldLen; - - void clear() - { - this->release(); - memset(fields,'\0',FIXED_SIZE); - //arm with minimal default values: - PutUns32LE(0x04034b50, &fields[FileHeader::o_sig] ); - PutUns16LE(0x14, &fields[FileHeader::o_extractVersion] ); - } - - FileHeader() : filename(0),filenameLen(0),extraField(0),extraFieldLen(0) - { - clear(); - }; - - // reads entire *FileHeader* structure from file (starting at current position) - void read(LFA_FileRef file) - { - this->release(); - - LFA_Read( file , fields , FIXED_SIZE , true ); - - XMP_Uns32 tmp32 = GetUns32LE( &this->fields[FileHeader::o_sig] ); - XMP_Validate( SIG == tmp32, "invalid header", kXMPErr_BadFileFormat ); - filenameLen = GetUns16LE( &this->fields[FileHeader::o_fileNameLength] ); - extraFieldLen = GetUns16LE( &this->fields[FileHeader::o_extraFieldLength] ); - - // nb unlike the CDFileHeader the FileHeader will in practice never have - // extra fields. Reasoning: File headers never carry (their own) offsets, - // (un)compressed size of XMP will hardly ever reach 4 GB - - if (filenameLen) { - filename = new char[filenameLen]; - LFA_Read(file,filename,filenameLen,true); - } - if (extraFieldLen) { - extraField = new char[extraFieldLen]; - LFA_Read(file,extraField,extraFieldLen,true); - // *** NB: this WOULD need parsing for content files that are - // compressed or uncompressed >4GB (VERY unlikely for XMP) - } - } - - // writes structure to file (starting at current position) - void write(LFA_FileRef file) - { - XMP_Validate( SIG == GetUns32LE( &this->fields[FileHeader::o_sig] ), "invalid header on write", kXMPErr_BadFileFormat ); - - filenameLen = GetUns16LE( &this->fields[FileHeader::o_fileNameLength] ); - extraFieldLen = GetUns16LE( &this->fields[FileHeader::o_extraFieldLength] ); - - LFA_Write( file , fields , FIXED_SIZE ); - if (filenameLen) LFA_Write( file, filename, filenameLen ); - if (extraFieldLen) LFA_Write( file, extraField,extraFieldLen ); - } - - void transfer(const FileHeader &orig) - { - memcpy(fields,orig.fields,FIXED_SIZE); - if (orig.extraField) - { - extraFieldLen=orig.extraFieldLen; - extraField = new char[extraFieldLen]; - memcpy(extraField,orig.extraField,extraFieldLen); - } - if (orig.filename) - { - filenameLen=orig.filenameLen; - filename = new char[filenameLen]; - memcpy(filename,orig.filename,filenameLen); - } - }; - - void setXMPFilename() - { - // only needed for fresh structs, thus enforcing rather than catering to memory issues - XMP_Enforce( (filenameLen==0) && (extraFieldLen == 0) ); - filenameLen = xmpFilenameLen; - PutUns16LE(filenameLen, &fields[FileHeader::o_fileNameLength] ); - filename = new char[xmpFilenameLen]; - memcpy(filename,"META-INF/metadata.xml",xmpFilenameLen); - } - - XMP_Uns32 sizeHeader() - { - return this->FIXED_SIZE + this->filenameLen + this->extraFieldLen; - } - - XMP_Uns32 sizeTotalCF() - { - //*** not zip64 bit safe yet, use only for non-large xmp packet - return this->sizeHeader() + GetUns32LE( &fields[FileHeader::o_sizeCompressed] ); - } - - ~FileHeader() - { - this->release(); - }; - - }; //class FileHeader - - ////// yes, this needs an own class - ////// offsets must be extracted, added, modified, - ////// come&go depending on being >0xffffff - ////class extraField { - //// private: - - - class CDFileHeader { - private: - void release() //*** needed or can go? - { - if (filename) delete filename; - if (extraField) delete extraField; - if (comment) delete comment; - filename=0; filenameLen=0; - extraField=0; extraFieldLen=0; - comment=0; commentLen=0; - } - - const static XMP_Uns32 SIG = 0x02014b50; - - public: - const static XMP_Uns16 o_sig = 0; //0x02014b50 - const static XMP_Uns16 o_versionMadeBy = 4; - const static XMP_Uns16 o_extractVersion = 6; - const static XMP_Uns16 o_flags = 8; - const static XMP_Uns16 o_compression = 10; - const static XMP_Uns16 o_lastmodTime = 12; - const static XMP_Uns16 o_lastmodDate = 14; - const static XMP_Uns16 o_crc32 = 16; - const static XMP_Uns16 o_sizeCompressed = 20; // 16bit stub - const static XMP_Uns16 o_sizeUncompressed = 24; // 16bit stub - const static XMP_Uns16 o_fileNameLength = 28; - const static XMP_Uns16 o_extraFieldLength = 30; - const static XMP_Uns16 o_commentLength = 32; - const static XMP_Uns16 o_diskNo = 34; - const static XMP_Uns16 o_internalAttribs = 36; - const static XMP_Uns16 o_externalAttribs = 38; - const static XMP_Uns16 o_offsetLocalHeader = 42; // 16bit stub - // total size is 4+12+12+10+8=46 - - const static int FIXED_SIZE = 46; - char fields[FIXED_SIZE]; - - // do not bet on any zero-freeness, - // certainly no zero termination (pascal strings), - // treat as data blocks - char* filename; - char* extraField; - char* comment; - XMP_Uns16 filenameLen; - XMP_Uns16 extraFieldLen; - XMP_Uns16 commentLen; - - // full, real, parsed 64 bit values - XMP_Int64 sizeUncompressed; - XMP_Int64 sizeCompressed; - XMP_Int64 offsetLocalHeader; - - CDFileHeader() : filename(0),extraField(0),comment(0),filenameLen(0), - extraFieldLen(0),commentLen(0),sizeUncompressed(0),sizeCompressed(0),offsetLocalHeader(0) - { - memset(fields,'\0',FIXED_SIZE); - //already arm with appropriate values where applicable: - PutUns32LE(0x02014b50, &fields[CDFileHeader::o_sig] ); - PutUns16LE(0x14, &fields[CDFileHeader::o_extractVersion] ); - }; - - // copy constructor - CDFileHeader(const CDFileHeader& orig) : filename(0),extraField(0),comment(0),filenameLen(0), - extraFieldLen(0),commentLen(0),sizeUncompressed(0),sizeCompressed(0),offsetLocalHeader(0) - { - memcpy(fields,orig.fields,FIXED_SIZE); - if (orig.extraField) - { - extraFieldLen=orig.extraFieldLen; - extraField = new char[extraFieldLen]; - memcpy(extraField , orig.extraField , extraFieldLen); - } - if (orig.filename) - { - filenameLen=orig.filenameLen; - filename = new char[filenameLen]; - memcpy(filename , orig.filename , filenameLen); - } - if (orig.comment) - { - commentLen=orig.commentLen; - comment = new char[commentLen]; - memcpy(comment , orig.comment , commentLen); - } - - filenameLen = orig.filenameLen; - extraFieldLen = orig.extraFieldLen; - commentLen = orig.commentLen; - - sizeUncompressed = orig.sizeUncompressed; - sizeCompressed = orig.sizeCompressed; - offsetLocalHeader = orig.offsetLocalHeader; - } - - // Assignment operator - CDFileHeader& operator=(const CDFileHeader& obj) - { - XMP_Throw("not supported",kXMPErr_Unimplemented); - } - - // reads entire structure from file (starting at current position) - void read(LFA_FileRef file) - { - this->release(); - - LFA_Read(file,fields,FIXED_SIZE,true); - XMP_Validate( SIG == GetUns32LE( &this->fields[CDFileHeader::o_sig] ), "invalid header", kXMPErr_BadFileFormat ); - - filenameLen = GetUns16LE( &this->fields[CDFileHeader::o_fileNameLength] ); - extraFieldLen = GetUns16LE( &this->fields[CDFileHeader::o_extraFieldLength] ); - commentLen = GetUns16LE( &this->fields[CDFileHeader::o_commentLength] ); - - if (filenameLen) { - filename = new char[filenameLen]; - LFA_Read(file,filename,filenameLen,true); - } - if (extraFieldLen) { - extraField = new char[extraFieldLen]; - LFA_Read(file,extraField,extraFieldLen,true); - } - if (commentLen) { - comment = new char[commentLen]; - LFA_Read(file,comment,commentLen,true); - } - - ////// GET ACTUAL 64 BIT VALUES ////////////////////////////////////////////// - // get 32bit goodies first, correct later - sizeUncompressed = GetUns32LE( &fields[o_sizeUncompressed] ); - sizeCompressed = GetUns32LE( &fields[o_sizeCompressed] ); - offsetLocalHeader = GetUns32LE( &fields[o_offsetLocalHeader] ); - - XMP_Int32 offset = 0; - while ( offset < extraFieldLen ) - { - XMP_Validate( (extraFieldLen - offset) >= 4, "need 4 bytes for next header ID+len", kXMPErr_BadFileFormat); - XMP_Uns16 headerID = GetUns16LE( &extraField[offset] ); - XMP_Uns16 dataSize = GetUns16LE( &extraField[offset+2] ); - offset += 4; - - XMP_Validate( (extraFieldLen - offset) <= dataSize, - "actual field lenght not given", kXMPErr_BadFileFormat); - if ( headerID == 0x1 ) //we only care about "Zip64 extended information extra field" - { - XMP_Validate( offset < extraFieldLen, "extra field too short", kXMPErr_BadFileFormat); - if (sizeUncompressed == 0xffffffff) - { - sizeUncompressed = GetUns64LE( &extraField[offset] ); - offset += 8; - } - if (sizeCompressed == 0xffffffff) - { - sizeCompressed = GetUns64LE( &extraField[offset] ); - offset += 8; - } - if (offsetLocalHeader == 0xffffffff) - { - offsetLocalHeader = GetUns64LE( &extraField[offset] ); - offset += 8; - } - } - else - { - offset += dataSize; - } // if - } // while - } // read() - - // writes structure to file (starting at current position) - void write(LFA_FileRef file) - { - //// WRITE BACK REAL 64 BIT VALUES, CREATE EXTRA FIELD /////////////// - //may only wipe extra field after obtaining all Info from it - if (extraField) delete extraField; - extraFieldLen=0; - - if ( ( sizeUncompressed > 0xffffffff ) || - ( sizeCompressed > 0xffffffff ) || - ( offsetLocalHeader > 0xffffffff ) ) - { - extraField = new char[64]; // actual maxlen is 32 - extraFieldLen = 4; //first fields are for ID, size - if ( sizeUncompressed > 0xffffffff ) - { - PutUns64LE( sizeUncompressed, &extraField[extraFieldLen] ); - extraFieldLen += 8; - sizeUncompressed = 0xffffffff; - } - if ( sizeCompressed > 0xffffffff ) - { - PutUns64LE( sizeCompressed, &extraField[extraFieldLen] ); - extraFieldLen += 8; - sizeCompressed = 0xffffffff; - } - if ( offsetLocalHeader > 0xffffffff ) - { - PutUns64LE( offsetLocalHeader, &extraField[extraFieldLen] ); - extraFieldLen += 8; - offsetLocalHeader = 0xffffffff; - } - - //write ID, dataSize - PutUns16LE( 0x0001, &extraField[0] ); - PutUns16LE( extraFieldLen-4, &extraField[2] ); - //extraFieldSize - PutUns16LE( extraFieldLen, &this->fields[CDFileHeader::o_extraFieldLength] ); - } - - // write out 32-bit ('ff-stubs' or not) - PutUns32LE( (XMP_Uns32)sizeUncompressed, &fields[o_sizeUncompressed] ); - PutUns32LE( (XMP_Uns32)sizeCompressed, &fields[o_sizeCompressed] ); - PutUns32LE( (XMP_Uns32)offsetLocalHeader, &fields[o_offsetLocalHeader] ); - - /// WRITE ///////////////////////////////////////////////////////////////// - XMP_Enforce( SIG == GetUns32LE( &this->fields[CDFileHeader::o_sig] ) ); - - LFA_Write( file , fields , FIXED_SIZE ); - if (filenameLen) LFA_Write( file, filename , filenameLen ); - if (extraFieldLen) LFA_Write( file, extraField , extraFieldLen ); - if (commentLen) LFA_Write( file, extraField , extraFieldLen ); - } - - void setXMPFilename() - { - if (filename) delete filename; - filenameLen = xmpFilenameLen; - filename = new char[xmpFilenameLen]; - PutUns16LE(filenameLen, &fields[CDFileHeader::o_fileNameLength] ); - memcpy(filename,"META-INF/metadata.xml",xmpFilenameLen); - } - - XMP_Int64 size() - { - XMP_Int64 r = this->FIXED_SIZE + this->filenameLen + this->commentLen; - // predict serialization size - if ( (sizeUncompressed > 0xffffffff)||(sizeCompressed > 0xffffffff)||(offsetLocalHeader>0xffffffff) ) - { - r += 4; //extra fields necessary - if (sizeUncompressed > 0xffffffff) r += 8; - if (sizeCompressed > 0xffffffff) r += 8; - if (offsetLocalHeader > 0xffffffff) r += 8; - } - return r; - } - - ~CDFileHeader() - { - this->release(); - }; - }; // class CDFileHeader - - class EndOfCD { - private: - const static XMP_Uns32 SIG = 0x06054b50; - void UCFECD_Free() - { - if(commentLen) delete comment; - commentLen = 0; - } - public: - const static XMP_Int32 o_Sig = 0; - const static XMP_Int32 o_CdNumEntriesDisk = 8; // same-same for UCF, since single-volume - const static XMP_Int32 o_CdNumEntriesTotal = 10;// must update both - const static XMP_Int32 o_CdSize = 12; - const static XMP_Int32 o_CdOffset = 16; - const static XMP_Int32 o_CommentLen = 20; - - const static int FIXED_SIZE = 22; - char fields[FIXED_SIZE]; - - char* comment; - XMP_Uns16 commentLen; - - EndOfCD() : comment(0), commentLen(0) - { - //nothing - }; - - void read (LFA_FileRef file) - { - UCFECD_Free(); - - LFA_Read(file,fields,FIXED_SIZE,true); - XMP_Validate( this->SIG == GetUns32LE( &this->fields[o_Sig] ), "invalid header", kXMPErr_BadFileFormat ); - - commentLen = GetUns16LE( &this->fields[o_CommentLen] ); - if(commentLen) - { - comment = new char[commentLen]; - LFA_Read(file,comment,commentLen,true); - } - }; - - void write(LFA_FileRef file) - { - XMP_Enforce( this->SIG == GetUns32LE( &this->fields[o_Sig] ) ); - commentLen = GetUns16LE( &this->fields[o_CommentLen] ); - LFA_Write( file , fields , FIXED_SIZE ); - if (commentLen) - LFA_Write ( file, comment, commentLen ); - } - - ~EndOfCD() - { - if (comment) delete comment; - }; - }; //class EndOfCD - - //////////////////////////////////////////////////////////////////////////////////// - // EMBEDDING MATH - // - // a = content files before xmp (always 0 thus ommited) - // b/b2 = content files behind xmp (before/after injection) - // x/x2 = offset xmp content header + content file (before/after injection) - // cd/cd = central directory - // h/h2 = end of central directory record - XMP_Int64 b,b2,x,x2,cd,cd2,cdx,cdx2,z,z2,h,h2, - // length thereof ('2' only where possibly different) - // using XMP_Int64 here also for length (not XMP_Int32), - // to be prepared for zip64, our LFA functions might need things in multiple chunks... - al,bl,xl,x2l,cdl,cd2l,cdxl,cdx2l,z2l,hl,fl,f2l; - XMP_Uns16 numCF,numCF2; - - bool wasCompressed; // ..before, false if no prior xmp - bool compressXMP; // compress this time? - bool inPlacePossible; - /* bool isZip64; <=> z2 != 0 */ - - FileHeader xmpFileHeader; - CDFileHeader xmpCDHeader; - - XMP_StringPtr uncomprPacketStr; - XMP_StringLen uncomprPacketLen; - XMP_StringPtr finalPacketStr; - XMP_StringLen finalPacketLen; - std::vector<CDFileHeader> cdEntries; - EndOfCD endOfCD; - void writeOut( LFA_FileRef sourceFile, LFA_FileRef targetFile, bool isRewrite, bool isInPlace); - -}; // UCF_MetaHandler - -// ================================================================================================= - -#endif /* __UCF_Handler_hpp__ */ - - diff --git a/source/XMPFiles/FileHandlers/XDCAMEX_Handler.cpp b/source/XMPFiles/FileHandlers/XDCAMEX_Handler.cpp deleted file mode 100644 index 009ced0..0000000 --- a/source/XMPFiles/FileHandlers/XDCAMEX_Handler.cpp +++ /dev/null @@ -1,809 +0,0 @@ -// ================================================================================================= -// ADOBE SYSTEMS INCORPORATED -// Copyright 2008 Adobe Systems Incorporated -// 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 "XDCAMEX_Handler.hpp" -#include "XDCAM_Support.hpp" -#include "MD5.h" - -using namespace std; - -// ================================================================================================= -/// \file XDCAMEX_Handler.cpp -/// \brief Folder format handler for XDCAMEX. -/// -/// This handler is for the XDCAMEX video format. -/// -/// .../MyMovie/ -/// BPAV/ -/// MEDIAPRO.XML -/// MEDIAPRO.BUP -/// CLPR/ -/// 709_001_01/ -/// 709_001_01.SMI -/// 709_001_01.MP4 -/// 709_001_01M01.XML -/// 709_001_01R01.BIM -/// 709_001_01I01.PPN -/// 709_001_02/ -/// 709_002_01/ -/// 709_003_01/ -/// TAKR/ -/// 709_001/ -/// 709_001.SMI -/// 709_001M01.XML -/// -/// The Backup files (.BUP) are optional. No files or directories other than those listed are -/// allowed in the BPAV directory. The CLPR (clip root) directory may contain only clip directories, -/// which may only contain the clip files listed. The TAKR (take root) direcory may contail only -/// take directories, which may only contain take files. The take root directory can be empty. -/// MEDIPRO.XML contains information on clip and take management. -/// -/// Each clip directory contains a media file (.MP4), a clip info file (.SMI), a real time metadata -/// file (.BIM), a non real time metadata file (.XML), and a picture pointer file (.PPN). A take -/// directory conatins a take info and non real time take metadata files. -// ================================================================================================= - -// ================================================================================================= -// XDCAMEX_CheckFormat -// =================== -// -// This version checks for the presence of a top level BPAV directory, and the required files and -// directories immediately within it. The CLPR and TAKR subfolders are required, as is MEDIAPRO.XML. -// -// The state of the string parameters depends on the form of the path passed by the client. If the -// client passed a logical clip path, like ".../MyMovie/012_3456_01", the parameters are: -// rootPath - ".../MyMovie" -// gpName - empty -// parentName - empty -// leafName - "012_3456_01" -// If the client passed a full file path, like ".../MyMovie/BPAV/CLPR/012_3456_01/012_3456_01M01.XML", they are: -// rootPath - ".../MyMovie/BPAV" -// gpName - "CLPR" -// parentName - "012_3456_01" -// leafName - "012_3456_01M01" - -// ! The common code has shifted the gpName, parentName, and leafName strings to upper case. It has -// ! also made sure that for a logical clip path the rootPath is an existing folder, and that the -// ! file exists for a full file path. - -// ! Using explicit '/' as a separator when creating paths, it works on Windows. - -bool XDCAMEX_CheckFormat ( XMP_FileFormat format, - const std::string & _rootPath, - const std::string & gpName, - const std::string & parentName, - const std::string & _leafName, - XMPFiles * parent ) -{ - std::string rootPath = _rootPath; - std::string clipName = _leafName; - std::string grandGPName; - - std::string bpavPath ( rootPath ); - - // Do some initial checks on the gpName and parentName. - - if ( gpName.empty() != parentName.empty() ) return false; // Must be both empty or both non-empty. - - if ( gpName.empty() ) { - - // This is the logical clip path case. Make sure .../MyMovie/BPAV/CLPR is a folder. - bpavPath += kDirChar; // The rootPath was just ".../MyMovie". - bpavPath += "BPAV"; - if ( GetChildMode ( bpavPath, "CLPR" ) != kFMode_IsFolder ) return false; - - } else { - - // This is the explicit file case. Make sure the ancestry is OK. Set the clip name from the - // parent folder name. - - if ( gpName != "CLPR" ) return false; - SplitLeafName ( &rootPath, &grandGPName ); - MakeUpperCase ( &grandGPName ); - if ( grandGPName != "BPAV" ) return false; - if ( ! XMP_LitNMatch ( parentName.c_str(), clipName.c_str(), parentName.size() ) ) return false; - - clipName = parentName; - - } - - // Check the rest of the required general structure. - if ( GetChildMode ( bpavPath, "TAKR" ) != kFMode_IsFolder ) return false; - if ( GetChildMode ( bpavPath, "MEDIAPRO.XML" ) != kFMode_IsFile ) return false; - - // Make sure the clip's .MP4 and .SMI files exist. - std::string tempPath ( bpavPath ); - tempPath += kDirChar; - tempPath += "CLPR"; - tempPath += kDirChar; - tempPath += clipName; - tempPath += kDirChar; - tempPath += clipName; - tempPath += ".MP4"; - if ( GetFileMode ( tempPath.c_str() ) != kFMode_IsFile ) return false; - tempPath.erase ( tempPath.size()-3 ); - tempPath += "SMI"; - if ( GetFileMode ( tempPath.c_str() ) != kFMode_IsFile ) return false; - - // And now save the psuedo path for the handler object. - tempPath = rootPath; - tempPath += kDirChar; - tempPath += clipName; - size_t pathLen = tempPath.size() + 1; // Include a terminating nul. - parent->tempPtr = malloc ( pathLen ); - if ( parent->tempPtr == 0 ) XMP_Throw ( "No memory for XDCAMEX clip info", kXMPErr_NoMemory ); - memcpy ( parent->tempPtr, tempPath.c_str(), pathLen ); - - return true; - -} // XDCAMEX_CheckFormat - -// ================================================================================================= -// XDCAMEX_MetaHandlerCTor -// ======================= - -XMPFileHandler * XDCAMEX_MetaHandlerCTor ( XMPFiles * parent ) -{ - return new XDCAMEX_MetaHandler ( parent ); - -} // XDCAMEX_MetaHandlerCTor - -// ================================================================================================= -// XDCAMEX_MetaHandler::XDCAMEX_MetaHandler -// ======================================== - -XDCAMEX_MetaHandler::XDCAMEX_MetaHandler ( XMPFiles * _parent ) : expat(0) -{ - this->parent = _parent; // Inherited, can't set in the prefix. - this->handlerFlags = kXDCAMEX_HandlerFlags; - this->stdCharForm = kXMP_Char8Bit; - - // Extract the root path and clip name from tempPtr. - - XMP_Assert ( this->parent->tempPtr != 0 ); - - this->rootPath.assign ( (char*) this->parent->tempPtr ); - free ( this->parent->tempPtr ); - this->parent->tempPtr = 0; - - SplitLeafName ( &this->rootPath, &this->clipName ); - -} // XDCAMEX_MetaHandler::XDCAMEX_MetaHandler - -// ================================================================================================= -// XDCAMEX_MetaHandler::~XDCAMEX_MetaHandler -// ========================================= - -XDCAMEX_MetaHandler::~XDCAMEX_MetaHandler() -{ - - this->CleanupLegacyXML(); - if ( this->parent->tempPtr != 0 ) { - free ( this->parent->tempPtr ); - this->parent->tempPtr = 0; - } - -} // XDCAMEX_MetaHandler::~XDCAMEX_MetaHandler - -// ================================================================================================= -// XDCAMEX_MetaHandler::MakeClipFilePath -// ===================================== - -void XDCAMEX_MetaHandler::MakeClipFilePath ( std::string * path, XMP_StringPtr suffix ) -{ - - *path = this->rootPath; - *path += kDirChar; - *path += "BPAV"; - *path += kDirChar; - *path += "CLPR"; - *path += kDirChar; - *path += this->clipName; - *path += kDirChar; - *path += this->clipName; - *path += suffix; - -} // XDCAMEX_MetaHandler::MakeClipFilePath - -// ================================================================================================= -// XDCAMEX_MetaHandler::MakeLegacyDigest -// ===================================== - -// *** Early hack version. - -#define kHexDigits "0123456789ABCDEF" - -void XDCAMEX_MetaHandler::MakeLegacyDigest ( std::string * digestStr ) -{ - digestStr->erase(); - if ( this->clipMetadata == 0 ) return; // Bail if we don't have any legacy XML. - XMP_Assert ( this->expat != 0 ); - - XMP_StringPtr xdcNS = this->xdcNS.c_str(); - XML_NodePtr legacyContext, legacyProp; - - legacyContext = this->clipMetadata->GetNamedElement ( xdcNS, "Access" ); - if ( legacyContext == 0 ) return; - - MD5_CTX context; - unsigned char digestBin [16]; - MD5Init ( &context ); - - legacyProp = legacyContext->GetNamedElement ( xdcNS, "Creator" ); - if ( (legacyProp != 0) && legacyProp->IsLeafContentNode() && (! legacyProp->content.empty()) ) { - const XML_Node * xmlValue = legacyProp->content[0]; - MD5Update ( &context, (XMP_Uns8*)xmlValue->value.c_str(), (unsigned int)xmlValue->value.size() ); - } - - legacyProp = legacyContext->GetNamedElement ( xdcNS, "CreationDate" ); - if ( (legacyProp != 0) && legacyProp->IsLeafContentNode() && (! legacyProp->content.empty()) ) { - const XML_Node * xmlValue = legacyProp->content[0]; - MD5Update ( &context, (XMP_Uns8*)xmlValue->value.c_str(), (unsigned int)xmlValue->value.size() ); - } - - legacyProp = legacyContext->GetNamedElement ( xdcNS, "LastUpdateDate" ); - if ( (legacyProp != 0) && legacyProp->IsLeafContentNode() && (! legacyProp->content.empty()) ) { - const XML_Node * xmlValue = legacyProp->content[0]; - MD5Update ( &context, (XMP_Uns8*)xmlValue->value.c_str(), (unsigned int)xmlValue->value.size() ); - } - - MD5Final ( digestBin, &context ); - - char buffer [40]; - for ( int in = 0, out = 0; in < 16; in += 1, out += 2 ) { - XMP_Uns8 byte = digestBin[in]; - buffer[out] = kHexDigits [ byte >> 4 ]; - buffer[out+1] = kHexDigits [ byte & 0xF ]; - } - buffer[32] = 0; - digestStr->append ( buffer ); - -} // XDCAMEX_MetaHandler::MakeLegacyDigest - -// ================================================================================================= -// XDCAMEX_MetaHandler::CleanupLegacyXML -// ===================================== - -void XDCAMEX_MetaHandler::CleanupLegacyXML() -{ - - if ( this->expat != 0 ) { delete ( this->expat ); this->expat = 0; } - - clipMetadata = 0; // ! Was a pointer into the expat tree. - -} // XDCAMEX_MetaHandler::CleanupLegacyXML - -// ================================================================================================= -// XDCAMEX_MetaHandler::CacheFileData -// ================================== - -void XDCAMEX_MetaHandler::CacheFileData() -{ - XMP_Assert ( ! this->containsXMP ); - - // See if the clip's .XMP file exists. - - std::string xmpPath; - this->MakeClipFilePath ( &xmpPath, "M01.XMP" ); - if ( GetFileMode ( xmpPath.c_str() ) != kFMode_IsFile ) return; // No XMP. - - // Read the entire .XMP file. - - char openMode = 'r'; - if ( this->parent->openFlags & kXMPFiles_OpenForUpdate ) openMode = 'w'; - - LFA_FileRef xmpFile = LFA_Open ( xmpPath.c_str(), openMode ); - if ( xmpFile == 0 ) return; // The open failed. - - XMP_Int64 xmpLen = LFA_Measure ( xmpFile ); - if ( xmpLen > 100*1024*1024 ) { - XMP_Throw ( "XDCAMEX XMP is outrageously large", kXMPErr_InternalFailure ); // Sanity check. - } - - this->xmpPacket.erase(); - this->xmpPacket.reserve ( (size_t)xmpLen ); - this->xmpPacket.append ( (size_t)xmpLen, ' ' ); - - XMP_Int32 ioCount = LFA_Read ( xmpFile, (void*)this->xmpPacket.data(), (XMP_Int32)xmpLen, kLFA_RequireAll ); - XMP_Assert ( ioCount == xmpLen ); - - this->packetInfo.offset = 0; - this->packetInfo.length = (XMP_Int32)xmpLen; - FillPacketInfo ( this->xmpPacket, &this->packetInfo ); - - XMP_Assert ( this->parent->fileRef == 0 ); - if ( openMode == 'r' ) { - LFA_Close ( xmpFile ); - } else { - this->parent->fileRef = xmpFile; - } - - this->containsXMP = true; - -} // XDCAMEX_MetaHandler::CacheFileData - -// ================================================================================================= -// XDCAMEX_MetaHandler::GetTakeDuration -// ==================================== - -void XDCAMEX_MetaHandler::GetTakeDuration ( const std::string & takeURI, std::string & duration ) -{ - - // Some versions of gcc can't tolerate goto's across declarations. - // *** Better yet, avoid this cruft with self-cleaning objects. - #define CleanupAndExit \ - { \ - if (expat != 0) delete expat; \ - if (takeXMLFile.fileRef != 0) LFA_Close ( takeXMLFile.fileRef ); \ - return; \ - } - - duration.clear(); - - // Build a directory string to the take .xml file. - - std::string takeDir ( takeURI ); - takeDir.erase ( 0, 1 ); // Change the leading "//" to "/", then all '/' to kDirChar. - if ( kDirChar != '/' ) { - for ( size_t i = 0, limit = takeDir.size(); i < limit; ++i ) { - if ( takeDir[i] == '/' ) takeDir[i] = kDirChar; - } - } - - std::string takePath ( this->rootPath ); - takePath += kDirChar; - takePath += "BPAV"; - takePath += takeDir; - - // Replace .SMI with M01.XML. - if ( takePath.size() > 4 ) { - takePath.erase ( takePath.size() - 4, 4 ); - takePath += "M01.XML"; - } - - // Parse MEDIAPRO.XML - - XML_NodePtr takeRootElem = 0; - XML_NodePtr context = 0; - AutoFile takeXMLFile; - - takeXMLFile.fileRef = LFA_Open ( takePath.c_str(), 'r' ); - if ( takeXMLFile.fileRef == 0 ) return; // The open failed. - - ExpatAdapter * expat = XMP_NewExpatAdapter ( ExpatAdapter::kUseLocalNamespaces ); - if ( this->expat == 0 ) return; - - XMP_Uns8 buffer [64*1024]; - while ( true ) { - XMP_Int32 ioCount = LFA_Read ( takeXMLFile.fileRef, buffer, sizeof(buffer) ); - if ( ioCount == 0 ) break; - expat->ParseBuffer ( buffer, ioCount, false /* not the end */ ); - } - - expat->ParseBuffer ( 0, 0, true ); // End the parse. - LFA_Close ( takeXMLFile.fileRef ); - takeXMLFile.fileRef = 0; - - // Get the root node of the XML tree. - - XML_Node & mediaproXMLTree = expat->tree; - for ( size_t i = 0, limit = mediaproXMLTree.content.size(); i < limit; ++i ) { - if ( mediaproXMLTree.content[i]->kind == kElemNode ) { - takeRootElem = mediaproXMLTree.content[i]; - } - } - if ( takeRootElem == 0 ) CleanupAndExit - - XMP_StringPtr rlName = takeRootElem->name.c_str() + takeRootElem->nsPrefixLen; - if ( ! XMP_LitMatch ( rlName, "NonRealTimeMeta" ) ) CleanupAndExit - - // MediaProfile, Contents - XMP_StringPtr ns = takeRootElem->ns.c_str(); - context = takeRootElem->GetNamedElement ( ns, "Duration" ); - if ( context != 0 ) { - XMP_StringPtr durationValue = context->GetAttrValue ( "value" ); - if ( durationValue != 0 ) duration = durationValue; - } - - CleanupAndExit - #undef CleanupAndExit - -} - -// ================================================================================================= -// XDCAMEX_MetaHandler::GetTakeUMID -// ================================ - -void XDCAMEX_MetaHandler::GetTakeUMID ( const std::string& clipUMID, - std::string& takeUMID, - std::string& takeXMLURI ) -{ - - // Some versions of gcc can't tolerate goto's across declarations. - // *** Better yet, avoid this cruft with self-cleaning objects. - #define CleanupAndExit \ - { \ - if (expat != 0) delete expat; \ - if (mediaproXMLFile.fileRef != 0) LFA_Close ( mediaproXMLFile.fileRef ); \ - return; \ - } - - takeUMID.clear(); - takeXMLURI.clear(); - - // Build a directory string to the MEDIAPRO file. - - std::string mediapropath ( this->rootPath ); - mediapropath += kDirChar; - mediapropath += "BPAV"; - mediapropath += kDirChar; - mediapropath += "MEDIAPRO.XML"; - - // Parse MEDIAPRO.XML. - - XML_NodePtr mediaproRootElem = 0; - XML_NodePtr contentContext = 0, materialContext = 0; - - AutoFile mediaproXMLFile; - mediaproXMLFile.fileRef = LFA_Open ( mediapropath.c_str(), 'r' ); - if ( mediaproXMLFile.fileRef == 0 ) return; // The open failed. - - ExpatAdapter * expat = XMP_NewExpatAdapter ( ExpatAdapter::kUseLocalNamespaces ); - if ( this->expat == 0 ) return; - - XMP_Uns8 buffer [64*1024]; - while ( true ) { - XMP_Int32 ioCount = LFA_Read ( mediaproXMLFile.fileRef, buffer, sizeof(buffer) ); - if ( ioCount == 0 ) break; - expat->ParseBuffer ( buffer, ioCount, false /* not the end */ ); - } - - expat->ParseBuffer ( 0, 0, true ); // End the parse. - LFA_Close ( mediaproXMLFile.fileRef ); - mediaproXMLFile.fileRef = 0; - - // Get the root node of the XML tree. - - XML_Node & mediaproXMLTree = expat->tree; - for ( size_t i = 0, limit = mediaproXMLTree.content.size(); i < limit; ++i ) { - if ( mediaproXMLTree.content[i]->kind == kElemNode ) { - mediaproRootElem = mediaproXMLTree.content[i]; - } - } - - if ( mediaproRootElem == 0 ) CleanupAndExit - XMP_StringPtr rlName = mediaproRootElem->name.c_str() + mediaproRootElem->nsPrefixLen; - if ( ! XMP_LitMatch ( rlName, "MediaProfile" ) ) CleanupAndExit - - // MediaProfile, Contents - - XMP_StringPtr ns = mediaproRootElem->ns.c_str(); - contentContext = mediaproRootElem->GetNamedElement ( ns, "Contents" ); - - if ( contentContext != 0 ) { - - size_t numMaterialElems = contentContext->CountNamedElements ( ns, "Material" ); - - for ( size_t i = 0; i < numMaterialElems; ++i ) { // Iterate over Material tags. - - XML_NodePtr materialElement = contentContext->GetNamedElement ( ns, "Material", i ); - XMP_Assert ( materialElement != 0 ); - - XMP_StringPtr umid = materialElement->GetAttrValue ( "umid" ); - XMP_StringPtr uri = materialElement->GetAttrValue ( "uri" ); - - if ( umid == 0 ) umid = ""; - if ( uri == 0 ) uri = ""; - - size_t numComponents = materialElement->CountNamedElements ( ns, "Component" ); - - for ( size_t j = 0; j < numComponents; ++j ) { - - XML_NodePtr componentElement = materialElement->GetNamedElement ( ns, "Component", j ); - XMP_Assert ( componentElement != 0 ); - - XMP_StringPtr compUMID = componentElement->GetAttrValue ( "umid" ); - - if ( (compUMID != 0) && (compUMID == clipUMID) ) { - takeUMID = umid; - takeXMLURI = uri; - break; - } - - } - - if ( ! takeUMID.empty() ) break; - - } - - } - - CleanupAndExit - #undef CleanupAndExit - -} - -// ================================================================================================= -// XDCAMEX_MetaHandler::ProcessXMP -// =============================== - -void XDCAMEX_MetaHandler::ProcessXMP() -{ - - // Some versions of gcc can't tolerate goto's across declarations. - // *** Better yet, avoid this cruft with self-cleaning objects. - #define CleanupAndExit \ - { \ - bool openForUpdate = XMP_OptionIsSet ( this->parent->openFlags, kXMPFiles_OpenForUpdate ); \ - if ( ! openForUpdate ) this->CleanupLegacyXML(); \ - return; \ - } - - if ( this->processedXMP ) return; - this->processedXMP = true; // Make sure only called once. - - if ( this->containsXMP ) { - this->xmpObj.ParseFromBuffer ( this->xmpPacket.c_str(), (XMP_StringLen)this->xmpPacket.size() ); - } - - // NonRealTimeMeta -> XMP by schema. - std::string thisUMID, takeUMID, takeXMLURI, takeDuration; - std::string xmlPath; - this->MakeClipFilePath ( &xmlPath, "M01.XML" ); - - AutoFile xmlFile; - xmlFile.fileRef = LFA_Open ( xmlPath.c_str(), 'r' ); - if ( xmlFile.fileRef == 0 ) return; // The open failed. - - this->expat = XMP_NewExpatAdapter ( ExpatAdapter::kUseLocalNamespaces ); - if ( this->expat == 0 ) XMP_Throw ( "XDCAMEX_MetaHandler: Can't create Expat adapter", kXMPErr_NoMemory ); - - XMP_Uns8 buffer [64*1024]; - while ( true ) { - XMP_Int32 ioCount = LFA_Read ( xmlFile.fileRef, buffer, sizeof(buffer) ); - if ( ioCount == 0 ) break; - this->expat->ParseBuffer ( buffer, ioCount, false /* not the end */ ); - } - this->expat->ParseBuffer ( 0, 0, true ); // End the parse. - - LFA_Close ( xmlFile.fileRef ); - xmlFile.fileRef = 0; - - // The root element should be NonRealTimeMeta in some namespace. Take whatever this file uses. - - XML_Node & xmlTree = this->expat->tree; - XML_NodePtr rootElem = 0; - - for ( size_t i = 0, limit = xmlTree.content.size(); i < limit; ++i ) { - if ( xmlTree.content[i]->kind == kElemNode ) rootElem = xmlTree.content[i]; - } - - if ( rootElem == 0 ) CleanupAndExit - XMP_StringPtr rootLocalName = rootElem->name.c_str() + rootElem->nsPrefixLen; - if ( ! XMP_LitMatch ( rootLocalName, "NonRealTimeMeta" ) ) CleanupAndExit - - this->legacyNS = rootElem->ns; - - // Check the legacy digest. - - XMP_StringPtr legacyNS = this->legacyNS.c_str(); - this->clipMetadata = rootElem; // ! Save the NonRealTimeMeta pointer for other use. - - std::string oldDigest, newDigest; - bool digestFound = this->xmpObj.GetStructField ( kXMP_NS_XMP, "NativeDigests", kXMP_NS_XMP, "XDCAMEX", &oldDigest, 0 ); - if ( digestFound ) { - this->MakeLegacyDigest ( &newDigest ); - if ( oldDigest == newDigest ) CleanupAndExit - } - - // If we get here we need find and import the actual legacy elements using the current namespace. - // Either there is no old digest in the XMP, or the digests differ. In the former case keep any - // existing XMP, in the latter case take new legacy values. - this->containsXMP = XDCAM_Support::GetLegacyMetaData ( &this->xmpObj, rootElem, legacyNS, digestFound, thisUMID ); - - // If this clip is part of a take, add the take number to the relation field, and get the - // duration from the take metadata. - GetTakeUMID ( thisUMID, takeUMID, takeXMLURI ); - - // If this clip is part of a take, update the duration to reflect the take duration rather than - // the clip duration, and add the take name as a shot name. - - if ( ! takeXMLURI.empty() ) { - - // Update duration. This property already exists from clip legacy metadata. - GetTakeDuration ( takeXMLURI, takeDuration ); - if ( ! takeDuration.empty() ) { - this->xmpObj.SetStructField ( kXMP_NS_DM, "duration", kXMP_NS_DM, "value", takeDuration ); - containsXMP = true; - } - - if ( digestFound || (! this->xmpObj.DoesPropertyExist ( kXMP_NS_DM, "shotName" )) ) { - - std::string takeName; - SplitLeafName ( &takeXMLURI, &takeName ); - - // Check for the xml suffix, and delete if it exists. - size_t pos = takeName.rfind(".SMI"); - if ( pos != std::string::npos ) { - - takeName.erase ( pos ); - - // delete the take number suffix if it exists. - if ( takeName.size() > 3 ) { - - size_t suffix = takeName.size() - 3; - char c1 = takeName[suffix]; - char c2 = takeName[suffix+1]; - char c3 = takeName[suffix+2]; - if ( ('U' == c1) && ('0' <= c2) && (c2 <= '9') && ('0' <= c3) && (c3 <= '9') ) { - takeName.erase ( suffix ); - } - - this->xmpObj.SetProperty ( kXMP_NS_DM, "shotName", takeName, kXMP_DeleteExisting ); - containsXMP = true; - - } - - } - - } - - } - - if ( (! takeUMID.empty()) && - (digestFound || (! this->xmpObj.DoesPropertyExist ( kXMP_NS_DC, "relation" ))) ) { - this->xmpObj.DeleteProperty ( kXMP_NS_DC, "relation" ); - this->xmpObj.AppendArrayItem ( kXMP_NS_DC, "relation", kXMP_PropArrayIsUnordered, takeUMID ); - this->containsXMP = true; - } - - CleanupAndExit - #undef CleanupAndExit - -} // XDCAMEX_MetaHandler::ProcessXMP - - -// ================================================================================================= -// XDCAMEX_MetaHandler::UpdateFile -// =============================== -// -// Note that UpdateFile is only called from XMPFiles::CloseFile, so it is OK to close the file here. - -void XDCAMEX_MetaHandler::UpdateFile ( bool doSafeUpdate ) -{ - if ( ! this->needsUpdate ) return; - this->needsUpdate = false; // Make sure only called once. - - LFA_FileRef oldFile = 0; - std::string filePath, tempPath; - - // Update the internal legacy XML tree if we have one, and set the digest in the XMP. - - bool updateLegacyXML = false; - - if ( this->clipMetadata != 0 ) { - updateLegacyXML = XDCAM_Support::SetLegacyMetaData ( this->clipMetadata, &this->xmpObj, this->legacyNS.c_str()); - } - - std::string newDigest; - this->MakeLegacyDigest ( &newDigest ); - this->xmpObj.SetStructField ( kXMP_NS_XMP, "NativeDigests", kXMP_NS_XMP, "XDCAMEX", newDigest.c_str(), kXMP_DeleteExisting ); - this->xmpObj.SerializeToBuffer ( &this->xmpPacket, this->GetSerializeOptions() ); - - // Update the legacy XML file if necessary. - - if ( updateLegacyXML ) { - - std::string legacyXML; - this->expat->tree.Serialize ( &legacyXML ); - - this->MakeClipFilePath ( &filePath, "M01.XML" ); - oldFile = LFA_Open ( filePath.c_str(), 'w' ); - - if ( oldFile == 0 ) { - - // The XML does not exist yet. - - this->MakeClipFilePath ( &filePath, "M01.XML" ); - oldFile = LFA_Create ( filePath.c_str() ); - if ( oldFile == 0 ) XMP_Throw ( "Failure creating XDCAMEX legacy XML file", kXMPErr_ExternalFailure ); - LFA_Write ( oldFile, legacyXML.data(), (XMP_StringLen)legacyXML.size() ); - LFA_Close ( oldFile ); - - } else if ( ! doSafeUpdate ) { - - // Over write the existing XML file. - - LFA_Seek ( oldFile, 0, SEEK_SET ); - LFA_Truncate ( oldFile, 0 ); - LFA_Write ( oldFile, legacyXML.data(), (XMP_StringLen)legacyXML.size() ); - LFA_Close ( oldFile ); - - } else { - - // Do a safe update. - - // *** We really need an LFA_SwapFiles utility. - - this->MakeClipFilePath ( &filePath, "M01.XML" ); - - CreateTempFile ( filePath, &tempPath ); - LFA_FileRef tempFile = LFA_Open ( tempPath.c_str(), 'w' ); - LFA_Write ( tempFile, legacyXML.data(), (XMP_StringLen)legacyXML.size() ); - LFA_Close ( tempFile ); - - LFA_Close ( oldFile ); - LFA_Delete ( filePath.c_str() ); - LFA_Rename ( tempPath.c_str(), filePath.c_str() ); - - } - - } - - oldFile = this->parent->fileRef; - - if ( oldFile == 0 ) { - - // The XMP does not exist yet. - - std::string xmpPath; - this->MakeClipFilePath ( &xmpPath, "M01.XMP" ); - - LFA_FileRef xmpFile = LFA_Create ( xmpPath.c_str() ); - if ( xmpFile == 0 ) XMP_Throw ( "Failure creating XDCAMEX XMP file", kXMPErr_ExternalFailure ); - LFA_Write ( xmpFile, this->xmpPacket.data(), (XMP_StringLen)this->xmpPacket.size() ); - LFA_Close ( xmpFile ); - - } else if ( ! doSafeUpdate ) { - - // Over write the existing XMP file. - - LFA_Seek ( oldFile, 0, SEEK_SET ); - LFA_Truncate ( oldFile, 0 ); - LFA_Write ( oldFile, this->xmpPacket.data(), (XMP_StringLen)this->xmpPacket.size() ); - LFA_Close ( oldFile ); - - } else { - - // Do a safe update. - - // *** We really need an LFA_SwapFiles utility. - - std::string xmpPath, tempPath; - - this->MakeClipFilePath ( &xmpPath, "M01.XMP" ); - - CreateTempFile ( xmpPath, &tempPath ); - LFA_FileRef tempFile = LFA_Open ( tempPath.c_str(), 'w' ); - LFA_Write ( tempFile, this->xmpPacket.data(), (XMP_StringLen)this->xmpPacket.size() ); - LFA_Close ( tempFile ); - - LFA_Close ( oldFile ); - LFA_Delete ( xmpPath.c_str() ); - LFA_Rename ( tempPath.c_str(), xmpPath.c_str() ); - - } - - this->parent->fileRef = 0; - -} // XDCAMEX_MetaHandler::UpdateFile - -// ================================================================================================= -// XDCAMEX_MetaHandler::WriteFile -// ============================== - -void XDCAMEX_MetaHandler::WriteFile ( LFA_FileRef sourceRef, const std::string & sourcePath ) -{ - - // ! WriteFile is not supposed to be called for handlers that own the file. - XMP_Throw ( "XDCAMEX_MetaHandler::WriteFile should not be called", kXMPErr_InternalFailure ); - -} // XDCAMEX_MetaHandler::WriteFile - -// ================================================================================================= diff --git a/source/XMPFiles/FileHandlers/XDCAMEX_Handler.hpp b/source/XMPFiles/FileHandlers/XDCAMEX_Handler.hpp deleted file mode 100644 index ee7b23f..0000000 --- a/source/XMPFiles/FileHandlers/XDCAMEX_Handler.hpp +++ /dev/null @@ -1,81 +0,0 @@ -#ifndef __XDCAMEX_Handler_hpp__ -#define __XDCAMEX_Handler_hpp__ 1 - -// ================================================================================================= -// ADOBE SYSTEMS INCORPORATED -// Copyright 2008 Adobe Systems Incorporated -// 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 "XMP_Environment.h" // ! This must be the first include. - -#include "XMPFiles_Impl.hpp" - -#include "ExpatAdapter.hpp" - -// ================================================================================================= -/// \file XDCAMEX_Handler.hpp -/// \brief Folder format handler for XDCAMEX. -// ================================================================================================= - -extern XMPFileHandler * XDCAMEX_MetaHandlerCTor ( XMPFiles * parent ); - -extern bool XDCAMEX_CheckFormat ( XMP_FileFormat format, - const std::string & rootPath, - const std::string & gpName, - const std::string & parentName, - const std::string & leafName, - XMPFiles * parent ); - -static const XMP_OptionBits kXDCAMEX_HandlerFlags = (kXMPFiles_CanInjectXMP | - kXMPFiles_CanExpand | - kXMPFiles_CanRewrite | - kXMPFiles_PrefersInPlace | - kXMPFiles_CanReconcile | - kXMPFiles_AllowsOnlyXMP | - kXMPFiles_ReturnsRawPacket | - kXMPFiles_HandlerOwnsFile | - kXMPFiles_AllowsSafeUpdate | - kXMPFiles_FolderBasedFormat); - -class XDCAMEX_MetaHandler : public XMPFileHandler -{ -public: - - void CacheFileData(); - void ProcessXMP(); - - XMP_OptionBits GetSerializeOptions() // *** These should be standard for standalone XMP files. - { return (kXMP_UseCompactFormat | kXMP_OmitPacketWrapper); }; - - void UpdateFile ( bool doSafeUpdate ); - void WriteFile ( LFA_FileRef sourceRef, const std::string & sourcePath ); - - XDCAMEX_MetaHandler ( XMPFiles * _parent ); - virtual ~XDCAMEX_MetaHandler(); - -private: - - XDCAMEX_MetaHandler() : expat(0) {}; // Hidden on purpose. - - void MakeClipFilePath ( std::string * path, XMP_StringPtr suffix ); - void MakeLegacyDigest ( std::string * digestStr ); - - void GetTakeUMID( const std::string& clipUMID, std::string& takeUMID, std::string& takeXMLURI ); - void GetTakeDuration( const std::string& takeUMID, std::string& duration ); - - void CleanupLegacyXML(); - - std::string rootPath, clipName, xdcNS, legacyNS, clipUMID; - - ExpatAdapter * expat; - XML_Node * clipMetadata; - -}; // XDCAMEX_MetaHandler - -// ================================================================================================= - -#endif /* __XDCAMEX_Handler_hpp__ */ diff --git a/source/XMPFiles/FileHandlers/XDCAM_Handler.cpp b/source/XMPFiles/FileHandlers/XDCAM_Handler.cpp deleted file mode 100644 index d88109e..0000000 --- a/source/XMPFiles/FileHandlers/XDCAM_Handler.cpp +++ /dev/null @@ -1,710 +0,0 @@ -// ================================================================================================= -// ADOBE SYSTEMS INCORPORATED -// Copyright 2007 Adobe Systems Incorporated -// 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 "XDCAM_Handler.hpp" -#include "XDCAM_Support.hpp" -#include "MD5.h" - -using namespace std; - -// ================================================================================================= -/// \file XDCAM_Handler.cpp -/// \brief Folder format handler for XDCAM. -/// -/// This handler is for the XDCAM video format. This is a pseudo-package, visible files but with a very -/// well-defined layout and naming rules. There are 2 different layouts for XDCAM, called FAM and SAM. -/// The FAM layout is used by "normal" XDCAM devices. The SAM layout is used by XDCAM-EX devices. -/// -/// A typical FAM layout looks like (note mixed case for General, Clip, Edit, and Sub folders): -/// -/// .../MyMovie/ -/// INDEX.XML -/// DISCMETA.XML -/// MEDIAPRO.XML -/// General/ -/// unknown files -/// Clip/ -/// C0001.MXF -/// C0001M01.XML -/// C0001M01.XMP -/// C0002.MXF -/// C0002M01.XML -/// C0002M01.XMP -/// Sub/ -/// C0001S01.MXF -/// C0002S01.MXF -/// Edit/ -/// E0001E01.SMI -/// E0001M01.XML -/// E0002E01.SMI -/// E0002M01.XML -/// -/// A typical SAM layout looks like: -/// -/// .../MyMovie/ -/// GENERAL/ -/// unknown files -/// PROAV/ -/// INDEX.XML -/// INDEX.BUP -/// DISCMETA.XML -/// DISCINFO.XML -/// DISCINFO.BUP -/// CLPR/ -/// C0001/ -/// C0001C01.SMI -/// C0001V01.MXF -/// C0001A01.MXF -/// C0001A02.MXF -/// C0001R01.BIM -/// C0001I01.PPN -/// C0001M01.XML -/// C0001M01.XMP -/// C0001S01.MXF -/// C0002/ -/// ... -/// EDTR/ -/// E0001/ -/// E0001E01.SMI -/// E0001M01.XML -/// E0002/ -/// ... -/// -/// Note that the Sony documentation uses the folder names "General", "Clip", "Sub", and "Edit". We -/// use all caps here. Common code has already shifted the names, we want to be case insensitive. -/// -/// From the user's point of view, .../MyMovie contains XDCAM stuff, in this case 2 clips whose raw -/// names are C0001 and C0002. There may be mapping information for nicer clip names to the raw -/// names, but that can be ignored for now. Each clip is stored as a collection of files, each file -/// holding some specific aspect of the clip's data. -/// -/// The XDCAM handler operates on clips. The path from the client of XMPFiles can be either a logical -/// clip path, like ".../MyMovie/C0001", or a full path to one of the files. In the latter case the -/// handler must figure out the intended clip, it must not blindly use the named file. -/// -/// Once the XDCAM structure and intended clip are identified, the handler only deals with the .XMP -/// and .XML files in the CLIP or CLPR/<clip> folders. The .XMP file, if present, contains the XMP -/// for the clip. The .XML file must be present to define the existance of the clip. It contains a -/// variety of information about the clip, including some legacy metadata. -/// -// ================================================================================================= - -// ================================================================================================= -// XDCAM_CheckFormat -// ================= -// -// This version does fairly simple checks. The top level folder (.../MyMovie) must have exactly 1 -// child, a folder called CONTENTS. This must have a subfolder called CLIP. It may also have -// subfolders called VIDEO, AUDIO, ICON, VOICE, and PROXY. Any mixture of these additional folders -// is allowed, but no other children are allowed in CONTENTS. The CLIP folder must contain a .XML -// file for the desired clip. The name checks are case insensitive. -// -// The state of the string parameters depends on the form of the path passed by the client. If the -// client passed a logical clip path, like ".../MyMovie/C0001", the parameters are: -// rootPath - ".../MyMovie" -// gpName - empty -// parentName - empty -// leafName - "C0001" -// -// If the client passed a FAM file path, like ".../MyMovie/Edit/E0001E01.SMI", they are: -// rootPath - "..." -// gpName - "MyMovie" -// parentName - "EDIT" (common code has shifted the case) -// leafName - "E0001E01" -// -// If the client passed a SAM file path, like ".../MyMovie/PROAV/CLPR/C0001/C0001A02.MXF", they are: -// rootPath - ".../MyMovie/PROAV" -// gpName - "CLPR" -// parentName - "C0001" -// leafName - "C0001A02" -// -// For both FAM and SAM the leading character of the leafName for an existing file might be coerced -// to 'C' to form the logical clip name. And suffix such as "M01" must be removed for FAM. We don't -// need to worry about that for SAM, that uses the <clip> folder name. - -// ! The FAM format supports general clip file names through an ALIAS.XML mapping file. The simple -// ! existence check has an edge case bug, left to be fixed later. If the ALIAS.XML file exists, but -// ! some of the clips still have "raw" names, and we're passed an existing file path in the EDIT -// ! folder, we will fail to do the leading 'E' to 'C' coercion. We might also erroneously remove a -// ! suffix from a mapped essence file with a name like ClipX01.MXF. - -// ! The common code has shifted the gpName, parentName, and leafName strings to uppercase. It has -// ! also made sure that for a logical clip path the rootPath is an existing folder, and that the -// ! file exists for a full file path. - -bool XDCAM_CheckFormat ( XMP_FileFormat format, - const std::string & _rootPath, - const std::string & _gpName, - const std::string & parentName, - const std::string & leafName, - XMPFiles * parent ) -{ - std::string rootPath = _rootPath; // ! Need tweaking in the existing file cases (FAM and SAM). - std::string gpName = _gpName; - - bool isFAM = false; - - std::string tempPath, childName; - XMP_FolderInfo folderInfo; - - std::string clipName = leafName; - - // Do some basic checks on the root path and component names. Decide if this is FAM or SAM. - - if ( gpName.empty() != parentName.empty() ) return false; // Must be both empty or both non-empty. - - if ( gpName.empty() ) { - - // This is the logical clip path case. Just look for PROAV to see if this is FAM or SAM. - if ( GetChildMode ( rootPath, "PROAV" ) != kFMode_IsFolder ) isFAM = true; - - } else { - - // This is the existing file case. See if this is FAM or SAM, tweak the clip name as needed. - - if ( (parentName == "CLIP") || (parentName == "EDIT") || (parentName == "SUB") ) { - // ! The standard says Clip/Edit/Sub, but the caller has already shifted to upper case. - isFAM = true; - } else if ( (gpName != "CLPR") && (gpName != "EDTR") ) { - return false; - } - - if ( isFAM ) { - - // Put the proper root path together. Clean up the clip name if needed. - - if ( ! rootPath.empty() ) rootPath += kDirChar; - rootPath += gpName; - gpName.erase(); - - if ( GetChildMode ( rootPath, "ALIAS.XML" ) != kFMode_IsFile ) { - clipName[0] = 'C'; // ! See notes above about pending bug. - } - - if ( clipName.size() > 3 ) { - size_t clipMid = clipName.size() - 3; - char c1 = clipName[clipMid]; - char c2 = clipName[clipMid+1]; - char c3 = clipName[clipMid+2]; - if ( ('A' <= c1) && (c1 <= 'Z') && - ('0' <= c2) && (c2 <= '9') && ('0' <= c3) && (c3 <= '9') ) { - clipName.erase ( clipMid ); - } - } - - } else { - - // Fix the clip name. Check for and strip the "PROAV" suffix on the root path. - - clipName = parentName; // ! We have a folder with the (almost) exact clip name. - clipName[0] = 'C'; - - std::string proav; - SplitLeafName ( &rootPath, &proav ); - MakeUpperCase ( &proav ); - if ( (rootPath.empty()) || (proav != "PROAV") ) return false; - - } - - } - - // Make sure the general XDCAM package structure is legit. Set tempPath as a bogus path of the - // form <root>/<FAM-or-SAM>/<clip>, e.g. ".../MyMovie/FAM/C0001". This is passed the handler via - // the tempPtr hackery. - - if ( isFAM ) { - - if ( (format != kXMP_XDCAM_FAMFile) && (format != kXMP_UnknownFile) ) return false; - - tempPath = rootPath; - - if ( GetChildMode ( tempPath, "INDEX.XML" ) != kFMode_IsFile ) return false; - if ( GetChildMode ( tempPath, "DISCMETA.XML" ) != kFMode_IsFile ) return false; - if ( GetChildMode ( tempPath, "MEDIAPRO.XML" ) != kFMode_IsFile ) return false; - - tempPath += kDirChar; - tempPath += "Clip"; // ! Yes, mixed case. - tempPath += kDirChar; - tempPath += clipName; - tempPath += "M01.XML"; - if ( GetFileMode ( tempPath.c_str() ) != kFMode_IsFile ) return false; - - tempPath = rootPath; - tempPath += kDirChar; - tempPath += "FAM"; - tempPath += kDirChar; - tempPath += clipName; - - } else { - - if ( (format != kXMP_XDCAM_SAMFile) && (format != kXMP_UnknownFile) ) return false; - - // We already know about the PROAV folder, just check below it. - - tempPath = rootPath; - tempPath += kDirChar; - tempPath += "PROAV"; - - if ( GetChildMode ( tempPath, "INDEX.XML" ) != kFMode_IsFile ) return false; - if ( GetChildMode ( tempPath, "DISCMETA.XML" ) != kFMode_IsFile ) return false; - if ( GetChildMode ( tempPath, "DISCINFO.XML" ) != kFMode_IsFile ) return false; - if ( GetChildMode ( tempPath, "CLPR" ) != kFMode_IsFolder ) return false; - - tempPath += kDirChar; - tempPath += "CLPR"; - tempPath += kDirChar; - tempPath += clipName; - if ( GetFileMode ( tempPath.c_str() ) != kFMode_IsFolder ) return false; - - tempPath += kDirChar; - tempPath += clipName; - tempPath += "M01.XML"; - if ( GetFileMode ( tempPath.c_str() ) != kFMode_IsFile ) return false; - - tempPath = rootPath; - tempPath += kDirChar; - tempPath += "SAM"; - tempPath += kDirChar; - tempPath += clipName; - - } - - // Save the pseudo-path for the handler object. A bit of a hack, but the only way to get info - // from here to there. - - size_t pathLen = tempPath.size() + 1; // Include a terminating nul. - parent->tempPtr = malloc ( pathLen ); - if ( parent->tempPtr == 0 ) XMP_Throw ( "No memory for XDCAM clip info", kXMPErr_NoMemory ); - memcpy ( parent->tempPtr, tempPath.c_str(), pathLen ); // AUDIT: Safe, allocated above. - - return true; - -} // XDCAM_CheckFormat - -// ================================================================================================= -// XDCAM_MetaHandlerCTor -// ===================== - -XMPFileHandler * XDCAM_MetaHandlerCTor ( XMPFiles * parent ) -{ - return new XDCAM_MetaHandler ( parent ); - -} // XDCAM_MetaHandlerCTor - -// ================================================================================================= -// XDCAM_MetaHandler::XDCAM_MetaHandler -// ==================================== - -XDCAM_MetaHandler::XDCAM_MetaHandler ( XMPFiles * _parent ) : isFAM(false), expat(0) -{ - - this->parent = _parent; // Inherited, can't set in the prefix. - this->handlerFlags = kXDCAM_HandlerFlags; - this->stdCharForm = kXMP_Char8Bit; - - // Extract the root path, clip name, and FAM/SAM flag from tempPtr. - - XMP_Assert ( this->parent->tempPtr != 0 ); - - this->rootPath.assign ( (char*) this->parent->tempPtr ); - free ( this->parent->tempPtr ); - this->parent->tempPtr = 0; - - SplitLeafName ( &this->rootPath, &this->clipName ); - - std::string temp; - SplitLeafName ( &this->rootPath, &temp ); - XMP_Assert ( (temp == "FAM") || (temp == "SAM") ); - if ( temp == "FAM" ) this->isFAM = true; - XMP_Assert ( this->isFAM ? (this->parent->format == kXMP_XDCAM_FAMFile) : (this->parent->format == kXMP_XDCAM_SAMFile) ); - -} // XDCAM_MetaHandler::XDCAM_MetaHandler - -// ================================================================================================= -// XDCAM_MetaHandler::~XDCAM_MetaHandler -// ===================================== - -XDCAM_MetaHandler::~XDCAM_MetaHandler() -{ - - this->CleanupLegacyXML(); - if ( this->parent->tempPtr != 0 ) { - free ( this->parent->tempPtr ); - this->parent->tempPtr = 0; - } - -} // XDCAM_MetaHandler::~XDCAM_MetaHandler - -// ================================================================================================= -// XDCAM_MetaHandler::MakeClipFilePath -// =================================== - -void XDCAM_MetaHandler::MakeClipFilePath ( std::string * path, XMP_StringPtr suffix ) -{ - - *path = this->rootPath; - *path += kDirChar; - - if ( this->isFAM ) { - *path += "Clip"; // ! Yes, mixed case. - } else { - *path += "PROAV"; - *path += kDirChar; - *path += "CLPR"; - *path += kDirChar; - *path += this->clipName; - } - - *path += kDirChar; - *path += this->clipName; - *path += suffix; - -} // XDCAM_MetaHandler::MakeClipFilePath - -// ================================================================================================= -// XDCAM_MetaHandler::MakeLegacyDigest -// =================================== - -// *** Early hack version. - -#define kHexDigits "0123456789ABCDEF" - -void XDCAM_MetaHandler::MakeLegacyDigest ( std::string * digestStr ) -{ - digestStr->erase(); - if ( this->clipMetadata == 0 ) return; // Bail if we don't have any legacy XML. - XMP_Assert ( this->expat != 0 ); - - XMP_StringPtr xdcNS = this->xdcNS.c_str(); - XML_NodePtr legacyContext, legacyProp; - - legacyContext = this->clipMetadata->GetNamedElement ( xdcNS, "Access" ); - if ( legacyContext == 0 ) return; - - MD5_CTX context; - unsigned char digestBin [16]; - MD5Init ( &context ); - - legacyProp = legacyContext->GetNamedElement ( xdcNS, "Creator" ); - if ( (legacyProp != 0) && legacyProp->IsLeafContentNode() && (! legacyProp->content.empty()) ) { - const XML_Node * xmlValue = legacyProp->content[0]; - MD5Update ( &context, (XMP_Uns8*)xmlValue->value.c_str(), (unsigned int)xmlValue->value.size() ); - } - - legacyProp = legacyContext->GetNamedElement ( xdcNS, "CreationDate" ); - if ( (legacyProp != 0) && legacyProp->IsLeafContentNode() && (! legacyProp->content.empty()) ) { - const XML_Node * xmlValue = legacyProp->content[0]; - MD5Update ( &context, (XMP_Uns8*)xmlValue->value.c_str(), (unsigned int)xmlValue->value.size() ); - } - - legacyProp = legacyContext->GetNamedElement ( xdcNS, "LastUpdateDate" ); - if ( (legacyProp != 0) && legacyProp->IsLeafContentNode() && (! legacyProp->content.empty()) ) { - const XML_Node * xmlValue = legacyProp->content[0]; - MD5Update ( &context, (XMP_Uns8*)xmlValue->value.c_str(), (unsigned int)xmlValue->value.size() ); - } - - MD5Final ( digestBin, &context ); - - char buffer [40]; - for ( int in = 0, out = 0; in < 16; in += 1, out += 2 ) { - XMP_Uns8 byte = digestBin[in]; - buffer[out] = kHexDigits [ byte >> 4 ]; - buffer[out+1] = kHexDigits [ byte & 0xF ]; - } - buffer[32] = 0; - digestStr->append ( buffer ); - -} // XDCAM_MetaHandler::MakeLegacyDigest - -// ================================================================================================= -// P2_MetaHandler::CleanupLegacyXML -// ================================ - -void XDCAM_MetaHandler::CleanupLegacyXML() -{ - - if ( this->expat != 0 ) { delete ( this->expat ); this->expat = 0; } - - clipMetadata = 0; // ! Was a pointer into the expat tree. - -} // XDCAM_MetaHandler::CleanupLegacyXML - -// ================================================================================================= -// XDCAM_MetaHandler::CacheFileData -// ================================ - -void XDCAM_MetaHandler::CacheFileData() -{ - XMP_Assert ( ! this->containsXMP ); - - // See if the clip's .XMP file exists. - - std::string xmpPath; - this->MakeClipFilePath ( &xmpPath, "M01.XMP" ); - if ( GetFileMode ( xmpPath.c_str() ) != kFMode_IsFile ) return; // No XMP. - - // Read the entire .XMP file. - - char openMode = 'r'; - if ( this->parent->openFlags & kXMPFiles_OpenForUpdate ) openMode = 'w'; - - LFA_FileRef xmpFile = LFA_Open ( xmpPath.c_str(), openMode ); - if ( xmpFile == 0 ) return; // The open failed. - - XMP_Int64 xmpLen = LFA_Measure ( xmpFile ); - if ( xmpLen > 100*1024*1024 ) { - XMP_Throw ( "XDCAM XMP is outrageously large", kXMPErr_InternalFailure ); // Sanity check. - } - - this->xmpPacket.erase(); - this->xmpPacket.reserve ( (size_t)xmpLen ); - this->xmpPacket.append ( (size_t)xmpLen, ' ' ); - - XMP_Int32 ioCount = LFA_Read ( xmpFile, (void*)this->xmpPacket.data(), (XMP_Int32)xmpLen, kLFA_RequireAll ); - XMP_Assert ( ioCount == xmpLen ); - - this->packetInfo.offset = 0; - this->packetInfo.length = (XMP_Int32)xmpLen; - FillPacketInfo ( this->xmpPacket, &this->packetInfo ); - - XMP_Assert ( this->parent->fileRef == 0 ); - if ( openMode == 'r' ) { - LFA_Close ( xmpFile ); - } else { - this->parent->fileRef = xmpFile; - } - - this->containsXMP = true; - -} // XDCAM_MetaHandler::CacheFileData - -// ================================================================================================= -// XDCAM_MetaHandler::ProcessXMP -// ============================= - -void XDCAM_MetaHandler::ProcessXMP() -{ - - // Some versions of gcc can't tolerate goto's across declarations. - // *** Better yet, avoid this cruft with self-cleaning objects. - #define CleanupAndExit \ - { \ - bool openForUpdate = XMP_OptionIsSet ( this->parent->openFlags, kXMPFiles_OpenForUpdate ); \ - if ( ! openForUpdate ) this->CleanupLegacyXML(); \ - return; \ - } - - if ( this->processedXMP ) return; - this->processedXMP = true; // Make sure only called once. - - if ( this->containsXMP ) { - this->xmpObj.ParseFromBuffer ( this->xmpPacket.c_str(), (XMP_StringLen)this->xmpPacket.size() ); - } - - // NonRealTimeMeta -> XMP by schema - std::string xmlPath, umid; - this->MakeClipFilePath ( &xmlPath, "M01.XML" ); - - AutoFile xmlFile; - xmlFile.fileRef = LFA_Open ( xmlPath.c_str(), 'r' ); - if ( xmlFile.fileRef == 0 ) return; // The open failed. - - this->expat = XMP_NewExpatAdapter ( ExpatAdapter::kUseLocalNamespaces ); - if ( this->expat == 0 ) XMP_Throw ( "XDCAM_MetaHandler: Can't create Expat adapter", kXMPErr_NoMemory ); - - XMP_Uns8 buffer [64*1024]; - while ( true ) { - XMP_Int32 ioCount = LFA_Read ( xmlFile.fileRef, buffer, sizeof(buffer) ); - if ( ioCount == 0 ) break; - this->expat->ParseBuffer ( buffer, ioCount, false /* not the end */ ); - } - this->expat->ParseBuffer ( 0, 0, true ); // End the parse. - - LFA_Close ( xmlFile.fileRef ); - xmlFile.fileRef = 0; - - // The root element should be NonRealTimeMeta in some namespace. Take whatever this file uses. - - XML_Node & xmlTree = this->expat->tree; - XML_NodePtr rootElem = 0; - - for ( size_t i = 0, limit = xmlTree.content.size(); i < limit; ++i ) { - if ( xmlTree.content[i]->kind == kElemNode ) { - rootElem = xmlTree.content[i]; - } - } - - if ( rootElem == 0 ) CleanupAndExit - XMP_StringPtr rootLocalName = rootElem->name.c_str() + rootElem->nsPrefixLen; - if ( ! XMP_LitMatch ( rootLocalName, "NonRealTimeMeta" ) ) CleanupAndExit - - this->legacyNS = rootElem->ns; - - // Check the legacy digest. - - XMP_StringPtr legacyNS = this->legacyNS.c_str(); - - this->clipMetadata = rootElem; // ! Save the NonRealTimeMeta pointer for other use. - - std::string oldDigest, newDigest; - bool digestFound = this->xmpObj.GetStructField ( kXMP_NS_XMP, "NativeDigests", kXMP_NS_XMP, "XDCAM", &oldDigest, 0 ); - if ( digestFound ) { - this->MakeLegacyDigest ( &newDigest ); - if ( oldDigest == newDigest ) CleanupAndExit - } - - // If we get here we need find and import the actual legacy elements using the current namespace. - // Either there is no old digest in the XMP, or the digests differ. In the former case keep any - // existing XMP, in the latter case take new legacy values. - - this->containsXMP = XDCAM_Support::GetLegacyMetaData ( &this->xmpObj, rootElem, legacyNS, digestFound, umid ); - - CleanupAndExit - #undef CleanupAndExit - -} // XDCAM_MetaHandler::ProcessXMP - -// ================================================================================================= -// XDCAM_MetaHandler::UpdateFile -// ============================= -// -// Note that UpdateFile is only called from XMPFiles::CloseFile, so it is OK to close the file here. - -void XDCAM_MetaHandler::UpdateFile ( bool doSafeUpdate ) -{ - if ( ! this->needsUpdate ) return; - this->needsUpdate = false; // Make sure only called once. - - LFA_FileRef oldFile = 0; - std::string filePath, tempPath; - - // Update the internal legacy XML tree if we have one, and set the digest in the XMP. - - bool updateLegacyXML = false; - - if ( this->clipMetadata != 0 ) { - updateLegacyXML = XDCAM_Support::SetLegacyMetaData ( this->clipMetadata, &this->xmpObj, this->legacyNS.c_str()); - } - - std::string newDigest; - this->MakeLegacyDigest ( &newDigest ); - this->xmpObj.SetStructField ( kXMP_NS_XMP, "NativeDigests", kXMP_NS_XMP, "XDCAM", newDigest.c_str(), kXMP_DeleteExisting ); - this->xmpObj.SerializeToBuffer ( &this->xmpPacket, this->GetSerializeOptions() ); - - // Update the legacy XML file if necessary. - - if ( updateLegacyXML ) { - - std::string legacyXML; - this->expat->tree.Serialize ( &legacyXML ); - - this->MakeClipFilePath ( &filePath, "M01.XML" ); - oldFile = LFA_Open ( filePath.c_str(), 'w' ); - - if ( oldFile == 0 ) { - - // The XML does not exist yet. - - this->MakeClipFilePath ( &filePath, "M01.XML" ); - oldFile = LFA_Create ( filePath.c_str() ); - if ( oldFile == 0 ) XMP_Throw ( "Failure creating XDCAMEX legacy XML file", kXMPErr_ExternalFailure ); - LFA_Write ( oldFile, legacyXML.data(), (XMP_StringLen)legacyXML.size() ); - LFA_Close ( oldFile ); - - } else if ( ! doSafeUpdate ) { - - // Over write the existing XML file. - - LFA_Seek ( oldFile, 0, SEEK_SET ); - LFA_Truncate ( oldFile, 0 ); - LFA_Write ( oldFile, legacyXML.data(), (XMP_StringLen)legacyXML.size() ); - LFA_Close ( oldFile ); - - } else { - - // Do a safe update. - - // *** We really need an LFA_SwapFiles utility. - - this->MakeClipFilePath ( &filePath, "M01.XML" ); - - CreateTempFile ( filePath, &tempPath ); - LFA_FileRef tempFile = LFA_Open ( tempPath.c_str(), 'w' ); - LFA_Write ( tempFile, legacyXML.data(), (XMP_StringLen)legacyXML.size() ); - LFA_Close ( tempFile ); - - LFA_Close ( oldFile ); - LFA_Delete ( filePath.c_str() ); - LFA_Rename ( tempPath.c_str(), filePath.c_str() ); - - } - - } - - oldFile = this->parent->fileRef; - - if ( oldFile == 0 ) { - - // The XMP does not exist yet. - - std::string xmpPath; - this->MakeClipFilePath ( &xmpPath, "M01.XMP" ); - - LFA_FileRef xmpFile = LFA_Create ( xmpPath.c_str() ); - if ( xmpFile == 0 ) XMP_Throw ( "Failure creating XDCAM XMP file", kXMPErr_ExternalFailure ); - LFA_Write ( xmpFile, this->xmpPacket.data(), (XMP_StringLen)this->xmpPacket.size() ); - LFA_Close ( xmpFile ); - - } else if ( ! doSafeUpdate ) { - - // Over write the existing XMP file. - - LFA_Seek ( oldFile, 0, SEEK_SET ); - LFA_Truncate ( oldFile, 0 ); - LFA_Write ( oldFile, this->xmpPacket.data(), (XMP_StringLen)this->xmpPacket.size() ); - LFA_Close ( oldFile ); - - } else { - - // Do a safe update. - - // *** We really need an LFA_SwapFiles utility. - - std::string xmpPath, tempPath; - - this->MakeClipFilePath ( &xmpPath, "M01.XMP" ); - - CreateTempFile ( xmpPath, &tempPath ); - LFA_FileRef tempFile = LFA_Open ( tempPath.c_str(), 'w' ); - LFA_Write ( tempFile, this->xmpPacket.data(), (XMP_StringLen)this->xmpPacket.size() ); - LFA_Close ( tempFile ); - - LFA_Close ( oldFile ); - LFA_Delete ( xmpPath.c_str() ); - LFA_Rename ( tempPath.c_str(), xmpPath.c_str() ); - - } - - this->parent->fileRef = 0; - -} // XDCAM_MetaHandler::UpdateFile - -// ================================================================================================= -// XDCAM_MetaHandler::WriteFile -// ============================ - -void XDCAM_MetaHandler::WriteFile ( LFA_FileRef sourceRef, const std::string & sourcePath ) -{ - - // ! WriteFile is not supposed to be called for handlers that own the file. - XMP_Throw ( "XDCAM_MetaHandler::WriteFile should not be called", kXMPErr_InternalFailure ); - -} // XDCAM_MetaHandler::WriteFile - -// ================================================================================================= diff --git a/source/XMPFiles/FileHandlers/XDCAM_Handler.hpp b/source/XMPFiles/FileHandlers/XDCAM_Handler.hpp deleted file mode 100644 index 55f61dc..0000000 --- a/source/XMPFiles/FileHandlers/XDCAM_Handler.hpp +++ /dev/null @@ -1,82 +0,0 @@ -#ifndef __XDCAM_Handler_hpp__ -#define __XDCAM_Handler_hpp__ 1 - -// ================================================================================================= -// ADOBE SYSTEMS INCORPORATED -// Copyright 2007 Adobe Systems Incorporated -// 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 "XMP_Environment.h" // ! This must be the first include. - -#include "XMPFiles_Impl.hpp" - -#include "ExpatAdapter.hpp" - -// ================================================================================================= -/// \file XDCAM_Handler.hpp -/// \brief Folder format handler for XDCAM. -/// -/// This header ... -/// -// ================================================================================================= - -extern XMPFileHandler * XDCAM_MetaHandlerCTor ( XMPFiles * parent ); - -extern bool XDCAM_CheckFormat ( XMP_FileFormat format, - const std::string & rootPath, - const std::string & gpName, - const std::string & parentName, - const std::string & leafName, - XMPFiles * parent ); - -static const XMP_OptionBits kXDCAM_HandlerFlags = (kXMPFiles_CanInjectXMP | - kXMPFiles_CanExpand | - kXMPFiles_CanRewrite | - kXMPFiles_PrefersInPlace | - kXMPFiles_CanReconcile | - kXMPFiles_AllowsOnlyXMP | - kXMPFiles_ReturnsRawPacket | - kXMPFiles_HandlerOwnsFile | - kXMPFiles_AllowsSafeUpdate | - kXMPFiles_FolderBasedFormat); - -class XDCAM_MetaHandler : public XMPFileHandler -{ -public: - - void CacheFileData(); - void ProcessXMP(); - - XMP_OptionBits GetSerializeOptions() // *** These should be standard for standalone XMP files. - { return (kXMP_UseCompactFormat | kXMP_OmitPacketWrapper); }; - - void UpdateFile ( bool doSafeUpdate ); - void WriteFile ( LFA_FileRef sourceRef, const std::string & sourcePath ); - - XDCAM_MetaHandler ( XMPFiles * _parent ); - virtual ~XDCAM_MetaHandler(); - -private: - - XDCAM_MetaHandler() : isFAM(false), expat(0), clipMetadata(0) {}; // Hidden on purpose. - - void MakeClipFilePath ( std::string * path, XMP_StringPtr suffix ); - void MakeLegacyDigest ( std::string * digestStr ); - void CleanupLegacyXML(); - - std::string rootPath, clipName, xdcNS, legacyNS; - - bool isFAM; - - ExpatAdapter * expat; - XML_Node * clipMetadata; // ! Don't delete, points into the Expat tree. - -}; // XDCAM_MetaHandler - -// ================================================================================================= - -#endif /* __XDCAM_Handler_hpp__ */ diff --git a/source/XMPFiles/FormatSupport/ASF_Support.cpp b/source/XMPFiles/FormatSupport/ASF_Support.cpp deleted file mode 100644 index 1180f9d..0000000 --- a/source/XMPFiles/FormatSupport/ASF_Support.cpp +++ /dev/null @@ -1,1436 +0,0 @@ -// ================================================================================================= -// ADOBE SYSTEMS INCORPORATED -// Copyright 2006 Adobe Systems Incorporated -// 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 "ASF_Support.hpp" -#include "UnicodeConversions.hpp" - -#if XMP_WinBuild - #define snprintf _snprintf - #pragma warning ( disable : 4996 ) // '...' was declared deprecated - #pragma warning ( disable : 4267 ) // *** conversion (from size_t), possible loss of date (many 64 bit related) -#endif - -// ============================================================================================= - -// Platforms other than Win -#if ! XMP_WinBuild -int IsEqualGUID ( const GUID& guid1, const GUID& guid2 ) -{ - return (memcmp ( &guid1, &guid2, sizeof(GUID) ) == 0); -} -#endif - -ASF_Support::ASF_Support() : legacyManager(0), posFileSizeInfo(0) {} - -ASF_Support::ASF_Support ( ASF_LegacyManager* _legacyManager ) : posFileSizeInfo(0) -{ - legacyManager = _legacyManager; -} - -ASF_Support::~ASF_Support() -{ - legacyManager = 0; -} - -// ============================================================================================= - -long ASF_Support::OpenASF ( LFA_FileRef fileRef, ObjectState & inOutObjectState ) -{ - XMP_Uns64 pos = 0; - XMP_Uns64 len; - - try { - pos = LFA_Seek ( fileRef, 0, SEEK_SET ); - } catch ( ... ) {} - - if ( pos != 0 ) return 0; - - // read first and following chunks - while ( ReadObject ( fileRef, inOutObjectState, &len, pos) ) {} - - return inOutObjectState.objects.size(); - -} - -// ============================================================================================= - -bool ASF_Support::ReadObject ( LFA_FileRef fileRef, ObjectState & inOutObjectState, XMP_Uns64 * objectLength, XMP_Uns64 & inOutPosition ) -{ - - try { - - XMP_Uns64 startPosition = inOutPosition; - long bytesRead; - ASF_ObjectBase objectBase; - - bytesRead = LFA_Read ( fileRef, &objectBase, kASF_ObjectBaseLen, true ); - if ( bytesRead != kASF_ObjectBaseLen ) return false; - - *objectLength = GetUns64LE ( &objectBase.size ); - inOutPosition += *objectLength; - - ObjectData newObject; - - newObject.pos = startPosition; - newObject.len = *objectLength; - newObject.guid = objectBase.guid; - - // xmpIsLastObject indicates, that the XMP-object is the last top-level object - // reset here, if any another object is read - inOutObjectState.xmpIsLastObject = false; - - if ( IsEqualGUID ( ASF_Header_Object, newObject.guid ) ) { - - // header object ? - this->ReadHeaderObject ( fileRef, inOutObjectState, newObject ); - - } else if ( IsEqualGUID ( ASF_XMP_Metadata, newObject.guid ) ) { - - // check object for XMP GUID - inOutObjectState.xmpPos = newObject.pos + kASF_ObjectBaseLen; - inOutObjectState.xmpLen = newObject.len - kASF_ObjectBaseLen; - inOutObjectState.xmpIsLastObject = true; - inOutObjectState.xmpObject = newObject; - newObject.xmp = true; - - } - - inOutObjectState.objects.push_back ( newObject ); - - LFA_Seek ( fileRef, inOutPosition, SEEK_SET ); - - } catch ( ... ) { - - return false; - - } - - return true; - -} - -// ============================================================================================= - -bool ASF_Support::ReadHeaderObject ( LFA_FileRef fileRef, ObjectState& inOutObjectState, const ObjectData& newObject ) -{ - if ( ! IsEqualGUID ( ASF_Header_Object, newObject.guid) || (! legacyManager ) ) return false; - - std::string buffer; - - legacyManager->SetPadding(0); - - try { - - // read header-object structure - XMP_Uns64 pos = newObject.pos; - XMP_Uns32 bufferSize = kASF_ObjectBaseLen + 6; - - buffer.clear(); - buffer.reserve ( bufferSize ); - buffer.assign ( bufferSize, ' ' ); - LFA_Seek ( fileRef, pos, SEEK_SET ); - LFA_Read ( fileRef, const_cast<char*>(buffer.data()), bufferSize, true ); - - XMP_Uns64 read = bufferSize; - pos += bufferSize; - - // read contained header objects - XMP_Uns32 numberOfHeaders = GetUns32LE ( &buffer[24] ); - ASF_ObjectBase objectBase; - - while ( read < newObject.len ) { - - LFA_Seek ( fileRef, pos, SEEK_SET ); - if ( kASF_ObjectBaseLen != LFA_Read ( fileRef, &objectBase, kASF_ObjectBaseLen, true ) ) break; - - LFA_Seek ( fileRef, pos, SEEK_SET ); - objectBase.size = GetUns64LE ( &objectBase.size ); - - if ( IsEqualGUID ( ASF_File_Properties_Object, objectBase.guid) && (objectBase.size >= 104 ) ) { - - buffer.clear(); - buffer.reserve ( XMP_Uns32( objectBase.size ) ); - buffer.assign ( XMP_Uns32( objectBase.size ), ' ' ); - LFA_Read ( fileRef, const_cast<char*>(buffer.data()), XMP_Int32(objectBase.size), true ); - - // save position of filesize-information - posFileSizeInfo = (pos + 40); - - // creation date - std::string sub ( buffer.substr ( 48, 8 ) ); - legacyManager->SetField ( ASF_LegacyManager::fieldCreationDate, sub ); - - // broadcast flag set ? - XMP_Uns32 flags = GetUns32LE ( &buffer[88] ); - inOutObjectState.broadcast = (flags & 1); - legacyManager->SetBroadcast ( inOutObjectState.broadcast ); - - legacyManager->SetObjectExists ( ASF_LegacyManager::objectFileProperties ); - - } else if ( IsEqualGUID ( ASF_Content_Description_Object, objectBase.guid) && (objectBase.size >= 34 ) ) { - - buffer.clear(); - buffer.reserve ( XMP_Uns32( objectBase.size ) ); - buffer.assign ( XMP_Uns32( objectBase.size ), ' ' ); - LFA_Read ( fileRef, const_cast<char*>(buffer.data()), XMP_Int32(objectBase.size), true ); - - XMP_Uns16 titleLen = GetUns16LE ( &buffer[24] ); - XMP_Uns16 authorLen = GetUns16LE ( &buffer[26] ); - XMP_Uns16 copyrightLen = GetUns16LE ( &buffer[28] ); - XMP_Uns16 descriptionLen = GetUns16LE ( &buffer[30] ); - XMP_Uns16 ratingLen = GetUns16LE ( &buffer[32] ); - - XMP_Uns16 fieldPos = 34; - - std::string titleStr = buffer.substr ( fieldPos, titleLen ); - fieldPos += titleLen; - legacyManager->SetField ( ASF_LegacyManager::fieldTitle, titleStr ); - - std::string authorStr = buffer.substr ( fieldPos, authorLen ); - fieldPos += authorLen; - legacyManager->SetField ( ASF_LegacyManager::fieldAuthor, authorStr ); - - std::string copyrightStr = buffer.substr ( fieldPos, copyrightLen ); - fieldPos += copyrightLen; - legacyManager->SetField ( ASF_LegacyManager::fieldCopyright, copyrightStr ); - - std::string descriptionStr = buffer.substr ( fieldPos, descriptionLen ); - fieldPos += descriptionLen; - legacyManager->SetField ( ASF_LegacyManager::fieldDescription, descriptionStr ); - - /* rating is currently not part of reconciliation - std::string ratingStr = buffer.substr ( fieldPos, ratingLen ); - fieldPos += ratingLen; - legacyData.append ( titleStr ); - */ - - legacyManager->SetObjectExists ( ASF_LegacyManager::objectContentDescription ); - - } else if ( IsEqualGUID ( ASF_Content_Branding_Object, objectBase.guid ) ) { - - buffer.clear(); - buffer.reserve ( XMP_Uns32( objectBase.size ) ); - buffer.assign ( XMP_Uns32( objectBase.size ), ' ' ); - LFA_Read ( fileRef, const_cast<char*>(buffer.data()), XMP_Int32(objectBase.size), true ); - - XMP_Uns32 fieldPos = 28; - - // copyright URL is 3. element with variable size - for ( int i = 1; i <= 3 ; ++i ) { - XMP_Uns32 len = GetUns32LE ( &buffer[fieldPos] ); - if ( i == 3 ) { - std::string copyrightURLStr = buffer.substr ( fieldPos + 4, len ); - legacyManager->SetField ( ASF_LegacyManager::fieldCopyrightURL, copyrightURLStr ); - } - fieldPos += (len + 4); - } - - legacyManager->SetObjectExists ( ASF_LegacyManager::objectContentBranding ); - -#if ! Exclude_LicenseURL_Recon - - } else if ( IsEqualGUID ( ASF_Content_Encryption_Object, objectBase.guid ) ) { - - buffer.clear(); - buffer.reserve ( XMP_Uns32( objectBase.size ) ); - buffer.assign ( XMP_Uns32( objectBase.size ), ' ' ); - LFA_Read ( fileRef, const_cast<char*>(buffer.data()), XMP_Int32(objectBase.size), true ); - - XMP_Uns32 fieldPos = 24; - - // license URL is 4. element with variable size - for ( int i = 1; i <= 4 ; ++i ) { - XMP_Uns32 len = GetUns32LE ( &buffer[fieldPos] ); - if ( i == 4 ) { - std::string licenseURLStr = buffer.substr ( fieldPos + 4, len ); - legacyManager->SetField ( ASF_LegacyManager::fieldLicenseURL, licenseURLStr ); - } - fieldPos += (len + 4); - } - - legacyManager->SetObjectExists ( objectContentEncryption ); - -#endif - - } else if ( IsEqualGUID ( ASF_Padding_Object, objectBase.guid ) ) { - - legacyManager->SetPadding ( legacyManager->GetPadding() + (objectBase.size - 24) ); - - } else if ( IsEqualGUID ( ASF_Header_Extension_Object, objectBase.guid ) ) { - - this->ReadHeaderExtensionObject ( fileRef, inOutObjectState, pos, objectBase ); - - } - - pos += objectBase.size; - read += objectBase.size; - } - - } catch ( ... ) { - - return false; - - } - - legacyManager->ComputeDigest(); - - return true; -} - -// ============================================================================================= - -bool ASF_Support::WriteHeaderObject ( LFA_FileRef sourceRef, LFA_FileRef destRef, const ObjectData& object, ASF_LegacyManager& _legacyManager, bool usePadding ) -{ - if ( ! IsEqualGUID ( ASF_Header_Object, object.guid ) ) return false; - - bool ret = false; - - std::string buffer; - XMP_Uns16 valueUns16LE; - XMP_Uns32 valueUns32LE; - XMP_Uns64 valueUns64LE; - - try { - - // read header-object structure - XMP_Uns64 pos = object.pos; - XMP_Uns32 bufferSize = kASF_ObjectBaseLen + 6; - - buffer.clear(); - buffer.reserve ( bufferSize ); - buffer.assign ( bufferSize, ' ' ); - LFA_Seek ( sourceRef, pos, SEEK_SET ); - LFA_Read ( sourceRef, const_cast<char*>(buffer.data()), bufferSize, true ); - - XMP_Uns64 read = bufferSize; - pos += bufferSize; - - // read contained header objects - XMP_Uns32 numberOfHeaders = GetUns32LE ( &buffer[24] ); - ASF_ObjectBase objectBase; - - // prepare new header in memory - std::string header; - - int changedObjects = _legacyManager.changedObjects(); - int exportedObjects = 0; - int writtenObjects = 0; - - header.append ( buffer.c_str(), bufferSize ); - - while ( read < object.len ) { - - LFA_Seek ( sourceRef, pos, SEEK_SET ); - if ( kASF_ObjectBaseLen != LFA_Read ( sourceRef, &objectBase, kASF_ObjectBaseLen, true ) ) break; - - LFA_Seek ( sourceRef, pos, SEEK_SET ); - objectBase.size = GetUns64LE ( &objectBase.size ); - - int headerStartPos = header.size(); - - // save position of filesize-information - if ( IsEqualGUID ( ASF_File_Properties_Object, objectBase.guid ) ) { - posFileSizeInfo = (headerStartPos + 40); - } - - // write objects - if ( IsEqualGUID ( ASF_File_Properties_Object, objectBase.guid ) && - (objectBase.size >= 104) && (changedObjects & ASF_LegacyManager::objectFileProperties) ) { - - // copy object and replace creation-date - buffer.reserve ( XMP_Uns32 ( objectBase.size ) ); - buffer.assign ( XMP_Uns32 ( objectBase.size ), ' ' ); - LFA_Read ( sourceRef, const_cast<char*>(buffer.data()), XMP_Int32(objectBase.size), true ); - header.append ( buffer, 0, XMP_Uns32( objectBase.size ) ); - - if ( ! _legacyManager.GetBroadcast() ) { - buffer = _legacyManager.GetField ( ASF_LegacyManager::fieldCreationDate ); - ReplaceString ( header, buffer, (headerStartPos + 48), 8 ); - } - - exportedObjects |= ASF_LegacyManager::objectFileProperties; - - } else if ( IsEqualGUID ( ASF_Content_Description_Object, objectBase.guid ) && - (objectBase.size >= 34) && (changedObjects & ASF_LegacyManager::objectContentDescription) ) { - - // re-create object with xmp-data - buffer.reserve ( XMP_Uns32( objectBase.size ) ); - buffer.assign ( XMP_Uns32( objectBase.size ), ' ' ); - LFA_Read ( sourceRef, const_cast<char*>(buffer.data()), XMP_Int32(objectBase.size), true ); - // write header only - header.append ( buffer, 0, XMP_Uns32( kASF_ObjectBaseLen ) ); - - // write length fields - - XMP_Uns16 titleLen = _legacyManager.GetField ( ASF_LegacyManager::fieldTitle).size( ); - valueUns16LE = MakeUns16LE ( titleLen ); - header.append ( (const char*)&valueUns16LE, 2 ); - - XMP_Uns16 authorLen = _legacyManager.GetField ( ASF_LegacyManager::fieldAuthor).size( ); - valueUns16LE = MakeUns16LE ( authorLen ); - header.append ( (const char*)&valueUns16LE, 2 ); - - XMP_Uns16 copyrightLen = _legacyManager.GetField ( ASF_LegacyManager::fieldCopyright).size( ); - valueUns16LE = MakeUns16LE ( copyrightLen ); - header.append ( (const char*)&valueUns16LE, 2 ); - - XMP_Uns16 descriptionLen = _legacyManager.GetField ( ASF_LegacyManager::fieldDescription).size( ); - valueUns16LE = MakeUns16LE ( descriptionLen ); - header.append ( (const char*)&valueUns16LE, 2 ); - - // retrieve existing overall length of preceding fields - XMP_Uns16 precedingLen = 0; - precedingLen += GetUns16LE ( &buffer[24] ); // Title - precedingLen += GetUns16LE ( &buffer[26] ); // Author - precedingLen += GetUns16LE ( &buffer[28] ); // Copyright - precedingLen += GetUns16LE ( &buffer[30] ); // Description - // retrieve existing 'Rating' length - XMP_Uns16 ratingLen = GetUns16LE ( &buffer[32] ); // Rating - valueUns16LE = MakeUns16LE ( ratingLen ); - header.append ( (const char*)&valueUns16LE, 2 ); - - // write field contents - - header.append ( _legacyManager.GetField ( ASF_LegacyManager::fieldTitle ) ); - header.append ( _legacyManager.GetField ( ASF_LegacyManager::fieldAuthor ) ); - header.append ( _legacyManager.GetField ( ASF_LegacyManager::fieldCopyright ) ); - header.append ( _legacyManager.GetField ( ASF_LegacyManager::fieldDescription ) ); - header.append ( buffer, (34 + precedingLen), ratingLen ); - - // update new object size - valueUns64LE = MakeUns64LE ( header.size() - headerStartPos ); - std::string newSize ( (const char*)&valueUns64LE, 8 ); - ReplaceString ( header, newSize, (headerStartPos + 16), 8 ); - - exportedObjects |= ASF_LegacyManager::objectContentDescription; - - } else if ( IsEqualGUID ( ASF_Content_Branding_Object, objectBase.guid ) && - (changedObjects & ASF_LegacyManager::objectContentBranding) ) { - - // re-create object with xmp-data - buffer.reserve ( XMP_Uns32( objectBase.size ) ); - buffer.assign ( XMP_Uns32( objectBase.size ), ' ' ); - LFA_Read ( sourceRef, const_cast<char*>(buffer.data()), XMP_Int32(objectBase.size), true ); - - // calculate size of fields coming before 'Copyright URL' - XMP_Uns32 length = 28; - length += (GetUns32LE ( &buffer[length] ) + 4); // Banner Image Data - length += (GetUns32LE ( &buffer[length] ) + 4); // Banner Image URL - - // write first part of header - header.append ( buffer, 0, length ); - - // copyright URL - length = _legacyManager.GetField ( ASF_LegacyManager::fieldCopyrightURL).size( ); - valueUns32LE = MakeUns32LE ( length ); - header.append ( (const char*)&valueUns32LE, 4 ); - header.append ( _legacyManager.GetField ( ASF_LegacyManager::fieldCopyrightURL ) ); - - // update new object size - valueUns64LE = MakeUns64LE ( header.size() - headerStartPos ); - std::string newSize ( (const char*)&valueUns64LE, 8 ); - ReplaceString ( header, newSize, (headerStartPos + 16), 8 ); - - exportedObjects |= ASF_LegacyManager::objectContentBranding; - -#if ! Exclude_LicenseURL_Recon - - } else if ( IsEqualGUID ( ASF_Content_Encryption_Object, objectBase.guid ) && - (changedObjects & ASF_LegacyManager::objectContentEncryption) ) { - - // re-create object with xmp-data - buffer.reserve ( XMP_Uns32( objectBase.size ) ); - buffer.assign ( XMP_Uns32( objectBase.size ), ' ' ); - LFA_Read ( sourceRef, const_cast<char*>(buffer.data()), XMP_Int32(objectBase.size), true ); - - // calculate size of fields coming before 'License URL' - XMP_Uns32 length = 24; - length += (GetUns32LE ( &buffer[length] ) + 4); // Secret Data - length += (GetUns32LE ( &buffer[length] ) + 4); // Protection Type - length += (GetUns32LE ( &buffer[length] ) + 4); // Key ID - - // write first part of header - header.append ( buffer, 0, length ); - - // License URL - length = _legacyManager.GetField ( ASF_LegacyManager::fieldLicenseURL).size( ); - valueUns32LE = MakeUns32LE ( length ); - header.append ( (const char*)&valueUns32LE, 4 ); - header.append ( _legacyManager.GetField ( ASF_LegacyManager::fieldLicenseURL ) ); - - // update new object size - valueUns64LE = MakeUns64LE ( header.size() - headerStartPos ); - std::string newSize ( (const char*)&valueUns64LE, 8 ); - ReplaceString ( header, newSize, (headerStartPos + 16), 8 ); - - exportedObjects |= ASF_LegacyManager::objectContentEncryption; - -#endif - - } else if ( IsEqualGUID ( ASF_Header_Extension_Object, objectBase.guid ) && usePadding ) { - - // re-create object if padding needs to be used - buffer.reserve ( XMP_Uns32( objectBase.size ) ); - buffer.assign ( XMP_Uns32( objectBase.size ), ' ' ); - LFA_Read ( sourceRef, const_cast<char*>(buffer.data()), XMP_Int32(objectBase.size), true ); - - ASF_Support::WriteHeaderExtensionObject ( buffer, &header, objectBase, 0 ); - - } else if ( IsEqualGUID ( ASF_Padding_Object, objectBase.guid ) && usePadding ) { - - // eliminate padding (will be created as last object) - - } else { - - // simply copy all other objects - buffer.reserve ( XMP_Uns32( objectBase.size ) ); - buffer.assign ( XMP_Uns32( objectBase.size ), ' ' ); - LFA_Read ( sourceRef, const_cast<char*>(buffer.data()), XMP_Int32(objectBase.size), true ); - - header.append ( buffer, 0, XMP_Uns32( objectBase.size ) ); - - } - - pos += objectBase.size; - read += objectBase.size; - - writtenObjects ++; - - } - - // any objects to create ? - int newObjects = (changedObjects ^ exportedObjects); - - if ( newObjects ) { - - // create new objects with xmp-data - int headerStartPos; - ASF_ObjectBase newObjectBase; - XMP_Uns32 length; - - if ( newObjects & ASF_LegacyManager::objectContentDescription ) { - - headerStartPos = header.size(); - newObjectBase.guid = ASF_Content_Description_Object; - newObjectBase.size = 0; - - // write object header - header.append ( (const char*)&newObjectBase, kASF_ObjectBaseLen ); - - XMP_Uns16 titleLen = _legacyManager.GetField ( ASF_LegacyManager::fieldTitle).size( ); - valueUns16LE = MakeUns16LE ( titleLen ); - header.append ( (const char*)&valueUns16LE, 2 ); - - XMP_Uns16 authorLen = _legacyManager.GetField ( ASF_LegacyManager::fieldAuthor).size( ); - valueUns16LE = MakeUns16LE ( authorLen ); - header.append ( (const char*)&valueUns16LE, 2 ); - - XMP_Uns16 copyrightLen = _legacyManager.GetField ( ASF_LegacyManager::fieldCopyright).size( ); - valueUns16LE = MakeUns16LE ( copyrightLen ); - header.append ( (const char*)&valueUns16LE, 2 ); - - XMP_Uns16 descriptionLen = _legacyManager.GetField ( ASF_LegacyManager::fieldDescription).size( ); - valueUns16LE = MakeUns16LE ( descriptionLen ); - header.append ( (const char*)&valueUns16LE, 2 ); - - XMP_Uns16 ratingLen = 0; - valueUns16LE = MakeUns16LE ( ratingLen ); - header.append ( (const char*)&valueUns16LE, 2 ); - - // write field contents - - header.append ( _legacyManager.GetField ( ASF_LegacyManager::fieldTitle ) ); - header.append ( _legacyManager.GetField ( ASF_LegacyManager::fieldAuthor ) ); - header.append ( _legacyManager.GetField ( ASF_LegacyManager::fieldCopyright ) ); - header.append ( _legacyManager.GetField ( ASF_LegacyManager::fieldDescription ) ); - - // update new object size - valueUns64LE = MakeUns64LE ( header.size() - headerStartPos ); - std::string newSize ( (const char*)&valueUns64LE, 8 ); - ReplaceString ( header, newSize, (headerStartPos + 16), 8 ); - - newObjects &= ~ASF_LegacyManager::objectContentDescription; - - writtenObjects ++; - - } - - if ( newObjects & ASF_LegacyManager::objectContentBranding ) { - - headerStartPos = header.size(); - newObjectBase.guid = ASF_Content_Branding_Object; - newObjectBase.size = 0; - - // write object header - header.append ( (const char*)&newObjectBase, kASF_ObjectBaseLen ); - - // write 'empty' fields - header.append ( 12, '\0' ); - - // copyright URL - length = _legacyManager.GetField ( ASF_LegacyManager::fieldCopyrightURL).size( ); - valueUns32LE = MakeUns32LE ( length ); - header.append ( (const char*)&valueUns32LE, 4 ); - header.append ( _legacyManager.GetField ( ASF_LegacyManager::fieldCopyrightURL ) ); - - // update new object size - valueUns64LE = MakeUns64LE ( header.size() - headerStartPos ); - std::string newSize ( (const char*)&valueUns64LE, 8 ); - ReplaceString ( header, newSize, (headerStartPos + 16), 8 ); - - newObjects &= ~ASF_LegacyManager::objectContentBranding; - - writtenObjects ++; - - } - -#if ! Exclude_LicenseURL_Recon - - if ( newObjects & ASF_LegacyManager::objectContentEncryption ) { - - headerStartPos = header.size(); - newObjectBase.guid = ASF_Content_Encryption_Object; - newObjectBase.size = 0; - - // write object header - header.append ( (const char*)&newObjectBase, kASF_ObjectBaseLen ); - - // write 'empty' fields - header.append ( 12, '\0' ); - - // License URL - length = _legacyManager.GetField ( ASF_LegacyManager::fieldLicenseURL).size( ); - valueUns32LE = MakeUns32LE ( length ); - header.append ( (const char*)&valueUns32LE, 4 ); - header.append ( _legacyManager.GetField ( ASF_LegacyManager::fieldLicenseURL ) ); - - // update new object size - valueUns64LE = MakeUns64LE ( header.size() - headerStartPos ); - std::string newSize ( (const char*)&valueUns64LE, 8 ); - ReplaceString ( header, newSize, (headerStartPos + 16), 8 ); - - newObjects &= ~ASF_LegacyManager::objectContentEncryption; - - writtenObjects ++; - - } - -#endif - - } - - // create padding object ? - if ( usePadding && (header.size ( ) < object.len ) ) { - ASF_Support::CreatePaddingObject ( &header, (object.len - header.size()) ); - writtenObjects ++; - } - - // update new header-object size - valueUns64LE = MakeUns64LE ( header.size() ); - std::string newValue ( (const char*)&valueUns64LE, 8 ); - ReplaceString ( header, newValue, 16, 8 ); - - // update new number of Header objects - valueUns32LE = MakeUns32LE ( writtenObjects ); - newValue = std::string ( (const char*)&valueUns32LE, 4 ); - ReplaceString ( header, newValue, 24, 4 ); - - // if we are operating on the same file (in-place update), place pointer before writing - if ( sourceRef == destRef ) LFA_Seek ( destRef, object.pos, SEEK_SET ); - - // write header - LFA_Write ( destRef, header.c_str(), header.size() ); - - } catch ( ... ) { - - ret = false; - - } - - return ret; - -} - -// ============================================================================================= - -bool ASF_Support::UpdateHeaderObject ( LFA_FileRef fileRef, const ObjectData& object, ASF_LegacyManager& _legacyManager ) -{ - return ASF_Support::WriteHeaderObject ( fileRef, fileRef, object, _legacyManager, true ); -} - -// ============================================================================================= - -bool ASF_Support::UpdateFileSize ( LFA_FileRef fileRef ) -{ - if ( fileRef == 0 ) return false; - - XMP_Uns64 posCurrent = LFA_Seek ( fileRef, 0, SEEK_CUR ); - XMP_Uns64 newSizeLE = MakeUns64LE ( LFA_Measure ( fileRef ) ); - - if ( this->posFileSizeInfo != 0 ) { - - LFA_Seek ( fileRef, this->posFileSizeInfo, SEEK_SET ); - - } else { - - // The position of the file size field is not known, find it. - - ASF_ObjectBase objHeader; - - // Read the Header object at the start of the file. - - LFA_Seek ( fileRef, 0, SEEK_SET ); - LFA_Read ( fileRef, &objHeader, kASF_ObjectBaseLen, kLFA_RequireAll ); - if ( ! IsEqualGUID ( ASF_Header_Object, objHeader.guid ) ) return false; - - XMP_Uns32 childCount; - LFA_Read ( fileRef, &childCount, 4, kLFA_RequireAll ); - childCount = GetUns32LE ( &childCount ); - - LFA_Seek ( fileRef, 2, SEEK_CUR ); // Skip the 2 reserved bytes. - - // Look for the File Properties object in the Header's children. - - for ( ; childCount > 0; --childCount ) { - LFA_Read ( fileRef, &objHeader, kASF_ObjectBaseLen, kLFA_RequireAll ); - if ( IsEqualGUID ( ASF_File_Properties_Object, objHeader.guid ) ) break; - XMP_Uns64 dataLen = GetUns64LE ( &objHeader.size ) - 24; - LFA_Seek ( fileRef, dataLen, SEEK_CUR ); // Skip this object's data. - } - if ( childCount == 0 ) return false; - - // Seek to the file size field. - - XMP_Uns64 fpoSize = GetUns64LE ( &objHeader.size ); - if ( fpoSize < (16+8+16+8) ) return false; - LFA_Seek ( fileRef, 16, SEEK_CUR ); // Skip to the file size field. - - } - - LFA_Write ( fileRef, &newSizeLE, 8 ); // Write the new file size. - - LFA_Seek ( fileRef, posCurrent, SEEK_SET ); - return true; - -} - -// ============================================================================================= - -bool ASF_Support::ReadHeaderExtensionObject ( LFA_FileRef fileRef, ObjectState& inOutObjectState, const XMP_Uns64& _pos, const ASF_ObjectBase& _objectBase ) -{ - if ( ! IsEqualGUID ( ASF_Header_Extension_Object, _objectBase.guid) || (! legacyManager ) ) return false; - - try { - - // read extended header-object structure beginning at the data part (offset = 46) - const XMP_Uns64 offset = 46; - XMP_Uns64 read = 0; - XMP_Uns64 data = (_objectBase.size - offset); - XMP_Uns64 pos = (_pos + offset); - - ASF_ObjectBase objectBase; - - while ( read < data ) { - - LFA_Seek ( fileRef, pos, SEEK_SET ); - if ( kASF_ObjectBaseLen != LFA_Read ( fileRef, &objectBase, kASF_ObjectBaseLen, true ) ) break; - - objectBase.size = GetUns64LE ( &objectBase.size ); - - if ( IsEqualGUID ( ASF_Padding_Object, objectBase.guid ) ) { - legacyManager->SetPadding ( legacyManager->GetPadding() + (objectBase.size - 24) ); - } - - pos += objectBase.size; - read += objectBase.size; - - } - - } catch ( ... ) { - - return false; - - } - - return true; - -} - -// ============================================================================================= - -bool ASF_Support::WriteHeaderExtensionObject ( const std::string& buffer, std::string* header, const ASF_ObjectBase& _objectBase, const int /*reservePadding*/ ) -{ - if ( ! IsEqualGUID ( ASF_Header_Extension_Object, _objectBase.guid ) || (! header) || (buffer.size() < 46) ) return false; - - const XMP_Uns64 offset = 46; - int startPos = header->size(); - - // copy header base - header->append ( buffer, 0, offset ); - - // read extended header-object structure beginning at the data part (offset = 46) - XMP_Uns64 read = 0; - XMP_Uns64 data = (_objectBase.size - offset); - XMP_Uns64 pos = offset; - - ASF_ObjectBase objectBase; - - while ( read < data ) { - - memcpy ( &objectBase, &buffer[int(pos)], kASF_ObjectBaseLen ); - objectBase.size = GetUns64LE ( &objectBase.size ); - - if ( IsEqualGUID ( ASF_Padding_Object, objectBase.guid ) ) { - // eliminate - } else { - // copy other objects - header->append ( buffer, XMP_Uns32(pos), XMP_Uns32(objectBase.size) ); - } - - pos += objectBase.size; - read += objectBase.size; - - } - - // update header extension data size - XMP_Uns32 valueUns32LE = MakeUns32LE ( header->size() - startPos - offset ); - std::string newDataSize ( (const char*)&valueUns32LE, 4 ); - ReplaceString ( *header, newDataSize, (startPos + 42), 4 ); - - // update new object size - XMP_Uns64 valueUns64LE = MakeUns64LE ( header->size() - startPos ); - std::string newObjectSize ( (const char*)&valueUns64LE, 8 ); - ReplaceString ( *header, newObjectSize, (startPos + 16), 8 ); - - return true; - -} - -// ============================================================================================= - -bool ASF_Support::CreatePaddingObject ( std::string* header, const XMP_Uns64 size ) -{ - if ( ( ! header) || (size < 24) ) return false; - - ASF_ObjectBase newObjectBase; - - newObjectBase.guid = ASF_Padding_Object; - newObjectBase.size = MakeUns64LE ( size ); - - // write object header - header->append ( (const char*)&newObjectBase, kASF_ObjectBaseLen ); - - // write 'empty' padding - header->append ( XMP_Uns32 ( size - 24 ), '\0' ); - - return true; - -} - -// ============================================================================================= - -bool ASF_Support::WriteXMPObject ( LFA_FileRef fileRef, XMP_Uns32 len, const char* inBuffer ) -{ - bool ret = false; - - ASF_ObjectBase objectBase = { ASF_XMP_Metadata, 0 }; - objectBase.size = MakeUns64LE ( len + kASF_ObjectBaseLen ); - - try { - LFA_Write ( fileRef, &objectBase, kASF_ObjectBaseLen ); - LFA_Write ( fileRef, inBuffer, len ); - ret = true; - } catch ( ... ) {} - - return ret; - -} - -// ============================================================================================= - -bool ASF_Support::UpdateXMPObject ( LFA_FileRef fileRef, const ObjectData& object, XMP_Uns32 len, const char * inBuffer ) -{ - bool ret = false; - - ASF_ObjectBase objectBase = { ASF_XMP_Metadata, 0 }; - objectBase.size = MakeUns64LE ( len + kASF_ObjectBaseLen ); - - try { - LFA_Seek ( fileRef, object.pos, SEEK_SET ); - LFA_Write ( fileRef, &objectBase, kASF_ObjectBaseLen ); - LFA_Write ( fileRef, inBuffer, len ); - ret = true; - } catch ( ... ) {} - - return ret; - -} - -// ============================================================================================= - -bool ASF_Support::CopyObject ( LFA_FileRef sourceRef, LFA_FileRef destRef, const ObjectData& object ) -{ - try { - LFA_Seek ( sourceRef, object.pos, SEEK_SET ); - LFA_Copy ( sourceRef, destRef, object.len ); - } catch ( ... ) { - return false; - } - - return true; - -} - -// ============================================================================================= - -bool ASF_Support::ReadBuffer ( LFA_FileRef fileRef, XMP_Uns64 & pos, XMP_Uns64 len, char * outBuffer ) -{ - try { - - if ( (fileRef == 0) || (outBuffer == 0) ) return false; - - LFA_Seek (fileRef, pos, SEEK_SET ); - long bytesRead = LFA_Read ( fileRef, outBuffer, XMP_Int32(len), true ); - if ( XMP_Uns32 ( bytesRead ) != len ) return false; - - return true; - - } catch ( ... ) {} - - return false; - -} - -// ============================================================================================= - -bool ASF_Support::WriteBuffer ( LFA_FileRef fileRef, XMP_Uns64 & pos, XMP_Uns32 len, const char * inBuffer ) -{ - try { - - if ( (fileRef == 0) || (inBuffer == 0) ) return false; - - LFA_Seek (fileRef, pos, SEEK_SET ); - LFA_Write( fileRef, inBuffer, len ); - - return true; - - } catch ( ... ) {} - - return false; - -} - -// ================================================================================================= - -std::string ASF_Support::ReplaceString ( std::string& operand, std::string& str, int offset, int count ) -{ - std::basic_string<char>::iterator iterF1, iterL1, iterF2, iterL2; - - iterF1 = operand.begin() + offset; - iterL1 = operand.begin() + offset + count; - iterF2 = str.begin(); - iterL2 = str.begin() + count; - - return operand.replace ( iterF1, iterL1, iterF2, iterL2 ); - -} - -// ================================================================================================= - -ASF_LegacyManager::ASF_LegacyManager() : fields(fieldLast), broadcastSet(false), digestComputed(false), - imported(false), objectsExisting(0), objectsToExport(0), legacyDiff(0), padding(0) -{ - // Nothing more to do. -} - -// ================================================================================================= - -ASF_LegacyManager::~ASF_LegacyManager() -{ - // Nothing to do. -} - -// ================================================================================================= - -bool ASF_LegacyManager::SetField ( fieldType field, const std::string& value ) -{ - if ( field >= fieldLast ) return false; - - unsigned int maxSize = this->GetFieldMaxSize ( field ); - - if (value.size ( ) <= maxSize ) { - fields[field] = value; - } else { - fields[field] = value.substr ( 0, maxSize ); - } - - if ( field == fieldCopyrightURL ) NormalizeStringDisplayASCII ( fields[field] ); - - #if ! Exclude_LicenseURL_Recon - if ( field == fieldLicenseURL ) NormalizeStringDisplayASCII ( fields[field] ); - #endif - - return true; - -} - -// ================================================================================================= - -std::string ASF_LegacyManager::GetField ( fieldType field ) -{ - if ( field >= fieldLast ) return std::string(); - return fields[field]; -} - -// ================================================================================================= - -unsigned int ASF_LegacyManager::GetFieldMaxSize ( fieldType field ) -{ - unsigned int maxSize = 0; - - switch ( field ) { - - case fieldCreationDate : - maxSize = 8; - break; - - case fieldTitle : - case fieldAuthor : - case fieldCopyright : - case fieldDescription : - maxSize = 0xFFFF; - break; - - case fieldCopyrightURL : -#if ! Exclude_LicenseURL_Recon - case fieldLicenseURL : -#endif - maxSize = 0xFFFFFFFF; - break; - - default: - break; - - } - - return maxSize; - -} - -// ================================================================================================= - -void ASF_LegacyManager::SetObjectExists ( objectType object ) -{ - objectsExisting |= object; -} - -// ================================================================================================= - -void ASF_LegacyManager::SetBroadcast ( const bool broadcast ) -{ - broadcastSet = broadcast; -} - -// ================================================================================================= - -bool ASF_LegacyManager::GetBroadcast() -{ - return broadcastSet; -} - -// ================================================================================================= - -void ASF_LegacyManager::ComputeDigest() -{ - MD5_CTX context; - MD5_Digest digest; - char buffer[40]; - - MD5Init ( &context ); - digestStr.clear(); - digestStr.reserve ( 160 ); - - for ( int type=0; type < fieldLast; ++type ) { - - if (fields[type].size ( ) > 0 ) { - snprintf ( buffer, sizeof(buffer), "%d,", type ); - digestStr.append ( buffer ); - MD5Update ( &context, (XMP_Uns8*)fields[type].data(), fields[type].size() ); - } - - } - - digestStr[digestStr.size()-1] = ';'; - - MD5Final ( digest, &context ); - - size_t in, out; - for ( in = 0, out = 0; in < 16; in += 1, out += 2 ) { - XMP_Uns8 byte = digest[in]; - buffer[out] = ReconcileUtils::kHexDigits [ byte >> 4 ]; - buffer[out+1] = ReconcileUtils::kHexDigits [ byte & 0xF ]; - } - buffer[32] = 0; - - digestStr.append ( buffer ); - - digestComputed = true; - -} - -// ================================================================================================= - -bool ASF_LegacyManager::CheckDigest ( const SXMPMeta& xmp ) -{ - bool ret = false; - - if ( ! digestComputed ) this->ComputeDigest(); - - std::string oldDigest; - - if ( xmp.GetProperty ( kXMP_NS_ASF, "NativeDigest", &oldDigest, 0 ) ) { - ret = (digestStr == oldDigest); - } - - return ret; - -} - -// ================================================================================================= - -void ASF_LegacyManager::SetDigest ( SXMPMeta* xmp ) -{ - if ( ! digestComputed ) this->ComputeDigest(); - - xmp->SetProperty ( kXMP_NS_ASF, "NativeDigest", digestStr.c_str() ); - -} - -// ================================================================================================= - -void ASF_LegacyManager::ImportLegacy ( SXMPMeta* xmp ) -{ - std::string utf8; - - if ( ! broadcastSet ) { - ConvertMSDateToISODate ( fields[fieldCreationDate], &utf8 ); - if ( ! utf8.empty() ) xmp->SetProperty ( kXMP_NS_XMP, "CreateDate", utf8.c_str(), kXMP_DeleteExisting ); - } - - FromUTF16 ( (UTF16Unit*)fields[fieldTitle].c_str(), (fields[fieldTitle].size() / 2), &utf8, false ); - if ( ! utf8.empty() ) xmp->SetLocalizedText ( kXMP_NS_DC, "title", "", "x-default", utf8.c_str(), kXMP_DeleteExisting ); - - xmp->DeleteProperty ( kXMP_NS_DC, "creator" ); - FromUTF16 ( (UTF16Unit*)fields[fieldAuthor].c_str(), (fields[fieldAuthor].size() / 2), &utf8, false ); - if ( ! utf8.empty() ) SXMPUtils::SeparateArrayItems ( xmp, kXMP_NS_DC, "creator", - (kXMP_PropArrayIsOrdered | kXMPUtil_AllowCommas), utf8.c_str() ); - - FromUTF16 ( (UTF16Unit*)fields[fieldCopyright].c_str(), (fields[fieldCopyright].size() / 2), &utf8, false ); - if ( ! utf8.empty() ) xmp->SetLocalizedText ( kXMP_NS_DC, "rights", "", "x-default", utf8.c_str(), kXMP_DeleteExisting ); - - FromUTF16 ( (UTF16Unit*)fields[fieldDescription].c_str(), (fields[fieldDescription].size() / 2), &utf8, false ); - if ( ! utf8.empty() ) xmp->SetLocalizedText ( kXMP_NS_DC, "description", "", "x-default", utf8.c_str(), kXMP_DeleteExisting ); - - if ( ! utf8.empty() ) xmp->SetProperty ( kXMP_NS_XMP_Rights, "WebStatement", fields[fieldCopyrightURL].c_str(), kXMP_DeleteExisting ); - -#if ! Exclude_LicenseURL_Recon - if ( ! fields[fieldLicenseURL].empty() ) xmp->SetProperty ( kXMP_NS_XMP_Rights, "Certificate", fields[fieldLicenseURL].c_str(), kXMP_DeleteExisting ); -#endif - - imported = true; - -} - -// ================================================================================================= - -int ASF_LegacyManager::ExportLegacy ( const SXMPMeta& xmp ) -{ - int changed = 0; - objectsToExport = 0; - legacyDiff = 0; - - std::string utf8; - std::string utf16; - XMP_OptionBits flags; - - if ( ! broadcastSet ) { - if ( xmp.GetProperty ( kXMP_NS_XMP, "CreateDate", &utf8, &flags ) ) { - std::string date; - ConvertISODateToMSDate ( utf8, &date ); - if ( fields[fieldCreationDate] != date ) { - legacyDiff += date.size(); - legacyDiff -= fields[fieldCreationDate].size(); - this->SetField ( fieldCreationDate, date ); - objectsToExport |= objectFileProperties; - changed ++; - } - } - } - - if ( xmp.GetLocalizedText ( kXMP_NS_DC, "title", "", "x-default", 0, &utf8, &flags ) ) { - NormalizeStringTrailingNull ( utf8 ); - ToUTF16 ( (const UTF8Unit*)utf8.data(), utf8.size(), &utf16, false ); - if ( fields[fieldTitle] != utf16 ) { - legacyDiff += utf16.size(); - legacyDiff -= fields[fieldTitle].size(); - this->SetField ( fieldTitle, utf16 ); - objectsToExport |= objectContentDescription; - changed ++; - } - } - - utf8.clear(); - SXMPUtils::CatenateArrayItems ( xmp, kXMP_NS_DC, "creator", 0, 0, kXMPUtil_AllowCommas, &utf8 ); - if ( ! utf8.empty() ) { - NormalizeStringTrailingNull ( utf8 ); - ToUTF16 ( (const UTF8Unit*)utf8.data(), utf8.size(), &utf16, false ); - if ( fields[fieldAuthor] != utf16 ) { - legacyDiff += utf16.size(); - legacyDiff -= fields[fieldAuthor].size(); - this->SetField ( fieldAuthor, utf16 ); - objectsToExport |= objectContentDescription; - changed ++; - } - } - - if ( xmp.GetLocalizedText ( kXMP_NS_DC, "rights", "", "x-default", 0, &utf8, &flags ) ) { - NormalizeStringTrailingNull ( utf8 ); - ToUTF16 ( (const UTF8Unit*)utf8.data(), utf8.size(), &utf16, false ); - if ( fields[fieldCopyright] != utf16 ) { - legacyDiff += utf16.size(); - legacyDiff -= fields[fieldCopyright].size(); - this->SetField ( fieldCopyright, utf16 ); - objectsToExport |= objectContentDescription; - changed ++; - } - } - - if ( xmp.GetLocalizedText ( kXMP_NS_DC, "description", "", "x-default", 0, &utf8, &flags ) ) { - NormalizeStringTrailingNull ( utf8 ); - ToUTF16 ( (const UTF8Unit*)utf8.data(), utf8.size(), &utf16, false ); - if ( fields[fieldDescription] != utf16 ) { - legacyDiff += utf16.size(); - legacyDiff -= fields[fieldDescription].size(); - this->SetField ( fieldDescription, utf16 ); - objectsToExport |= objectContentDescription; - changed ++; - } - } - - if ( xmp.GetProperty ( kXMP_NS_XMP_Rights, "WebStatement", &utf8, &flags ) ) { - NormalizeStringTrailingNull ( utf8 ); - if ( fields[fieldCopyrightURL] != utf8 ) { - legacyDiff += utf8.size(); - legacyDiff -= fields[fieldCopyrightURL].size(); - this->SetField ( fieldCopyrightURL, utf8 ); - objectsToExport |= objectContentBranding; - changed ++; - } - } - -#if ! Exclude_LicenseURL_Recon - if ( xmp.GetProperty ( kXMP_NS_XMP_Rights, "Certificate", &utf8, &flags ) ) { - NormalizeStringTrailingNull ( utf8 ); - if ( fields[fieldLicenseURL] != utf8 ) { - legacyDiff += utf8.size(); - legacyDiff -= fields[fieldLicenseURL].size(); - this->SetField ( fieldLicenseURL, utf8 ); - objectsToExport |= objectContentEncryption; - changed ++; - } - } -#endif - - // find objects, that would need to be created on legacy export - int newObjects = (objectsToExport & !objectsExisting); - - // calculate minimum storage for new objects, that might be created on export - if ( newObjects & objectContentDescription ) - legacyDiff += sizeContentDescription; - if ( newObjects & objectContentBranding ) - legacyDiff += sizeContentBranding; - if ( newObjects & objectContentEncryption ) - legacyDiff += sizeContentEncryption; - - ComputeDigest(); - - return changed; - -} - -// ================================================================================================= - -bool ASF_LegacyManager::hasLegacyChanged() -{ - return (objectsToExport != 0); -} - -// ================================================================================================= - -XMP_Int64 ASF_LegacyManager::getLegacyDiff() -{ - return (this->hasLegacyChanged() ? legacyDiff : 0); -} - -// ================================================================================================= - -int ASF_LegacyManager::changedObjects() -{ - return objectsToExport; -} - -// ================================================================================================= - -void ASF_LegacyManager::SetPadding ( XMP_Int64 _padding ) -{ - padding = _padding; -} - -// ================================================================================================= - -XMP_Int64 ASF_LegacyManager::GetPadding() -{ - return padding; -} - -// ================================================================================================= - -std::string ASF_LegacyManager::NormalizeStringDisplayASCII ( std::string& operand ) -{ - std::basic_string<char>::iterator current = operand.begin(); - std::basic_string<char>::iterator end = operand.end();; - - for ( ; (current != end); ++current ) { - char element = *current; - if ( ( (element < 0x21) && (element != 0x00)) || (element > 0x7e) ) { - *current = '?'; - } - } - - return operand; - -} - -// ================================================================================================= - -std::string ASF_LegacyManager::NormalizeStringTrailingNull ( std::string& operand ) -{ - if ( ( operand.size() > 0) && (operand[operand.size() - 1] != '\0') ) { - operand.append ( 1, '\0' ); - } - - return operand; - -} - -// ================================================================================================= - -int ASF_LegacyManager::DaysInMonth ( XMP_Int32 year, XMP_Int32 month ) -{ - - static short daysInMonth[13] = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; - // Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec - - int days = daysInMonth [ month ]; - if ( (month == 2) && IsLeapYear ( year ) ) days += 1; - - return days; - -} - -// ================================================================================================= - -bool ASF_LegacyManager::IsLeapYear ( long year ) -{ - - if ( year < 0 ) year = -year + 1; // Fold the negative years, assuming there is a year 0. - - if ( (year % 4) != 0 ) return false; // Not a multiple of 4. - if ( (year % 100) != 0 ) return true; // A multiple of 4 but not a multiple of 100. - if ( (year % 400) == 0 ) return true; // A multiple of 400. - - return false; // A multiple of 100 but not a multiple of 400. - -} - -// ================================================================================================= - -void ASF_LegacyManager::ConvertMSDateToISODate ( std::string& source, std::string* dest ) -{ - - XMP_Int64 creationDate = GetUns64LE ( source.c_str() ); - XMP_Int64 totalSecs = creationDate / (10*1000*1000); - XMP_Int32 nanoSec = ( ( XMP_Int32) (creationDate - (totalSecs * 10*1000*1000)) ) * 100; - - XMP_Int32 days = (XMP_Int32) (totalSecs / 86400); - totalSecs -= ( ( XMP_Int64)days * 86400 ); - - XMP_Int32 hour = (XMP_Int32) (totalSecs / 3600); - totalSecs -= ( ( XMP_Int64)hour * 3600 ); - - XMP_Int32 minute = (XMP_Int32) (totalSecs / 60); - totalSecs -= ( ( XMP_Int64)minute * 60 ); - - XMP_Int32 second = (XMP_Int32)totalSecs; - - // A little more simple code converts this to an XMP date string: - - XMP_DateTime date; - memset ( &date, 0, sizeof ( date ) ); - - date.year = 1601; // The MS date origin. - date.month = 1; - date.day = 1; - - date.day += days; // Add in the delta. - date.hour = hour; - date.minute = minute; - date.second = second; - date.nanoSecond = nanoSec; - - date.hasTimeZone = true; // ! Needed for ConvertToUTCTime to do anything. - SXMPUtils::ConvertToUTCTime ( &date ); // Normalize the date/time. - SXMPUtils::ConvertFromDate ( date, dest ); // Convert to an ISO 8601 string. - -} - -// ================================================================================================= - -void ASF_LegacyManager::ConvertISODateToMSDate ( std::string& source, std::string* dest ) -{ - XMP_DateTime date; - SXMPUtils::ConvertToDate ( source, &date ); - SXMPUtils::ConvertToUTCTime ( &date ); - - XMP_Int64 creationDate; - creationDate = date.nanoSecond / 100; - creationDate += (XMP_Int64 ( date.second) * (10*1000*1000) ); - creationDate += (XMP_Int64 ( date.minute) * 60 * (10*1000*1000) ); - creationDate += (XMP_Int64 ( date.hour) * 3600 * (10*1000*1000) ); - - XMP_Int32 days = (date.day - 1); - - --date.month; - while ( date.month >= 1 ) { - days += DaysInMonth ( date.year, date.month ); - --date.month; - } - - --date.year; - while ( date.year >= 1601 ) { - days += (IsLeapYear ( date.year) ? 366 : 365 ); - --date.year; - } - - creationDate += (XMP_Int64 ( days) * 86400 * (10*1000*1000) ); - - creationDate = GetUns64LE ( &creationDate ); - dest->assign ( (const char*)&creationDate, 8 ); - -} diff --git a/source/XMPFiles/FormatSupport/ASF_Support.hpp b/source/XMPFiles/FormatSupport/ASF_Support.hpp deleted file mode 100644 index e170cb2..0000000 --- a/source/XMPFiles/FormatSupport/ASF_Support.hpp +++ /dev/null @@ -1,224 +0,0 @@ -#ifndef __ASF_Support_hpp__ -#define __ASF_Support_hpp__ 1 - -// ================================================================================================= -// ADOBE SYSTEMS INCORPORATED -// Copyright 2006 Adobe Systems Incorporated -// 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 "XMP_Environment.h" // ! This must be the first include. - -#include "XMPFiles_Impl.hpp" -#include "Reconcile_Impl.hpp" - -// currently exclude LicenseURL from reconciliation -#define Exclude_LicenseURL_Recon 1 -#define EXCLUDE_LICENSEURL_RECON 1 - -// Defines for platforms other than Win -#if ! XMP_WinBuild - - typedef struct _GUID - { - XMP_Uns32 Data1; - XMP_Uns16 Data2; - XMP_Uns16 Data3; - XMP_Uns8 Data4[8]; - } GUID; - - int IsEqualGUID ( const GUID& guid1, const GUID& guid2 ); - - static const GUID GUID_NULL = { 0x0, 0x0, 0x0, { 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 } }; - -#endif - -// header object -static const GUID ASF_Header_Object = { MakeUns32LE(0x75b22630), MakeUns16LE(0x668e), MakeUns16LE(0x11cf), { 0xa6, 0xd9, 0x00, 0xaa, 0x00, 0x62, 0xce, 0x6c } }; -// contains ... -static const GUID ASF_File_Properties_Object = { MakeUns32LE(0x8cabdca1), MakeUns16LE(0xa947), MakeUns16LE(0x11cf), { 0x8e, 0xe4, 0x00, 0xc0, 0x0c, 0x20, 0x53, 0x65 } }; -static const GUID ASF_Content_Description_Object = { MakeUns32LE(0x75b22633), MakeUns16LE(0x668e), MakeUns16LE(0x11cf), { 0xa6, 0xd9, 0x00, 0xaa, 0x00, 0x62, 0xce, 0x6c } }; -static const GUID ASF_Content_Branding_Object = { MakeUns32LE(0x2211b3fa), MakeUns16LE(0xbd23), MakeUns16LE(0x11d2), { 0xb4, 0xb7, 0x00, 0xa0, 0xc9, 0x55, 0xfc, 0x6e } }; -static const GUID ASF_Content_Encryption_Object = { MakeUns32LE(0x2211b3fb), MakeUns16LE(0xbd23), MakeUns16LE(0x11d2), { 0xb4, 0xb7, 0x00, 0xa0, 0xc9, 0x55, 0xfc, 0x6e } }; -// padding -// Remark: regarding to Microsofts spec only the ASF_Header_Object contains a ASF_Padding_Object -// Real world files show, that the ASF_Header_Extension_Object contains a ASF_Padding_Object -static const GUID ASF_Header_Extension_Object = { MakeUns32LE(0x5fbf03b5), MakeUns16LE(0xa92e), MakeUns16LE(0x11cf), { 0x8e, 0xe3, 0x00, 0xc0, 0x0c, 0x20, 0x53, 0x65 } }; -static const GUID ASF_Padding_Object = { MakeUns32LE(0x1806d474), MakeUns16LE(0xcadf), MakeUns16LE(0x4509), { 0xa4, 0xba, 0x9a, 0xab, 0xcb, 0x96, 0xaa, 0xe8 } }; - -// data object -static const GUID ASF_Data_Object = { MakeUns32LE(0x75b22636), MakeUns16LE(0x668e), MakeUns16LE(0x11cf), { 0xa6, 0xd9, 0x00, 0xaa, 0x00, 0x62, 0xce, 0x6c } }; - -// XMP object -static const GUID ASF_XMP_Metadata = { MakeUns32LE(0xbe7acfcb), MakeUns16LE(0x97a9), MakeUns16LE(0x42e8), { 0x9c, 0x71, 0x99, 0x94, 0x91, 0xe3, 0xaf, 0xac } }; - -static const int guidLen = sizeof(GUID); - -typedef struct _ASF_ObjectBase -{ - GUID guid; - XMP_Uns64 size; - -} ASF_ObjectBase; - -static const int kASF_ObjectBaseLen = sizeof(ASF_ObjectBase); - -// ================================================================================================= - -class ASF_LegacyManager { -public: - - enum objectType { - objectFileProperties = 1 << 0, - objectContentDescription = 1 << 1, - objectContentBranding = 1 << 2, - objectContentEncryption = 1 << 3 - }; - - enum minObjectSize { - sizeContentDescription = 34, - sizeContentBranding = 40, - sizeContentEncryption = 40 - }; - - enum fieldType { - // File_Properties_Object - fieldCreationDate = 0, - // Content_Description_Object - fieldTitle, - fieldAuthor, - fieldCopyright, - fieldDescription, - // Content_Branding_Object - fieldCopyrightURL, - #if ! Exclude_LicenseURL_Recon - // Content_Encryption_Object - fieldLicenseURL, - #endif - // last - fieldLast - }; - - ASF_LegacyManager(); - virtual ~ASF_LegacyManager(); - - bool SetField ( fieldType field, const std::string& value ); - std::string GetField ( fieldType field ); - unsigned int GetFieldMaxSize ( fieldType field ); - - void SetObjectExists ( objectType object ); - - void SetBroadcast ( const bool broadcast ); - bool GetBroadcast(); - - void ComputeDigest(); - bool CheckDigest ( const SXMPMeta& xmp ); - void SetDigest ( SXMPMeta* xmp ); - - void ImportLegacy ( SXMPMeta* xmp ); - int ExportLegacy ( const SXMPMeta& xmp ); - bool hasLegacyChanged(); - XMP_Int64 getLegacyDiff(); - int changedObjects(); - - void SetPadding ( XMP_Int64 padding ); - XMP_Int64 GetPadding(); - -private: - - typedef std::vector<std::string> TFields; - TFields fields; - bool broadcastSet; - - std::string digestStr; - bool digestComputed; - - bool imported; - int objectsExisting; - int objectsToExport; - XMP_Int64 legacyDiff; - XMP_Int64 padding; - - static std::string NormalizeStringDisplayASCII ( std::string& operand ); - static std::string NormalizeStringTrailingNull ( std::string& operand ); - - static void ConvertMSDateToISODate ( std::string& source, std::string* dest ); - static void ConvertISODateToMSDate ( std::string& source, std::string* dest ); - - static int DaysInMonth ( XMP_Int32 year, XMP_Int32 month ); - static bool IsLeapYear ( long year ); - -}; // class ASF_LegacyManager - -// ================================================================================================= - -class ASF_Support { -public: - - class ObjectData { - public: - ObjectData() : pos(0), len(0), guid(GUID_NULL), xmp(false) {} - virtual ~ObjectData() {} - XMP_Uns64 pos; // file offset of object - XMP_Uns64 len; // length of object data - GUID guid; // object GUID - bool xmp; // object with XMP ? - }; - - typedef std::vector<ObjectData> ObjectVector; - typedef ObjectVector::iterator ObjectIterator; - - class ObjectState { - - public: - ObjectState() : xmpPos(0), xmpLen(0), xmpIsLastObject(false), broadcast(false) {} - virtual ~ObjectState() {} - XMP_Uns64 xmpPos; - XMP_Uns64 xmpLen; - bool xmpIsLastObject; - bool broadcast; - ObjectData xmpObject; - ObjectVector objects; - }; - - ASF_Support(); - ASF_Support ( ASF_LegacyManager* legacyManager ); - virtual ~ASF_Support(); - - long OpenASF ( LFA_FileRef fileRef, ObjectState & inOutObjectState ); - - bool ReadObject ( LFA_FileRef fileRef, ObjectState & inOutObjectState, XMP_Uns64 * objectLength, XMP_Uns64 & inOutPosition ); - - bool ReadHeaderObject ( LFA_FileRef fileRef, ObjectState& inOutObjectState, const ObjectData& newObject ); - bool WriteHeaderObject ( LFA_FileRef sourceRef, LFA_FileRef destRef, const ObjectData& object, ASF_LegacyManager& legacyManager, bool usePadding ); - bool UpdateHeaderObject ( LFA_FileRef fileRef, const ObjectData& object, ASF_LegacyManager& legacyManager ); - - bool UpdateFileSize ( LFA_FileRef fileRef ); - - bool ReadHeaderExtensionObject ( LFA_FileRef fileRef, ObjectState& inOutObjectState, const XMP_Uns64& pos, const ASF_ObjectBase& objectBase ); - static bool WriteHeaderExtensionObject ( const std::string& buffer, std::string* header, const ASF_ObjectBase& objectBase, const int reservePadding ); - - static bool CreatePaddingObject ( std::string* header, const XMP_Uns64 size ); - - static bool WriteXMPObject ( LFA_FileRef fileRef, XMP_Uns32 len, const char* inBuffer ); - static bool UpdateXMPObject ( LFA_FileRef fileRef, const ObjectData& object, XMP_Uns32 len, const char * inBuffer ); - static bool CopyObject ( LFA_FileRef sourceRef, LFA_FileRef destRef, const ObjectData& object ); - - static bool ReadBuffer ( LFA_FileRef fileRef, XMP_Uns64 & pos, XMP_Uns64 len, char * outBuffer ); - static bool WriteBuffer ( LFA_FileRef fileRef, XMP_Uns64 & pos, XMP_Uns32 len, const char * inBuffer ); - -private: - - ASF_LegacyManager* legacyManager; - XMP_Uns64 posFileSizeInfo; - - static std::string ReplaceString ( std::string& operand, std::string& str, int offset, int count ); - -}; // class ASF_Support - -// ================================================================================================= - -#endif // __ASF_Support_hpp__ diff --git a/source/XMPFiles/FormatSupport/ID3_Support.hpp b/source/XMPFiles/FormatSupport/ID3_Support.hpp deleted file mode 100644 index e243d24..0000000 --- a/source/XMPFiles/FormatSupport/ID3_Support.hpp +++ /dev/null @@ -1,784 +0,0 @@ -#ifndef __ID3_Support_hpp__ -#define __ID3_Support_hpp__ 1 - -// ================================================================================================= -// ADOBE SYSTEMS INCORPORATED -// Copyright 2008 Adobe Systems Incorporated -// 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 "XMP_Environment.h" // ! This must be the first include. -#include "XMP_Const.h" -#include "XMPFiles_Impl.hpp" -#include "UnicodeConversions.hpp" -#include "Reconcile_Impl.hpp" -#include <vector> - -#if XMP_WinBuild - #define stricmp _stricmp -#else - static int stricmp ( const char * left, const char * right ) // Case insensitive ASCII compare. - { - char chL = *left; // ! Allow for 0 passes in the loop (one string is empty). - char chR = *right; // ! Return -1 for stricmp ( "a", "Z" ). - - for ( ; (*left != 0) && (*right != 0); ++left, ++right ) { - chL = *left; - chR = *right; - if ( chL == chR ) continue; - if ( ('A' <= chL) && (chL <= 'Z') ) chL |= 0x20; - if ( ('A' <= chR) && (chR <= 'Z') ) chR |= 0x20; - if ( chL != chR ) break; - } - - if ( chL == chR ) return 0; - if ( chL < chR ) return -1; - return 1; - } -#endif - -#define MIN(a, b) ((a) < (b) ? (a) : (b)) - -namespace ID3_Support -{ - // Genres - static char Genres[128][32]={ - "Blues", // 0 - "Classic Rock", // 1 - "Country", // 2 - "Dance", - "Disco", - "Funk", - "Grunge", - "Hip-Hop", - "Jazz", // 8 - "Metal", - "New Age", // 10 - "Oldies", - "Other", // 12 - "Pop", - "R&B", - "Rap", - "Reggae", // 16 - "Rock", // 17 - "Techno", - "Industrial", - "Alternative", - "Ska", - "Death Metal", - "Pranks", - "Soundtrack", // 24 - "Euro-Techno", - "Ambient", - "Trip-Hop", - "Vocal", - "Jazz+Funk", - "Fusion", - "Trance", - "Classical", // 32 - "Instrumental", - "Acid", - "House", - "Game", - "Sound Clip", - "Gospel", - "Noise", - "AlternRock", - "Bass", - "Soul", //42 - "Punk", - "Space", - "Meditative", - "Instrumental Pop", - "Instrumental Rock", - "Ethnic", - "Gothic", - "Darkwave", - "Techno-Industrial", - "Electronic", - "Pop-Folk", - "Eurodance", - "Dream", - "Southern Rock", - "Comedy", - "Cult", - "Gangsta", - "Top 40", - "Christian Rap", - "Pop/Funk", - "Jungle", - "Native American", - "Cabaret", - "New Wave", // 66 - "Psychadelic", - "Rave", - "Showtunes", - "Trailer", - "Lo-Fi", - "Tribal", - "Acid Punk", - "Acid Jazz", - "Polka", - "Retro", - "Musical", - "Rock & Roll", - "Hard Rock", - "Folk", // 80 - "Folk-Rock", - "National Folk", - "Swing", - "Fast Fusion", - "Bebob", - "Latin", - "Revival", - "Celtic", - "Bluegrass", // 89 - "Avantgarde", - "Gothic Rock", - "Progressive Rock", - "Psychedelic Rock", - "Symphonic Rock", - "Slow Rock", - "Big Band", - "Chorus", - "Easy Listening", - "Acoustic", - "Humour", // 100 - "Speech", - "Chanson", - "Opera", - "Chamber Music", - "Sonata", - "Symphony", - "Booty Bass", - "Primus", - "Porn Groove", - "Satire", - "Slow Jam", - "Club", - "Tango", - "Samba", - "Folklore", - "Ballad", - "Power Ballad", - "Rhythmic Soul", - "Freestyle", - "Duet", - "Punk Rock", - "Drum Solo", - "A capella", - "Euro-House", - "Dance Hall", - "Unknown" // 126 - }; - - ////////////////////////////////////////////////////////// - // ID3 specific helper routines - inline XMP_Int32 synchToInt32(XMP_Uns32 rawDataBE) // see part 6.2 of spec - { - XMP_Validate( 0 == (rawDataBE & 0x80808080),"input not synchsafe", kXMPErr_InternalFailure ); - XMP_Int32 r = - ( rawDataBE & 0x0000007F ) - + ( rawDataBE >> 1 & 0x00003F80 ) - + ( rawDataBE >> 2 & 0x001FC000 ) - + ( rawDataBE >> 3 & 0x0FE00000 ); - return r; - } - - inline XMP_Uns32 int32ToSynch(XMP_Int32 value) - { - XMP_Validate( 0 <= 0x0FFFFFFF, "value too big", kXMPErr_InternalFailure ); - XMP_Uns32 r = - ( (value & 0x0000007F) << 0 ) - + ( (value & 0x00003F80) << 1 ) - + ( (value & 0x001FC000) << 2 ) - + ( (value & 0x0FE00000) << 3 ); - return r; - } - - ////////////////////////////////////////////////////////// - // data structures - class ID3Header - { - public: - const static XMP_Uns16 o_id = 0; - const static XMP_Uns16 o_version_major = 3; - const static XMP_Uns16 o_version_minor = 4; - const static XMP_Uns16 o_flags = 5; - const static XMP_Uns16 o_size = 6; - - const static int FIXED_SIZE = 10; - char fields[FIXED_SIZE]; - - bool read(LFA_FileRef file) - { - LFA_Read( file, fields, FIXED_SIZE, true ); - - if ( !CheckBytes( &fields[ID3Header::o_id], "ID3", 3 ) ) - { - // chuck in default contents: - const static char defaultHeader[FIXED_SIZE] = { 'I', 'D', '3', 3, 0, 0, 0, 0, 0, 0 }; - memcpy( this->fields, defaultHeader, FIXED_SIZE); // NBA: implicitly protected by FIXED_SIZE design. - - return false; // no header found (o.k.) thus stick with new, default header constructed above - } - - XMP_Uns8 major = fields[o_version_major]; - XMP_Uns8 minor = fields[o_version_minor]; - XMP_Validate( major==3 || major==4, "invalid ID3 major version", kXMPErr_BadFileFormat ); - XMP_Validate( minor != 0xFF, "invalid ID3 minor version", kXMPErr_BadFileFormat ); - - return true; - } - - void write(LFA_FileRef file, XMP_Int64 tagSize) - { - XMP_Assert( tagSize < 0x10000000 ); // 256 MB limit due to synching. - XMP_Assert( tagSize > 0x0 ); // 256 MB limit due to synching. - PutUns32BE( ( int32ToSynch ( (XMP_Uns32)tagSize - 10 )), &this->fields[ID3Header::o_size] ); - - LFA_Write( file, fields, FIXED_SIZE ); - } - - ~ID3Header() - { - //nothing - }; - }; // ID3Header - - - class ID3v2Frame - { - public: - // fixed - const static XMP_Uns16 o_id = 0; - const static XMP_Uns16 o_size = 4; // size after unsync, excludes frame header. - const static XMP_Uns16 o_flags = 8; - - const static int FIXED_SIZE = 10; - char fields[FIXED_SIZE]; - - XMP_Uns32 id; - XMP_Uns16 flags; - - bool active; //default: true. flag is lowered, if another frame with replaces this one as "last meaningful frame of its kind" - bool changed; //default: false. flag is raised, if setString() is used - - // variable-size frame content - char* content; - XMP_Int32 contentSize; // size of variable content, right as its stored in o_size (total size - 10) - - /** - * a mere empty hull for reading in frames. - */ - ID3v2Frame():id(0),content(0),contentSize(0),flags(0),active(true),changed(false) - { - memset(fields, 0, FIXED_SIZE); - } - - /** - * frame constructor - */ - ID3v2Frame( XMP_Uns32 id ):id(0),content(0),contentSize(0),flags(0),active(true),changed(false) - { - memset(fields, 0, FIXED_SIZE); - - this->id = id; - PutUns32BE( id, &fields[o_id]); - this->flags = 0x0000; - PutUns16BE( flags, &fields[o_flags]); - } - - /** - * copy constructor: - */ - ID3v2Frame( const ID3v2Frame& orig) - { - XMP_Throw("not implemented",kXMPErr_InternalFailure); - } - - /** - * sets a new Content (aka property) - */ - void setFrameValue( const std::string& rawvalue, bool needDescriptor = false , bool utf16le = false, bool isXMPPRIVFrame = false, bool needEncodingByte = true ) - { - // the thing to insert - std::string value; - value.erase(); - - if ( isXMPPRIVFrame ) - { - // this flag must be used exclusive: - XMP_Assert( ! needDescriptor ); - XMP_Assert( ! utf16le ); - - value.append( "XMP\0", 4 ); - value.append( rawvalue ); - value.append( "\0", 1 ); // final zero byte - } - else // not an XMP Priv frame - { - if (needEncodingByte) // don't add encoding byte for example for WCOP - { - if ( utf16le ) - value.append( "\x1", 1 ); // 'unicode encoding' (must be UTF-16, be or le) - else - value.append( "\x0", 1 ); - } - - if ( needDescriptor ) // language descriptor - value.append( "eng", 3 ); // language, empty content description - if ( utf16le ) - { - if ( needDescriptor ) - value.append( "\xFF\xFE\0\0", 4 ); // BOM, 16-bit zero (empty descriptor) - - value.append( "\xFF\xFE", 2 ); - std::string utf16str; - ToUTF16( (XMP_Uns8*) rawvalue.c_str(), rawvalue.size(), &utf16str, false ); - value.append( utf16str ); - value.append( "\0\0", 2 ); - } - else // write as plain Latin-1 - { - std::string convertedValue; // just precaution (mostly used for plain ascii like numbers, etc) - convertedValue.erase(); - ReconcileUtils::UTF8ToLatin1( rawvalue.c_str(), rawvalue.size(), &convertedValue ); - - if ( needDescriptor ) - value.append( "\0", 1 ); // BOM, 16-bit zero (empty descriptor) - value.append( convertedValue ); - value.append( "\0", 1 ); // final zero byte - } - } // of "not an XMP Priv frame" - - this->changed = true; - this->release(); - - XMP_StringPtr valueCStr = value.c_str(); - this->contentSize = (XMP_Int32) value.size(); - XMP_Validate( this->contentSize < 0xA00000, "XMP Property exceeds 20MB in size", kXMPErr_InternalFailure ); - content = new char[ this->contentSize ]; - memcpy( content , valueCStr , this->contentSize ); - } - - void write(LFA_FileRef file, XMP_Uns8 majorVersion) - { - // write back size field: - if ( majorVersion < 4 ) - PutUns32BE( contentSize, &fields[o_size] ); - else - PutUns32BE( int32ToSynch( contentSize ), &fields[o_size] ); - - // write out fixed fields - LFA_Write( file, this->fields, FIXED_SIZE); - // write out contents - LFA_Write( file, this->content, contentSize ); - } - - /** - * majorVersion must be 3 or 4 - * @returns total size of frame - */ - XMP_Int64 read(LFA_FileRef file, XMP_Uns8 majorVersion ) - { - XMP_Assert( majorVersion == 3 || majorVersion == 4 ); - this->release(); // ensures/allows reuse of 'curFrame' - - XMP_Int64 start = LFA_Tell( file ); - LFA_Read( file, fields, FIXED_SIZE, true ); - - id = GetUns32BE( &fields[o_id] ); - - //only padding to come? - if (id == 0x00000000) - { - LFA_Seek( file, -10, SEEK_CUR ); //rewind 10 byte - return 0; // zero signals, not a valid frame - } - //verify id to be valid (4x A-Z and 0-9) - XMP_Validate( - (( fields[0]>0x40 && fields[0]<0x5B) || ( fields[0]>0x2F && fields[0]<0x3A)) && //A-Z,0-9 - (( fields[1]>0x40 && fields[1]<0x5B) || ( fields[1]>0x2F && fields[1]<0x3A)) && - (( fields[2]>0x40 && fields[2]<0x5B) || ( fields[2]>0x2F && fields[2]<0x3A)) && - (( fields[3]>0x40 && fields[3]<0x5B) || ( fields[3]>0x2F && fields[3]<0x3A)) - ,"invalid Frame ID", kXMPErr_BadFileFormat); - - flags = GetUns16BE( &fields[o_flags] ); - XMP_Validate( 0==(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 - contentSize = ( majorVersion < 4) ? - GetUns32BE( &fields[o_size] ) - : synchToInt32(GetUns32BE( &fields[o_size] )); - - XMP_Validate( contentSize >= 0, "negative frame size", kXMPErr_BadFileFormat ); - // >20MB in a single frame?, assume broken file - XMP_Validate( contentSize < 0xA00000, "single frame exceeds 20MB", kXMPErr_BadFileFormat ); - - content = new char[ contentSize ]; - - LFA_Read( file, content, contentSize, true ); - XMP_Assert( (LFA_Tell( file ) - start) == FIXED_SIZE + contentSize ); //trivial - return LFA_Tell( file ) - start; - } - - /** - * two types of COMM frames should be preserved but otherwise ignored - * * a 6-field long header just having - * encoding(1 byte),lang(3 bytes) and 0x00 31 (no descriptor, "1" as content") - * perhaps only used to indicate client language - * * COMM frames whose description begins with engiTun, these are iTunes flags - * - * returns true: job done as expted - * false: do not use this frame, but preserve (i.e. iTunes marker COMM frame) - */ - bool advancePastCOMMDescriptor(XMP_Int32& pos) - { - if ( (contentSize-pos) <= 3 ) - return false; // silent error, no room left behing language tag - - if ( !CheckBytes( &content[pos], "eng", 3 ) ) - return false; // not an error, but leave all non-eng tags alone... - - pos += 3; // skip lang tag - if ( pos >= contentSize ) - return false; // silent error - - // skip past descriptor: - while ( pos < contentSize ) - if ( content[pos++] == 0x00 ) - break; - // skip yet-another double zero if applicable - if ( (pos<contentSize) && (content[pos] == 0x00) ) - pos++; - - // check for "1" language indicator comment - if (pos == 5) - if ( contentSize == 6) - if ( GetUns16BE(&content[4]) == 0x0031 ) - return false; - - // check for iTunes tag-comment - if ( pos > 4 ) //there's a content descriptor ( 1 + 3 + zeroTerminator) - { - std::string descriptor = std::string(&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. - } - - /* - * returns the frame content as a proper UTF8 string - * * w/o the initial encoding byte - * * dealing with BOM's - * - * @returns: by value: character string with the value - * as return value: false if frame is "not of intereset" despite a generally - * "interesting" frame ID, these are - * * iTunes-'marker' COMM frame - */ - bool getFrameValue( XMP_Uns8 majorVersion, XMP_Uns32 frameID, std::string* utf8string ) - { - XMP_Assert( contentSize >= 0 ); - XMP_Assert( contentSize < 0xA00000 ); // more than 20 MB per Propety would be very odd... - XMP_Assert( content != 0 ); - - if ( contentSize == 0) - { - utf8string->erase(); - return true; // ...it is "of interest", even if empty contents. - } - - XMP_Int32 pos = 0; // going through input string - XMP_Uns8 encByte = 0; - // WCOP does not have an encoding byte - // => for all others: use [0] as EncByte, advance pos - if ( frameID != 0x57434F50 ) - { - encByte = content[0]; - pos++; - } - - // mode specific forks (so far only COMM) - bool commMode = ( - ( frameID == 0x434F4D4D ) || // COMM - ( frameID == 0x55534C54 ) // USLT - ); - - switch (encByte) - { - case 0: //ISO-8859-1, 0-terminated - { - if (commMode) - if (! advancePastCOMMDescriptor( pos )) // do and check result - return false; // not a frame of interest! - - char* localPtr = &content[pos]; - size_t localLen = 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 - { - std::string tmp(this->content, this->contentSize); - - if (commMode) - if (! advancePastCOMMDescriptor( pos )) // do and check result - return false; // not a frame of interest! - - bool bigEndian = true; // assume for now (if no BOM follows) - if ( GetUns16BE(&content[pos]) == 0xFEFF ) - { - pos += 2; - bigEndian = true; - } - else if ( GetUns16BE(&content[pos]) == 0xFFFE ) - { - pos += 2; - bigEndian = false; - } - - FromUTF16( (UTF16Unit*)&content[pos], (contentSize - pos) / 2, utf8string, bigEndian ); - } - break; - case 3: // UTF-8 unicode, terminated \0 - // swallow any BOM, just in case - if ( (GetUns32BE(&content[pos]) & 0xFFFFFF00 ) == 0xEFBBBF00 ) - pos += 3; - - if (commMode) - if (! advancePastCOMMDescriptor( pos )) // do and check result - return false; // not a frame of interest! - - utf8string->assign( &content[pos], 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() - { - this->release(); - } - - void release() - { - if ( content ) - delete content; - content = 0; - contentSize = 0; - } - }; // ID3Header - - - - class ID3v1Tag - { - public: - // fixed - const static XMP_Uns16 o_tag = 0; // must be "TAG" - const static XMP_Uns16 o_title = 3; - const static XMP_Uns16 o_artist = 33; - const static XMP_Uns16 o_album = 63; - const static XMP_Uns16 o_year = 93; - const static XMP_Uns16 o_comment = 97; - const static XMP_Uns16 o_genre = 127; // last by: index, or 255 - - // optional (ID3v1.1) track number) - const static XMP_Uns16 o_zero = 125; //must be zero, for trackNo to be valid - const static XMP_Uns16 o_trackNo = 126; //trackNo - - const static int FIXED_SIZE = 128; - - /** - * @returns returns true, if ID3v1 (or v1.1) exists, otherwise false. - // sets XMP properties en route - */ - bool read( LFA_FileRef file, SXMPMeta* meta ) - { - if ( LFA_Measure( file ) <= 128 ) // ensure sufficient room - return false; - LFA_Seek( file, -128, SEEK_END); - - XMP_Uns32 tagID = LFA_ReadInt32_BE( file ); - tagID = tagID & 0xFFFFFF00; // wipe 4th byte - if ( tagID != 0x54414700 ) - return false; // must be "TAG" - LFA_Seek( file, -1, SEEK_CUR ); //rewind 1 - - ///////////////////////////////////////////////////////// - XMP_Uns8 buffer[31]; // nothing is bigger here, than 30 bytes (offsets [0]-[29]) - buffer[30]=0; // wipe last byte - std::string utf8string; - - // title ////////////////////////////////////////////////////// - LFA_Read( file, buffer, 30, true ); - std::string title( (char*) buffer ); //security: guaranteed to 0-terminate after 30 bytes - if ( ! title.empty() ) - { - ReconcileUtils::Latin1ToUTF8 ( title.c_str(), title.size(), &utf8string ); - meta->SetLocalizedText( kXMP_NS_DC, "title", "" , "x-default" , utf8string.c_str() ); - } - // artist ////////////////////////////////////////////////////// - LFA_Read( file, buffer, 30, true ); - std::string artist( (char*) buffer ); - if ( ! artist.empty() ) - { - ReconcileUtils::Latin1ToUTF8 ( artist.c_str(), artist.size(), &utf8string ); - meta->SetProperty( kXMP_NS_DM, "artist", utf8string.c_str() ); - } - // album ////////////////////////////////////////////////////// - LFA_Read( file, buffer, 30, true ); - std::string album( (char*) buffer ); - if ( ! album.empty() ) - { - ReconcileUtils::Latin1ToUTF8 ( album.c_str(), album.size(), &utf8string ); - meta->SetProperty( kXMP_NS_DM, "album", utf8string.c_str() ); - } - // year ////////////////////////////////////////////////////// - LFA_Read( file, buffer, 4, true ); - buffer[4]=0; // ensure 0-term - std::string year( (char*) buffer ); - if ( ! year.empty() ) - { // should be moot for a year, but let's be safe: - ReconcileUtils::Latin1ToUTF8 ( year.c_str(), year.size(), &utf8string ); - meta->SetProperty( kXMP_NS_XMP, "CreateDate", utf8string.c_str() ); - } - // comment ////////////////////////////////////////////////////// - LFA_Read( file, buffer, 30, true ); - std::string comment( (char*) buffer ); - if ( ! comment.empty() ) - { - ReconcileUtils::Latin1ToUTF8 ( comment.c_str(), comment.size(), &utf8string ); - meta->SetProperty( kXMP_NS_DM, "logComment", utf8string.c_str() ); - } - // trackNo (ID3v1.1) ///////////////////////////////////////////// - if ( buffer[28] == 0 ) - { - XMP_Uns8 trackNo = buffer[29]; - if ( trackNo > 0 ) // 0 := unset - { - std::string trackStr; - SXMPUtils::ConvertFromInt( trackNo, 0, &trackStr ); - meta->SetProperty( kXMP_NS_DM, "trackNumber", trackStr.c_str() ); - } - } - // genre ////////////////////////////////////////////////////// - XMP_Uns8 genreNo = LFA_ReadUns8( file ); - if ( (genreNo > 0) && (genreNo < 127) ) // 0 := unset, 127 := 'unknown' - { - meta->SetProperty( kXMP_NS_DM, "genre", Genres[genreNo] ); - } - - return true; // ID3Tag found - } - - void write( LFA_FileRef file, SXMPMeta* meta ) - { - // move in position (extension if applicable happens by caller) - std::string zeros( 128, '\0' ); - std::string utf8, latin1; - - LFA_Seek( file, -128, SEEK_END); - LFA_Write( file, zeros.data(), 128 ); - - // TAG ///////////////////////////////////////////// - LFA_Seek( file, -128, SEEK_END); - LFA_WriteUns8( file, 'T' ); - LFA_WriteUns8( file, 'A' ); - LFA_WriteUns8( file, 'G' ); - - // title ////////////////////////////////////////////////////// - if ( meta->GetLocalizedText( kXMP_NS_DC, "title", "", "x-default", 0, &utf8, kXMP_NoOptions )) - { - LFA_Seek( file, -128 + 3, SEEK_END); - ReconcileUtils::UTF8ToLatin1( utf8.c_str(), utf8.size(), &latin1 ); - LFA_Write( file, latin1.c_str(), MIN( 30, (XMP_Int32)latin1.size() ) ); - } - // artist ////////////////////////////////////////////////////// - if ( meta->GetProperty( kXMP_NS_DM, "artist", &utf8, kXMP_NoOptions )) - { - LFA_Seek( file, -128 + 33, SEEK_END); - ReconcileUtils::UTF8ToLatin1( utf8.c_str(), utf8.size(), &latin1 ); - LFA_Write( file, latin1.c_str(), MIN( 30, (XMP_Int32)latin1.size() ) ); - } - // album ////////////////////////////////////////////////////// - if ( meta->GetProperty( kXMP_NS_DM, "album", &utf8, kXMP_NoOptions )) - { - LFA_Seek( file, -128 + 63, SEEK_END); - ReconcileUtils::UTF8ToLatin1( utf8.c_str(), utf8.size(), &latin1 ); - LFA_Write( file, latin1.c_str(), MIN( 30, (XMP_Int32)latin1.size() ) ); - } - // year ////////////////////////////////////////////////////// - if ( meta->GetProperty( kXMP_NS_XMP, "CreateDate", &utf8, kXMP_NoOptions )) - { - XMP_DateTime dateTime; - SXMPUtils::ConvertToDate( utf8, &dateTime ); - if ( dateTime.hasDate ) - { - SXMPUtils::ConvertFromInt ( dateTime.year, "", &latin1 ); - LFA_Seek ( file, -128 + 93, SEEK_END ); - LFA_Write ( file, latin1.c_str(), MIN ( 4, (XMP_Int32)latin1.size() ) ); - } - } - // comment (write 30 bytes first, see truncation later) //////////// - if ( meta->GetProperty( kXMP_NS_DM, "logComment", &utf8, kXMP_NoOptions )) - { - LFA_Seek ( file, -128 + 97, SEEK_END ); - ReconcileUtils::UTF8ToLatin1 ( utf8.c_str(), utf8.size(), &latin1 ); - LFA_Write ( file, latin1.c_str(), MIN ( 30, (XMP_Int32)latin1.size() ) ); - } - // genre //////////////////////////////////////////////////////////////// - if ( meta->GetProperty( kXMP_NS_DM, "genre", &utf8, kXMP_NoOptions )) - { - XMP_Uns8 genreNo = 0; - - // try to find (case insensitive) match: - int i; - const char* genreCString = utf8.c_str(); - for ( i=0; i < 127; ++i ) { - if ( (strlen( genreCString ) == strlen(Genres[i])) && //fixing buggy stricmp behaviour on PPC - (stricmp( genreCString, Genres[i] ) == 0 )) { - genreNo = i; // found - break; - } // if - } // for - - LFA_Seek( file, -128 + 127, SEEK_END); - LFA_WriteUns8( file, genreNo ); - } - - // trackNo //////////////////////////////////////////////////////////// - if ( meta->GetProperty( kXMP_NS_DM, "trackNumber", &utf8, kXMP_NoOptions )) - { - XMP_Uns8 trackNo = 0; - try - { - trackNo = (XMP_Uns8) SXMPUtils::ConvertToInt( utf8.c_str() ); - - LFA_Seek( file, -128 + 125, SEEK_END); - LFA_WriteUns8( file , 0 ); // ID3v1.1 extension - LFA_WriteUns8( file, trackNo ); - } - catch ( XMP_Error& ) - { - // forgive, just don't set this one. - } - } - } - - }; // ID3v1Tag - -} // namespace ID3_Support -#endif // __ID3_Support_hpp__ diff --git a/source/XMPFiles/FormatSupport/IPTC_Support.cpp b/source/XMPFiles/FormatSupport/IPTC_Support.cpp deleted file mode 100644 index b1514dd..0000000 --- a/source/XMPFiles/FormatSupport/IPTC_Support.cpp +++ /dev/null @@ -1,745 +0,0 @@ -// ================================================================================================= -// ADOBE SYSTEMS INCORPORATED -// Copyright 2006 Adobe Systems Incorporated -// 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 "IPTC_Support.hpp" -#include "EndianUtils.hpp" -#include "Reconcile_Impl.hpp" - -// ================================================================================================= -/// \file IPTC_Support.cpp -/// \brief XMPFiles support for IPTC (IIM) DataSets. -/// -// ================================================================================================= - -enum { kUTF8_IncomingMode = 0, kUTF8_LosslessMode = 1, kUTF8_AlwaysMode = 2 }; -#ifndef kUTF8_Mode - #define kUTF8_Mode kUTF8_AlwaysMode -#endif - -const DataSetCharacteristics kKnownDataSets[] = - { { kIPTC_ObjectType, kIPTC_UnmappedText, 67, "", "" }, // Not mapped to XMP. - { kIPTC_IntellectualGenre, kIPTC_MapSpecial, 68, kXMP_NS_IPTCCore, "IntellectualGenre" }, // Only the name part is in the XMP. - { kIPTC_Title, kIPTC_MapLangAlt, 64, kXMP_NS_DC, "title" }, - { kIPTC_EditStatus, kIPTC_UnmappedText, 64, "", "" }, // Not mapped to XMP. - { kIPTC_EditorialUpdate, kIPTC_UnmappedText, 2, "", "" }, // Not mapped to XMP. - { kIPTC_Urgency, kIPTC_MapSimple, 1, kXMP_NS_Photoshop, "Urgency" }, - { kIPTC_SubjectCode, kIPTC_MapSpecial, 236, kXMP_NS_IPTCCore, "SubjectCode" }, // Only the reference number is in the XMP. - { kIPTC_Category, kIPTC_MapSimple, 3, kXMP_NS_Photoshop, "Category" }, - { kIPTC_SuppCategory, kIPTC_MapArray, 32, kXMP_NS_Photoshop, "SupplementalCategories" }, - { kIPTC_FixtureIdentifier, kIPTC_UnmappedText, 32, "", "" }, // Not mapped to XMP. - { kIPTC_Keyword, kIPTC_MapArray, 64, kXMP_NS_DC, "subject" }, - { kIPTC_ContentLocCode, kIPTC_UnmappedText, 3, "", "" }, // Not mapped to XMP. - { kIPTC_ContentLocName, kIPTC_UnmappedText, 64, "", "" }, // Not mapped to XMP. - { kIPTC_ReleaseDate, kIPTC_UnmappedText, 8, "", "" }, // Not mapped to XMP. - { kIPTC_ReleaseTime, kIPTC_UnmappedText, 11, "", "" }, // Not mapped to XMP. - { kIPTC_ExpDate, kIPTC_UnmappedText, 8, "", "" }, // Not mapped to XMP. - { kIPTC_ExpTime, kIPTC_UnmappedText, 11, "", "" }, // Not mapped to XMP. - { kIPTC_Instructions, kIPTC_MapSimple, 256, kXMP_NS_Photoshop, "Instructions" }, - { kIPTC_ActionAdvised, kIPTC_UnmappedText, 2, "", "" }, // Not mapped to XMP. - { kIPTC_RefService, kIPTC_UnmappedText, 10, "", "" }, // Not mapped to XMP. ! Interleave 2:45, 2:47, 2:50! - { kIPTC_RefDate, kIPTC_UnmappedText, 8, "", "" }, // Not mapped to XMP. ! Interleave 2:45, 2:47, 2:50! - { kIPTC_RefNumber, kIPTC_UnmappedText, 8, "", "" }, // Not mapped to XMP. ! Interleave 2:45, 2:47, 2:50! - { kIPTC_DateCreated, kIPTC_MapSpecial, 8, kXMP_NS_Photoshop, "DateCreated" }, // ! Reformatted date. Combined with 2:60, TimeCreated. - { kIPTC_TimeCreated, kIPTC_UnmappedText, 11, "", "" }, // ! Combined with 2:55, DateCreated. - { kIPTC_DigitalCreateDate, kIPTC_Map3Way, 8, "", "" }, // ! 3 way Exif-IPTC-XMP date/time set. Combined with 2:63, DigitalCreateTime. - { kIPTC_DigitalCreateTime, kIPTC_UnmappedText, 11, "", "" }, // ! Combined with 2:62, DigitalCreateDate. - { kIPTC_OriginProgram, kIPTC_UnmappedText, 32, "", "" }, // Not mapped to XMP. - { kIPTC_ProgramVersion, kIPTC_UnmappedText, 10, "", "" }, // Not mapped to XMP. - { kIPTC_ObjectCycle, kIPTC_UnmappedText, 1, "", "" }, // Not mapped to XMP. - { kIPTC_Creator, kIPTC_Map3Way, 32, "", "" }, // ! In the 3 way Exif-IPTC-XMP set. - { kIPTC_CreatorJobtitle, kIPTC_MapSimple, 32, kXMP_NS_Photoshop, "AuthorsPosition" }, - { kIPTC_City, kIPTC_MapSimple, 32, kXMP_NS_Photoshop, "City" }, - { kIPTC_Location, kIPTC_MapSimple, 32, kXMP_NS_IPTCCore, "Location" }, - { kIPTC_State, kIPTC_MapSimple, 32, kXMP_NS_Photoshop, "State" }, - { kIPTC_CountryCode, kIPTC_MapSimple, 3, kXMP_NS_IPTCCore, "CountryCode" }, - { kIPTC_Country, kIPTC_MapSimple, 64, kXMP_NS_Photoshop, "Country" }, - { kIPTC_JobID, kIPTC_MapSimple, 32, kXMP_NS_Photoshop, "TransmissionReference" }, - { kIPTC_Headline, kIPTC_MapSimple, 256, kXMP_NS_Photoshop, "Headline" }, - { kIPTC_Provider, kIPTC_MapSimple, 32, kXMP_NS_Photoshop, "Credit" }, - { kIPTC_Source, kIPTC_MapSimple, 32, kXMP_NS_Photoshop, "Source" }, - { kIPTC_CopyrightNotice, kIPTC_Map3Way, 128, "", "" }, // ! In the 3 way Exif-IPTC-XMP set. - { kIPTC_Contact, kIPTC_UnmappedText, 128, "", "" }, // Not mapped to XMP. - { kIPTC_Description, kIPTC_Map3Way, 2000, "", "" }, // ! In the 3 way Exif-IPTC-XMP set. - { kIPTC_DescriptionWriter, kIPTC_MapSimple, 32, kXMP_NS_Photoshop, "CaptionWriter" }, - { kIPTC_RasterizedCaption, kIPTC_UnmappedBin, 7360, "", "" }, // Not mapped to XMP. ! Binary data! - { kIPTC_ImageType, kIPTC_UnmappedText, 2, "", "" }, // Not mapped to XMP. - { kIPTC_ImageOrientation, kIPTC_UnmappedText, 1, "", "" }, // Not mapped to XMP. - { kIPTC_LanguageID, kIPTC_UnmappedText, 3, "", "" }, // Not mapped to XMP. - { kIPTC_AudioType, kIPTC_UnmappedText, 2, "", "" }, // Not mapped to XMP. - { kIPTC_AudioSampleRate, kIPTC_UnmappedText, 6, "", "" }, // Not mapped to XMP. - { kIPTC_AudioSampleRes, kIPTC_UnmappedText, 2, "", "" }, // Not mapped to XMP. - { kIPTC_AudioDuration, kIPTC_UnmappedText, 6, "", "" }, // Not mapped to XMP. - { kIPTC_AudioOutcue, kIPTC_UnmappedText, 64, "", "" }, // Not mapped to XMP. - { kIPTC_PreviewFormat, kIPTC_UnmappedBin, 2, "", "" }, // Not mapped to XMP. ! Binary data! - { kIPTC_PreviewFormatVers, kIPTC_UnmappedBin, 2, "", "" }, // Not mapped to XMP. ! Binary data! - { kIPTC_PreviewData, kIPTC_UnmappedBin, 256000, "", "" }, // Not mapped to XMP. ! Binary data! - { 255, kIPTC_MapSpecial, 0, 0, 0 } }; // ! Must be last as a sentinel. - -// A combination of the IPTC "Subject Reference System Guidelines" and IIMv4.1 Appendix G. -const IntellectualGenreMapping kIntellectualGenreMappings[] = -{ { "001", "Current" }, - { "002", "Analysis" }, - { "003", "Archive material" }, - { "004", "Background" }, - { "005", "Feature" }, - { "006", "Forecast" }, - { "007", "History" }, - { "008", "Obituary" }, - { "009", "Opinion" }, - { "010", "Polls and surveys" }, - { "010", "Polls & Surveys" }, - { "011", "Profile" }, - { "012", "Results listings and statistics" }, - { "012", "Results Listings & Tables" }, - { "013", "Side bar and supporting information" }, - { "013", "Side bar & Supporting information" }, - { "014", "Summary" }, - { "015", "Transcript and verbatim" }, - { "015", "Transcript & Verbatim" }, - { "016", "Interview" }, - { "017", "From the scene" }, - { "017", "From the Scene" }, - { "018", "Retrospective" }, - { "019", "Synopsis" }, - { "019", "Statistics" }, - { "020", "Update" }, - { "021", "Wrapup" }, - { "021", "Wrap-up" }, - { "022", "Press release" }, - { "022", "Press Release" }, - { "023", "Quote" }, - { "024", "Press-digest" }, - { "025", "Review" }, - { "026", "Curtain raiser" }, - { "027", "Actuality" }, - { "028", "Question and answer" }, - { "029", "Music" }, - { "030", "Response to a question" }, - { "031", "Raw sound" }, - { "032", "Scener" }, - { "033", "Text only" }, - { "034", "Voicer" }, - { "035", "Fixture" }, - { 0, 0 } }; // ! Must be last as a sentinel. - -// ================================================================================================= -// FindKnownDataSet -// ================ - -static const DataSetCharacteristics* FindKnownDataSet ( XMP_Uns8 id ) -{ - size_t i = 0; - - while ( kKnownDataSets[i].id < id ) ++i; // The list is short enough for a linear search. - - if ( kKnownDataSets[i].id != id ) return 0; - return &kKnownDataSets[i]; - -} // FindKnownDataSet - -// ================================================================================================= -// IPTC_Manager::ParseMemoryDataSets -// ================================= -// -// Parse the legacy IIM block. Offsets and lengths are kept for any 1:90 DataSet and all of Record 2. - -void IPTC_Manager::ParseMemoryDataSets ( const void* data, XMP_Uns32 length, bool copyData /* = true */ ) -{ - // Get rid of any existing data. - - DataSetMap::iterator dsPos = this->dataSets.begin(); - DataSetMap::iterator dsEnd = this->dataSets.end(); - - for ( ; dsPos != dsEnd; ++dsPos ) this->DisposeLooseValue ( dsPos->second ); - - this->dataSets.clear(); - - if ( this->ownedContent ) free ( this->iptcContent ); - this->ownedContent = false; // Set to true later if the content is copied. - this->iptcContent = 0; - this->iptcLength = 0; - - this->changed = false; - - if ( length == 0 ) return; - if ( *((XMP_Uns8*)data) != 0x1C ) XMP_Throw ( "Not valid IPTC, no leading 0x1C", kXMPErr_BadIPTC ); - - // Allocate space for the full in-memory data and copy it. - - if ( length > 10*1024*1024 ) XMP_Throw ( "Outrageous length for memory-based IPTC", kXMPErr_BadIPTC ); - this->iptcLength = length; - - if ( ! copyData ) { - this->iptcContent = (XMP_Uns8*)data; - } else { - this->iptcContent = (XMP_Uns8*) malloc(length); - if ( this->iptcContent == 0 ) XMP_Throw ( "Out of memory", kXMPErr_NoMemory ); - memcpy ( this->iptcContent, data, length ); // AUDIT: Safe, malloc'ed length bytes above. - this->ownedContent = true; - } - - // Build the map of the 2:* DataSets. - - XMP_Uns8* iptcPtr = this->iptcContent; - XMP_Uns8* iptcEnd = iptcPtr + length; - XMP_Uns8* iptcLimit = iptcEnd - kMinDataSetSize; - XMP_Uns32 dsLen; // ! The large form can have values up to 4GB in length. - - this->utf8Encoding = false; - - bool found190 = false; - bool found2xx = false; - - for ( ; iptcPtr <= iptcLimit; iptcPtr += dsLen ) { - - XMP_Uns8* dsPtr = iptcPtr; - XMP_Uns8 oneC = *iptcPtr; - XMP_Uns8 recNum = *(iptcPtr+1); - XMP_Uns8 dsNum = *(iptcPtr+2); - - if ( oneC != 0x1C ) break; // No more DataSets. - - dsLen = GetUns16BE ( iptcPtr+3 ); // ! Compute dsLen before any "continue", needed for loop increment! - iptcPtr += 5; // Advance to the data (or extended length). - - if ( (dsLen & 0x8000) != 0 ) { - XMP_Assert ( dsLen <= 0xFFFF ); - XMP_Uns32 lenLen = dsLen & 0x7FFF; - if ( iptcPtr > iptcEnd-lenLen ) break; // Bad final DataSet. Throw instead? - dsLen = 0; - for ( XMP_Uns16 i = 0; i < lenLen; ++i, ++iptcPtr ) { - dsLen = (dsLen << 8) + *iptcPtr; - } - } - - if ( iptcPtr > (iptcEnd - dsLen) ) break; // Bad final DataSet. Throw instead? - - if ( recNum == 0 ) continue; // Should not be a record 0. Throw instead? - - if ( recNum == 1 ) { - if ( dsNum == 90 ) { - found190 = true; - this->offset190 = (XMP_Uns32) (dsPtr - this->iptcContent); - this->length190 = 5 + dsLen; - if ( (dsLen == 3) && (memcmp ( iptcPtr, "\x1B\x25\x47", 3 ) == 0) ) this->utf8Encoding = true; - } - continue; // Ignore all other record 1 DataSets. - } - - if ( recNum == 2 ) { - if ( ! found2xx ) { - found2xx = true; - this->offset2xx = (XMP_Uns32) (dsPtr - this->iptcContent); - this->length2xx = this->iptcLength - this->offset2xx; // ! In case there are no other records. - } - } else { - this->length2xx = (XMP_Uns32) (dsPtr - (this->iptcContent + this->offset2xx)); - break; // The records are in ascending order, done with 2:xx DataSets. - } - - XMP_Assert ( recNum == 2 ); - if ( dsNum == 0 ) continue; // Ignore 2:00 when reading, it is created explicitly when writing. - - DataSetInfo dsInfo ( dsNum, dsLen, iptcPtr ); - DataSetMap::iterator dsPos = this->dataSets.find ( dsNum ); - - bool repeatable = false; - - const DataSetCharacteristics* knownDS = FindKnownDataSet ( dsNum ); - - if ( (knownDS == 0) || (knownDS->mapForm == kIPTC_MapArray) ) { - repeatable = true; // Allow repeats for unknown DataSets. - } else if ( (dsNum == kIPTC_Creator) || (dsNum == kIPTC_SubjectCode) ) { - repeatable = true; - } - - if ( repeatable || (dsPos == this->dataSets.end()) ) { - DataSetMap::value_type mapValue ( dsNum, dsInfo ); - (void) this->dataSets.insert ( this->dataSets.upper_bound ( dsNum ), mapValue ); - } else { - this->DisposeLooseValue ( dsPos->second ); - dsPos->second = dsInfo; // Keep the last copy of illegal repeats. - } - - } - -} // IPTC_Manager::ParseMemoryDataSets - -// ================================================================================================= -// IPTC_Manager::GetDataSet -// ======================== - -size_t IPTC_Manager::GetDataSet ( XMP_Uns8 id, DataSetInfo* info, size_t which /* = 0 */ ) const -{ - - DataSetMap::const_iterator dsPos = this->dataSets.lower_bound ( id ); - if ( (dsPos == this->dataSets.end()) || (id != dsPos->second.id) ) return 0; - - size_t dsCount = this->dataSets.count ( id ); - if ( which >= dsCount ) return 0; // Valid range for which is 0 .. count-1. - - if ( info != 0 ) { - for ( size_t i = 0; i < which; ++i ) ++dsPos; // Can't do "dsPos += which", no iter+int operator. - *info = dsPos->second; - } - - return dsCount; - -} // IPTC_Manager::GetDataSet - -// ================================================================================================= -// IPTC_Manager::GetDataSet_UTF8 -// ============================= - -size_t IPTC_Manager::GetDataSet_UTF8 ( XMP_Uns8 id, std::string * utf8Str, size_t which /* = 0 */ ) const -{ - if ( utf8Str != 0 ) utf8Str->erase(); - - DataSetInfo dsInfo; - size_t dsCount = GetDataSet ( id, &dsInfo, which ); - if ( dsCount == 0 ) return 0; - - if ( utf8Str != 0 ) { - if ( this->utf8Encoding ) { - utf8Str->assign ( (char*)dsInfo.dataPtr, dsInfo.dataLen ); - } else if ( ! ignoreLocalText ) { - ReconcileUtils::LocalToUTF8 ( dsInfo.dataPtr, dsInfo.dataLen, utf8Str ); - } else if ( ReconcileUtils::IsASCII ( dsInfo.dataPtr, dsInfo.dataLen ) ) { - utf8Str->assign ( (char*)dsInfo.dataPtr, dsInfo.dataLen ); - } - } - - return dsCount; - -} // IPTC_Manager::GetDataSet_UTF8 - -// ================================================================================================= -// IPTC_Manager::DisposeLooseValue -// =============================== -// -// Dispose of loose values from SetDataSet calls after the last UpdateMemoryDataSets. - -// ! Don't try to make the DataSetInfo struct be self-cleaning. It is a primary public type, returned -// ! from GetDataSet. Making it self-cleaning would get into nasty assignment and pointer ownership -// ! issues, far worse than doing this explicit cleanup. - -void IPTC_Manager::DisposeLooseValue ( DataSetInfo & dsInfo ) -{ - if ( dsInfo.dataLen == 0 ) return; - - XMP_Uns8* dataBegin = this->iptcContent; - XMP_Uns8* dataEnd = dataBegin + this->iptcLength; - - if ( ((XMP_Uns8*)dsInfo.dataPtr < dataBegin) || ((XMP_Uns8*)dsInfo.dataPtr >= dataEnd) ) { - free ( (void*) dsInfo.dataPtr ); - dsInfo.dataPtr = 0; - } - -} // IPTC_Manager::DisposeLooseValue - -// ================================================================================================= -// ================================================================================================= - -// ================================================================================================= -// IPTC_Writer::~IPTC_Writer -// ========================= -// -// Dispose of loose values from SetDataSet calls after the last UpdateMemoryDataSets. - -IPTC_Writer::~IPTC_Writer() -{ - DataSetMap::iterator dsPos = this->dataSets.begin(); - DataSetMap::iterator dsEnd = this->dataSets.end(); - - for ( ; dsPos != dsEnd; ++dsPos ) this->DisposeLooseValue ( dsPos->second ); - -} // IPTC_Writer::~IPTC_Writer - -// ================================================================================================= -// IPTC_Writer::SetDataSet_UTF8 -// ============================ - -void IPTC_Writer::SetDataSet_UTF8 ( XMP_Uns8 id, const void* utf8Ptr, XMP_Uns32 utf8Len, long which /* = -1 */ ) -{ - const DataSetCharacteristics* knownDS = FindKnownDataSet ( id ); - if ( knownDS == 0 ) XMP_Throw ( "Can only set known IPTC DataSets", kXMPErr_InternalFailure ); - - // Decide which character encoding to use and get a temporary pointer to the value. - - XMP_Uns8 * tempPtr; - XMP_Uns32 dataLen; - std::string localStr; - - if ( kUTF8_Mode == kUTF8_AlwaysMode ) { - - // Always use UTF-8. - if ( ! this->utf8Encoding ) this->ConvertToUTF8(); - tempPtr = (XMP_Uns8*) utf8Ptr; - dataLen = utf8Len; - - } else if ( kUTF8_Mode == kUTF8_IncomingMode ) { - - // Only use UTF-8 if that was what the parsed block used. - if ( this->utf8Encoding ) { - tempPtr = (XMP_Uns8*) utf8Ptr; - dataLen = utf8Len; - } else if ( ! ignoreLocalText ) { - ReconcileUtils::UTF8ToLocal ( utf8Ptr, utf8Len, &localStr ); - tempPtr = (XMP_Uns8*) localStr.data(); - dataLen = (XMP_Uns32) localStr.size(); - } else { - if ( ! ReconcileUtils::IsASCII ( utf8Ptr, utf8Len ) ) return; - tempPtr = (XMP_Uns8*) utf8Ptr; - dataLen = utf8Len; - } - - } else if ( kUTF8_Mode == kUTF8_LosslessMode ) { - - // Convert to UTF-8 if needed to prevent round trip loss. - if ( this->utf8Encoding ) { - tempPtr = (XMP_Uns8*) utf8Ptr; - dataLen = utf8Len; - } else if ( ! ignoreLocalText ) { - std::string rtStr; - ReconcileUtils::UTF8ToLocal ( utf8Ptr, utf8Len, &localStr ); - ReconcileUtils::LocalToUTF8 ( localStr.data(), localStr.size(), &rtStr ); - if ( (rtStr.size() == utf8Len) && (memcmp ( rtStr.data(), utf8Ptr, utf8Len ) == 0) ) { - tempPtr = (XMP_Uns8*) localStr.data(); // No loss, keep local encoding. - dataLen = (XMP_Uns32) localStr.size(); - } else { - this->ConvertToUTF8(); // Had loss, change everything to UTF-8. - XMP_Assert ( this->utf8Encoding ); - tempPtr = (XMP_Uns8*) utf8Ptr; - dataLen = utf8Len; - } - } else { - if ( ! ReconcileUtils::IsASCII ( utf8Ptr, utf8Len ) ) return; - tempPtr = (XMP_Uns8*) utf8Ptr; - dataLen = utf8Len; - } - - } - - // Set the value for this DataSet, making a non-transient copy of the value. Respect UTF-8 character - // boundaries when truncating. This is easy to check. If the first truncated byte has 10 in the - // high order 2 bits then we are in the middle of a UTF-8 multi-byte character. - // Back up to just before a byte with 11 in the high order 2 bits. - - if ( dataLen > knownDS->maxLen ) { - dataLen = (XMP_Uns32)knownDS->maxLen; - if ( this->utf8Encoding && ((tempPtr[dataLen] >> 6) == 2) ) { - for ( ; (dataLen > 0) && ((tempPtr[dataLen] >> 6) != 3); --dataLen ) {} - } - } - - DataSetMap::iterator dsPos = this->dataSets.find ( id ); - long currCount = (long) this->dataSets.count ( id ); - - bool repeatable = false; - - if ( knownDS->mapForm == kIPTC_MapArray ) { - repeatable = true; - } else if ( (id == kIPTC_Creator) || (id == kIPTC_SubjectCode) ) { - repeatable = true; - } - - if ( ! repeatable ) { - - if ( which > 0 ) XMP_Throw ( "Non-repeatable IPTC DataSet", kXMPErr_BadParam ); - - } else { - - if ( which < 0 ) which = currCount; // The default is to append. - - if ( which > currCount ) { - XMP_Throw ( "Invalid index for IPTC DataSet", kXMPErr_BadParam ); - } else if ( which == currCount ) { - dsPos = this->dataSets.end(); // To make later checks do the right thing. - } else { - dsPos = this->dataSets.lower_bound ( id ); - for ( ; which > 0; --which ) ++dsPos; - } - - } - - if ( dsPos != this->dataSets.end() ) { - if ( (dsPos->second.dataLen == dataLen) && (memcmp ( dsPos->second.dataPtr, tempPtr, dataLen ) == 0) ) { - return; // ! New value matches the old, don't update. - } - } - - XMP_Uns8 * dataPtr = (XMP_Uns8*) malloc ( dataLen ); - if ( dataPtr == 0 ) XMP_Throw ( "Out of memory", kXMPErr_NoMemory ); - memcpy ( dataPtr, tempPtr, dataLen ); // AUDIT: Safe, malloc'ed dataLen bytes above. - - DataSetInfo dsInfo ( id, dataLen, dataPtr ); - - if ( dsPos != this->dataSets.end() ) { - this->DisposeLooseValue ( dsPos->second ); - dsPos->second = dsInfo; - } else { - DataSetMap::value_type mapValue ( id, dsInfo ); - (void) this->dataSets.insert ( this->dataSets.upper_bound ( id ), mapValue ); - } - - this->changed = true; - -} // IPTC_Writer::SetDataSet_UTF8 - -// ================================================================================================= -// IPTC_Writer::DeleteDataSet -// ========================== - -void IPTC_Writer::DeleteDataSet ( XMP_Uns8 id, long which /* = -1 */ ) -{ - DataSetMap::iterator dsBegin = this->dataSets.lower_bound ( id ); // Set for which == -1. - DataSetMap::iterator dsEnd = this->dataSets.upper_bound ( id ); - - if ( dsBegin == dsEnd ) return; // Nothing to delete. - - if ( which >= 0 ) { - long currCount = (long) this->dataSets.count ( id ); - if ( which >= currCount ) return; // Nothing to delete. - for ( ; which > 0; --which ) ++dsBegin; - dsEnd = dsBegin; ++dsEnd; // ! Can't do "dsEnd = dsBegin+1"! - } - - for ( DataSetMap::iterator dsPos = dsBegin; dsPos != dsEnd; ++dsPos ) { - this->DisposeLooseValue ( dsPos->second ); - } - - this->dataSets.erase ( dsBegin, dsEnd ); - this->changed = true; - -} // IPTC_Writer::DeleteDataSet - -// ================================================================================================= -// IPTC_Writer::UpdateMemoryDataSets -// ================================= -// -// Reconstruct the entire IIM block. This does not include any final alignment padding, that is an -// artifact of some specific wrappers such as Photoshop image resources. - -void IPTC_Writer::UpdateMemoryDataSets() -{ - if ( ! this->changed ) return; - - DataSetMap::iterator dsPos; - DataSetMap::iterator dsEnd = this->dataSets.end(); - - if ( kUTF8_Mode == kUTF8_LosslessMode ) { - if ( this->utf8Encoding ) { - if ( ! this->CheckRoundTripLoss() ) this->ConvertToLocal(); - } else { - if ( this->CheckRoundTripLoss() ) this->ConvertToUTF8(); - } - } - - // Compute the length of the new IIM block, including space for records other than 2. All other - // records are preserved as-is, except for 1:90. If local text is used then 1:90 is omitted, - // if UTF-8 text is used then 1:90 is written. - - XMP_Uns32 midOffset = this->offset190 + this->length190; - XMP_Uns32 midLength = this->offset2xx - midOffset; - - XMP_Uns32 suffixOffset = this->offset2xx + this->length2xx; - XMP_Uns32 suffixLength = this->iptcLength - suffixOffset; - - XMP_Uns32 newLength = this->offset190 + (5+2); // For things before 1:90 and 2:0. - if ( this->utf8Encoding ) newLength += (5+3); // For 1:90, if written. - newLength += midLength; // For things between 1:90 and 2:xx. - newLength += suffixLength; // For records after 2. - - for ( dsPos = this->dataSets.begin(); dsPos != dsEnd; ++dsPos ) { // Accumulate the 2:xx sizes. - XMP_Uns32 dsLen = dsPos->second.dataLen; - newLength += (5 + dsLen); - if ( dsLen > 0x7FFF ) newLength += 4; // We always use a 4 byte extended length for big values. - } - - // Allocate the new IIM block. - - XMP_Uns8* newContent = (XMP_Uns8*) malloc ( newLength ); - if ( newContent == 0 ) XMP_Throw ( "Out of memory", kXMPErr_NoMemory ); - - XMP_Uns8* dsPtr = newContent; - - // Write the 1:xx DataSets, including whatever is appropriate for 1:90. - - if ( this->offset190 > 0 ) { // Whatever was before an existing 1:90. - memcpy ( dsPtr, this->iptcContent, this->offset190 ); // AUDIT: Within range of allocation. - dsPtr += this->offset190; - } - - if ( this->utf8Encoding ) { // Write 1:90 only if text is UTF-8. - memcpy ( dsPtr, "\x1C\x01\x5A\x00\x03\x1B\x25\x47", (5+3) ); // AUDIT: Within range of allocation. - dsPtr += (5+3); - } - - if ( midLength > 0 ) { // Write the remainder before record 2. - memcpy ( dsPtr, (this->iptcContent + midOffset), midLength ); // AUDIT: Within range of allocation. - dsPtr += midLength; - } - - // Write the 2:xx DataSets, starting with an explicit 2:00. - - if ( this->utf8Encoding ) { - // Start with 2:00 for version 4. - memcpy ( dsPtr, "\x1C\x02\x00\x00\x02\x00\x04", (5+2) ); // AUDIT: Within range of allocation. - dsPtr += (5+2); - } else { - // Start with 2:00 for version 2. - // *** We should probably write version 4 all the time. This is a late CS3 change, don't want - // *** to risk breaking other apps that might be strict about version checking. - memcpy ( dsPtr, "\x1C\x02\x00\x00\x02\x00\x02", (5+2) ); // AUDIT: Within range of allocation. - dsPtr += (5+2); - } - - // Fill in the record 2 DataSets that have values. - - for ( dsPos = this->dataSets.begin(); dsPos != dsEnd; ++dsPos ) { - - DataSetInfo & dsInfo = dsPos->second; - - dsPtr[0] = 0x1C; - dsPtr[1] = 2; - dsPtr[2] = dsInfo.id; - dsPtr += 3; - - XMP_Uns32 dsLen = dsInfo.dataLen; - if ( dsLen <= 0x7FFF ) { - PutUns16BE ( (XMP_Uns16)dsLen, dsPtr ); - dsPtr += 2; - } else { - PutUns16BE ( 0x8004, dsPtr ); - PutUns32BE ( dsLen, dsPtr+2 ); - dsPtr += 6; - } - - if ( dsLen > (newLength - (dsPtr - newContent)) ) { - XMP_Throw ( "Buffer overrun", kXMPErr_InternalFailure ); - } - memcpy ( dsPtr, dsInfo.dataPtr, dsLen ); // AUDIT: Protected by above check. - dsPtr += dsLen; - - } - - if ( suffixLength > 0 ) { // Write the records after 2. - memcpy ( dsPtr, (this->iptcContent + suffixOffset), suffixLength ); // AUDIT: Within range of allocation. - dsPtr += suffixLength; - } - - XMP_Assert ( dsPtr == (newContent + newLength) ); - - // Parse the new block, it is the best way to reset internal info and rebuild the map. - - this->ParseMemoryDataSets ( newContent, newLength, false ); // Don't make another copy of the content. - XMP_Assert ( this->iptcLength == newLength ); - this->ownedContent = (newLength > 0); // We really do own the new content, if not empty. - -} // IPTC_Writer::UpdateMemoryDataSets - -// ================================================================================================= -// IPTC_Writer::ConvertToUTF8 -// ========================== -// -// Convert the values of existing text DataSets to UTF-8. For now we only accept text DataSets. - -void IPTC_Writer::ConvertToUTF8() -{ - XMP_Assert ( ! this->utf8Encoding ); - std::string utf8Str; - - DataSetMap::iterator dsPos = this->dataSets.begin(); - DataSetMap::iterator dsEnd = this->dataSets.end(); - - for ( ; dsPos != dsEnd; ++dsPos ) { - - DataSetInfo & dsInfo = dsPos->second; - - ReconcileUtils::LocalToUTF8 ( dsInfo.dataPtr, dsInfo.dataLen, &utf8Str ); - this->DisposeLooseValue ( dsInfo ); - - dsInfo.dataLen = (XMP_Uns32)utf8Str.size(); - dsInfo.dataPtr = (XMP_Uns8*) malloc ( dsInfo.dataLen ); - if ( dsInfo.dataPtr == 0 ) XMP_Throw ( "Out of memory", kXMPErr_NoMemory ); - memcpy ( dsInfo.dataPtr, utf8Str.data(), dsInfo.dataLen ); // AUDIT: Safe, malloc'ed dataLen bytes above. - - } - - this->utf8Encoding = true; - -} // IPTC_Writer::ConvertToUTF8 - -// ================================================================================================= -// IPTC_Writer::ConvertToLocal -// =========================== -// -// Convert the values of existing text DataSets to local. For now we only accept text DataSets. - -void IPTC_Writer::ConvertToLocal() -{ - XMP_Assert ( this->utf8Encoding ); - std::string localStr; - - DataSetMap::iterator dsPos = this->dataSets.begin(); - DataSetMap::iterator dsEnd = this->dataSets.end(); - - for ( ; dsPos != dsEnd; ++dsPos ) { - - DataSetInfo & dsInfo = dsPos->second; - - ReconcileUtils::UTF8ToLocal ( dsInfo.dataPtr, dsInfo.dataLen, &localStr ); - this->DisposeLooseValue ( dsInfo ); - - dsInfo.dataLen = (XMP_Uns32)localStr.size(); - dsInfo.dataPtr = (XMP_Uns8*) malloc ( dsInfo.dataLen ); - if ( dsInfo.dataPtr == 0 ) XMP_Throw ( "Out of memory", kXMPErr_NoMemory ); - memcpy ( dsInfo.dataPtr, localStr.data(), dsInfo.dataLen ); // AUDIT: Safe, malloc'ed dataLen bytes above. - - } - - this->utf8Encoding = false; - -} // IPTC_Writer::ConvertToLocal - -// ================================================================================================= -// IPTC_Writer::CheckRoundTripLoss -// =============================== -// -// See if we still need UTF-8 because of round-trip loss. Returns true if there is loss. - -bool IPTC_Writer::CheckRoundTripLoss() -{ - XMP_Assert ( this->utf8Encoding ); - std::string localStr, rtStr; - - DataSetMap::iterator dsPos = this->dataSets.begin(); - DataSetMap::iterator dsEnd = this->dataSets.end(); - - for ( ; dsPos != dsEnd; ++dsPos ) { - - DataSetInfo & dsInfo = dsPos->second; - - XMP_StringPtr utf8Ptr = (XMP_StringPtr) dsInfo.dataPtr; - XMP_StringLen utf8Len = dsInfo.dataLen; - - ReconcileUtils::UTF8ToLocal ( utf8Ptr, utf8Len, &localStr ); - ReconcileUtils::LocalToUTF8 ( localStr.data(), localStr.size(), &rtStr ); - - if ( (rtStr.size() != utf8Len) || (memcmp ( rtStr.data(), utf8Ptr, utf8Len ) != 0) ) { - return true; // Had round-trip loss, keep UTF-8. - } - - } - - return false; // No loss. - -} // IPTC_Writer::CheckRoundTripLoss - -// ================================================================================================= diff --git a/source/XMPFiles/FormatSupport/IPTC_Support.hpp b/source/XMPFiles/FormatSupport/IPTC_Support.hpp deleted file mode 100644 index 470a6d4..0000000 --- a/source/XMPFiles/FormatSupport/IPTC_Support.hpp +++ /dev/null @@ -1,304 +0,0 @@ -#ifndef __IPTC_Support_hpp__ -#define __IPTC_Support_hpp__ 1 - -// ================================================================================================= -// ADOBE SYSTEMS INCORPORATED -// Copyright 2006 Adobe Systems Incorporated -// 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 "XMP_Environment.h" // ! This must be the first include. - -#include <map> - -#include "XMP_Const.h" -#include "XMPFiles_Impl.hpp" -#include "EndianUtils.hpp" - -// ================================================================================================= -/// \file IPTC_Support.hpp -/// \brief XMPFiles support for IPTC (IIM) DataSets. -/// -/// This header provides IPTC (IIM) DataSet support specific to the needs of XMPFiles. This is not -/// intended for general purpose IPTC processing. There is a small tree of derived classes, 1 -/// virtual base class and 2 concrete leaf classes: -/// \code -/// IPTC_Manager - The root virtual base class. -/// IPTC_Reader - A derived concrete leaf class for memory-based read-only access. -/// IPTC_Writer - A derived concrete leaf class for memory-based read-write access. -/// \endcode -/// -/// \c IPTC_Manager declares all of the public methods except for specialized constructors in the -/// leaf classes. The read-only classes throw an XMP_Error exception for output methods like -/// \c SetDataSet. They return appropriate values for "safe" methods, \c IsChanged will return false -/// for example. -/// -/// The IPTC DataSet organization differs from TIFF tags and Photoshop image resources in allowing -/// muultiple occurrences for some IDs. The C++ STL multimap is a natural data structure for IPTC. -/// -/// Support is only provided for DataSet 1:90 to decide if local or UTF-8 text encoding is used, and -/// the following text valued DataSets: 2:05, 2:10, 2:15, 2:20, 2:25, 2:40, 2:55, 2:80, 2:85, 2:90, -/// 2:95, 2:101, 2:103, 2:105, 2:110, 2:115, 2:116, 2:120, and 2:122. DataSet 2:00 is ignored when -/// reading but always written. -/// -/// \note Unlike the TIFF_Manager and PSIR_Manager class trees, IPTC_Manager only provides in-memory -/// implementations. The total size of IPTC data is small enough to make this reasonable. -/// -/// \note These classes are for use only when directly compiled and linked. They should not be -/// packaged in a DLL by themselves. They do not provide any form of C++ ABI protection. -// ================================================================================================= - - -// ================================================================================================= -// ================================================================================================= - -enum { // List of recognized 2:* IIM DataSets. The names are from IIMv4 and IPTC4XMP. - kIPTC_ObjectType = 3, - kIPTC_IntellectualGenre = 4, - kIPTC_Title = 5, - kIPTC_EditStatus = 7, - kIPTC_EditorialUpdate = 8, - kIPTC_Urgency = 10, - kIPTC_SubjectCode = 12, - kIPTC_Category = 15, - kIPTC_SuppCategory = 20, - kIPTC_FixtureIdentifier = 22, - kIPTC_Keyword = 25, - kIPTC_ContentLocCode = 26, - kIPTC_ContentLocName = 27, - kIPTC_ReleaseDate = 30, - kIPTC_ReleaseTime = 35, - kIPTC_ExpDate = 37, - kIPTC_ExpTime = 38, - kIPTC_Instructions = 40, - kIPTC_ActionAdvised = 42, - kIPTC_RefService = 45, - kIPTC_RefDate = 47, - kIPTC_RefNumber = 50, - kIPTC_DateCreated = 55, - kIPTC_TimeCreated = 60, - kIPTC_DigitalCreateDate = 62, - kIPTC_DigitalCreateTime = 63, - kIPTC_OriginProgram = 65, - kIPTC_ProgramVersion = 70, - kIPTC_ObjectCycle = 75, - kIPTC_Creator = 80, - kIPTC_CreatorJobtitle = 85, - kIPTC_City = 90, - kIPTC_Location = 92, - kIPTC_State = 95, - kIPTC_CountryCode = 100, - kIPTC_Country = 101, - kIPTC_JobID = 103, - kIPTC_Headline = 105, - kIPTC_Provider = 110, - kIPTC_Source = 115, - kIPTC_CopyrightNotice = 116, - kIPTC_Contact = 118, - kIPTC_Description = 120, - kIPTC_DescriptionWriter = 122, - kIPTC_RasterizedCaption = 125, - kIPTC_ImageType = 130, - kIPTC_ImageOrientation = 131, - kIPTC_LanguageID = 135, - kIPTC_AudioType = 150, - kIPTC_AudioSampleRate = 151, - kIPTC_AudioSampleRes = 152, - kIPTC_AudioDuration = 153, - kIPTC_AudioOutcue = 154, - kIPTC_PreviewFormat = 200, - kIPTC_PreviewFormatVers = 201, - kIPTC_PreviewData = 202 -}; - -enum { // Forms of mapping legacy IPTC to XMP. Order is significant, see PhotoDataUtils::Import2WayIPTC! - kIPTC_MapSimple, // The XMP is simple, the last DataSet occurrence is kept. - kIPTC_MapLangAlt, // The XMP is a LangAlt x-default item, the last DataSet occurrence is kept. - kIPTC_MapArray, // The XMP is an unordered array, all DataSets are kept. - kIPTC_MapSpecial, // The mapping requires DataSet specific code. - kIPTC_Map3Way, // Has a 3 way mapping between Exif, IPTC, and XMP. - kIPTC_UnmappedText, // A text DataSet that is not mapped to XMP. - kIPTC_UnmappedBin // A binary DataSet that is not mapped to XMP. -}; - -struct DataSetCharacteristics { - XMP_Uns8 id; - XMP_Uns8 mapForm; - size_t maxLen; - XMP_StringPtr xmpNS; - XMP_StringPtr xmpProp; -}; - -extern const DataSetCharacteristics kKnownDataSets[]; - -struct IntellectualGenreMapping { - XMP_StringPtr refNum; // The reference number as a 3 digit string. - XMP_StringPtr name; // The intellectual genre name. -}; - -extern const IntellectualGenreMapping kIntellectualGenreMappings[]; - -// ================================================================================================= -// IPTC_Manager -// ============ - -class IPTC_Manager { -public: - - // --------------------------------------------------------------------------------------------- - // Types and constants. - - struct DataSetInfo { - XMP_Uns8 id; - XMP_Uns32 dataLen; - XMP_Uns8 * dataPtr; // ! The data is read-only. Raw data pointer, beware of character encoding. - DataSetInfo() : id(0), dataLen(0), dataPtr(0) {}; - DataSetInfo ( XMP_Uns8 _id, XMP_Uns32 _dataLen, XMP_Uns8 * _dataPtr ) - : id(_id), dataLen(_dataLen), dataPtr(_dataPtr) {}; - }; - - // --------------------------------------------------------------------------------------------- - // Parse a binary IPTC (IIM) block. - - void ParseMemoryDataSets ( const void* data, XMP_Uns32 length, bool copyData = true ); - - // --------------------------------------------------------------------------------------------- - // Get the information about a DataSet. Returns the number of occurrences. The "which" parameter - // selects the occurrence, they are numbered from 0 to count-1. Returns 0 if which is too large. - - size_t GetDataSet ( XMP_Uns8 id, DataSetInfo* info, size_t which = 0 ) const; - - // --------------------------------------------------------------------------------------------- - // Get the value of a text DataSet as UTF-8. The returned pointer must be treated as read-only. - // Calls GetDataSet then does a local to UTF-8 conversion if necessary. - - size_t GetDataSet_UTF8 ( XMP_Uns8 id, std::string * utf8Str, size_t which = 0 ) const; - - // --------------------------------------------------------------------------------------------- - // Set the value of a text DataSet from a UTF-8 string. Does a UTF-8 to local conversion if - // necessary. If the encoding mode is currently local and this value has round-trip loss, then - // the encoding mode will be changed to UTF-8 and all existing values will be converted. - // Modifies an existing occurrence if "which" is within range. Adds an occurrence if which - // equals the current count, or which is -1 and repeats are allowed. Throws an exception if - // which is too large. The dataPtr provides the raw data, text must be in the right encoding. - - virtual void SetDataSet_UTF8 ( XMP_Uns8 id, const void* utf8Ptr, XMP_Uns32 utf8Len, long which = -1 ) = 0; - - // --------------------------------------------------------------------------------------------- - // Delete an existing DataSet. Deletes all occurrences if which is -1. - - virtual void DeleteDataSet ( XMP_Uns8 id, long which = -1 ) = 0; - - // --------------------------------------------------------------------------------------------- - // Determine if any DataSets are changed. - - virtual bool IsChanged() = 0; - - // --------------------------------------------------------------------------------------------- - // Determine if UTF-8 or local text encoding is being used. - - bool UsingUTF8() const { return this->utf8Encoding; }; - - // -------------------------------------------------- - // Update the DataSets to reflect the changed values. - - virtual void UpdateMemoryDataSets() = 0; - - // --------------------------------------------------------------------------------------------- - // Get the location and size of the full IPTC block. The client must call UpdateMemoryDataSets - // first if appropriate. The returned dataPtr must be treated as read only. It exists until the - // IPTC_Manager destructor is called. - - XMP_Uns32 GetBlockInfo ( void** dataPtr ) const - { if ( dataPtr != 0 ) *dataPtr = this->iptcContent; return this->iptcLength; }; - - // --------------------------------------------------------------------------------------------- - - virtual ~IPTC_Manager() { if ( this->ownedContent ) free ( this->iptcContent ); }; - -protected: - - enum { kMinDataSetSize = 5 }; // 1+1+1+2 - - typedef std::multimap<XMP_Uns16,DataSetInfo> DataSetMap; - - DataSetMap dataSets; - - XMP_Uns8* iptcContent; - XMP_Uns32 iptcLength, offset190, length190, offset2xx, length2xx; - - bool changed; - bool ownedContent; // True if IPTC_Manager destructor needs to release the content block. - bool utf8Encoding; // True if text values are encoded as UTF-8. - - IPTC_Manager() : iptcContent(0), iptcLength(0), offset190(0), length190(0), offset2xx(0), length2xx(0), - changed(false), ownedContent(false), utf8Encoding(false) {}; - - void DisposeLooseValue ( DataSetInfo & dsInfo ); - -}; // IPTC_Manager - - -// ================================================================================================= -// ================================================================================================= - - -// ================================================================================================= -// IPTC_Reader -// =========== - -class IPTC_Reader : public IPTC_Manager { -public: - - IPTC_Reader() {}; - - void SetDataSet_UTF8 ( XMP_Uns8 id, const void* utf8Ptr, XMP_Uns32 utf8Len, long which = -1 ) { NotAppropriate(); }; - - void DeleteDataSet ( XMP_Uns8 id, long which = -1 ) { NotAppropriate(); }; - - bool IsChanged() { return false; }; - - void UpdateMemoryDataSets() { NotAppropriate(); }; - - virtual ~IPTC_Reader() {}; - -private: - - static inline void NotAppropriate() { XMP_Throw ( "Not appropriate for IPTC_Reader", kXMPErr_InternalFailure ); }; - -}; // IPTC_Reader - -// ================================================================================================= -// IPTC_Writer -// =========== - -class IPTC_Writer : public IPTC_Manager { -public: - - void SetDataSet_UTF8 ( XMP_Uns8 id, const void* utf8Ptr, XMP_Uns32 utf8Len, long which = -1 ); - - void DeleteDataSet ( XMP_Uns8 id, long which = -1 ); - - bool IsChanged() { return changed; }; - - void UpdateMemoryDataSets (); - - IPTC_Writer() {}; - - virtual ~IPTC_Writer(); - -private: - - void ConvertToUTF8(); - void ConvertToLocal(); - - bool CheckRoundTripLoss(); - -}; // IPTC_Writer - -// ================================================================================================= - -#endif // __IPTC_Support_hpp__ diff --git a/source/XMPFiles/FormatSupport/ISOBaseMedia_Support.cpp b/source/XMPFiles/FormatSupport/ISOBaseMedia_Support.cpp deleted file mode 100644 index 7d94734..0000000 --- a/source/XMPFiles/FormatSupport/ISOBaseMedia_Support.cpp +++ /dev/null @@ -1,149 +0,0 @@ -// ================================================================================================= -// ADOBE SYSTEMS INCORPORATED -// Copyright 2007 Adobe Systems Incorporated -// 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 "ISOBaseMedia_Support.hpp" -#include "XMPFiles_Impl.hpp" - -// ================================================================================================= -/// \file ISOBaseMedia_Support.cpp -/// \brief Manager for parsing and serializing ISO Base Media files ( MPEG-4 and JPEG-2000). -/// -// ================================================================================================= - -namespace ISOMedia { - -static BoxInfo voidInfo; - -// ================================================================================================= -// GetBoxInfo - from memory -// ======================== - -const XMP_Uns8 * GetBoxInfo ( const XMP_Uns8 * boxPtr, const XMP_Uns8 * boxLimit, - BoxInfo * info, bool throwErrors /* = false */ ) -{ - XMP_Uns32 u32Size; - - if ( info == 0 ) info = &voidInfo; - info->boxType = info->headerSize = 0; - info->contentSize = 0; - - if ( boxPtr >= boxLimit ) XMP_Throw ( "Bad offset to GetBoxInfo", kXMPErr_InternalFailure ); - - if ( (boxLimit - boxPtr) < 8 ) { // Is there enough space for a standard box header? - if ( throwErrors ) XMP_Throw ( "No space for ISO box header", kXMPErr_BadFileFormat ); - info->headerSize = (XMP_Uns32) (boxLimit - boxPtr); - return boxLimit; - } - - u32Size = GetUns32BE ( boxPtr ); - info->boxType = GetUns32BE ( boxPtr+4 ); - - if ( u32Size >= 8 ) { - info->headerSize = 8; // Normal explicit size case. - info->contentSize = u32Size - 8; - } else if ( u32Size == 0 ) { - info->headerSize = 8; // The box goes to EoF - treat it as "to limit". - info->contentSize = (boxLimit - boxPtr) - 8; - } else if ( u32Size != 1 ) { - if ( throwErrors ) XMP_Throw ( "Bad ISO box size, 2..7", kXMPErr_BadFileFormat ); - info->headerSize = 8; // Bad total size in the range of 2..7, treat as 8. - info->contentSize = 0; - } else { - if ( (boxLimit - boxPtr) < 16 ) { // Is there enough space for an extended box header? - if ( throwErrors ) XMP_Throw ( "No space for ISO extended header", kXMPErr_BadFileFormat ); - info->headerSize = (XMP_Uns32) (boxLimit - boxPtr); - return boxLimit; - } - XMP_Uns64 u64Size = GetUns64BE ( boxPtr+8 ); - if ( u64Size < 16 ) { - if ( throwErrors ) XMP_Throw ( "Bad ISO extended box size, < 16", kXMPErr_BadFileFormat ); - u64Size = 16; // Treat bad total size as 16. - } - info->headerSize = 16; - info->contentSize = u64Size - 16; - } - - XMP_Assert ( (XMP_Uns64)(boxLimit - boxPtr) >= (XMP_Uns64)info->headerSize ); - if ( info->contentSize > (XMP_Uns64)((boxLimit - boxPtr) - info->headerSize) ) { - if ( throwErrors ) XMP_Throw ( "Bad ISO box content size", kXMPErr_BadFileFormat ); - info->contentSize = ((boxLimit - boxPtr) - info->headerSize); // Trim a bad content size to the limit. - } - - return (boxPtr + info->headerSize + info->contentSize); - -} // GetBoxInfo - -// ================================================================================================= -// GetBoxInfo - from a file -// ======================== - -XMP_Uns64 GetBoxInfo ( LFA_FileRef fileRef, const XMP_Uns64 boxOffset, const XMP_Uns64 boxLimit, - BoxInfo * info, bool doSeek /* = true */, bool throwErrors /* = false */ ) -{ - XMP_Uns8 buffer [8]; - XMP_Uns32 u32Size; - - if ( info == 0 ) info = &voidInfo; - info->boxType = info->headerSize = 0; - info->contentSize = 0; - - if ( boxOffset >= boxLimit ) XMP_Throw ( "Bad offset to GetBoxInfo", kXMPErr_InternalFailure ); - - if ( (boxLimit - boxOffset) < 8 ) { // Is there enough space for a standard box header? - if ( throwErrors ) XMP_Throw ( "No space for ISO box header", kXMPErr_BadFileFormat ); - info->headerSize = (XMP_Uns32) (boxLimit - boxOffset); - return boxLimit; - } - - if ( doSeek ) LFA_Seek ( fileRef, boxOffset, SEEK_SET ); - (void) LFA_Read ( fileRef, buffer, 8, kLFA_RequireAll ); - - u32Size = GetUns32BE ( &buffer[0] ); - info->boxType = GetUns32BE ( &buffer[4] ); - - if ( u32Size >= 8 ) { - info->headerSize = 8; // Normal explicit size case. - info->contentSize = u32Size - 8; - } else if ( u32Size == 0 ) { - info->headerSize = 8; // The box goes to EoF. - info->contentSize = LFA_Measure(fileRef) - (boxOffset + 8); - } else if ( u32Size != 1 ) { - if ( throwErrors ) XMP_Throw ( "Bad ISO box size, 2..7", kXMPErr_BadFileFormat ); - info->headerSize = 8; // Bad total size in the range of 2..7, treat as 8. - info->contentSize = 0; - } else { - if ( (boxLimit - boxOffset) < 16 ) { // Is there enough space for an extended box header? - if ( throwErrors ) XMP_Throw ( "No space for ISO extended header", kXMPErr_BadFileFormat ); - info->headerSize = (XMP_Uns32) (boxLimit - boxOffset); - return boxLimit; - } - (void) LFA_Read ( fileRef, buffer, 8, kLFA_RequireAll ); - XMP_Uns64 u64Size = GetUns64BE ( &buffer[0] ); - if ( u64Size < 16 ) { - if ( throwErrors ) XMP_Throw ( "Bad ISO extended box size, < 16", kXMPErr_BadFileFormat ); - u64Size = 16; // Treat bad total size as 16. - } - info->headerSize = 16; - info->contentSize = u64Size - 16; - } - - XMP_Assert ( (boxLimit - boxOffset) >= info->headerSize ); - if ( info->contentSize > (boxLimit - boxOffset - info->headerSize) ) { - if ( throwErrors ) XMP_Throw ( "Bad ISO box content size", kXMPErr_BadFileFormat ); - info->contentSize = (boxLimit - boxOffset - info->headerSize); // Trim a bad content size to the limit. - } - - return (boxOffset + info->headerSize + info->contentSize); - -} // GetBoxInfo - -} // namespace ISO_Media - - -// ================================================================================================= diff --git a/source/XMPFiles/FormatSupport/ISOBaseMedia_Support.hpp b/source/XMPFiles/FormatSupport/ISOBaseMedia_Support.hpp deleted file mode 100644 index 4f50df7..0000000 --- a/source/XMPFiles/FormatSupport/ISOBaseMedia_Support.hpp +++ /dev/null @@ -1,100 +0,0 @@ -#ifndef __ISOBaseMedia_Support_hpp__ -#define __ISOBaseMedia_Support_hpp__ 1 - -// ================================================================================================= -// ADOBE SYSTEMS INCORPORATED -// Copyright 2007 Adobe Systems Incorporated -// 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 "XMP_Environment.h" // ! This must be the first include. - -#include "XMP_Const.h" -#include "LargeFileAccess.hpp" - -// ================================================================================================= -/// \file ISOBaseMedia_Support.hpp -/// \brief XMPFiles support for the ISO Base Media File Format. -/// -/// \note These classes are for use only when directly compiled and linked. They should not be -/// packaged in a DLL by themselves. They do not provide any form of C++ ABI protection. -// ================================================================================================= - -namespace ISOMedia { - - enum { - k_ftyp = 0x66747970UL, // File header Box, no version/flags. - - k_mp41 = 0x6D703431UL, // Compatible brand codes - k_mp42 = 0x6D703432UL, - k_f4v = 0x66347620UL, - k_qt = 0x71742020UL, - - k_moov = 0x6D6F6F76UL, // Container Box, no version/flags. - k_mvhd = 0x6D766864UL, // Data FullBox, has version/flags. - k_hdlr = 0x68646C72UL, - k_udta = 0x75647461UL, // Container Box, no version/flags. - k_cprt = 0x63707274UL, // Data FullBox, has version/flags. - k_uuid = 0x75756964UL, // Data Box, no version/flags. - k_free = 0x66726565UL, // Free space Box, no version/flags. - k_mdat = 0x6D646174UL, // Media data Box, no version/flags. - - k_trak = 0x7472616BUL, // Types for the QuickTime timecode track. - k_tkhd = 0x746B6864UL, - k_mdia = 0x6D646961UL, - k_mdhd = 0x6D646864UL, - k_tmcd = 0x746D6364UL, - k_mhlr = 0x6D686C72UL, - k_minf = 0x6D696E66UL, - k_stbl = 0x7374626CUL, - k_stsd = 0x73747364UL, - k_stsc = 0x73747363UL, - k_stco = 0x7374636FUL, - k_co64 = 0x636F3634UL, - - k_meta = 0x6D657461UL, // Types for the iTunes metadata boxes. - k_ilst = 0x696C7374UL, - k_mdir = 0x6D646972UL, - k_mean = 0x6D65616EUL, - k_name = 0x6E616D65UL, - k_data = 0x64617461UL, - k_hyphens = 0x2D2D2D2DUL, - - k_skip = 0x736B6970UL, // Additional classic QuickTime top level boxes. - k_wide = 0x77696465UL, - k_pnot = 0x706E6F74UL, - - k_XMP_ = 0x584D505FUL // The QuickTime variant XMP box. - }; - - static XMP_Uns32 k_xmpUUID [4] = { MakeUns32BE ( 0xBE7ACFCBUL ), - MakeUns32BE ( 0x97A942E8UL ), - MakeUns32BE ( 0x9C719994UL ), - MakeUns32BE ( 0x91E3AFACUL ) }; - - struct BoxInfo { - XMP_Uns32 boxType; // In memory as native endian! - XMP_Uns32 headerSize; // Normally 8 or 16, less than 8 if available space is too small. - XMP_Uns64 contentSize; // Always the real size, never 0 for "to EoF". - BoxInfo() : boxType(0), headerSize(0), contentSize(0) {}; - }; - - // Get basic info about a box in memory, returning a pointer to the following box. - const XMP_Uns8 * GetBoxInfo ( const XMP_Uns8 * boxPtr, const XMP_Uns8 * boxLimit, - BoxInfo * info, bool throwErrors = false ); - - // Get basic info about a box in a file, returning the offset of the following box. The I/O - // pointer is left at the start of the box's content. Returns the offset of the following box. - XMP_Uns64 GetBoxInfo ( LFA_FileRef fileRef, const XMP_Uns64 boxOffset, const XMP_Uns64 boxLimit, - BoxInfo * info, bool doSeek = true, bool throwErrors = false ); - -// XMP_Uns32 CountChildBoxes ( LFA_FileRef fileRef, const XMP_Uns64 childOffset, const XMP_Uns64 childLimit ); - -} // namespace ISO_Media - -// ================================================================================================= - -#endif // __ISOBaseMedia_Support_hpp__ diff --git a/source/XMPFiles/FormatSupport/MOOV_Support.cpp b/source/XMPFiles/FormatSupport/MOOV_Support.cpp deleted file mode 100644 index 1c89176..0000000 --- a/source/XMPFiles/FormatSupport/MOOV_Support.cpp +++ /dev/null @@ -1,542 +0,0 @@ -// ================================================================================================= -// ADOBE SYSTEMS INCORPORATED -// Copyright 2009 Adobe Systems Incorporated -// 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 "MOOV_Support.hpp" - -#include "ISOBaseMedia_Support.hpp" - -#include <string.h> - -// ================================================================================================= -/// \file MOOV_Support.cpp -/// \brief XMPFiles support for the 'moov' box in MPEG-4 and QuickTime files. -// ================================================================================================= - -// ================================================================================================= -// ================================================================================================= -// MOOV_Manager - The parsing and reading routines are all commmon -// ================================================================================================= -// ================================================================================================= - -#ifndef TraceParseMoovTree - #define TraceParseMoovTree 0 -#endif - -#ifndef TraceUpdateMoovTree - #define TraceUpdateMoovTree 0 -#endif - -// ================================================================================================= -// MOOV_Manager::PickContentPtr -// ============================ - -XMP_Uns8 * MOOV_Manager::PickContentPtr ( const BoxNode & node ) const -{ - if ( node.contentSize == 0 ) { - return 0; - } else if ( node.changed ) { - return (XMP_Uns8*) &node.changedContent[0]; - } else { - return (XMP_Uns8*) &this->fullSubtree[0] + node.offset + node.headerSize; - } -} // MOOV_Manager::PickContentPtr - -// ================================================================================================= -// MOOV_Manager::FillBoxInfo -// ========================= - -void MOOV_Manager::FillBoxInfo ( const BoxNode & node, BoxInfo * info ) const -{ - if ( info == 0 ) return; - - info->boxType = node.boxType; - info->childCount = (XMP_Uns32)node.children.size(); - info->contentSize = node.contentSize; - info->content = PickContentPtr ( node ); - -} // MOOV_Manager::FillBoxInfo - -// ================================================================================================= -// MOOV_Manager::GetBox -// ==================== -// -// Find a box given the type path. Pick the first child of each type. - -MOOV_Manager::BoxRef MOOV_Manager::GetBox ( const char * boxPath, BoxInfo * info ) const -{ - size_t pathLen = strlen(boxPath); - XMP_Assert ( (pathLen >= 4) && XMP_LitNMatch ( boxPath, "moov", 4 ) ); - if ( info != 0 ) memset ( info, 0, sizeof(BoxInfo) ); - - const char * pathPtr = boxPath + 5; // Skip the "moov/" portion. - const char * pathEnd = boxPath + pathLen; - - BoxRef currRef = &this->moovNode; - - while ( pathPtr < pathEnd ) { - - XMP_Assert ( (pathEnd - pathPtr) >= 4 ); - XMP_Uns32 boxType = GetUns32BE ( pathPtr ); - pathPtr += 5; // ! Don't care that the last step goes 1 too far. - - currRef = this->GetTypeChild ( currRef, boxType, 0 ); - if ( currRef == 0 ) return 0; - - } - - this->FillBoxInfo ( *((BoxNode*)currRef), info ); - return currRef; - -} // MOOV_Manager::GetBox - -// ================================================================================================= -// MOOV_Manager::GetNthChild -// ========================= - -MOOV_Manager::BoxRef MOOV_Manager::GetNthChild ( BoxRef parentRef, size_t childIndex, BoxInfo * info ) const -{ - XMP_Assert ( parentRef != 0 ); - const BoxNode & parent = *((BoxNode*)parentRef); - if ( info != 0 ) memset ( info, 0, sizeof(BoxInfo) ); - - if ( childIndex >= parent.children.size() ) return 0; - - const BoxNode & currNode = parent.children[childIndex]; - - this->FillBoxInfo ( currNode, info ); - return &currNode; - -} // MOOV_Manager::GetNthChild - -// ================================================================================================= -// MOOV_Manager::GetTypeChild -// ========================== - -MOOV_Manager::BoxRef MOOV_Manager::GetTypeChild ( BoxRef parentRef, XMP_Uns32 childType, BoxInfo * info ) const -{ - XMP_Assert ( parentRef != 0 ); - const BoxNode & parent = *((BoxNode*)parentRef); - if ( info != 0 ) memset ( info, 0, sizeof(BoxInfo) ); - if ( parent.children.empty() ) return 0; - - size_t i = 0, limit = parent.children.size(); - for ( ; i < limit; ++i ) { - const BoxNode & currNode = parent.children[i]; - if ( currNode.boxType == childType ) { - this->FillBoxInfo ( currNode, info ); - return &currNode; - } - } - - return 0; - -} // MOOV_Manager::GetTypeChild - -// ================================================================================================= -// MOOV_Manager::GetParsedOffset -// ============================= - -XMP_Uns32 MOOV_Manager::GetParsedOffset ( BoxRef ref ) const -{ - XMP_Assert ( ref != 0 ); - const BoxNode & node = *((BoxNode*)ref); - - if ( node.changed ) return 0; - return node.offset; - -} // MOOV_Manager::GetParsedOffset - -// ================================================================================================= -// MOOV_Manager::GetHeaderSize -// =========================== - -XMP_Uns32 MOOV_Manager::GetHeaderSize ( BoxRef ref ) const -{ - XMP_Assert ( ref != 0 ); - const BoxNode & node = *((BoxNode*)ref); - - if ( node.changed ) return 0; - return node.headerSize; - -} // MOOV_Manager::GetHeaderSize - -// ================================================================================================= -// MOOV_Manager::ParseMemoryTree -// ============================= -// -// Parse the fullSubtree data, building the BoxNode tree for the stuff that we care about. Tolerate -// errors like content ending too soon, make a best effoert to parse what we can. - -void MOOV_Manager::ParseMemoryTree ( XMP_Uns8 fileMode ) -{ - this->fileMode = fileMode; - - this->moovNode.offset = this->moovNode.boxType = 0; - this->moovNode.headerSize = this->moovNode.contentSize = 0; - this->moovNode.children.clear(); - this->moovNode.changedContent.clear(); - this->moovNode.changed = false; - - if ( this->fullSubtree.empty() ) return; - - ISOMedia::BoxInfo moovInfo; - const XMP_Uns8 * moovOrigin = &this->fullSubtree[0]; - const XMP_Uns8 * moovLimit = moovOrigin + this->fullSubtree.size(); - - (void) ISOMedia::GetBoxInfo ( moovOrigin, moovLimit, &moovInfo ); - XMP_Enforce ( moovInfo.boxType == ISOMedia::k_moov ); - - XMP_Uns64 fullMoovSize = moovInfo.headerSize + moovInfo.contentSize; - if ( fullMoovSize > moovBoxSizeLimit ) { // From here on we know 32-bit offsets are safe. - XMP_Throw ( "Oversize 'moov' box", kXMPErr_EnforceFailure ); - } - - this->moovNode.boxType = ISOMedia::k_moov; - this->moovNode.headerSize = moovInfo.headerSize; - this->moovNode.contentSize = (XMP_Uns32)moovInfo.contentSize; - - bool ignoreMetaBoxes = (fileMode == kFileIsTraditionalQT); // ! Don't want, these don't follow ISO spec. - #if TraceParseMoovTree - fprintf ( stderr, "Parsing 'moov' subtree, moovNode @ 0x%X, ignoreMetaBoxes = %d\n", - &this->moovNode, ignoreMetaBoxes ); - #endif - this->ParseNestedBoxes ( &this->moovNode, "moov", ignoreMetaBoxes ); - -} // MOOV_Manager::ParseMemoryTree - -// ================================================================================================= -// MOOV_Manager::ParseNestedBoxes -// ============================== -// -// Add the current level of child boxes to the parent node, recurse as appropriate. - -void MOOV_Manager::ParseNestedBoxes ( BoxNode * parentNode, const std::string & parentPath, bool ignoreMetaBoxes ) -{ - ISOMedia::BoxInfo isoInfo; - const XMP_Uns8 * moovOrigin = &this->fullSubtree[0]; - const XMP_Uns8 * childOrigin = moovOrigin + parentNode->offset + parentNode->headerSize; - const XMP_Uns8 * childLimit = childOrigin + parentNode->contentSize; - const XMP_Uns8 * nextChild; - - parentNode->contentSize = 0; // Exclude nested box size. - if ( parentNode->boxType == ISOMedia::k_meta ) { // ! The 'meta' box is a FullBox. - parentNode->contentSize = 4; - childOrigin += 4; - } - - for ( const XMP_Uns8 * currChild = childOrigin; currChild < childLimit; currChild = nextChild ) { - - nextChild = ISOMedia::GetBoxInfo ( currChild, childLimit, &isoInfo ); - if ( (isoInfo.boxType == 0) && - (isoInfo.headerSize < 8) && - (isoInfo.contentSize == 0) ) continue; // Skip trailing padding that QT sometimes writes. - - XMP_Uns32 childOffset = (XMP_Uns32) (currChild - moovOrigin); - parentNode->children.push_back ( BoxNode ( childOffset, isoInfo.boxType, isoInfo.headerSize, (XMP_Uns32)isoInfo.contentSize ) ); - BoxNode * newChild = &parentNode->children.back(); - - #if TraceParseMoovTree - size_t depth = (parentPath.size()+1) / 5; - for ( size_t i = 0; i < depth; ++i ) fprintf ( stderr, " " ); - XMP_Uns32 be32 = MakeUns32BE ( newChild->boxType ); - XMP_Uns32 addr32 = (XMP_Uns32) this->PickContentPtr ( *newChild ); - fprintf ( stderr, " Parsed %s/%.4s, offset 0x%X, size %d, content @ 0x%X, BoxNode @ 0x%X\n", - parentPath.c_str(), &be32, newChild->offset, newChild->contentSize, addr32, newChild ); - #endif - - const char * pathSuffix = 0; // Set to non-zero for boxes of interest. - char buffer[6]; buffer[0] = 0; - - switch ( isoInfo.boxType ) { // Want these boxes regardless of parent. - case ISOMedia::k_udta : pathSuffix = "/udta"; break; - case ISOMedia::k_meta : pathSuffix = "/meta"; break; - case ISOMedia::k_ilst : pathSuffix = "/ilst"; break; - case ISOMedia::k_trak : pathSuffix = "/trak"; break; - case ISOMedia::k_mdia : pathSuffix = "/mdia"; break; - case ISOMedia::k_minf : pathSuffix = "/minf"; break; - case ISOMedia::k_stbl : pathSuffix = "/stbl"; break; - } - if ( pathSuffix != 0 ) { - this->ParseNestedBoxes ( newChild, (parentPath + pathSuffix), ignoreMetaBoxes ); - } - - } - -} // MOOV_Manager::ParseNestedBoxes - -// ================================================================================================= -// MOOV_Manager::NoteChange -// ======================== - -void MOOV_Manager::NoteChange() -{ - - this->moovNode.changed = true; - -} // MOOV_Manager::NoteChange - -// ================================================================================================= -// MOOV_Manager::SetBox -// ==================== -// -// Save the new data, set this box's changed flag, and set the top changed flag. - -void MOOV_Manager::SetBox ( BoxRef theBox, const void* dataPtr, XMP_Uns32 size ) -{ - XMP_Enforce ( size < moovBoxSizeLimit ); - BoxNode * node = (BoxNode*)theBox; - - if ( node->contentSize == size ) { - - XMP_Uns8 * oldContent = PickContentPtr ( *node ); - if ( memcmp ( oldContent, dataPtr, size ) == 0 ) return; // No change. - memcpy ( oldContent, dataPtr, size ); // Update the old content in-place - this->moovNode.changed = true; - - #if TraceUpdateMoovTree - XMP_Uns32 be32 = MakeUns32BE ( node->boxType ); - fprintf ( stderr, "Updated '%.4s', parse offset 0x%X, same size\n", &be32, node->offset ); - #endif - - } else { - - node->changedContent.assign ( size, 0 ); // Fill with 0's first to get the storage. - memcpy ( &node->changedContent[0], dataPtr, size ); - node->contentSize = size; - node->changed = true; - this->moovNode.changed = true; - - #if TraceUpdateMoovTree - XMP_Uns32 be32 = MakeUns32BE ( node->boxType ); - XMP_Uns32 addr32 = (XMP_Uns32) this->PickContentPtr ( *node ); - fprintf ( stderr, "Updated '%.4s', parse offset 0x%X, new size %d, new content @ 0x%X\n", - &be32, node->offset, node->contentSize, addr32 ); - #endif - - } - -} // MOOV_Manager::SetBox - -// ================================================================================================= -// MOOV_Manager::SetBox -// ==================== -// -// Like above, but create the path to the box if necessary. - -void MOOV_Manager::SetBox ( const char * boxPath, const void* dataPtr, XMP_Uns32 size ) -{ - XMP_Enforce ( size < moovBoxSizeLimit ); - - size_t pathLen = strlen(boxPath); - XMP_Assert ( (pathLen >= 4) && XMP_LitNMatch ( boxPath, "moov", 4 ) ); - - const char * pathPtr = boxPath + 5; // Skip the "moov/" portion. - const char * pathEnd = boxPath + pathLen; - - BoxRef parentRef = 0; - BoxRef currRef = &this->moovNode; - - while ( pathPtr < pathEnd ) { - - XMP_Assert ( (pathEnd - pathPtr) >= 4 ); - XMP_Uns32 boxType = GetUns32BE ( pathPtr ); - pathPtr += 5; // ! Don't care that the last step goes 1 too far. - - parentRef = currRef; - currRef = this->GetTypeChild ( parentRef, boxType, 0 ); - if ( currRef == 0 ) currRef = this->AddChildBox ( parentRef, boxType, 0, 0 ); - - } - - this->SetBox ( currRef, dataPtr, size ); - -} // MOOV_Manager::SetBox - -// ================================================================================================= -// MOOV_Manager::AddChildBox -// ========================= - -MOOV_Manager::BoxRef MOOV_Manager::AddChildBox ( BoxRef parentRef, XMP_Uns32 childType, const void* dataPtr, XMP_Uns32 size ) -{ - BoxNode * parent = (BoxNode*)parentRef; - XMP_Assert ( parent != 0 ); - - parent->children.push_back ( BoxNode ( 0, childType, 0, 0 ) ); - BoxNode * newNode = &parent->children.back(); - this->SetBox ( newNode, dataPtr, size ); - - return newNode; - -} // MOOV_Manager::AddChildBox - -// ================================================================================================= -// MOOV_Manager::DeleteNthChild -// ============================ - -bool MOOV_Manager::DeleteNthChild ( BoxRef parentRef, size_t childIndex ) -{ - BoxNode * parent = (BoxNode*)parentRef; - - if ( childIndex >= parent->children.size() ) return false; - - parent->children.erase ( parent->children.begin() + childIndex ); - return true; - -} // MOOV_Manager::DeleteNthChild - -// ================================================================================================= -// MOOV_Manager::DeleteTypeChild -// ============================= - -bool MOOV_Manager::DeleteTypeChild ( BoxRef parentRef, XMP_Uns32 childType ) -{ - BoxNode * parent = (BoxNode*)parentRef; - - BoxListPos child = parent->children.begin(); - BoxListPos limit = parent->children.end(); - - for ( ; child != limit; ++child ) { - if ( child->boxType == childType ) { - parent->children.erase ( child ); - this->moovNode.changed = true; - return true; - } - } - - return false; - -} // MOOV_Manager::DeleteTypeChild - -// ================================================================================================= -// MOOV_Manager::NewSubtreeSize -// ============================ -// -// Determine the new (changed) size of a subtree. Ignore 'free' and 'wide' boxes. - -XMP_Uns32 MOOV_Manager::NewSubtreeSize ( const BoxNode & node, const std::string & parentPath ) -{ - XMP_Uns32 subtreeSize = 8 + node.contentSize; // All boxes will have 8 byte headers. - - if ( (node.boxType == ISOMedia::k_free) || (node.boxType == ISOMedia::k_wide) ) { - } - - for ( size_t i = 0, limit = node.children.size(); i < limit; ++i ) { - - char suffix[6]; - suffix[0] = '/'; - PutUns32BE ( node.boxType, &suffix[1] ); - suffix[5] = 0; - std::string nodePath = parentPath + suffix; - - subtreeSize += this->NewSubtreeSize ( node.children[i], nodePath ); - XMP_Enforce ( subtreeSize < moovBoxSizeLimit ); - - } - - return subtreeSize; - -} // MOOV_Manager::NewSubtreeSize - -// ================================================================================================= -// MOOV_Manager::AppendNewSubtree -// ============================== -// -// Append this node's header, content, and children. Because the 'meta' box is a FullBox with nested -// boxes, there can be both content and children. Ignore 'free' and 'wide' boxes. - -#define IncrNewPtr(count) { newPtr += count; XMP_Enforce ( newPtr <= newEnd ); } - -#if TraceUpdateMoovTree - static XMP_Uns8 * newOrigin; -#endif - -XMP_Uns8 * MOOV_Manager::AppendNewSubtree ( const BoxNode & node, const std::string & parentPath, - XMP_Uns8 * newPtr, XMP_Uns8 * newEnd ) -{ - if ( (node.boxType == ISOMedia::k_free) || (node.boxType == ISOMedia::k_wide) ) { - } - - XMP_Assert ( (node.boxType != ISOMedia::k_meta) ? (node.children.empty() || (node.contentSize == 0)) : - (node.children.empty() || (node.contentSize == 4)) ); - - XMP_Enforce ( (XMP_Uns32)(newEnd - newPtr) >= (8 + node.contentSize) ); - - #if TraceUpdateMoovTree - XMP_Uns32 be32 = MakeUns32BE ( node.boxType ); - XMP_Uns32 newOffset = (XMP_Uns32) (newPtr - newOrigin); - XMP_Uns32 addr32 = (XMP_Uns32) this->PickContentPtr ( node ); - fprintf ( stderr, " Appending %s/%.4s @ 0x%X, size %d, content @ 0x%X\n", - parentPath.c_str(), &be32, newOffset, node.contentSize, addr32 ); - #endif - - // Leave the size as 0 for now, append the type and content. - - XMP_Uns8 * boxOrigin = newPtr; // Save origin to fill in the final size. - PutUns32BE ( node.boxType, (newPtr + 4) ); - IncrNewPtr ( 8 ); - - if ( node.contentSize != 0 ) { - const XMP_Uns8 * content = PickContentPtr( node ); - memcpy ( newPtr, content, node.contentSize ); - IncrNewPtr ( node.contentSize ); - } - - // Append the nested boxes. - - if ( ! node.children.empty() ) { - - char suffix[6]; - suffix[0] = '/'; - PutUns32BE ( node.boxType, &suffix[1] ); - suffix[5] = 0; - std::string nodePath = parentPath + suffix; - - for ( size_t i = 0, limit = node.children.size(); i < limit; ++i ) { - newPtr = this->AppendNewSubtree ( node.children[i], nodePath, newPtr, newEnd ); - } - - } - - // Fill in the final size. - - PutUns32BE ( (XMP_Uns32)(newPtr - boxOrigin), boxOrigin ); - - return newPtr; - -} // MOOV_Manager::AppendNewSubtree - -// ================================================================================================= -// MOOV_Manager::UpdateMemoryTree -// ============================== - -void MOOV_Manager::UpdateMemoryTree() -{ - if ( ! this->IsChanged() ) return; - - XMP_Uns32 newSize = this->NewSubtreeSize ( this->moovNode, "" ); - XMP_Enforce ( newSize < moovBoxSizeLimit ); - - RawDataBlock newData; - newData.assign ( newSize, 0 ); // Prefill with zeroes, can't append multiple items to a vector. - - XMP_Uns8 * newPtr = &newData[0]; - XMP_Uns8 * newEnd = newPtr + newSize; - - #if TraceUpdateMoovTree - fprintf ( stderr, "Starting MOOV_Manager::UpdateMemoryTree\n" ); - newOrigin = newPtr; - #endif - - XMP_Uns8 * trueEnd = this->AppendNewSubtree ( this->moovNode, "", newPtr, newEnd ); - XMP_Enforce ( trueEnd == newEnd ); - - this->fullSubtree.swap ( newData ); - this->ParseMemoryTree ( this->fileMode ); - -} // MOOV_Manager::UpdateMemoryTree diff --git a/source/XMPFiles/FormatSupport/MOOV_Support.hpp b/source/XMPFiles/FormatSupport/MOOV_Support.hpp deleted file mode 100644 index 3e19a9d..0000000 --- a/source/XMPFiles/FormatSupport/MOOV_Support.hpp +++ /dev/null @@ -1,215 +0,0 @@ -#ifndef __MOOV_Support_hpp__ -#define __MOOV_Support_hpp__ 1 - -// ================================================================================================= -// ADOBE SYSTEMS INCORPORATED -// Copyright 2009 Adobe Systems Incorporated -// 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 "XMP_Environment.h" // ! This must be the first include. - -#include <vector> - -#include "XMP_Const.h" -#include "XMPFiles_Impl.hpp" -#include "EndianUtils.hpp" - -typedef vector<XMP_Uns8> RawDataBlock; - -#define moovBoxSizeLimit 100*1024*1024 - -// ================================================================================================= -// MOOV_Manager -// ============ - -class MOOV_Manager { -public: - - // --------------------------------------------------------------------------------------------- - // Types and constants - - enum { // Values for fileMode. - kFileIsNormalISO = 0, // A "normal" MPEG-4 file, no 'qt ' compatible brand. - kFileIsModernQT = 1, // Has an 'ftyp' box and 'qt ' compatible brand. - kFileIsTraditionalQT = 2 // Old QuickTime, no 'ftyp' box. - }; - - typedef const void * BoxRef; // Valid until a sibling or higher box is added or deleted. - - struct BoxInfo { - XMP_Uns32 boxType; // In memory as native endian, compares work with ISOMedia::k_* constants. - XMP_Uns32 childCount; // ! A 'meta' box has both content (version/flags) and children! - XMP_Uns32 contentSize; // Does not include the size of nested boxes. - const XMP_Uns8 * content; // Null if contentSize is zero. - BoxInfo() : boxType(0), childCount(0), contentSize(0), content(0) {}; - }; - - // --------------------------------------------------------------------------------------------- - // GetBox - Pick a box given a '/' separated list of box types. Picks the 1st of each type. - // GetNthChild - Pick the overall n-th child of the parent, zero based. - // GetTypeChild - Pick the first child of the given type. - // GetParsedOffset - Get the box's offset in the parsed tree, 0 if changed since parsing. - // GetHeaderSize - Get the box's header size in the parsed tree, 0 if changed since parsing. - - BoxRef GetBox ( const char * boxPath, BoxInfo * info ) const; - - BoxRef GetNthChild ( BoxRef parentRef, size_t childIndex, BoxInfo * info ) const; - BoxRef GetTypeChild ( BoxRef parentRef, XMP_Uns32 childType, BoxInfo * info ) const; - - XMP_Uns32 GetParsedOffset ( BoxRef ref ) const; - XMP_Uns32 GetHeaderSize ( BoxRef ref ) const; - - // --------------------------------------------------------------------------------------------- - // NoteChange - Note overall change, value was directly replaced. - // SetBox(ref) - Replace the content with a copy of the given data. - // SetBox(path) - Like above, but creating path to the box if necessary. - // AddChildBox - Add a child of the given type, using a copy of the given data (may be null) - - void NoteChange(); - - void SetBox ( BoxRef theBox, const void* dataPtr, XMP_Uns32 size ); - void SetBox ( const char * boxPath, const void* dataPtr, XMP_Uns32 size ); - - BoxRef AddChildBox ( BoxRef parentRef, XMP_Uns32 childType, const void * dataPtr, XMP_Uns32 size ); - - // --------------------------------------------------------------------------------------------- - // DeleteNthChild - Delete the overall n-th child, return true if there was one. - // DeleteTypeChild - Delete the first child of the given type, return true if there was one. - - bool DeleteNthChild ( BoxRef parentRef, size_t childIndex ); - bool DeleteTypeChild ( BoxRef parentRef, XMP_Uns32 childType ); - - // --------------------------------------------------------------------------------------------- - - bool IsChanged() const { return this->moovNode.changed; }; - - // --------------------------------------------------------------------------------------------- - // The client is expected to fill in fullSubtree before calling ParseMemoryTree, and directly - // use fullSubtree after calling UpdateMemoryTree. - // - // IMPORTANT: We only support cases where the 'moov' subtree is significantly less than 4 GB, in - // particular with a threshold of probably 100 MB. This has 2 big impacts: we can safely use - // 32-bit offsets and sizes, and comfortably assume everything will fit in available heap space. - - RawDataBlock fullSubtree; // The entire 'moov' box, straight from the file or from UpdateMemoryTree. - - void ParseMemoryTree ( XMP_Uns8 fileMode ); - void UpdateMemoryTree(); - - // --------------------------------------------------------------------------------------------- - - #pragma pack (1) // ! These must match the file layout! - - struct Content_mvhd_0 { - XMP_Uns32 vFlags; // 0 - XMP_Uns32 creationTime; // 4 - XMP_Uns32 modificationTime; // 8 - XMP_Uns32 timescale; // 12 - XMP_Uns32 duration; // 16 - XMP_Int32 rate; // 20 - XMP_Int16 volume; // 24 - XMP_Uns16 pad_1; // 26 - XMP_Uns32 pad_2, pad_3; // 28 - XMP_Int32 matrix [9]; // 36 - XMP_Uns32 preDef [6]; // 72 - XMP_Uns32 nextTrackID; // 96 - }; // 100 - - struct Content_mvhd_1 { - XMP_Uns32 vFlags; // 0 - XMP_Uns64 creationTime; // 4 - XMP_Uns64 modificationTime; // 12 - XMP_Uns32 timescale; // 20 - XMP_Uns64 duration; // 24 - XMP_Int32 rate; // 32 - XMP_Int16 volume; // 36 - XMP_Uns16 pad_1; // 38 - XMP_Uns32 pad_2, pad_3; // 40 - XMP_Int32 matrix [9]; // 48 - XMP_Uns32 preDef [6]; // 84 - XMP_Uns32 nextTrackID; // 108 - }; // 112 - - struct Content_hdlr { // An 'hdlr' box as defined by ISO 14496-12. Maps OK to the QuickTime box. - XMP_Uns32 versionFlags; // 0 - XMP_Uns32 preDef; // 4 - XMP_Uns32 handlerType; // 8 - XMP_Uns32 reserved [3]; // 12 - // Plus optional component name string, null terminated UTF-8. - }; // 24 - - struct Content_stsd_entry { - XMP_Uns32 entrySize; // 0 - XMP_Uns32 format; // 4 - XMP_Uns8 reserved_1 [6]; // 8 - XMP_Uns16 dataRefIndex; // 14 - XMP_Uns32 reserved_2; // 16 - XMP_Uns32 flags; // 20 - XMP_Uns32 timeScale; // 24 - XMP_Uns32 frameDuration; // 28 - XMP_Uns8 frameCount; // 32 - XMP_Uns8 reserved_3; // 33 - // Plus optional trailing ISO boxes. - }; // 34 - - struct Content_stsc_entry { - XMP_Uns32 firstChunkNumber; // 0 - XMP_Uns32 samplesPerChunk; // 4 - XMP_Uns32 sampleDescrID; // 8 - }; // 12 - - // --------------------------------------------------------------------------------------------- - - MOOV_Manager() : fileMode(0) - { - XMP_Assert ( sizeof ( Content_mvhd_0 ) == 100 ); // Make sure the structs really are packed. - XMP_Assert ( sizeof ( Content_mvhd_1 ) == 112 ); - XMP_Assert ( sizeof ( Content_hdlr ) == 24 ); - XMP_Assert ( sizeof ( Content_stsd_entry ) == 34 ); - XMP_Assert ( sizeof ( Content_stsc_entry ) == 12 ); - }; - - virtual ~MOOV_Manager() {}; - -private: - - struct BoxNode; - typedef std::vector<BoxNode> BoxList; - typedef BoxList::iterator BoxListPos; - - struct BoxNode { - // ! Don't have a parent link, it will get destroyed by vector growth! - - XMP_Uns32 offset; // The offset in the fullSubtree, 0 if not in the parse. - XMP_Uns32 boxType; - XMP_Uns32 headerSize; // The actual header size in the fullSubtree, 0 if not in the parse. - XMP_Uns32 contentSize; // The current content size, does not include nested boxes. - BoxList children; - RawDataBlock changedContent; // Might be empty even if changed is true. - bool changed; // If true, the content is in changedContent, else in fullSubtree. - - BoxNode() : offset(0), boxType(0), headerSize(0), contentSize(0), changed(false) {}; - BoxNode ( XMP_Uns32 _offset, XMP_Uns32 _boxType, XMP_Uns32 _headerSize, XMP_Uns32 _contentSize ) - : offset(_offset), boxType(_boxType), headerSize(_headerSize), contentSize(_contentSize), changed(false) {}; - - }; - - XMP_Uns8 fileMode; - BoxNode moovNode; - - void ParseNestedBoxes ( BoxNode * parentNode, const std::string & parentPath, bool ignoreMetaBoxes ); - - XMP_Uns8 * PickContentPtr ( const BoxNode & node ) const; - void FillBoxInfo ( const BoxNode & node, BoxInfo * info ) const; - - XMP_Uns32 NewSubtreeSize ( const BoxNode & node, const std::string & parentPath ); - XMP_Uns8 * AppendNewSubtree ( const BoxNode & node, const std::string & parentPath, - XMP_Uns8 * newPtr, XMP_Uns8 * newEnd ); - -}; // MOOV_Manager - -#endif // __MOOV_Support_hpp__ diff --git a/source/XMPFiles/FormatSupport/MacScriptExtracts.h b/source/XMPFiles/FormatSupport/MacScriptExtracts.h deleted file mode 100644 index 9856183..0000000 --- a/source/XMPFiles/FormatSupport/MacScriptExtracts.h +++ /dev/null @@ -1,244 +0,0 @@ -#ifndef __MacScriptExtracts__ -#define __MacScriptExtracts__ - -// Extracts of script (smXyz) and language (langXyz) enums from Apple's old Script.h. -// These are used to support "traditional" QuickTime metadata processing. - -/* - Script codes: - These specify a Mac OS encoding that is related to a FOND ID range. - Some of the encodings have several variants (e.g. for different localized systems) - which all share the same script code. - Not all of these script codes are currently supported by Apple software. - Notes: - - Script code 0 (smRoman) is also used (instead of smGreek) for the Greek encoding - in the Greek localized system. - - Script code 28 (smEthiopic) is also used for the Inuit encoding in the Inuktitut - system. -*/ -enum { - smRoman = 0, - smJapanese = 1, - smTradChinese = 2, /* Traditional Chinese*/ - smKorean = 3, - smArabic = 4, - smHebrew = 5, - smGreek = 6, - smCyrillic = 7, - smRSymbol = 8, /* Right-left symbol*/ - smDevanagari = 9, - smGurmukhi = 10, - smGujarati = 11, - smOriya = 12, - smBengali = 13, - smTamil = 14, - smTelugu = 15, - smKannada = 16, /* Kannada/Kanarese*/ - smMalayalam = 17, - smSinhalese = 18, - smBurmese = 19, - smKhmer = 20, /* Khmer/Cambodian*/ - smThai = 21, - smLao = 22, - smGeorgian = 23, - smArmenian = 24, - smSimpChinese = 25, /* Simplified Chinese*/ - smTibetan = 26, - smMongolian = 27, - smEthiopic = 28, - smGeez = 28, /* Synonym for smEthiopic*/ - smCentralEuroRoman = 29, /* For Czech, Slovak, Polish, Hungarian, Baltic langs*/ - smVietnamese = 30, - smExtArabic = 31, /* extended Arabic*/ - smUninterp = 32 /* uninterpreted symbols, e.g. palette symbols*/ -}; - -/* Extended script code for full Unicode input*/ -enum { - smUnicodeScript = 0x7E -}; - -/* Obsolete script code names (kept for backward compatibility):*/ -enum { - smChinese = 2, /* (Use smTradChinese or smSimpChinese)*/ - smRussian = 7, /* Use smCyrillic*/ - /* smMaldivian = 25: deleted, no code for Maldivian*/ - smLaotian = 22, /* Use smLao */ - smAmharic = 28, /* Use smEthiopic or smGeez*/ - smSlavic = 29, /* Use smCentralEuroRoman*/ - smEastEurRoman = 29, /* Use smCentralEuroRoman*/ - smSindhi = 31, /* Use smExtArabic*/ - smKlingon = 32 -}; - -/* - Language codes: - These specify a language implemented using a particular Mac OS encoding. - Not all of these language codes are currently supported by Apple software. -*/ -enum { - langEnglish = 0, /* smRoman script*/ - langFrench = 1, /* smRoman script*/ - langGerman = 2, /* smRoman script*/ - langItalian = 3, /* smRoman script*/ - langDutch = 4, /* smRoman script*/ - langSwedish = 5, /* smRoman script*/ - langSpanish = 6, /* smRoman script*/ - langDanish = 7, /* smRoman script*/ - langPortuguese = 8, /* smRoman script*/ - langNorwegian = 9, /* (Bokmal) smRoman script*/ - langHebrew = 10, /* smHebrew script*/ - langJapanese = 11, /* smJapanese script*/ - langArabic = 12, /* smArabic script*/ - langFinnish = 13, /* smRoman script*/ - langGreek = 14, /* Greek script (monotonic) using smRoman script code*/ - langIcelandic = 15, /* modified smRoman/Icelandic script*/ - langMaltese = 16, /* Roman script*/ - langTurkish = 17, /* modified smRoman/Turkish script*/ - langCroatian = 18, /* modified smRoman/Croatian script*/ - langTradChinese = 19, /* Chinese (Mandarin) in traditional characters*/ - langUrdu = 20, /* smArabic script*/ - langHindi = 21, /* smDevanagari script*/ - langThai = 22, /* smThai script*/ - langKorean = 23 /* smKorean script*/ -}; - -enum { - langLithuanian = 24, /* smCentralEuroRoman script*/ - langPolish = 25, /* smCentralEuroRoman script*/ - langHungarian = 26, /* smCentralEuroRoman script*/ - langEstonian = 27, /* smCentralEuroRoman script*/ - langLatvian = 28, /* smCentralEuroRoman script*/ - langSami = 29, /* language of the Sami people of N. Scandinavia */ - langFaroese = 30, /* modified smRoman/Icelandic script */ - langFarsi = 31, /* modified smArabic/Farsi script*/ - langPersian = 31, /* Synonym for langFarsi*/ - langRussian = 32, /* smCyrillic script*/ - langSimpChinese = 33, /* Chinese (Mandarin) in simplified characters*/ - langFlemish = 34, /* smRoman script*/ - langIrishGaelic = 35, /* smRoman or modified smRoman/Celtic script (without dot above) */ - langAlbanian = 36, /* smRoman script*/ - langRomanian = 37, /* modified smRoman/Romanian script*/ - langCzech = 38, /* smCentralEuroRoman script*/ - langSlovak = 39, /* smCentralEuroRoman script*/ - langSlovenian = 40, /* modified smRoman/Croatian script*/ - langYiddish = 41, /* smHebrew script*/ - langSerbian = 42, /* smCyrillic script*/ - langMacedonian = 43, /* smCyrillic script*/ - langBulgarian = 44, /* smCyrillic script*/ - langUkrainian = 45, /* modified smCyrillic/Ukrainian script*/ - langByelorussian = 46, /* smCyrillic script*/ - langBelorussian = 46 /* Synonym for langByelorussian */ -}; - -enum { - langUzbek = 47, /* Cyrillic script*/ - langKazakh = 48, /* Cyrillic script*/ - langAzerbaijani = 49, /* Azerbaijani in Cyrillic script*/ - langAzerbaijanAr = 50, /* Azerbaijani in Arabic script*/ - langArmenian = 51, /* smArmenian script*/ - langGeorgian = 52, /* smGeorgian script*/ - langMoldavian = 53, /* smCyrillic script*/ - langKirghiz = 54, /* Cyrillic script*/ - langTajiki = 55, /* Cyrillic script*/ - langTurkmen = 56, /* Cyrillic script*/ - langMongolian = 57, /* Mongolian in smMongolian script*/ - langMongolianCyr = 58, /* Mongolian in Cyrillic script*/ - langPashto = 59, /* Arabic script*/ - langKurdish = 60, /* smArabic script*/ - langKashmiri = 61, /* Arabic script*/ - langSindhi = 62, /* Arabic script*/ - langTibetan = 63, /* smTibetan script*/ - langNepali = 64, /* smDevanagari script*/ - langSanskrit = 65, /* smDevanagari script*/ - langMarathi = 66, /* smDevanagari script*/ - langBengali = 67, /* smBengali script*/ - langAssamese = 68, /* smBengali script*/ - langGujarati = 69, /* smGujarati script*/ - langPunjabi = 70 /* smGurmukhi script*/ -}; - -enum { - langOriya = 71, /* smOriya script*/ - langMalayalam = 72, /* smMalayalam script*/ - langKannada = 73, /* smKannada script*/ - langTamil = 74, /* smTamil script*/ - langTelugu = 75, /* smTelugu script*/ - langSinhalese = 76, /* smSinhalese script*/ - langBurmese = 77, /* smBurmese script*/ - langKhmer = 78, /* smKhmer script*/ - langLao = 79, /* smLao script*/ - langVietnamese = 80, /* smVietnamese script*/ - langIndonesian = 81, /* smRoman script*/ - langTagalog = 82, /* Roman script*/ - langMalayRoman = 83, /* Malay in smRoman script*/ - langMalayArabic = 84, /* Malay in Arabic script*/ - langAmharic = 85, /* smEthiopic script*/ - langTigrinya = 86, /* smEthiopic script*/ - langOromo = 87, /* smEthiopic script*/ - langSomali = 88, /* smRoman script*/ - langSwahili = 89, /* smRoman script*/ - langKinyarwanda = 90, /* smRoman script*/ - langRuanda = 90, /* synonym for langKinyarwanda*/ - langRundi = 91, /* smRoman script*/ - langNyanja = 92, /* smRoman script*/ - langChewa = 92, /* synonym for langNyanja*/ - langMalagasy = 93, /* smRoman script*/ - langEsperanto = 94 /* Roman script*/ -}; - -enum { - langWelsh = 128, /* modified smRoman/Celtic script*/ - langBasque = 129, /* smRoman script*/ - langCatalan = 130, /* smRoman script*/ - langLatin = 131, /* smRoman script*/ - langQuechua = 132, /* smRoman script*/ - langGuarani = 133, /* smRoman script*/ - langAymara = 134, /* smRoman script*/ - langTatar = 135, /* Cyrillic script*/ - langUighur = 136, /* Arabic script*/ - langDzongkha = 137, /* (lang of Bhutan) smTibetan script*/ - langJavaneseRom = 138, /* Javanese in smRoman script*/ - langSundaneseRom = 139, /* Sundanese in smRoman script*/ - langGalician = 140, /* smRoman script*/ - langAfrikaans = 141 /* smRoman script */ -}; - -enum { - langBreton = 142, /* smRoman or modified smRoman/Celtic script */ - langInuktitut = 143, /* Inuit script using smEthiopic script code */ - langScottishGaelic = 144, /* smRoman or modified smRoman/Celtic script */ - langManxGaelic = 145, /* smRoman or modified smRoman/Celtic script */ - langIrishGaelicScript = 146, /* modified smRoman/Gaelic script (using dot above) */ - langTongan = 147, /* smRoman script */ - langGreekAncient = 148, /* Classical Greek, polytonic orthography */ - langGreenlandic = 149, /* smRoman script */ - langAzerbaijanRoman = 150, /* Azerbaijani in Roman script */ - langNynorsk = 151 /* Norwegian Nyorsk in smRoman*/ -}; - -enum { - langUnspecified = 32767 /* Special code for use in resources (such as 'itlm') */ -}; - -/* - Obsolete language code names (kept for backward compatibility): - Misspelled, ambiguous, misleading, considered pejorative, archaic, etc. -*/ -enum { - langPortugese = 8, /* Use langPortuguese*/ - langMalta = 16, /* Use langMaltese*/ - langYugoslavian = 18, /* (use langCroatian, langSerbian, etc.)*/ - langChinese = 19, /* (use langTradChinese or langSimpChinese)*/ - langLettish = 28, /* Use langLatvian */ - langLapponian = 29, /* Use langSami*/ - langLappish = 29, /* Use langSami*/ - langSaamisk = 29, /* Use langSami */ - langFaeroese = 30, /* Use langFaroese */ - langIrish = 35, /* Use langIrishGaelic */ - langGalla = 87, /* Use langOromo */ - langAfricaans = 141, /* Use langAfrikaans */ - langGreekPoly = 148 /* Use langGreekAncient*/ -}; - -#endif /* __MacScriptExtracts__ */ diff --git a/source/XMPFiles/FormatSupport/PNG_Support.cpp b/source/XMPFiles/FormatSupport/PNG_Support.cpp deleted file mode 100644 index 988edda..0000000 --- a/source/XMPFiles/FormatSupport/PNG_Support.cpp +++ /dev/null @@ -1,335 +0,0 @@ -// ================================================================================================= -// ADOBE SYSTEMS INCORPORATED -// Copyright 2008 Adobe Systems Incorporated -// 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 "PNG_Support.hpp" -#include <string.h> - -typedef std::basic_string<unsigned char> filebuffer; - -namespace CRC -{ - /* Table of CRCs of all 8-bit messages. */ - static unsigned long crc_table[256]; - - /* Flag: has the table been computed? Initially false. */ - static int crc_table_computed = 0; - - /* Make the table for a fast CRC. */ - static void make_crc_table(void) - { - unsigned long c; - int n, k; - - for (n = 0; n < 256; n++) - { - c = (unsigned long) n; - for (k = 0; k < 8; k++) - { - if (c & 1) - { - c = 0xedb88320L ^ (c >> 1); - } - else - { - c = c >> 1; - } - } - crc_table[n] = c; - } - crc_table_computed = 1; - } - - /* Update a running CRC with the bytes buf[0..len-1]--the CRC - should be initialized to all 1's, and the transmitted value - is the 1's complement of the final running CRC (see the - crc() routine below). */ - - static unsigned long update_crc(unsigned long crc, unsigned char *buf, int len) - { - unsigned long c = crc; - int n; - - if (!crc_table_computed) - { - make_crc_table(); - } - - for (n = 0; n < len; n++) - { - c = crc_table[(c ^ buf[n]) & 0xff] ^ (c >> 8); - } - - return c; - } - - /* Return the CRC of the bytes buf[0..len-1]. */ - static unsigned long crc(unsigned char *buf, int len) - { - return update_crc(0xffffffffL, buf, len) ^ 0xffffffffL; - } -} // namespace CRC - -namespace PNG_Support -{ - enum chunkType { - // Critical chunks - (shall appear in this order, except PLTE is optional) - IHDR = 'IHDR', - PLTE = 'PLTE', - IDAT = 'IDAT', - IEND = 'IEND', - // Ancillary chunks - (need not appear in this order) - cHRM = 'cHRM', - gAMA = 'gAMA', - iCCP = 'iCCP', - sBIT = 'sBIT', - sRGB = 'sRGB', - bKGD = 'bKGD', - hIST = 'hIST', - tRNS = 'tRNS', - pHYs = 'pHYs', - sPLT = 'sPLT', - tIME = 'tIME', - iTXt = 'iTXt', - tEXt = 'tEXt', - zTXt = 'zTXt' - - }; - - // ============================================================================================= - - long OpenPNG ( LFA_FileRef fileRef, ChunkState & inOutChunkState ) - { - XMP_Uns64 pos = 0; - long name; - XMP_Uns32 len; - - pos = LFA_Seek ( fileRef, 8, SEEK_SET ); - if (pos != 8) return 0; - - // read first and following chunks - while ( ReadChunk ( fileRef, inOutChunkState, &name, &len, pos) ) {} - - return (long)inOutChunkState.chunks.size(); - - } - - // ============================================================================================= - - bool ReadChunk ( LFA_FileRef fileRef, ChunkState & inOutChunkState, long * chunkType, XMP_Uns32 * chunkLength, XMP_Uns64 & inOutPosition ) - { - try - { - XMP_Uns64 startPosition = inOutPosition; - long bytesRead; - char buffer[4]; - - bytesRead = LFA_Read ( fileRef, buffer, 4 ); - if ( bytesRead != 4 ) return false; - inOutPosition += 4; - *chunkLength = GetUns32BE(buffer); - - bytesRead = LFA_Read ( fileRef, buffer, 4 ); - if ( bytesRead != 4 ) return false; - inOutPosition += 4; - *chunkType = GetUns32BE(buffer); - - inOutPosition += *chunkLength; - - bytesRead = LFA_Read ( fileRef, buffer, 4 ); - if ( bytesRead != 4 ) return false; - inOutPosition += 4; - long crc = GetUns32BE(buffer); - - ChunkData newChunk; - - newChunk.pos = startPosition; - newChunk.len = *chunkLength; - newChunk.type = *chunkType; - - // check for XMP in iTXt-chunk - if (newChunk.type == iTXt) - { - CheckiTXtChunkHeader(fileRef, inOutChunkState, newChunk); - } - - inOutChunkState.chunks.push_back ( newChunk ); - - LFA_Seek ( fileRef, inOutPosition, SEEK_SET ); - - } catch ( ... ) { - - return false; - - } - - return true; - - } - - // ============================================================================================= - - bool WriteXMPChunk ( LFA_FileRef fileRef, XMP_Uns32 len, const char* inBuffer ) - { - bool ret = false; - unsigned long datalen = (4 + ITXT_HEADER_LEN + len); - unsigned char* buffer = new unsigned char[datalen]; - - try - { - size_t pos = 0; - memcpy(&buffer[pos], ITXT_CHUNK_TYPE, 4); - pos += 4; - memcpy(&buffer[pos], ITXT_HEADER_DATA, ITXT_HEADER_LEN); - pos += ITXT_HEADER_LEN; - memcpy(&buffer[pos], inBuffer, len); - - unsigned long crc_value = MakeUns32BE( CalculateCRC( buffer, datalen )); - datalen -= 4; - unsigned long len_value = MakeUns32BE( datalen ); - datalen += 4; - - LFA_Write(fileRef, &len_value, 4); - LFA_Write(fileRef, buffer, datalen); - LFA_Write(fileRef, &crc_value, 4); - - ret = true; - } - catch ( ... ) {} - - delete [] buffer; - - return ret; - } - - // ============================================================================================= - - bool CopyChunk ( LFA_FileRef sourceRef, LFA_FileRef destRef, ChunkData& chunk ) - { - try - { - LFA_Seek (sourceRef, chunk.pos, SEEK_SET ); - LFA_Copy (sourceRef, destRef, (chunk.len + 12)); - - } catch ( ... ) { - - return false; - - } - - return true; - } - - // ============================================================================================= - - unsigned long UpdateChunkCRC( LFA_FileRef fileRef, ChunkData& inOutChunkData ) - { - unsigned long ret = 0; - unsigned long datalen = (inOutChunkData.len + 4); - unsigned char* buffer = new unsigned char[datalen]; - - try - { - LFA_Seek(fileRef, (inOutChunkData.pos + 4), SEEK_SET); - - size_t pos = 0; - long bytesRead = LFA_Read ( fileRef, &buffer[pos], (inOutChunkData.len + 4) ); - - unsigned long crc = CalculateCRC( buffer, (inOutChunkData.len + 4) ); - unsigned long crc_value = MakeUns32BE( crc ); - - LFA_Seek(fileRef, (inOutChunkData.pos + 4 + 4 + inOutChunkData.len), SEEK_SET); - LFA_Write(fileRef, &crc_value, 4); - - ret = crc; - } - catch ( ... ) {} - - delete [] buffer; - - return ret; - } - - // ============================================================================================= - - bool CheckIHDRChunkHeader ( ChunkData& inOutChunkData ) - { - return (inOutChunkData.type == IHDR); - } - - // ============================================================================================= - - unsigned long CheckiTXtChunkHeader ( LFA_FileRef fileRef, ChunkState& inOutChunkState, ChunkData& inOutChunkData ) - { - try - { - LFA_Seek(fileRef, (inOutChunkData.pos + 8), SEEK_SET); - - char buffer[ITXT_HEADER_LEN]; - long bytesRead = LFA_Read ( fileRef, buffer, ITXT_HEADER_LEN ); - - if (bytesRead == ITXT_HEADER_LEN) - { - if (memcmp(buffer, ITXT_HEADER_DATA, ITXT_HEADER_LEN) == 0) - { - // return length of XMP - if (inOutChunkData.len > ITXT_HEADER_LEN) - { - inOutChunkState.xmpPos = inOutChunkData.pos + 8 + ITXT_HEADER_LEN; - inOutChunkState.xmpLen = inOutChunkData.len - ITXT_HEADER_LEN; - inOutChunkState.xmpChunk = inOutChunkData; - inOutChunkData.xmp = true; - - return inOutChunkState.xmpLen; - } - } - } - } - catch ( ... ) {} - - return 0; - } - - bool ReadBuffer ( LFA_FileRef fileRef, XMP_Uns64 & pos, XMP_Uns32 len, char * outBuffer ) - { - try - { - if ( (fileRef == 0) || (outBuffer == 0) ) return false; - - LFA_Seek (fileRef, pos, SEEK_SET ); - long bytesRead = LFA_Read ( fileRef, outBuffer, len ); - if ( XMP_Uns32(bytesRead) != len ) return false; - - return true; - } - catch ( ... ) {} - - return false; - } - - bool WriteBuffer ( LFA_FileRef fileRef, XMP_Uns64 & pos, XMP_Uns32 len, const char * inBuffer ) - { - try - { - if ( (fileRef == 0) || (inBuffer == 0) ) return false; - - LFA_Seek (fileRef, pos, SEEK_SET ); - LFA_Write( fileRef, inBuffer, len ); - - return true; - } - catch ( ... ) {} - - return false; - } - - unsigned long CalculateCRC( unsigned char* inBuffer, XMP_Uns32 len ) - { - return CRC::update_crc(0xffffffffL, inBuffer, len) ^ 0xffffffffL; - } - -} // namespace PNG_Support diff --git a/source/XMPFiles/FormatSupport/PNG_Support.hpp b/source/XMPFiles/FormatSupport/PNG_Support.hpp deleted file mode 100644 index b10f899..0000000 --- a/source/XMPFiles/FormatSupport/PNG_Support.hpp +++ /dev/null @@ -1,74 +0,0 @@ -#ifndef __PNG_Support_hpp__ -#define __PNG_Support_hpp__ 1 - -// ================================================================================================= -// ADOBE SYSTEMS INCORPORATED -// Copyright 2007 Adobe Systems Incorporated -// 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 "XMP_Environment.h" // ! This must be the first include. - -#include "XMPFiles_Impl.hpp" - -#define PNG_SIGNATURE_LEN 8 -#define PNG_SIGNATURE_DATA "\x89\x50\x4E\x47\x0D\x0A\x1A\x0A" - -#define ITXT_CHUNK_TYPE "iTXt" - -#define ITXT_HEADER_LEN 22 -#define ITXT_HEADER_DATA "XML:com.adobe.xmp\0\0\0\0\0" - -namespace PNG_Support -{ - class ChunkData - { - public: - ChunkData() : pos(0), len(0), type(0), xmp(false) {} - virtual ~ChunkData() {} - - // | length | type | data | crc(type+data) | - // | 4 | 4 | val(length) | 4 | - // - XMP_Uns64 pos; // file offset of chunk - XMP_Uns32 len; // length of chunk data - long type; // name/type of chunk - bool xmp; // iTXt-chunk with XMP ? - }; - - typedef std::vector<ChunkData> ChunkVector; - typedef ChunkVector::iterator ChunkIterator; - - class ChunkState - { - public: - ChunkState() : xmpPos(0), xmpLen(0) {} - virtual ~ChunkState() {} - - XMP_Uns64 xmpPos; - XMP_Uns32 xmpLen; - ChunkData xmpChunk; - ChunkVector chunks; /* vector of chunks */ - }; - - long OpenPNG ( LFA_FileRef fileRef, ChunkState& inOutChunkState ); - - bool ReadChunk ( LFA_FileRef fileRef, ChunkState& inOutChunkState, long* chunkType, XMP_Uns32* chunkLength, XMP_Uns64& inOutPosition ); - bool WriteXMPChunk ( LFA_FileRef fileRef, XMP_Uns32 len, const char* inBuffer ); - bool CopyChunk ( LFA_FileRef sourceRef, LFA_FileRef destRef, ChunkData& chunk ); - unsigned long UpdateChunkCRC( LFA_FileRef fileRef, ChunkData& inOutChunkData ); - - bool CheckIHDRChunkHeader ( ChunkData& inOutChunkData ); - unsigned long CheckiTXtChunkHeader ( LFA_FileRef fileRef, ChunkState& inOutChunkState, ChunkData& inOutChunkData ); - - bool ReadBuffer ( LFA_FileRef fileRef, XMP_Uns64& pos, XMP_Uns32 len, char* outBuffer ); - bool WriteBuffer ( LFA_FileRef fileRef, XMP_Uns64& pos, XMP_Uns32 len, const char* inBuffer ); - - unsigned long CalculateCRC( unsigned char* inBuffer, XMP_Uns32 len ); - -} // namespace PNG_Support - -#endif // __PNG_Support_hpp__ diff --git a/source/XMPFiles/FormatSupport/PSIR_FileWriter.cpp b/source/XMPFiles/FormatSupport/PSIR_FileWriter.cpp deleted file mode 100644 index 0e57b49..0000000 --- a/source/XMPFiles/FormatSupport/PSIR_FileWriter.cpp +++ /dev/null @@ -1,568 +0,0 @@ -// ================================================================================================= -// ADOBE SYSTEMS INCORPORATED -// Copyright 2006 Adobe Systems Incorporated -// 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 "PSIR_Support.hpp" -#include "EndianUtils.hpp" - -#include <string.h> - -// ================================================================================================= -/// \file PSIR_FileWriter.cpp -/// \brief Implementation of the file-based or read-write form of PSIR_Manager. -// ================================================================================================= - -// ================================================================================================= -// IsMetadataImgRsrc -// ================= - -static inline bool IsMetadataImgRsrc ( XMP_Uns16 id ) -{ - if ( id == 0 ) return false; - - int i; - for ( i = 0; id < kPSIR_MetadataIDs[i]; ++i ) {} - if ( id == kPSIR_MetadataIDs[i] ) return true; - return false; - -} // IsMetadataImgRsrc - -// ================================================================================================= -// PSIR_FileWriter::DeleteExistingInfo -// =================================== -// -// Delete all existing info about image resources. - -void PSIR_FileWriter::DeleteExistingInfo() -{ - XMP_Assert ( ! (this->memParsed && this->fileParsed) ); - - if ( this->memParsed ) { - if ( this->ownedContent ) free ( this->memContent ); - } else { - InternalRsrcMap::iterator irPos = this->imgRsrcs.begin(); - InternalRsrcMap::iterator irEnd = this->imgRsrcs.end(); - for ( ; irPos != irEnd; ++irPos ) irPos->second.changed = true; // Fool the InternalRsrcInfo destructor. - } - - this->imgRsrcs.clear(); - - this->memContent = 0; - this->memLength = 0; - - this->changed = false; - this->legacyDeleted = false; - this->memParsed = false; - this->fileParsed = false; - this->ownedContent = false; - -} // PSIR_FileWriter::DeleteExistingInfo - -// ================================================================================================= -// PSIR_FileWriter::~PSIR_FileWriter -// ================================= - -PSIR_FileWriter::~PSIR_FileWriter() -{ - XMP_Assert ( ! (this->memParsed && this->fileParsed) ); - - if ( this->ownedContent ) { - XMP_Assert ( this->memContent != 0 ); - free ( this->memContent ); - } - -} // PSIR_FileWriter::~PSIR_FileWriter - -// ================================================================================================= -// PSIR_FileWriter::GetImgRsrc -// =========================== - -bool PSIR_FileWriter::GetImgRsrc ( XMP_Uns16 id, ImgRsrcInfo* info ) const -{ - InternalRsrcMap::const_iterator rsrcPos = this->imgRsrcs.find ( id ); - if ( rsrcPos == this->imgRsrcs.end() ) return false; - - const InternalRsrcInfo & rsrcInfo = rsrcPos->second; - - if ( info != 0 ) { - info->id = rsrcInfo.id; - info->dataLen = rsrcInfo.dataLen; - info->dataPtr = rsrcInfo.dataPtr; - info->origOffset = rsrcInfo.origOffset; - } - - return true; - -} // PSIR_FileWriter::GetImgRsrc - -// ================================================================================================= -// PSIR_FileWriter::SetImgRsrc -// =========================== - -void PSIR_FileWriter::SetImgRsrc ( XMP_Uns16 id, const void* clientPtr, XMP_Uns32 length ) -{ - InternalRsrcInfo* rsrcPtr = 0; - InternalRsrcMap::iterator rsrcPos = this->imgRsrcs.find ( id ); - - if ( rsrcPos == this->imgRsrcs.end() ) { - - // This resource is not yet in the map, create the map entry. - InternalRsrcMap::value_type mapValue ( id, InternalRsrcInfo ( id, length, this->fileParsed ) ); - rsrcPos = this->imgRsrcs.insert ( rsrcPos, mapValue ); - rsrcPtr = &rsrcPos->second; - - } else { - - rsrcPtr = &rsrcPos->second; - - // The resource already exists, make sure the value is actually changing. - if ( (length == rsrcPtr->dataLen) && - (memcmp ( rsrcPtr->dataPtr, clientPtr, length ) == 0) ) { - return; - } - - rsrcPtr->FreeData(); // Release any existing data allocation. - rsrcPtr->dataLen = length; // And this might be changing. - - } - - rsrcPtr->changed = true; - rsrcPtr->dataPtr = malloc ( length ); - if ( rsrcPtr->dataPtr == 0 ) XMP_Throw ( "Out of memory", kXMPErr_NoMemory ); - memcpy ( rsrcPtr->dataPtr, clientPtr, length ); // AUDIT: Safe, malloc'ed length bytes above. - - this->changed = true; - -} // PSIR_FileWriter::SetImgRsrc - -// ================================================================================================= -// PSIR_FileWriter::DeleteImgRsrc -// ============================== - -void PSIR_FileWriter::DeleteImgRsrc ( XMP_Uns16 id ) -{ - InternalRsrcMap::iterator rsrcPos = this->imgRsrcs.find ( id ); - if ( rsrcPos == this->imgRsrcs.end() ) return; // Nothing to delete. - - this->imgRsrcs.erase ( id ); - this->changed = true; - if ( id != kPSIR_XMP ) this->legacyDeleted = true; - -} // PSIR_FileWriter::DeleteImgRsrc - -// ================================================================================================= -// PSIR_FileWriter::IsLegacyChanged -// ================================ - -bool PSIR_FileWriter::IsLegacyChanged() -{ - - if ( ! this->changed ) return false; - if ( this->legacyDeleted ) return true; - - InternalRsrcMap::iterator irPos = this->imgRsrcs.begin(); - InternalRsrcMap::iterator irEnd = this->imgRsrcs.end(); - - for ( ; irPos != irEnd; ++irPos ) { - const InternalRsrcInfo & rsrcInfo = irPos->second; - if ( rsrcInfo.changed && (rsrcInfo.id != kPSIR_XMP) ) return true; - } - - return false; // Can get here if the XMP is the only thing changed. - -} // PSIR_FileWriter::IsLegacyChanged - -// ================================================================================================= -// 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. - - 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 - -// ================================================================================================= -// PSIR_FileWriter::ParseFileResources -// =================================== - -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 - -// ================================================================================================= -// PSIR_FileWriter::UpdateMemoryResources -// ====================================== - -XMP_Uns32 PSIR_FileWriter::UpdateMemoryResources ( void** dataPtr ) -{ - if ( this->fileParsed ) XMP_Throw ( "Not memory based", kXMPErr_EnforceFailure ); - - // Compute the size and allocate the new image resource block. - - XMP_Uns32 newLength = 0; - - InternalRsrcMap::iterator irPos = this->imgRsrcs.begin(); - InternalRsrcMap::iterator irEnd = this->imgRsrcs.end(); - - for ( ; irPos != irEnd; ++irPos ) { // Add in the lengths for the 8BIM resources. - const InternalRsrcInfo & rsrcInfo = irPos->second; - newLength += 10; - newLength += ((rsrcInfo.dataLen + 1) & 0xFFFFFFFEUL); - if ( rsrcInfo.rsrcName == 0 ) { - newLength += 2; - } else { - XMP_Uns32 nameLen = rsrcInfo.rsrcName[0]; - newLength += ((nameLen + 2) & 0xFFFFFFFEUL); // ! Yes, +2. - } - } - - for ( size_t i = 0; i < this->otherRsrcs.size(); ++i ) { // Add in the non-8BIM resources. - newLength += this->otherRsrcs[i].rsrcLength; - } - - XMP_Uns8* newContent = (XMP_Uns8*) malloc ( newLength ); - if ( newContent == 0 ) XMP_Throw ( "Out of memory", kXMPErr_NoMemory ); - - // Fill in the new image resource block. - - XMP_Uns8* rsrcPtr = newContent; - - for ( irPos = this->imgRsrcs.begin(); irPos != irEnd; ++irPos ) { // Do the 8BIM resources. - - const InternalRsrcInfo & rsrcInfo = irPos->second; - - PutUns32BE ( k8BIM, rsrcPtr ); - rsrcPtr += 4; - PutUns16BE ( rsrcInfo.id, rsrcPtr ); - rsrcPtr += 2; - - if ( rsrcInfo.rsrcName == 0 ) { - PutUns16BE ( 0, rsrcPtr ); - rsrcPtr += 2; - } else { - XMP_Uns32 nameLen = rsrcInfo.rsrcName[0]; - if ( (nameLen+1) > (newLength - (rsrcPtr - newContent)) ) { - XMP_Throw ( "Buffer overrun", kXMPErr_InternalFailure ); - } - memcpy ( rsrcPtr, rsrcInfo.rsrcName, nameLen+1 ); // AUDIT: Protected by the above check. - rsrcPtr += nameLen+1; - if ( (nameLen & 1) == 0 ) { - *rsrcPtr = 0; - ++rsrcPtr; - } - } - - PutUns32BE ( rsrcInfo.dataLen, rsrcPtr ); - rsrcPtr += 4; - if ( rsrcInfo.dataLen > (newLength - (rsrcPtr - newContent)) ) { - XMP_Throw ( "Buffer overrun", kXMPErr_InternalFailure ); - } - memcpy ( rsrcPtr, rsrcInfo.dataPtr, rsrcInfo.dataLen ); // AUDIT: Protected by the above check. - rsrcPtr += rsrcInfo.dataLen; - if ( (rsrcInfo.dataLen & 1) != 0 ) { // Pad to an even length if necessary. - *rsrcPtr = 0; - ++rsrcPtr; - } - - } - - for ( size_t i = 0; i < this->otherRsrcs.size(); ++i ) { // Do the non-8BIM resources. - XMP_Uns8* srcPtr = this->memContent + this->otherRsrcs[i].rsrcOffset; - XMP_Uns32 srcLen = this->otherRsrcs[i].rsrcLength; - if ( srcLen > (newLength - (rsrcPtr - newContent)) ) { - XMP_Throw ( "Buffer overrun", kXMPErr_InternalFailure ); - } - memcpy ( rsrcPtr, srcPtr, srcLen ); // AUDIT: Protected by the above check. - rsrcPtr += srcLen; // No need to pad, included in the original resource length. - } - - XMP_Assert ( rsrcPtr == (newContent + newLength) ); - - // Parse the rebuilt image resource block. This is the easiest way to reconstruct the map. - - this->ParseMemoryResources ( newContent, newLength, false ); - this->ownedContent = (newLength > 0); // ! We really do own the new content, if not empty. - - if ( dataPtr != 0 ) *dataPtr = newContent; - return newLength; - -} // PSIR_FileWriter::UpdateMemoryResources - -// ================================================================================================= -// PSIR_FileWriter::UpdateFileResources -// ==================================== - -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 diff --git a/source/XMPFiles/FormatSupport/PSIR_MemoryReader.cpp b/source/XMPFiles/FormatSupport/PSIR_MemoryReader.cpp deleted file mode 100644 index c372dce..0000000 --- a/source/XMPFiles/FormatSupport/PSIR_MemoryReader.cpp +++ /dev/null @@ -1,97 +0,0 @@ -// ================================================================================================= -// ADOBE SYSTEMS INCORPORATED -// Copyright 2006 Adobe Systems Incorporated -// 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 "PSIR_Support.hpp" -#include "EndianUtils.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. - - 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 info ( id, dataLen, psirPtr, psirOffset ); - this->imgRsrcs[id] = info; - } - - psirPtr += ((dataLen + 1) & 0xFFFFFFFEUL); // ! Round up to an even offset. - - } - -} // PSIR_MemoryReader::ParseMemoryResources diff --git a/source/XMPFiles/FormatSupport/PSIR_Support.hpp b/source/XMPFiles/FormatSupport/PSIR_Support.hpp deleted file mode 100644 index 8b5c507..0000000 --- a/source/XMPFiles/FormatSupport/PSIR_Support.hpp +++ /dev/null @@ -1,318 +0,0 @@ -#ifndef __PSIR_Support_hpp__ -#define __PSIR_Support_hpp__ 1 - -// ================================================================================================= -// ADOBE SYSTEMS INCORPORATED -// Copyright 2006 Adobe Systems Incorporated -// 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 "XMP_Environment.h" // ! This must be the first include. - -#include <map> - -#include "XMP_Const.h" -#include "XMPFiles_Impl.hpp" -#include "EndianUtils.hpp" - -// ================================================================================================= -/// \file PSIR_Support.hpp -/// \brief XMPFiles support for Photoshop image resources. -/// -/// This header provides Photoshop image resource (PSIR) support specific to the needs of XMPFiles. -/// This is not intended for general purpose PSIR processing. PSIR_Manager is an abstract base -/// class with 2 concrete derived classes, PSIR_MemoryReader and PSIR_FileWriter. -/// -/// PSIR_MemoryReader provides read-only support for PSIR streams that are small enough to be kept -/// entirely in memory. This allows optimizations to reduce heap usage and processing code. It is -/// sufficient for browsing access to the image resources (mainly the IPTC) in JPEG files. Think of -/// PSIR_MemoryReader as "memory-based AND read-only". -/// -/// PSIR_FileWriter is for cases where updates are needed or the PSIR stream is too large to be kept -/// entirely in memory. Think of PSIR_FileWriter as "file-based OR read-write". -/// -/// The needs of XMPFiles are well defined metadata access. Only a few image resources are handled. -/// This is the case for all of the derived classes, even though the memory based ones happen to -/// have all of the image resources in memory. Being "handled" means being in the image resource -/// map used by GetImgRsrc. The handled image resources are: -/// \li 1028 - IPTC -/// \li 1034 - Copyrighted flag -/// \li 1035 - Copyright information URL -/// \li 1058 - Exif metadata -/// \li 1060 - XMP -/// \li 1061 - IPTC digest -/// -/// \note These classes are for use only when directly compiled and linked. They should not be -/// packaged in a DLL by themselves. They do not provide any form of C++ ABI protection. -// ================================================================================================= - - -// These aren't inside PSIR_Manager because the static array can't be initialized there. - -enum { - k8BIM = 0x3842494DUL, // The 4 ASCII characters '8BIM'. - kMinImgRsrcSize = 4+2+2+4 // The minimum size for an image resource. -}; - -enum { - kPSIR_IPTC = 1028, - kPSIR_CopyrightFlag = 1034, - kPSIR_CopyrightURL = 1035, - kPSIR_Exif = 1058, - kPSIR_XMP = 1060, - kPSIR_IPTCDigest = 1061 -}; - -enum { kPSIR_MetadataCount = 6 }; -static const XMP_Uns16 kPSIR_MetadataIDs[] = // ! Must be in descending order with 0 sentinel. - { kPSIR_IPTCDigest, kPSIR_XMP, kPSIR_Exif, kPSIR_CopyrightURL, kPSIR_CopyrightFlag, kPSIR_IPTC, 0 }; - -// ================================================================================================= -// ================================================================================================= - -// NOTE: Although Photoshop image resources have a type and ID, for metadatya we only care about -// those of type "8BIM". Resources of other types are preserved in files, but can't be individually -// accessed through the PSIR_Manager API. - -// ================================================================================================= -// PSIR_Manager -// ============ - -class PSIR_Manager { // The abstract base class. -public: - - // --------------------------------------------------------------------------------------------- - // Types and constants - - struct ImgRsrcInfo { - XMP_Uns16 id; - XMP_Uns32 dataLen; - const void* dataPtr; // ! The data is read-only! - XMP_Uns32 origOffset; // The offset (at parse time) of the resource data. - ImgRsrcInfo() : id(0), dataLen(0), dataPtr(0), origOffset(0) {}; - ImgRsrcInfo ( XMP_Uns16 _id, XMP_Uns32 _dataLen, void* _dataPtr, XMP_Uns32 _origOffset ) - : id(_id), dataLen(_dataLen), dataPtr(_dataPtr), origOffset(_origOffset) {}; - }; - - // The origOffset is the absolute file offset for file parses, the memory block offset for - // memory parses. It is the offset of the resource data portion, not the overall resource. - - // --------------------------------------------------------------------------------------------- - // Get the information about a "handled" image resource. Returns false if the image resource is - // not handled, even if it was present in the parsed input. - - virtual bool GetImgRsrc ( XMP_Uns16 id, ImgRsrcInfo* info ) const = 0; - - // --------------------------------------------------------------------------------------------- - // Set the value for an image resource. It can be any resource, even one not originally handled. - - virtual void SetImgRsrc ( XMP_Uns16 id, const void* dataPtr, XMP_Uns32 length ) = 0; - - // --------------------------------------------------------------------------------------------- - // Delete an image resource. Does nothing if the image resource does not exist. - - virtual void DeleteImgRsrc ( XMP_Uns16 id ) = 0; - - // --------------------------------------------------------------------------------------------- - // Determine if the image resources are changed. - - virtual bool IsChanged() = 0; - virtual bool IsLegacyChanged() = 0; - - // --------------------------------------------------------------------------------------------- - - virtual void ParseMemoryResources ( const void* data, XMP_Uns32 length, bool copyData = true ) = 0; - virtual void ParseFileResources ( LFA_FileRef fileRef, XMP_Uns32 length ) = 0; - - // --------------------------------------------------------------------------------------------- - // Update the image resources to reflect the changed values. Both \c UpdateMemoryResources and - // \c UpdateFileResources return the new size of the image resource block. The dataPtr returned - // by \c UpdateMemoryResources must be treated as read only. It exists until the PSIR_Manager - // destructor is called. UpdateMemoryResources can be used on a read-only instance to get the - // raw data block info. - - virtual XMP_Uns32 UpdateMemoryResources ( void** dataPtr ) = 0; - virtual XMP_Uns32 UpdateFileResources ( LFA_FileRef sourceRef, LFA_FileRef destRef, - IOBuffer * ioBuf, XMP_AbortProc abortProc, void * abortArg ) = 0; - - // --------------------------------------------------------------------------------------------- - - virtual ~PSIR_Manager() {}; - -protected: - - PSIR_Manager() {}; - -}; // PSIR_Manager - - -// ================================================================================================= -// ================================================================================================= - - -// ================================================================================================= -// PSIR_MemoryReader -// ================= - -class PSIR_MemoryReader : public PSIR_Manager { // The leaf class for memory-based read-only access. -public: - - bool GetImgRsrc ( XMP_Uns16 id, ImgRsrcInfo* info ) const; - - void SetImgRsrc ( XMP_Uns16 id, const void* dataPtr, XMP_Uns32 length ) { NotAppropriate(); }; - - void DeleteImgRsrc ( XMP_Uns16 id ) { NotAppropriate(); }; - - bool IsChanged() { return false; }; - bool IsLegacyChanged() { return false; }; - - void ParseMemoryResources ( const void* data, XMP_Uns32 length, bool copyData = true ); - void ParseFileResources ( LFA_FileRef file, XMP_Uns32 length ) { NotAppropriate(); }; - - XMP_Uns32 UpdateMemoryResources ( void** dataPtr ) { if ( dataPtr != 0 ) *dataPtr = psirContent; return psirLength; }; - XMP_Uns32 UpdateFileResources ( LFA_FileRef sourceRef, LFA_FileRef destRef, - IOBuffer * ioBuf, XMP_AbortProc abortProc, void * abortArg ) { NotAppropriate(); return 0; }; - - PSIR_MemoryReader() : ownedContent(false), psirLength(0), psirContent(0) {}; - - virtual ~PSIR_MemoryReader() { if ( this->ownedContent ) free ( this->psirContent ); }; - -private: - - // Memory usage notes: PSIR_MemoryReader is for memory-based read-only usage (both apply). There - // is no need to ever allocate separate blocks of memory, everything is used directly from the - // PSIR stream. - - bool ownedContent; - - XMP_Uns32 psirLength; - XMP_Uns8* psirContent; - - typedef std::map<XMP_Uns16,ImgRsrcInfo> ImgRsrcMap; - - ImgRsrcMap imgRsrcs; - - static inline void NotAppropriate() { XMP_Throw ( "Not appropriate for PSIR_Reader", kXMPErr_InternalFailure ); }; - -}; // PSIR_MemoryReader - - -// ================================================================================================= -// ================================================================================================= - - -// ================================================================================================= -// PSIR_FileWriter -// =============== - -class PSIR_FileWriter : public PSIR_Manager { // The leaf class for file-based read-write access. -public: - - bool GetImgRsrc ( XMP_Uns16 id, ImgRsrcInfo* info ) const; - - void SetImgRsrc ( XMP_Uns16 id, const void* dataPtr, XMP_Uns32 length ); - - void DeleteImgRsrc ( XMP_Uns16 id ); - - bool IsChanged() { return this->changed; }; - - bool IsLegacyChanged(); - - void ParseMemoryResources ( const void* data, XMP_Uns32 length, bool copyData = true ); - void ParseFileResources ( LFA_FileRef file, XMP_Uns32 length ); - - XMP_Uns32 UpdateMemoryResources ( void** dataPtr ); - XMP_Uns32 UpdateFileResources ( LFA_FileRef sourceRef, LFA_FileRef destRef, - IOBuffer * ioBuf, XMP_AbortProc abortProc, void * abortArg ); - - PSIR_FileWriter() : changed(false), legacyDeleted(false), memParsed(false), fileParsed(false), - ownedContent(false), memLength(0), memContent(0) {}; - - virtual ~PSIR_FileWriter(); - - // Memory usage notes: PSIR_FileWriter is for file-based OR read/write usage. For memory-based - // streams the dataPtr and rsrcName are initially into the stream, they become a separate - // allocation if changed. For file-based streams they are always a separate allocation. - - // ! The working data values are always big endian, no matter where stored. It is the client's - // ! responsibility to flip them as necessary. - - static const bool kIsFileBased = true; // For use in the InternalRsrcInfo constructor. - static const bool kIsMemoryBased = false; - - struct InternalRsrcInfo { - public: - - bool changed; - bool fileBased; - XMP_Uns16 id; - XMP_Uns32 dataLen; - void* dataPtr; // ! Null if the value is not captured! - XMP_Uns32 origOffset; // The offset (at parse time) of the resource data. - XMP_Uns8* rsrcName; // ! A Pascal string, leading length byte, no nul terminator! - - inline void FreeData() { - if ( this->fileBased || this->changed ) { - if ( this->dataPtr != 0 ) { free ( this->dataPtr ); this->dataPtr = 0; } - } - } - - inline void FreeName() { - if ( this->fileBased || this->changed ) { - if ( this->rsrcName != 0 ) { free ( this->rsrcName ); this->rsrcName = 0; } - } - } - - InternalRsrcInfo ( XMP_Uns16 _id, XMP_Uns32 _dataLen, bool _fileBased ) - : changed(false), fileBased(_fileBased), id(_id), dataLen(_dataLen), dataPtr(0), - origOffset(0), rsrcName(0) {}; - ~InternalRsrcInfo() { this->FreeData(); this->FreeName(); }; - - void operator= ( const InternalRsrcInfo & in ) - { - // ! Gag! Transfer ownership of the dataPtr and rsrcName! - this->FreeData(); - memcpy ( this, &in, sizeof(*this) ); // AUDIT: Use of sizeof(InternalRsrcInfo) is safe. - *((void**)&in.dataPtr) = 0; // The pointer is now owned by "this". - *((void**)&in.rsrcName) = 0; // The pointer is now owned by "this". - }; - - private: - - InternalRsrcInfo() // Hidden on purpose, fileBased must be properly set. - : changed(false), fileBased(false), id(0), dataLen(0), dataPtr(0), origOffset(0), rsrcName(0) {}; - - }; - - // The origOffset is the absolute file offset for file parses, the memory block offset for - // memory parses. It is the offset of the resource data portion, not the overall resource. - -private: - - bool changed, legacyDeleted; - bool memParsed, fileParsed; - bool ownedContent; - - XMP_Uns32 memLength; - XMP_Uns8* memContent; - - typedef std::map<XMP_Uns16,InternalRsrcInfo> InternalRsrcMap; - InternalRsrcMap imgRsrcs; - - struct OtherRsrcInfo { // For the resources of types other than "8BIM". - XMP_Uns32 rsrcOffset; // The offset of the resource origin, the type field. - XMP_Uns32 rsrcLength; // The full length of the resource, offset to the next resource. - OtherRsrcInfo() : rsrcOffset(0), rsrcLength(0) {}; - OtherRsrcInfo ( XMP_Uns32 _rsrcOffset, XMP_Uns32 _rsrcLength ) - : rsrcOffset(_rsrcOffset), rsrcLength(_rsrcLength) {}; - }; - std::vector<OtherRsrcInfo> otherRsrcs; - - void DeleteExistingInfo(); - -}; // PSIR_FileWriter - -#endif // __PSIR_Support_hpp__ diff --git a/source/XMPFiles/FormatSupport/QuickTime_Support.cpp b/source/XMPFiles/FormatSupport/QuickTime_Support.cpp deleted file mode 100644 index 31091ea..0000000 --- a/source/XMPFiles/FormatSupport/QuickTime_Support.cpp +++ /dev/null @@ -1,1148 +0,0 @@ -// ================================================================================================= -// ADOBE SYSTEMS INCORPORATED -// Copyright 2009 Adobe Systems Incorporated -// 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 "XMP_Environment.h" - -#if XMP_MacBuild - #include "Script.h" -#else - #include "MacScriptExtracts.h" -#endif - -#include "QuickTime_Support.hpp" - -#include "UnicodeConversions.hpp" -#include "UnicodeInlines.incl_cpp" -#include "Reconcile_Impl.hpp" - -// ================================================================================================= - -static const char * kMacRomanUTF8 [128] = { // UTF-8 mappings for MacRoman 80..FF. - "\xC3\x84", "\xC3\x85", "\xC3\x87", "\xC3\x89", "\xC3\x91", "\xC3\x96", "\xC3\x9C", "\xC3\xA1", - "\xC3\xA0", "\xC3\xA2", "\xC3\xA4", "\xC3\xA3", "\xC3\xA5", "\xC3\xA7", "\xC3\xA9", "\xC3\xA8", - "\xC3\xAA", "\xC3\xAB", "\xC3\xAD", "\xC3\xAC", "\xC3\xAE", "\xC3\xAF", "\xC3\xB1", "\xC3\xB3", - "\xC3\xB2", "\xC3\xB4", "\xC3\xB6", "\xC3\xB5", "\xC3\xBA", "\xC3\xB9", "\xC3\xBB", "\xC3\xBC", - "\xE2\x80\xA0", "\xC2\xB0", "\xC2\xA2", "\xC2\xA3", "\xC2\xA7", "\xE2\x80\xA2", "\xC2\xB6", "\xC3\x9F", - "\xC2\xAE", "\xC2\xA9", "\xE2\x84\xA2", "\xC2\xB4", "\xC2\xA8", "\xE2\x89\xA0", "\xC3\x86", "\xC3\x98", - "\xE2\x88\x9E", "\xC2\xB1", "\xE2\x89\xA4", "\xE2\x89\xA5", "\xC2\xA5", "\xC2\xB5", "\xE2\x88\x82", "\xE2\x88\x91", - "\xE2\x88\x8F", "\xCF\x80", "\xE2\x88\xAB", "\xC2\xAA", "\xC2\xBA", "\xCE\xA9", "\xC3\xA6", "\xC3\xB8", - "\xC2\xBF", "\xC2\xA1", "\xC2\xAC", "\xE2\x88\x9A", "\xC6\x92", "\xE2\x89\x88", "\xE2\x88\x86", "\xC2\xAB", - "\xC2\xBB", "\xE2\x80\xA6", "\xC2\xA0", "\xC3\x80", "\xC3\x83", "\xC3\x95", "\xC5\x92", "\xC5\x93", - "\xE2\x80\x93", "\xE2\x80\x94", "\xE2\x80\x9C", "\xE2\x80\x9D", "\xE2\x80\x98", "\xE2\x80\x99", "\xC3\xB7", "\xE2\x97\x8A", - "\xC3\xBF", "\xC5\xB8", "\xE2\x81\x84", "\xE2\x82\xAC", "\xE2\x80\xB9", "\xE2\x80\xBA", "\xEF\xAC\x81", "\xEF\xAC\x82", - "\xE2\x80\xA1", "\xC2\xB7", "\xE2\x80\x9A", "\xE2\x80\x9E", "\xE2\x80\xB0", "\xC3\x82", "\xC3\x8A", "\xC3\x81", - "\xC3\x8B", "\xC3\x88", "\xC3\x8D", "\xC3\x8E", "\xC3\x8F", "\xC3\x8C", "\xC3\x93", "\xC3\x94", - "\xEF\xA3\xBF", "\xC3\x92", "\xC3\x9A", "\xC3\x9B", "\xC3\x99", "\xC4\xB1", "\xCB\x86", "\xCB\x9C", - "\xC2\xAF", "\xCB\x98", "\xCB\x99", "\xCB\x9A", "\xC2\xB8", "\xCB\x9D", "\xCB\x9B", "\xCB\x87" -}; - -static const XMP_Uns32 kMacRomanCP [128] = { // Unicode codepoints for MacRoman 80..FF. - 0x00C4, 0x00C5, 0x00C7, 0x00C9, 0x00D1, 0x00D6, 0x00DC, 0x00E1, - 0x00E0, 0x00E2, 0x00E4, 0x00E3, 0x00E5, 0x00E7, 0x00E9, 0x00E8, - 0x00EA, 0x00EB, 0x00ED, 0x00EC, 0x00EE, 0x00EF, 0x00F1, 0x00F3, - 0x00F2, 0x00F4, 0x00F6, 0x00F5, 0x00FA, 0x00F9, 0x00FB, 0x00FC, - 0x2020, 0x00B0, 0x00A2, 0x00A3, 0x00A7, 0x2022, 0x00B6, 0x00DF, - 0x00AE, 0x00A9, 0x2122, 0x00B4, 0x00A8, 0x2260, 0x00C6, 0x00D8, - 0x221E, 0x00B1, 0x2264, 0x2265, 0x00A5, 0x00B5, 0x2202, 0x2211, - 0x220F, 0x03C0, 0x222B, 0x00AA, 0x00BA, 0x03A9, 0x00E6, 0x00F8, - 0x00BF, 0x00A1, 0x00AC, 0x221A, 0x0192, 0x2248, 0x2206, 0x00AB, - 0x00BB, 0x2026, 0x00A0, 0x00C0, 0x00C3, 0x00D5, 0x0152, 0x0153, - 0x2013, 0x2014, 0x201C, 0x201D, 0x2018, 0x2019, 0x00F7, 0x25CA, - 0x00FF, 0x0178, 0x2044, 0x20AC, 0x2039, 0x203A, 0xFB01, 0xFB02, - 0x2021, 0x00B7, 0x201A, 0x201E, 0x2030, 0x00C2, 0x00CA, 0x00C1, - 0x00CB, 0x00C8, 0x00CD, 0x00CE, 0x00CF, 0x00CC, 0x00D3, 0x00D4, - 0xF8FF, 0x00D2, 0x00DA, 0x00DB, 0x00D9, 0x0131, 0x02C6, 0x02DC, // ! U+F8FF is private use solid Apple icon. - 0x00AF, 0x02D8, 0x02D9, 0x02DA, 0x00B8, 0x02DD, 0x02DB, 0x02C7 -}; - -// ------------------------------------------------------------------------------------------------- - -static const XMP_Uns16 kMacLangToScript_0_94 [95] = { - - /* langEnglish (0) */ smRoman, - /* langFrench (1) */ smRoman, - /* langGerman (2) */ smRoman, - /* langItalian (3) */ smRoman, - /* langDutch (4) */ smRoman, - /* langSwedish (5) */ smRoman, - /* langSpanish (6) */ smRoman, - /* langDanish (7) */ smRoman, - /* langPortuguese (8) */ smRoman, - /* langNorwegian (9) */ smRoman, - - /* langHebrew (10) */ smHebrew, - /* langJapanese (11) */ smJapanese, - /* langArabic (12) */ smArabic, - /* langFinnish (13) */ smRoman, - /* langGreek (14) */ smRoman, - /* langIcelandic (15) */ smRoman, - /* langMaltese (16) */ smRoman, - /* langTurkish (17) */ smRoman, - /* langCroatian (18) */ smRoman, - /* langTradChinese (19) */ smTradChinese, - - /* langUrdu (20) */ smArabic, - /* langHindi (21) */ smDevanagari, - /* langThai (22) */ smThai, - /* langKorean (23) */ smKorean, - /* langLithuanian (24) */ smCentralEuroRoman, - /* langPolish (25) */ smCentralEuroRoman, - /* langHungarian (26) */ smCentralEuroRoman, - /* langEstonian (27) */ smCentralEuroRoman, - /* langLatvian (28) */ smCentralEuroRoman, - /* langSami (29) */ kNoMacScript, // ! Not known, missing from Apple comments. - - /* langFaroese (30) */ smRoman, - /* langFarsi (31) */ smArabic, - /* langRussian (32) */ smCyrillic, - /* langSimpChinese (33) */ smSimpChinese, - /* langFlemish (34) */ smRoman, - /* langIrishGaelic (35) */ smRoman, - /* langAlbanian (36) */ smRoman, - /* langRomanian (37) */ smRoman, - /* langCzech (38) */ smCentralEuroRoman, - /* langSlovak (39) */ smCentralEuroRoman, - - /* langSlovenian (40) */ smRoman, - /* langYiddish (41) */ smHebrew, - /* langSerbian (42) */ smCyrillic, - /* langMacedonian (43) */ smCyrillic, - /* langBulgarian (44) */ smCyrillic, - /* langUkrainian (45) */ smCyrillic, - /* langBelorussian (46) */ smCyrillic, - /* langUzbek (47) */ smCyrillic, - /* langKazakh (48) */ smCyrillic, - /* langAzerbaijani (49) */ smCyrillic, - - /* langAzerbaijanAr (50) */ smArabic, - /* langArmenian (51) */ smArmenian, - /* langGeorgian (52) */ smGeorgian, - /* langMoldavian (53) */ smCyrillic, - /* langKirghiz (54) */ smCyrillic, - /* langTajiki (55) */ smCyrillic, - /* langTurkmen (56) */ smCyrillic, - /* langMongolian (57) */ smMongolian, - /* langMongolianCyr (58) */ smCyrillic, - /* langPashto (59) */ smArabic, - - /* langKurdish (60) */ smArabic, - /* langKashmiri (61) */ smArabic, - /* langSindhi (62) */ smArabic, - /* langTibetan (63) */ smTibetan, - /* langNepali (64) */ smDevanagari, - /* langSanskrit (65) */ smDevanagari, - /* langMarathi (66) */ smDevanagari, - /* langBengali (67) */ smBengali, - /* langAssamese (68) */ smBengali, - /* langGujarati (69) */ smGujarati, - - /* langPunjabi (70) */ smGurmukhi, - /* langOriya (71) */ smOriya, - /* langMalayalam (72) */ smMalayalam, - /* langKannada (73) */ smKannada, - /* langTamil (74) */ smTamil, - /* langTelugu (75) */ smTelugu, - /* langSinhalese (76) */ smSinhalese, - /* langBurmese (77) */ smBurmese, - /* langKhmer (78) */ smKhmer, - /* langLao (79) */ smLao, - - /* langVietnamese (80) */ smVietnamese, - /* langIndonesian (81) */ smRoman, - /* langTagalog (82) */ smRoman, - /* langMalayRoman (83) */ smRoman, - /* langMalayArabic (84) */ smArabic, - /* langAmharic (85) */ smEthiopic, - /* langTigrinya (86) */ smEthiopic, - /* langOromo (87) */ smEthiopic, - /* langSomali (88) */ smRoman, - /* langSwahili (89) */ smRoman, - - /* langKinyarwanda (90) */ smRoman, - /* langRundi (91) */ smRoman, - /* langNyanja (92) */ smRoman, - /* langMalagasy (93) */ smRoman, - /* langEsperanto (94) */ smRoman - -}; // kMacLangToScript_0_94 - -static const XMP_Uns16 kMacLangToScript_128_151 [24] = { - - /* langWelsh (128) */ smRoman, - /* langBasque (129) */ smRoman, - - /* langCatalan (130) */ smRoman, - /* langLatin (131) */ smRoman, - /* langQuechua (132) */ smRoman, - /* langGuarani (133) */ smRoman, - /* langAymara (134) */ smRoman, - /* langTatar (135) */ smCyrillic, - /* langUighur (136) */ smArabic, - /* langDzongkha (137) */ smTibetan, - /* langJavaneseRom (138) */ smRoman, - /* langSundaneseRom (139) */ smRoman, - - /* langGalician (140) */ smRoman, - /* langAfrikaans (141) */ smRoman, - /* langBreton (142) */ smRoman, - /* langInuktitut (143) */ smEthiopic, - /* langScottishGaelic (144) */ smRoman, - /* langManxGaelic (145) */ smRoman, - /* langIrishGaelicScript (146) */ smRoman, - /* langTongan (147) */ smRoman, - /* langGreekAncient (148) */ smGreek, - /* langGreenlandic (149) */ smRoman, - - /* langAzerbaijanRoman (150) */ smRoman, - /* langNynorsk (151) */ smRoman - -}; // kMacLangToScript_128_151 - -// ------------------------------------------------------------------------------------------------- - -static const char * kMacToXMPLang_0_94 [95] = { - - /* langEnglish (0) */ "en", - /* langFrench (1) */ "fr", - /* langGerman (2) */ "de", - /* langItalian (3) */ "it", - /* langDutch (4) */ "nl", - /* langSwedish (5) */ "sv", - /* langSpanish (6) */ "es", - /* langDanish (7) */ "da", - /* langPortuguese (8) */ "pt", - /* langNorwegian (9) */ "no", - - /* langHebrew (10) */ "he", - /* langJapanese (11) */ "ja", - /* langArabic (12) */ "ar", - /* langFinnish (13) */ "fi", - /* langGreek (14) */ "el", - /* langIcelandic (15) */ "is", - /* langMaltese (16) */ "mt", - /* langTurkish (17) */ "tr", - /* langCroatian (18) */ "hr", - /* langTradChinese (19) */ "zh", - - /* langUrdu (20) */ "ur", - /* langHindi (21) */ "hi", - /* langThai (22) */ "th", - /* langKorean (23) */ "ko", - /* langLithuanian (24) */ "lt", - /* langPolish (25) */ "pl", - /* langHungarian (26) */ "hu", - /* langEstonian (27) */ "et", - /* langLatvian (28) */ "lv", - /* langSami (29) */ "se", - - /* langFaroese (30) */ "fo", - /* langFarsi (31) */ "fa", - /* langRussian (32) */ "ru", - /* langSimpChinese (33) */ "zh", - /* langFlemish (34) */ "nl", - /* langIrishGaelic (35) */ "ga", - /* langAlbanian (36) */ "sq", - /* langRomanian (37) */ "ro", - /* langCzech (38) */ "cs", - /* langSlovak (39) */ "sk", - - /* langSlovenian (40) */ "sl", - /* langYiddish (41) */ "yi", - /* langSerbian (42) */ "sr", - /* langMacedonian (43) */ "mk", - /* langBulgarian (44) */ "bg", - /* langUkrainian (45) */ "uk", - /* langBelorussian (46) */ "be", - /* langUzbek (47) */ "uz", - /* langKazakh (48) */ "kk", - /* langAzerbaijani (49) */ "az", - - /* langAzerbaijanAr (50) */ "az", - /* langArmenian (51) */ "hy", - /* langGeorgian (52) */ "ka", - /* langMoldavian (53) */ "ro", - /* langKirghiz (54) */ "ky", - /* langTajiki (55) */ "tg", - /* langTurkmen (56) */ "tk", - /* langMongolian (57) */ "mn", - /* langMongolianCyr (58) */ "mn", - /* langPashto (59) */ "ps", - - /* langKurdish (60) */ "ku", - /* langKashmiri (61) */ "ks", - /* langSindhi (62) */ "sd", - /* langTibetan (63) */ "bo", - /* langNepali (64) */ "ne", - /* langSanskrit (65) */ "sa", - /* langMarathi (66) */ "mr", - /* langBengali (67) */ "bn", - /* langAssamese (68) */ "as", - /* langGujarati (69) */ "gu", - - /* langPunjabi (70) */ "pa", - /* langOriya (71) */ "or", - /* langMalayalam (72) */ "ml", - /* langKannada (73) */ "kn", - /* langTamil (74) */ "ta", - /* langTelugu (75) */ "te", - /* langSinhalese (76) */ "si", - /* langBurmese (77) */ "my", - /* langKhmer (78) */ "km", - /* langLao (79) */ "lo", - - /* langVietnamese (80) */ "vi", - /* langIndonesian (81) */ "id", - /* langTagalog (82) */ "tl", - /* langMalayRoman (83) */ "ms", - /* langMalayArabic (84) */ "ms", - /* langAmharic (85) */ "am", - /* langTigrinya (86) */ "ti", - /* langOromo (87) */ "om", - /* langSomali (88) */ "so", - /* langSwahili (89) */ "sw", - - /* langKinyarwanda (90) */ "rw", - /* langRundi (91) */ "rn", - /* langNyanja (92) */ "ny", - /* langMalagasy (93) */ "mg", - /* langEsperanto (94) */ "eo" - -}; // kMacToXMPLang_0_94 - -static const char * kMacToXMPLang_128_151 [24] = { - - /* langWelsh (128) */ "cy", - /* langBasque (129) */ "eu", - - /* langCatalan (130) */ "ca", - /* langLatin (131) */ "la", - /* langQuechua (132) */ "qu", - /* langGuarani (133) */ "gn", - /* langAymara (134) */ "ay", - /* langTatar (135) */ "tt", - /* langUighur (136) */ "ug", - /* langDzongkha (137) */ "dz", - /* langJavaneseRom (138) */ "jv", - /* langSundaneseRom (139) */ "su", - - /* langGalician (140) */ "gl", - /* langAfrikaans (141) */ "af", - /* langBreton (142) */ "br", - /* langInuktitut (143) */ "iu", - /* langScottishGaelic (144) */ "gd", - /* langManxGaelic (145) */ "gv", - /* langIrishGaelicScript (146) */ "ga", - /* langTongan (147) */ "to", - /* langGreekAncient (148) */ "", // ! Has no ISO 639-1 2 letter code. - /* langGreenlandic (149) */ "kl", - - /* langAzerbaijanRoman (150) */ "az", - /* langNynorsk (151) */ "nn" - -}; // kMacToXMPLang_128_151 - -// ------------------------------------------------------------------------------------------------- - -#if XMP_WinBuild - -static UINT kMacScriptToWinCP[34] = { - /* smRoman (0) */ 10000, // There don't seem to be symbolic constants. - /* smJapanese (1) */ 10001, // From http://msdn.microsoft.com/en-us/library/dd317756(VS.85).aspx - /* smTradChinese (2) */ 10002, - /* smKorean (3) */ 10003, - /* smArabic (4) */ 10004, - /* smHebrew (5) */ 10005, - /* smGreek (6) */ 10006, - /* smCyrillic (7) */ 10007, - /* smRSymbol (8) */ 0, - /* smDevanagari (9) */ 0, - /* smGurmukhi (10) */ 0, - /* smGujarati (11) */ 0, - /* smOriya (12) */ 0, - /* smBengali (13) */ 0, - /* smTamil (14) */ 0, - /* smTelugu (15) */ 0, - /* smKannada (16) */ 0, - /* smMalayalam (17) */ 0, - /* smSinhalese (18) */ 0, - /* smBurmese (19) */ 0, - /* smKhmer (20) */ 0, - /* smThai (21) */ 10021, - /* smLao (22) */ 0, - /* smGeorgian (23) */ 0, - /* smArmenian (24) */ 0, - /* smSimpChinese (25) */ 10008, - /* smTibetan (26) */ 0, - /* smMongolian (27) */ 0, - /* smEthiopic (28) */ 0, - /* smGeez (28) */ 0, - /* smCentralEuroRoman (29) */ 10029, - /* smVietnamese (30) */ 0, - /* smExtArabic (31) */ 0, - /* smUninterp (32) */ 0 -}; // kMacScriptToWinCP - -static UINT kMacToWinCP_0_94 [95] = { - - /* langEnglish (0) */ 0, - /* langFrench (1) */ 0, - /* langGerman (2) */ 0, - /* langItalian (3) */ 0, - /* langDutch (4) */ 0, - /* langSwedish (5) */ 0, - /* langSpanish (6) */ 0, - /* langDanish (7) */ 0, - /* langPortuguese (8) */ 0, - /* langNorwegian (9) */ 0, - - /* langHebrew (10) */ 10005, - /* langJapanese (11) */ 10001, - /* langArabic (12) */ 10004, - /* langFinnish (13) */ 0, - /* langGreek (14) */ 10006, - /* langIcelandic (15) */ 10079, - /* langMaltese (16) */ 0, - /* langTurkish (17) */ 10081, - /* langCroatian (18) */ 10082, - /* langTradChinese (19) */ 10002, - - /* langUrdu (20) */ 0, - /* langHindi (21) */ 0, - /* langThai (22) */ 10021, - /* langKorean (23) */ 10003, - /* langLithuanian (24) */ 0, - /* langPolish (25) */ 0, - /* langHungarian (26) */ 0, - /* langEstonian (27) */ 0, - /* langLatvian (28) */ 0, - /* langSami (29) */ 0, - - /* langFaroese (30) */ 0, - /* langFarsi (31) */ 0, - /* langRussian (32) */ 0, - /* langSimpChinese (33) */ 10008, - /* langFlemish (34) */ 0, - /* langIrishGaelic (35) */ 0, - /* langAlbanian (36) */ 0, - /* langRomanian (37) */ 10010, - /* langCzech (38) */ 0, - /* langSlovak (39) */ 0, - - /* langSlovenian (40) */ 0, - /* langYiddish (41) */ 0, - /* langSerbian (42) */ 0, - /* langMacedonian (43) */ 0, - /* langBulgarian (44) */ 0, - /* langUkrainian (45) */ 10017, - /* langBelorussian (46) */ 0, - /* langUzbek (47) */ 0, - /* langKazakh (48) */ 0, - /* langAzerbaijani (49) */ 0, - - /* langAzerbaijanAr (50) */ 0, - /* langArmenian (51) */ 0, - /* langGeorgian (52) */ 0, - /* langMoldavian (53) */ 0, - /* langKirghiz (54) */ 0, - /* langTajiki (55) */ 0, - /* langTurkmen (56) */ 0, - /* langMongolian (57) */ 0, - /* langMongolianCyr (58) */ 0, - /* langPashto (59) */ 0, - - /* langKurdish (60) */ 0, - /* langKashmiri (61) */ 0, - /* langSindhi (62) */ 0, - /* langTibetan (63) */ 0, - /* langNepali (64) */ 0, - /* langSanskrit (65) */ 0, - /* langMarathi (66) */ 0, - /* langBengali (67) */ 0, - /* langAssamese (68) */ 0, - /* langGujarati (69) */ 0, - - /* langPunjabi (70) */ 0, - /* langOriya (71) */ 0, - /* langMalayalam (72) */ 0, - /* langKannada (73) */ 0, - /* langTamil (74) */ 0, - /* langTelugu (75) */ 0, - /* langSinhalese (76) */ 0, - /* langBurmese (77) */ 0, - /* langKhmer (78) */ 0, - /* langLao (79) */ 0, - - /* langVietnamese (80) */ 0, - /* langIndonesian (81) */ 0, - /* langTagalog (82) */ 0, - /* langMalayRoman (83) */ 0, - /* langMalayArabic (84) */ 0, - /* langAmharic (85) */ 0, - /* langTigrinya (86) */ 0, - /* langOromo (87) */ 0, - /* langSomali (88) */ 0, - /* langSwahili (89) */ 0, - - /* langKinyarwanda (90) */ 0, - /* langRundi (91) */ 0, - /* langNyanja (92) */ 0, - /* langMalagasy (93) */ 0, - /* langEsperanto (94) */ 0 - -}; // kMacToWinCP_0_94 - -#endif - -// ================================================================================================= -// GetMacScript -// ============ - -static XMP_Uns16 GetMacScript ( XMP_Uns16 macLang ) -{ - XMP_Uns16 macScript = kNoMacScript; - - if ( macLang <= 94 ) { - macScript = kMacLangToScript_0_94[macLang]; - } else if ( (128 <= macLang) && (macLang <= 151) ) { - macScript = kMacLangToScript_0_94[macLang-128]; - } - - return macScript; - -} // GetMacScript - -// ================================================================================================= -// GetWinCP -// ======== - -#if XMP_WinBuild - -static UINT GetWinCP ( XMP_Uns16 macLang ) -{ - UINT winCP = 0; - - if ( macLang <= 94 ) winCP = kMacToWinCP_0_94[macLang]; - - if ( winCP == 0 ) { - XMP_Uns16 macScript = GetMacScript ( macLang ); - if ( macScript != kNoMacScript ) winCP = kMacScriptToWinCP[macScript]; - } - - return winCP; - -} // GetWinCP - -#endif - -// ================================================================================================= -// GetXMPLang -// ========== - -static XMP_StringPtr GetXMPLang ( XMP_Uns16 macLang ) -{ - XMP_StringPtr xmpLang = ""; - - if ( macLang <= 94 ) { - xmpLang = kMacToXMPLang_0_94[macLang]; - } else if ( (128 <= macLang) && (macLang <= 151) ) { - xmpLang = kMacToXMPLang_128_151[macLang-128]; - } - - return xmpLang; - -} // GetXMPLang - -// ================================================================================================= -// GetMacLang -// ========== - -static XMP_Uns16 GetMacLang ( std::string * xmpLang ) -{ - if ( *xmpLang == "" ) return kNoMacLang; - - size_t hyphenPos = xmpLang->find ( '-' ); // Make sure the XMP language is "generic". - if ( hyphenPos != std::string::npos ) xmpLang->erase ( hyphenPos ); - - for ( XMP_Uns16 i = 0; i <= 94; ++i ) { // Using std::map would be faster. - if ( *xmpLang == kMacToXMPLang_0_94[i] ) return i; - } - - for ( XMP_Uns16 i = 128; i <= 151; ++i ) { // Using std::map would be faster. - if ( *xmpLang == kMacToXMPLang_128_151[i-128] ) return i; - } - - return kNoMacLang; - -} // GetMacLang - -// ================================================================================================= -// MacRomanToUTF8 -// ============== - -static void MacRomanToUTF8 ( const std::string & macRoman, std::string * utf8 ) -{ - utf8->erase(); - - for ( XMP_Uns8* chPtr = (XMP_Uns8*)macRoman.c_str(); *chPtr != 0; ++chPtr ) { // ! Don't trust that char is unsigned. - if ( *chPtr < 0x80 ) { - (*utf8) += (char)*chPtr; - } else { - (*utf8) += kMacRomanUTF8[(*chPtr)-0x80]; - } - } - -} // MacRomanToUTF8 - -// ================================================================================================= -// UTF8ToMacRoman -// ============== - -static void UTF8ToMacRoman ( const std::string & utf8, std::string * macRoman ) -{ - macRoman->erase(); - bool inNonMRSpan = false; - - for ( const XMP_Uns8 * chPtr = (XMP_Uns8*)utf8.c_str(); *chPtr != 0; ++chPtr ) { // ! Don't trust that char is unsigned. - if ( *chPtr < 0x80 ) { - (*macRoman) += (char)*chPtr; - inNonMRSpan = false; - } else { - XMP_Uns32 cp = GetCodePoint ( &chPtr ); - --chPtr; // Make room for the loop increment. - XMP_Uns8 mr; - for ( mr = 0; (mr < 0x80) && (cp != kMacRomanCP[mr]); ++mr ) {}; // Using std::map would be faster. - if ( mr < 0x80 ) { - (*macRoman) += (char)(mr+0x80); - inNonMRSpan = false; - } else if ( ! inNonMRSpan ) { - (*macRoman) += '?'; - inNonMRSpan = true; - } - } - } - -} // UTF8ToMacRoman - -// ================================================================================================= -// IsMacLangKnown -// ============== - -static inline bool IsMacLangKnown ( XMP_Uns16 macLang ) -{ - XMP_Uns16 macScript = GetMacScript ( macLang ); - if ( macScript == kNoMacScript ) return false; - - #if XMP_UNIXBuild - if ( macScript != smRoman ) return false; - #elif XMP_WinBuild - if ( GetWinCP(macLang) == 0 ) return false; - #endif - - return true; - -} // IsMacLangKnown - -// ================================================================================================= -// ConvertToMacLang -// ================ - -bool ConvertToMacLang ( const std::string & utf8Value, XMP_Uns16 macLang, std::string * macValue ) -{ - macValue->erase(); - if ( macLang == kNoMacLang ) macLang = 0; // *** Zero is English, ought to use the "active" OS lang. - if ( ! IsMacLangKnown ( macLang ) ) return false; - - #if XMP_MacBuild - XMP_Uns16 macScript = GetMacScript ( macLang ); - ReconcileUtils::UTF8ToMacEncoding ( macScript, macLang, (XMP_Uns8*)utf8Value.c_str(), utf8Value.size(), macValue ); - #elif XMP_UNIXBuild - UTF8ToMacRoman ( utf8Value, macValue ); - #elif XMP_WinBuild - UINT winCP = GetWinCP ( macLang ); - ReconcileUtils::UTF8ToWinEncoding ( winCP, (XMP_Uns8*)utf8Value.c_str(), utf8Value.size(), macValue ); - #endif - - return true; - -} // ConvertToMacLang - -// ================================================================================================= -// ConvertFromMacLang -// ================== - -bool ConvertFromMacLang ( const std::string & macValue, XMP_Uns16 macLang, std::string * utf8Value ) -{ - utf8Value->erase(); - if ( ! IsMacLangKnown ( macLang ) ) return false; - - #if XMP_MacBuild - XMP_Uns16 macScript = GetMacScript ( macLang ); - ReconcileUtils::MacEncodingToUTF8 ( macScript, macLang, (XMP_Uns8*)macValue.c_str(), macValue.size(), utf8Value ); - #elif XMP_UNIXBuild - MacRomanToUTF8 ( macValue, utf8Value ); - #elif XMP_WinBuild - UINT winCP = GetWinCP ( macLang ); - ReconcileUtils::WinEncodingToUTF8 ( winCP, (XMP_Uns8*)macValue.c_str(), macValue.size(), utf8Value ); - #endif - - return true; - -} // ConvertFromMacLang - -// ================================================================================================= -// ================================================================================================= -// TradQT_Manager -// ================================================================================================= -// ================================================================================================= - -// ================================================================================================= -// TradQT_Manager::ParseCachedBoxes -// ================================ -// -// Parse the cached '©...' children of the 'moov'/'udta' box. The contents of each cached box are -// a sequence of "mini boxes" analogous to XMP AltText arrays. Each mini box has a 16-bit size, -// 16-bit language code, and text. The size is only the text size. The language codes are Macintosh -// Script Manager langXyz codes. The text encoding is implicit in the language, see comments in -// Apple's Script.h header. - -bool TradQT_Manager::ParseCachedBoxes ( const MOOV_Manager & moovMgr ) -{ - MOOV_Manager::BoxInfo udtaInfo; - MOOV_Manager::BoxRef udtaRef = moovMgr.GetBox ( "moov/udta", &udtaInfo ); - if ( udtaRef == 0 ) return false; - - for ( XMP_Uns32 i = 0; i < udtaInfo.childCount; ++i ) { - - MOOV_Manager::BoxInfo currInfo; - MOOV_Manager::BoxRef currRef = moovMgr.GetNthChild ( udtaRef, i, &currInfo ); - if ( currRef == 0 ) break; // Sanity check, should not happen. - if ( (currInfo.boxType >> 24) != 0xA9 ) continue; - if ( currInfo.contentSize < 2+2+1 ) continue; // Want enough for a non-empty value. - - InfoMapPos newInfo = this->parsedBoxes.insert ( this->parsedBoxes.end(), - InfoMap::value_type ( currInfo.boxType, ParsedBoxInfo ( currInfo.boxType ) ) ); - std::vector<ValueInfo> * newValues = &newInfo->second.values; - - XMP_Uns8 * boxPtr = (XMP_Uns8*) currInfo.content; - XMP_Uns8 * boxEnd = boxPtr + currInfo.contentSize; - XMP_Uns16 miniLen, macLang; - - for ( ; boxPtr < boxEnd-4; boxPtr += miniLen ) { - - miniLen = 4 + GetUns16BE ( boxPtr ); // ! Include header in local miniLen. - macLang = GetUns16BE ( boxPtr+2); - if ( (miniLen <= 4) || (miniLen > (boxEnd - boxPtr)) ) continue; // Ignore bad or empty values. - - XMP_StringPtr valuePtr = (char*)(boxPtr+4); - size_t valueLen = miniLen - 4; - - newValues->push_back ( ValueInfo() ); - ValueInfo * newValue = &newValues->back(); - - // Only set the XMP language if the Mac script is known, i.e. the value can be converted. - - newValue->macLang = macLang; - if ( IsMacLangKnown ( macLang ) ) newValue->xmpLang = GetXMPLang ( macLang ); - newValue->macValue.assign ( valuePtr, valueLen ); - - } - - } - - return (! this->parsedBoxes.empty()); - -} // TradQT_Manager::ParseCachedBoxes - -// ================================================================================================= -// TradQT_Manager::ImportSimpleXMP -// =============================== -// -// Update a simple XMP property if the QT value looks newer. - -bool TradQT_Manager::ImportSimpleXMP ( XMP_Uns32 id, SXMPMeta * xmp, XMP_StringPtr ns, XMP_StringPtr prop ) const -{ - - try { - - InfoMapCPos infoPos = this->parsedBoxes.find ( id ); - if ( infoPos == this->parsedBoxes.end() ) return false; - if ( infoPos->second.values.empty() ) return false; - - std::string xmpValue, tempValue; - XMP_OptionBits flags; - bool xmpExists = xmp->GetProperty ( ns, prop, &xmpValue, &flags ); - if ( xmpExists && (! XMP_PropIsSimple ( flags )) ) { - XMP_Throw ( "TradQT_Manager::ImportSimpleXMP - XMP property must be simple", kXMPErr_BadParam ); - } - - bool convertOK; - const ValueInfo & qtItem = infoPos->second.values[0]; // ! Use the first QT entry. - - if ( xmpExists ) { - convertOK = ConvertToMacLang ( xmpValue, qtItem.macLang, &tempValue ); - if ( ! convertOK ) return false; // throw? - if ( tempValue == qtItem.macValue ) return false; // QT value matches back converted XMP value. - } - - convertOK = ConvertFromMacLang ( qtItem.macValue, qtItem.macLang, &tempValue ); - if ( ! convertOK ) return false; // throw? - xmp->SetProperty ( ns, prop, tempValue.c_str() ); - return true; - - } catch ( ... ) { - - return false; // Don't let one failure abort other imports. - - } - -} // TradQT_Manager::ImportSimpleXMP - -// ================================================================================================= -// TradQT_Manager::ImportLangItem -// ============================== -// -// Update a specific XMP AltText item if the QuickTime value looks newer. - -bool TradQT_Manager::ImportLangItem ( const ValueInfo & qtItem, SXMPMeta * xmp, - XMP_StringPtr ns, XMP_StringPtr langArray ) const -{ - - try { - - XMP_StringPtr genericLang, specificLang; - if ( qtItem.xmpLang[0] != 0 ) { - genericLang = qtItem.xmpLang; - specificLang = qtItem.xmpLang; - } else { - genericLang = ""; - specificLang = "x-default"; - } - - bool convertOK; - std::string xmpValue, tempValue, actualLang; - bool xmpExists = xmp->GetLocalizedText ( ns, langArray, genericLang, specificLang, &actualLang, &xmpValue, 0 ); - if ( xmpExists ) { - convertOK = ConvertToMacLang ( xmpValue, qtItem.macLang, &tempValue ); - if ( ! convertOK ) return false; // throw? - if ( tempValue == qtItem.macValue ) return true; // QT value matches back converted XMP value. - specificLang = actualLang.c_str(); - } - - convertOK = ConvertFromMacLang ( qtItem.macValue, qtItem.macLang, &tempValue ); - if ( ! convertOK ) return false; // throw? - xmp->SetLocalizedText ( ns, langArray, "", specificLang, tempValue.c_str() ); - return true; - - } catch ( ... ) { - - return false; // Don't let one failure abort other imports. - - } - -} // TradQT_Manager::ImportLangItem - -// ================================================================================================= -// TradQT_Manager::ImportLangAltXMP -// ================================ -// -// Update items in the XMP array if the QT value looks newer. - -bool TradQT_Manager::ImportLangAltXMP ( XMP_Uns32 id, SXMPMeta * xmp, XMP_StringPtr ns, XMP_StringPtr langArray ) const -{ - - try { - - InfoMapCPos infoPos = this->parsedBoxes.find ( id ); - if ( infoPos == this->parsedBoxes.end() ) return false; - if ( infoPos->second.values.empty() ) return false; // Quit now if there are no values. - - XMP_OptionBits flags; - bool xmpExists = xmp->GetProperty ( ns, langArray, 0, &flags ); - if ( ! xmpExists ) { - xmp->SetProperty ( ns, langArray, 0, kXMP_PropArrayIsAltText ); - } else if ( ! XMP_ArrayIsAltText ( flags ) ) { - XMP_Throw ( "TradQT_Manager::ImportLangAltXMP - XMP array must be AltText", kXMPErr_BadParam ); - } - - // Process all of the QT values, looking up the appropriate XMP language for each. - - bool haveMappings = false; - const ValueVector & qtValues = infoPos->second.values; - - for ( size_t i = 0, limit = qtValues.size(); i < limit; ++i ) { - const ValueInfo & qtItem = qtValues[i]; - if ( *qtItem.xmpLang == 0 ) continue; // Only do known mappings in the loop. - haveMappings |= this->ImportLangItem ( qtItem, xmp, ns, langArray ); - } - - if ( ! haveMappings ) { - // If nothing mapped, process the first QT item to XMP's "x-default". - haveMappings = this->ImportLangItem ( qtValues[0], xmp, ns, langArray ); // ! No xmpLang implies "x-default". - } - - return haveMappings; - - } catch ( ... ) { - - return false; // Don't let one failure abort other imports. - - } - -} // TradQT_Manager::ImportLangAltXMP - -// ================================================================================================= -// TradQT_Manager::ExportSimpleXMP -// =============================== -// -// Export a simple XMP value to the first existing QuickTime item. Delete all of the QT values if the -// XMP value is empty or the XMP does not exist. - -// ! We don't create new QuickTime items since we don't know the language. - -void TradQT_Manager::ExportSimpleXMP ( XMP_Uns32 id, const SXMPMeta & xmp, XMP_StringPtr ns, XMP_StringPtr prop, - bool createWithZeroLang /* = false */ ) -{ - std::string xmpValue, macValue; - - InfoMapPos infoPos = this->parsedBoxes.find ( id ); - bool qtFound = (infoPos != this->parsedBoxes.end()) && (! infoPos->second.values.empty()); - - bool xmpFound = xmp.GetProperty ( ns, prop, &xmpValue, 0 ); - if ( (! xmpFound) || (xmpValue.empty()) ) { - if ( qtFound ) { - this->parsedBoxes.erase ( infoPos ); - this->changed = true; - } - return; - } - - XMP_Assert ( xmpFound ); - if ( ! qtFound ) { - if ( ! createWithZeroLang ) return; - infoPos = this->parsedBoxes.insert ( this->parsedBoxes.end(), - InfoMap::value_type ( id, ParsedBoxInfo ( id ) ) ); - ValueVector * newValues = &infoPos->second.values; - newValues->push_back ( ValueInfo() ); - ValueInfo * newValue = &newValues->back(); - newValue->macLang = 0; // Happens to be langEnglish. - newValue->xmpLang = kMacToXMPLang_0_94[0]; - this->changed = infoPos->second.changed = true; - } - - ValueInfo * qtItem = &infoPos->second.values[0]; // ! Use the first QT entry. - if ( ! IsMacLangKnown ( qtItem->macLang ) ) return; - - bool convertOK = ConvertToMacLang ( xmpValue, qtItem->macLang, &macValue ); - if ( convertOK && (macValue != qtItem->macValue) ) { - qtItem->macValue = macValue; - this->changed = infoPos->second.changed = true; - } - -} // TradQT_Manager::ExportSimpleXMP - -// ================================================================================================= -// TradQT_Manager::ExportLangAltXMP -// ================================ -// -// Export XMP LangAlt array items to QuickTime, where the language and encoding mappings are known. -// If there are no known language and encoding mappings, map the XMP default item to the first -// existing QuickTime item. - -void TradQT_Manager::ExportLangAltXMP ( XMP_Uns32 id, const SXMPMeta & xmp, XMP_StringPtr ns, XMP_StringPtr langArray ) -{ - bool haveMappings = false; - std::string xmpPath, xmpValue, xmpLang, macValue; - - InfoMapPos infoPos = this->parsedBoxes.find ( id ); - if ( infoPos == this->parsedBoxes.end() ) { - infoPos = this->parsedBoxes.insert ( this->parsedBoxes.end(), - InfoMap::value_type ( id, ParsedBoxInfo ( id ) ) ); - } - - ValueVector * qtValues = &infoPos->second.values; - XMP_Index xmpCount = xmp.CountArrayItems ( ns, langArray ); - bool convertOK; - - if ( xmpCount == 0 ) { - // Delete the "mappable" QuickTime items if there are no XMP values. Leave the others alone. - for ( int i = (int)qtValues->size()-1; i > 0; --i ) { // ! Need a signed index. - if ( (*qtValues)[i].xmpLang[0] != 0 ) { - qtValues->erase ( qtValues->begin() + i ); - this->changed = infoPos->second.changed = true; - } - } - return; - } - - // Go through the XMP and look for a related macLang QuickTime item to update or create. - - for ( XMP_Index xmpIndex = 1; xmpIndex <= xmpCount; ++xmpIndex ) { // ! XMP index starts at 1! - - SXMPUtils::ComposeArrayItemPath ( ns, langArray, xmpIndex, &xmpPath ); - xmp.GetProperty ( ns, xmpPath.c_str(), &xmpValue, 0 ); - xmp.GetQualifier ( ns, xmpPath.c_str(), kXMP_NS_XML, "lang", &xmpLang, 0 ); - if ( xmpLang == "x-default" ) continue; - - XMP_Uns16 macLang = GetMacLang ( &xmpLang ); - if ( macLang == kNoMacLang ) continue; - - size_t qtIndex, qtLimit; - for ( qtIndex = 0, qtLimit = qtValues->size(); qtIndex < qtLimit; ++qtIndex ) { - if ( (*qtValues)[qtIndex].macLang == macLang ) break; - } - - if ( qtIndex == qtLimit ) { - // No existing QuickTime item, try to create one. - if ( ! IsMacLangKnown ( macLang ) ) continue; - qtValues->push_back ( ValueInfo() ); - qtIndex = qtValues->size() - 1; - ValueInfo * newItem = &((*qtValues)[qtIndex]); - newItem->macLang = macLang; - newItem->xmpLang = GetXMPLang ( macLang ); // ! Use the 2 character root language. - } - - ValueInfo * qtItem = &((*qtValues)[qtIndex]); - qtItem->marked = true; // Mark it whether updated or not, don't delete it in the next pass. - - convertOK = ConvertToMacLang ( xmpValue, qtItem->macLang, &macValue ); - if ( convertOK && (macValue != qtItem->macValue) ) { - qtItem->macValue.swap ( macValue ); // No need to make a copy. - haveMappings = true; - } - - } - this->changed |= haveMappings; - infoPos->second.changed |= haveMappings; - - // Go through the QuickTime items that are unmarked and delete those that have an xmpLang - // and known macScript. Clear all marks. - - for ( int i = (int)qtValues->size()-1; i > 0; --i ) { // ! Need a signed index. - ValueInfo * qtItem = &((*qtValues)[i]); - if ( qtItem->marked ) { - qtItem->marked = false; - } else if ( (qtItem->xmpLang[0] != 0) && IsMacLangKnown ( qtItem->macLang ) ) { - qtValues->erase ( qtValues->begin() + i ); - this->changed = infoPos->second.changed = true; - } - } - - // If there were no mappings, export the XMP default item to the first QT item. - - if ( (! haveMappings) && (! qtValues->empty()) ) { - - bool ok = xmp.GetLocalizedText ( ns, langArray, "", "x-default", 0, &xmpValue, 0 ); - if ( ! ok ) return; - - ValueInfo * qtItem = &((*qtValues)[0]); - if ( ! IsMacLangKnown ( qtItem->macLang ) ) return; - - convertOK = ConvertToMacLang ( xmpValue, qtItem->macLang, &macValue ); - if ( convertOK && (macValue != qtItem->macValue) ) { - qtItem->macValue.swap ( macValue ); // No need to make a copy. - this->changed = infoPos->second.changed = true; - } - - } - -} // TradQT_Manager::ExportLangAltXMP - -// ================================================================================================= -// TradQT_Manager::UpdateChangedBoxes -// ================================== - -void TradQT_Manager::UpdateChangedBoxes ( MOOV_Manager * moovMgr ) -{ - MOOV_Manager::BoxInfo udtaInfo; - MOOV_Manager::BoxRef udtaRef = moovMgr->GetBox ( "moov/udta", &udtaInfo ); - XMP_Assert ( (udtaRef != 0) || (udtaInfo.childCount == 0) ); - - if ( udtaRef != 0 ) { // Might not have been a moov/udta box in the parse. - - // First go through the moov/udta/©... children and delete those that are not in the map. - - for ( XMP_Uns32 ordinal = udtaInfo.childCount; ordinal > 0; --ordinal ) { // ! Go backwards because of deletions. - - MOOV_Manager::BoxInfo currInfo; - MOOV_Manager::BoxRef currRef = moovMgr->GetNthChild ( udtaRef, (ordinal-1), &currInfo ); - if ( currRef == 0 ) break; // Sanity check, should not happen. - if ( (currInfo.boxType >> 24) != 0xA9 ) continue; - if ( currInfo.contentSize < 2+2+1 ) continue; // These were skipped by ParseCachedBoxes. - - InfoMapPos infoPos = this->parsedBoxes.find ( currInfo.boxType ); - if ( infoPos == this->parsedBoxes.end() ) moovMgr->DeleteNthChild ( udtaRef, (ordinal-1) ); - - } - - } - - // Now go through the changed items in the map and update them in the moov/udta subtree. - - InfoMapCPos infoPos = this->parsedBoxes.begin(); - InfoMapCPos infoEnd = this->parsedBoxes.end(); - - for ( ; infoPos != infoEnd; ++infoPos ) { - - ParsedBoxInfo * qtItem = (ParsedBoxInfo*) &infoPos->second; - if ( ! qtItem->changed ) continue; - qtItem->changed = false; - - XMP_Uns32 qtTotalSize = 0; // Total size of the QT values, ignoring empty values. - for ( size_t i = 0, limit = qtItem->values.size(); i < limit; ++i ) { - if ( ! qtItem->values[i].macValue.empty() ) { - if ( qtItem->values[i].macValue.size() > 0xFFFF ) qtItem->values[i].macValue.erase ( 0xFFFF ); - qtTotalSize += (XMP_Uns32)(2+2 + qtItem->values[i].macValue.size()); - } - } - - if ( udtaRef == 0 ) { // Might not have been a moov/udta box in the parse. - moovMgr->SetBox ( "moov/udta", 0, 0 ); - udtaRef = moovMgr->GetBox ( "moov/udta", &udtaInfo ); - XMP_Assert ( udtaRef != 0 ); - } - - if ( qtTotalSize == 0 ) { - - moovMgr->DeleteTypeChild ( udtaRef, qtItem->id ); - - } else { - - // Compose the complete box content. - - RawDataBlock fullValue; - fullValue.assign ( qtTotalSize, 0 ); - XMP_Uns8 * valuePtr = &fullValue[0]; - - for ( size_t i = 0, limit = qtItem->values.size(); i < limit; ++i ) { - XMP_Assert ( qtItem->values[i].macValue.size() <= 0xFFFF ); - XMP_Uns16 textSize = (XMP_Uns16)qtItem->values[i].macValue.size(); - if ( textSize == 0 ) continue; - PutUns16BE ( textSize, valuePtr ); valuePtr += 2; - PutUns16BE ( qtItem->values[i].macLang, valuePtr ); valuePtr += 2; - memcpy ( valuePtr, qtItem->values[i].macValue.c_str(), textSize ); valuePtr += textSize; - } - - // Look for an existing box to update, else add a new one. - - MOOV_Manager::BoxInfo itemInfo; - MOOV_Manager::BoxRef itemRef = moovMgr->GetTypeChild ( udtaRef, qtItem->id, &itemInfo ); - - if ( itemRef != 0 ) { - moovMgr->SetBox ( itemRef, &fullValue[0], qtTotalSize ); - } else { - moovMgr->AddChildBox ( udtaRef, qtItem->id, &fullValue[0], qtTotalSize ); - } - - } - - } - -} // TradQT_Manager::UpdateChangedBoxes - -// ================================================================================================= diff --git a/source/XMPFiles/FormatSupport/QuickTime_Support.hpp b/source/XMPFiles/FormatSupport/QuickTime_Support.hpp deleted file mode 100644 index 160dfc8..0000000 --- a/source/XMPFiles/FormatSupport/QuickTime_Support.hpp +++ /dev/null @@ -1,103 +0,0 @@ -#ifndef __QuickTime_Support_hpp__ -#define __QuickTime_Support_hpp__ 1 - -// ================================================================================================= -// ADOBE SYSTEMS INCORPORATED -// Copyright 2009 Adobe Systems Incorporated -// 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 "XMP_Environment.h" // ! This must be the first include. - -#include <string> -#include <vector> -#include <map> - -#include "XMPFiles_Impl.hpp" -#include "LargeFileAccess.hpp" -#include "ISOBaseMedia_Support.hpp" -#include "MOOV_Support.hpp" - -// ================================================================================================= -// ================================================================================================= - -// ================================================================================================= -// TradQT_Manager -// ============== - -// Support for selected traditional QuickTime metadata items. The supported items are the children -// of the 'moov'/'udta' box whose type begins with 0xA9, a MacRoman copyright symbol. Each of these -// is a box whose contents are a sequence of "mini boxes" analogous to XMP AltText arrays. Each mini -// box has a 16-bit size, 16-bit language code, and text. The language code values are the old -// Macintosh Script Manager langXyz codes, the text encoding is implicit, see Mac Script.h. - -enum { // List of recognized items from the QuickTime 'moov'/'udta' box. - // These items are defined by Adobe. - kQTilst_Reel = 0xA952454CUL, // '©REL' - kQTilst_Timecode = 0xA954494DUL, // '©TIM' - kQTilst_TimeScale = 0xA9545343UL, // '©TSC' - kQTilst_TimeSize = 0xA954535AUL // '©TSZ' -}; - -enum { - kNoMacLang = 0xFFFF, - kNoMacScript = 0xFFFF -}; - -extern bool ConvertToMacLang ( const std::string & utf8Value, XMP_Uns16 macLang, std::string * macValue ); -extern bool ConvertFromMacLang ( const std::string & macValue, XMP_Uns16 macLang, std::string * utf8Value ); - -class TradQT_Manager { -public: - - TradQT_Manager() : changed(false) {}; - - bool ParseCachedBoxes ( const MOOV_Manager & moovMgr ); - - bool ImportSimpleXMP ( XMP_Uns32 id, SXMPMeta * xmp, XMP_StringPtr ns, XMP_StringPtr prop ) const; - bool ImportLangAltXMP ( XMP_Uns32 id, SXMPMeta * xmp, XMP_StringPtr ns, XMP_StringPtr langArray ) const; - - void ExportSimpleXMP ( XMP_Uns32 id, const SXMPMeta & xmp, XMP_StringPtr ns, XMP_StringPtr prop, - bool createWithZeroLang = false ); - void ExportLangAltXMP ( XMP_Uns32 id, const SXMPMeta & xmp, XMP_StringPtr ns, XMP_StringPtr langArray ); - - bool IsChanged() const { return this->changed; }; - - void UpdateChangedBoxes ( MOOV_Manager * moovMgr ); - -private: - - struct ValueInfo { - bool marked; - XMP_Uns16 macLang; - XMP_StringPtr xmpLang; // ! Only set if macLang is known, i.e. the value can be converted. - std::string macValue; - ValueInfo() : marked(false), macLang(kNoMacLang), xmpLang("") {}; - }; - typedef std::vector<ValueInfo> ValueVector; - typedef ValueVector::iterator ValueInfoPos; - typedef ValueVector::const_iterator ValueInfoCPos; - - struct ParsedBoxInfo { - XMP_Uns32 id; - ValueVector values; - bool changed; - ParsedBoxInfo() : id(0), changed(false) {}; - ParsedBoxInfo ( XMP_Uns32 _id ) : id(_id), changed(false) {}; - }; - - typedef std::map < XMP_Uns32, ParsedBoxInfo > InfoMap; // Metadata item kind and content info. - typedef InfoMap::iterator InfoMapPos; - typedef InfoMap::const_iterator InfoMapCPos; - - InfoMap parsedBoxes; - bool changed; - - bool ImportLangItem ( const ValueInfo & qtItem, SXMPMeta * xmp, XMP_StringPtr ns, XMP_StringPtr langArray ) const; - -}; // TradQT_Manager - -#endif // __QuickTime_Support_hpp__ diff --git a/source/XMPFiles/FormatSupport/RIFF.cpp b/source/XMPFiles/FormatSupport/RIFF.cpp deleted file mode 100644 index 3992edd..0000000 --- a/source/XMPFiles/FormatSupport/RIFF.cpp +++ /dev/null @@ -1,879 +0,0 @@ -// ================================================================================================= -// ADOBE SYSTEMS INCORPORATED -// Copyright 2009 Adobe Systems Incorporated -// 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. -// ================================================================================================= - -// must have access to handler class fields... -#include "RIFF.hpp" -#include "RIFF_Support.hpp" -#include "RIFF_Handler.hpp" - -using namespace RIFF; - -namespace RIFF { - -// GENERAL STATIC FUNCTIONS //////////////////////////////////////// - -Chunk* getChunk ( ContainerChunk* parent, RIFF_MetaHandler* handler ) -{ - LFA_FileRef file = handler->parent->fileRef; - XMP_Uns8 level = handler->level; - XMP_Uns32 peek = LFA_PeekUns32_LE( file ); - - if ( level == 0 ) - { - XMP_Validate( peek == kChunk_RIFF, "expected RIFF chunk not found", kXMPErr_BadFileFormat ); - XMP_Enforce( parent == NULL ); - } - else - { - XMP_Validate( peek != kChunk_RIFF, "unexpected RIFF chunk below top-level", kXMPErr_BadFileFormat ); - XMP_Enforce( parent != NULL ); - } - - switch( peek ) - { - case kChunk_RIFF: - return new ContainerChunk( parent, handler ); - case kChunk_LIST: - { - if ( level != 1 ) break; // only care on this level - - // look further (beyond 4+4 = beyond id+size) to check on relevance - LFA_Seek( file, 8, SEEK_CUR ); - XMP_Uns32 containerType = LFA_PeekUns32_LE( file ); - LFA_Seek( file, -8, SEEK_CUR ); - - bool isRelevantList = ( containerType== kType_INFO || containerType == kType_Tdat ); - if ( !isRelevantList ) break; - - return new ContainerChunk( parent, handler ); - } - case kChunk_XMP: - if ( level != 1 ) break; // ignore on inappropriate levels (might be compound metadata?) - return new XMPChunk( parent, handler ); - case kChunk_DISP: - { - if ( level != 1 ) break; // only care on this level - // peek even further to see if type is 0x001 and size is reasonable - LFA_Seek( file , 4, SEEK_CUR ); // jump DISP - XMP_Uns32 dispSize = LFA_ReadUns32_LE( file ); - XMP_Uns32 dispType = LFA_ReadUns32_LE( file ); - LFA_Seek( file , -12, SEEK_CUR); // rewind, be in front of chunkID again - - // only take as a relevant disp if both criteria met, - // otherwise treat as generic chunk! - if ( (dispType == 0x0001) && ( dispSize < 256 * 1024 ) ) - { - ValueChunk* r = new ValueChunk( parent, handler ); - handler->dispChunk = r; - return r; - } - break; // treat as irrelevant (non-0x1) DISP chunks as generic chunk - } - case kChunk_bext: - { - if ( level != 1 ) break; // only care on this level - // store for now in a value chunk - ValueChunk* r = new ValueChunk( parent, handler ); - handler->bextChunk = r; - return r; - } - case kChunk_PrmL: - { - if ( level != 1 ) break; // only care on this level - ValueChunk* r = new ValueChunk( parent, handler ); - handler->prmlChunk = r; - return r; - } - case kChunk_Cr8r: - { - if ( level != 1 ) break; // only care on this level - ValueChunk* r = new ValueChunk( parent, handler ); - handler->cr8rChunk = r; - return r; - } - case kChunk_JUNQ: - case kChunk_JUNK: - { - JunkChunk* r = new JunkChunk( parent, handler ); - return r; - } - } - // this "default:" section must be ouside switch bracket, to be - // reachable by all those break statements above: - - - // digest 'valuable' container chunks: LIST:INFO, LIST:Tdat - bool insideRelevantList = ( level==2 && parent->id == kChunk_LIST - && ( parent->containerType== kType_INFO || parent->containerType == kType_Tdat )); - - if ( insideRelevantList ) - { - ValueChunk* r = new ValueChunk( parent, handler ); - return r; - } - - // general chunk of no interest, treat as unknown blob - return new Chunk( parent, handler, true, chunk_GENERAL ); -} - -// BASE CLASS CHUNK /////////////////////////////////////////////// -// ad hoc creation -Chunk::Chunk( ContainerChunk* parent, ChunkType c, XMP_Uns32 id ) -{ - this->chunkType = c; // base class assumption - this->parent = parent; - this->id = id; - this->oldSize = 0; - this->newSize = 8; - this->oldPos = 0; // inevitable for ad-hoc - this->needSizeFix = false; - - // good parenting for latter destruction - if ( this->parent != NULL ) - { - this->parent->children.push_back( this ); - if( this->chunkType == chunk_VALUE ) - this->parent->childmap.insert( make_pair( this->id, (ValueChunk*) this ) ); - } -} - -// parsing creation -Chunk::Chunk( ContainerChunk* parent, RIFF_MetaHandler* handler, bool skip, ChunkType c ) -{ - chunkType = c; // base class assumption - this->parent = parent; - this->oldSize = 0; - this->hasChange = false; // [2414649] valid assumption at creation time - - LFA_FileRef file = handler->parent->fileRef; - - this->oldPos = LFA_Tell( file ); - this->id = LFA_ReadUns32_LE( file ); - this->oldSize = LFA_ReadUns32_LE( file ) + 8; - - // Make sure the size is within expected bounds. - XMP_Int64 chunkEnd = this->oldPos + this->oldSize; - XMP_Int64 chunkLimit = handler->oldFileSize; - if ( parent != 0 ) chunkLimit = parent->oldPos + parent->oldSize; - if ( chunkEnd > chunkLimit ) { - bool isUpdate = XMP_OptionIsSet ( handler->parent->openFlags, kXMPFiles_OpenForUpdate ); - bool repairFile = XMP_OptionIsSet ( handler->parent->openFlags, kXMPFiles_OpenRepairFile ); - if ( (! isUpdate) || (repairFile && (parent == 0)) ) { - this->oldSize = chunkLimit - this->oldPos; - } else { - XMP_Throw ( "Bad RIFF chunk size", kXMPErr_BadFileFormat ); - } - } - - this->newSize = this->oldSize; - this->needSizeFix = false; - - if ( skip ) - { - bool ok; - LFA_Seek( file, this->oldSize - 8 , SEEK_CUR, &ok ); - XMP_Validate( ok , "skipped beyond end of file (truncated file?)", kXMPErr_BadFileFormat ); - } - - // "good parenting", essential for latter destruction. - if ( this->parent != NULL ) - { - this->parent->children.push_back( this ); - if( this->chunkType == chunk_VALUE ) - this->parent->childmap.insert( make_pair( this->id, (ValueChunk*) this ) ); - } -} - -void Chunk::changesAndSize( RIFF_MetaHandler* handler ) -{ - // only unknown chunks should reach this method, - // all others must reach overloads, hence little to do here: - hasChange = false; // unknown chunk ==> no change, naturally - this->newSize = this->oldSize; -} - -std::string Chunk::toString(XMP_Uns8 level ) -{ - char buffer[256]; - snprintf( buffer, 255, "%.4s -- " - "oldSize: 0x%.8llX, " - "newSize: 0x%.8llX, " - "oldPos: 0x%.8llX\n", - (char*)(&this->id), this->oldSize, this->newSize, this->oldPos ); - return std::string(buffer); -} - -void Chunk::write( RIFF_MetaHandler* handler, LFA_FileRef file , bool isMainChunk ) -{ - throw new XMP_Error(kXMPErr_InternalFailure, "Chunk::write never to be called for unknown chunks."); -} - -Chunk::~Chunk() -{ - //nothing -} - -// CONTAINER CHUNK ///////////////////////////////////////////////// -// a) creation -// [2376832] expectedSize - minimum padding "parking size" to use, if not available append to end -ContainerChunk::ContainerChunk( ContainerChunk* parent, XMP_Uns32 id, XMP_Uns32 containerType ) : Chunk( NULL /* !! */, chunk_CONTAINER, id ) -{ - // accept no unparented ConatinerChunks - XMP_Enforce( parent != NULL ); - - this->containerType = containerType; - this->newSize = 12; - this->parent = parent; - - chunkVect* siblings = &parent->children; - - // add at end. ( oldSize==0 will flag optimization later in the process) - siblings->push_back( this ); -} - -// b) parsing -ContainerChunk::ContainerChunk( ContainerChunk* parent, RIFF_MetaHandler* handler ) : Chunk( parent, handler, false, chunk_CONTAINER ) -{ - bool repairMode = ( 0 != ( handler->parent->openFlags & kXMPFiles_OpenRepairFile )); - - try - { - LFA_FileRef file = handler->parent->fileRef; - XMP_Uns8 level = handler->level; - - // get type of container chunk - this->containerType = LFA_ReadUns32_LE( file ); - - // ensure legality of top-level chunks - if ( level == 0 && handler->riffChunks.size() > 0 ) - { - XMP_Validate( handler->parent->format == kXMP_AVIFile, "only AVI may have multiple top-level chunks", kXMPErr_BadFileFormat ); - XMP_Validate( this->containerType == kType_AVIX, "all chunks beyond main chunk must be type AVIX", kXMPErr_BadFileFormat ); - } - - // has *relevant* subChunks? (there might be e.g. non-INFO LIST chunks we don't care about) - bool hasSubChunks = ( ( this->id == kChunk_RIFF ) || - ( this->id == kChunk_LIST && this->containerType == kType_INFO ) || - ( this->id == kChunk_LIST && this->containerType == kType_Tdat ) - ); - XMP_Int64 endOfChunk = this->oldPos + this->oldSize; - - // this statement catches beyond-EoF-offsets on any level - // exception: level 0, tolerate if in repairMode - if ( (level == 0) && repairMode && (endOfChunk > handler->oldFileSize) ) - { - endOfChunk = handler->oldFileSize; // assign actual file size - this->oldSize = endOfChunk - this->oldPos; //reversely calculate correct oldSize - } - - XMP_Validate( endOfChunk <= handler->oldFileSize, "offset beyond EoF", kXMPErr_BadFileFormat ); - - Chunk* curChild = 0; - if ( hasSubChunks ) - { - handler->level++; - while ( LFA_Tell( file ) < endOfChunk ) - { - curChild = RIFF::getChunk( this, handler ); - - // digest pad byte - no value validation (0), since some 3rd party files have non-0-padding. - if ( LFA_Tell(file) % 2 == 1 ) - { - // [1521093] tolerate missing pad byte at very end of file: - XMP_Uns8 pad; - LFA_Read ( file, &pad, 1 ); // Read the pad, tolerate being at EOF. - - } - - // within relevant LISTs, relentlesly delete junk chunks (create a single one - // at end as part of updateAndChanges() - if ( (containerType== kType_INFO || containerType == kType_Tdat) - && ( curChild->chunkType == chunk_JUNK ) ) - { - this->children.pop_back(); - delete curChild; - } // for other chunks: join neighouring Junk chunks into one - else if ( (curChild->chunkType == chunk_JUNK) && ( this->children.size() >= 2 ) ) - { - // nb: if there are e.g 2 chunks, then last one is at(1), prev one at(0) ==> '-2' - Chunk* prevChunk = this->children.at( this->children.size() - 2 ); - if ( prevChunk->chunkType == chunk_JUNK ) - { - // stack up size to prior chunk - prevChunk->oldSize += curChild->oldSize; - prevChunk->newSize += curChild->newSize; - XMP_Enforce( prevChunk->oldSize == prevChunk->newSize ); - // destroy current chunk - this->children.pop_back(); - delete curChild; - } - } - } - handler->level--; - XMP_Validate( LFA_Tell( file ) == endOfChunk, "subchunks exceed outer chunk size", kXMPErr_BadFileFormat ); - - // pointers for later legacy processing - if ( level==1 && this->id==kChunk_LIST && this->containerType == kType_INFO ) - handler->listInfoChunk = this; - if ( level==1 && this->id==kChunk_LIST && this->containerType == kType_Tdat ) - handler->listTdatChunk = this; - } - else // skip non-interest container chunk - { - bool ok; - LFA_Seek( file, this->oldSize - 8 - 4, SEEK_CUR, &ok ); - XMP_Validate( ok , "skipped beyond end of file 2 (truncated file?)", kXMPErr_BadFileFormat ); - } // if - else - - } // try - catch (XMP_Error& e) { - this->release(); // free resources - if ( this->parent != 0) - this->parent->children.pop_back(); // hereby taken care of, so removing myself... - - throw e; // re-throw - } -} - -void ContainerChunk::changesAndSize( RIFF_MetaHandler* handler ) -{ - - // Walk the container subtree adjusting the children that have size changes. The only containers - // are RIFF and LIST chunks, they are treated differently. - // - // LISTs get recomposed as a whole. Existing JUNK children of a LIST are removed, existing real - // children are left in order with their new size, new children have already been appended. The - // LIST as a whole gets a new size that is the sum of the final children. - // - // Special rules apply to various children of a RIFF container. FIrst, adjacent JUNK children - // are combined, this simplifies maximal reuse. The children are recursively adjusted in order - // to get their final size. - // - // Try to determine the final placement of each RIFF child using general rules: - // - if the size is unchanged: leave at current location - // - if the chunk is at the end of the last RIFF chunk and grows: leave at current location - // - if there is enough following JUNK: add part of the JUNK, adjust remaining JUNK size - // - if it shrinks by 9 bytes or more: carve off trailing JUNK - // - try to find adequate JUNK in the current parent - // - // Use child-specific rules as a last resort: - // - if it is LIST:INFO: delete it, must be in first RIFF chunk - // - for others: move to end of last RIFF chunk, make old space JUNK - - // ! Don't create any junk chunks of exactly 8 bytes, just a header and no content. That has a - // ! size field of zero, which hits a crashing bug in some versions of Windows Media Player. - - bool isRIFFContainer = (this->id == kChunk_RIFF); - bool isLISTContainer = (this->id == kChunk_LIST); - XMP_Enforce ( isRIFFContainer | isLISTContainer ); - - XMP_Index childIndex; // Could be local to the loops, this simplifies debuging. Need a signed type! - Chunk * currChild; - - if ( this->children.empty() ) { - if ( isRIFFContainer) { - this->newSize = 12; // Keep a minimal size container. - } else { - this->newSize = 0; // Will get removed from parent in outer call. - } - this->hasChange = true; - return; // Nothing more to do without children. - } - - // Collapse adjacent RIFF junk children, remove all LIST junk children. Work back to front to - // simplify the effect of .erase() on the loop. Purposely ignore the first chunk. - - for ( childIndex = (XMP_Index)this->children.size() - 1; childIndex > 0; --childIndex ) { - - currChild = this->children[childIndex]; - if ( currChild->chunkType != chunk_JUNK ) continue; - - if ( isRIFFContainer ) { - Chunk * prevChild = this->children[childIndex-1]; - if ( prevChild->chunkType != chunk_JUNK ) continue; - prevChild->oldSize += currChild->oldSize; - prevChild->newSize += currChild->newSize; - prevChild->hasChange = true; - } - - this->children.erase ( this->children.begin() + childIndex ); - delete currChild; - this->hasChange = true; - - } - - // Process the children of RIFF and LIST containers to get their final size. Remove empty - // children. Work back to front to simplify the effect of .erase() on the loop. Do not ignore - // the first chunk. - - for ( childIndex = (XMP_Index)this->children.size() - 1; childIndex >= 0; --childIndex ) { - - currChild = this->children[childIndex]; - - ++handler->level; - currChild->changesAndSize ( handler ); - --handler->level; - - if ( (currChild->newSize == 8) || (currChild->newSize == 0) ) { // ! The newSIze is supposed to include the header. - this->children.erase ( this->children.begin() + childIndex ); - delete currChild; - this->hasChange = true; - } else { - this->hasChange |= currChild->hasChange; - currChild->needSizeFix = (currChild->newSize != currChild->oldSize); - if ( currChild->needSizeFix && (currChild->newSize > currChild->oldSize) && - (this == handler->lastChunk) && (childIndex+1 == (XMP_Index)this->children.size()) ) { - // Let an existing last-in-file chunk grow in-place. Shrinking is conceptually OK, - // but complicates later sanity check that the main AVI chunk is not OK to append - // other chunks later. Ignore new chunks, they might reuse junk space. - if ( currChild->oldSize != 0 ) currChild->needSizeFix = false; - } - } - - } - - // Go through the children of a RIFF container, adjusting the placement as necessary. In brief, - // things can only grow at the end of the last RIFF chunk, and non-junk chunks can't be shifted. - - if ( isRIFFContainer ) { - - for ( childIndex = 0; childIndex < (XMP_Index)this->children.size(); ++childIndex ) { - - currChild = this->children[childIndex]; - if ( ! currChild->needSizeFix ) continue; - currChild->needSizeFix = false; - - XMP_Int64 sizeDiff = currChild->newSize - currChild->oldSize; // Positive for growth. - XMP_Uns8 padSize = (currChild->newSize & 1); // Need a pad for odd size. - - // See if the following chunk is junk that can be utilized. - - Chunk * nextChild = 0; - if ( childIndex+1 < (XMP_Index)this->children.size() ) nextChild = this->children[childIndex+1]; - - if ( (nextChild != 0) && (nextChild->chunkType == chunk_JUNK) ) { - if ( nextChild->newSize >= (9 + sizeDiff + padSize) ) { - - // Incorporate part of the trailing junk, or make the trailing junk grow. - nextChild->newSize -= sizeDiff; - nextChild->newSize -= padSize; - nextChild->hasChange = true; - continue; - - } else if ( nextChild->newSize == (sizeDiff + padSize) ) { - - // Incorporate all of the trailing junk. - this->children.erase ( this->children.begin() + childIndex + 1 ); - delete nextChild; - continue; - - } - } - - // See if the chunk shrinks enough to turn the leftover space into junk. - - if ( (sizeDiff + padSize) <= -9 ) { - this->children.insert ( (this->children.begin() + childIndex + 1), new JunkChunk ( NULL, ((-sizeDiff) - padSize) ) ); - continue; - } - - // Look through the parent for a usable span of junk. - - XMP_Index junkIndex; - Chunk * junkChunk = 0; - for ( junkIndex = 0; junkIndex < (XMP_Index)this->children.size(); ++junkIndex ) { - junkChunk = this->children[junkIndex]; - if ( junkChunk->chunkType != chunk_JUNK ) continue; - if ( (junkChunk->newSize >= (9 + currChild->newSize + padSize)) || - (junkChunk->newSize == (currChild->newSize + padSize)) ) break; - } - - if ( junkIndex < (XMP_Index)this->children.size() ) { - - // Use part or all of the junk for the relocated chunk, replace the old space with junk. - - if ( junkChunk->newSize == (currChild->newSize + padSize) ) { - - // The found junk is an exact fit. - this->children[junkIndex] = currChild; - delete junkChunk; - - } else { - - // The found junk has excess space. Insert the moving chunk and shrink the junk. - XMP_Assert ( junkChunk->newSize >= (9 + currChild->newSize + padSize) ); - junkChunk->newSize -= (currChild->newSize + padSize); - junkChunk->hasChange = true; - this->children.insert ( (this->children.begin() + junkIndex), currChild ); - if ( junkIndex < childIndex ) ++childIndex; // The insertion moved the current child. - - } - - if ( currChild->oldSize != 0 ) { - this->children[childIndex] = new JunkChunk ( 0, currChild->oldSize ); // Replace the old space with junk. - } else { - this->children.erase ( this->children.begin() + childIndex ); // Remove the newly created chunk's old location. - --childIndex; // Make the next loop iteration not skip a chunk. - } - - continue; - - } - - // If this is a LIST:INFO chunk not in the last of multiple RIFF chunks, then give up - // and replace it with oldSize junk. Preserve the first RIFF chunk's original size. - - bool isListInfo = (currChild->id == kChunk_LIST) && (currChild->chunkType == chunk_CONTAINER) && - (((ContainerChunk*)currChild)->containerType == kType_INFO); - - if ( isListInfo && (handler->riffChunks.size() > 1) && - (this->id == kChunk_RIFF) && (this != handler->lastChunk) ) { - - if ( currChild->oldSize != 0 ) { - this->children[childIndex] = new JunkChunk ( 0, currChild->oldSize ); - } else { - this->children.erase ( this->children.begin() + childIndex ); - --childIndex; // Make the next loop iteration not skip a chunk. - } - - delete currChild; - continue; - - } - - // Move the chunk to the end of the last RIFF chunk and make the old space junk. - - if ( (this == handler->lastChunk) && (childIndex+1 == (XMP_Index)this->children.size()) ) continue; // Already last. - - handler->lastChunk->children.push_back( currChild ); - if ( currChild->oldSize != 0 ) { - this->children[childIndex] = new JunkChunk ( 0, currChild->oldSize ); // Replace the old space with junk. - } else { - this->children.erase ( this->children.begin() + childIndex ); // Remove the newly created chunk's old location. - --childIndex; // Make the next loop iteration not skip a chunk. - } - - } - - } - - // Compute the finished container's new size (for both RIFF and LIST). - - this->newSize = 12; // Start with standard container header. - for ( childIndex = 0; childIndex < (XMP_Index)this->children.size(); ++childIndex ) { - currChild = this->children[childIndex]; - this->newSize += currChild->newSize; - this->newSize += (this->newSize & 1); // Round up if odd. - } - - XMP_Validate ( (this->newSize <= 0xFFFFFFFFLL), "No single chunk may be above 4 GB", kXMPErr_Unimplemented ); - -} - -std::string ContainerChunk::toString(XMP_Uns8 level ) -{ - XMP_Int64 offset= 12; // compute offsets, just for informational purposes - // (actually only correct for first chunk) - - char buffer[256]; - snprintf( buffer, 255, "%.4s:%.4s, " - "oldSize: 0x%8llX, " - "newSize: 0x%.8llX, " - "oldPos: 0x%.8llX\n", - (char*)(&this->id), (char*)(&this->containerType), this->oldSize, this->newSize, this->oldPos ); - - std::string r(buffer); - chunkVectIter iter; - for( iter = this->children.begin(); iter != this->children.end(); iter++ ) - { - char buffer[256]; - snprintf( buffer, 250, "offset 0x%.8llX", offset ); - r += std::string ( level*4, ' ' ) + std::string( buffer ) + ":" + (*iter)->toString( level + 1 ); - offset += (*iter)->newSize; - if ( offset % 2 == 1 ) - offset++; - } - return std::string(r); -} - -void ContainerChunk::write( RIFF_MetaHandler* handler, LFA_FileRef file, bool isMainChunk ) -{ - bool ok; - if ( isMainChunk ) - LFA_Rewind( file ); - - // enforce even position - XMP_Int64 chunkStart = LFA_Tell(file); - XMP_Int64 chunkEnd = chunkStart + this->newSize; - XMP_Enforce( chunkStart % 2 == 0 ); - chunkVect *rc = &this->children; - - // [2473303] have to write back-to-front to avoid stomp-on-feet - XMP_Int64 childStart = chunkEnd; - for ( XMP_Int32 chunkNo = (XMP_Int32)(rc->size() -1); chunkNo >= 0; chunkNo-- ) - { - Chunk* cur = rc->at(chunkNo); - - // pad byte first - if ( cur->newSize % 2 == 1 ) - { - childStart--; - LFA_Seek( file, childStart, SEEK_SET ); - LFA_WriteUns8( file, 0 ); - } - - // then contents - childStart-= cur->newSize; - LFA_Seek( file, childStart, SEEK_SET ); - switch ( cur->chunkType ) - { - case chunk_GENERAL: //COULDDO enfore no change, since not write-out-able - if ( cur->oldPos != childStart ) - LFA_Move( file, cur->oldPos, file, childStart, cur->oldSize, 0, 0 ); - break; - default: - cur->write( handler, file, false ); - break; - } // switch - - } // for - XMP_Enforce ( chunkStart + 12 == childStart); - LFA_Seek( file, chunkStart, SEEK_SET, &ok ); - - LFA_WriteUns32_LE( file, this->id ); - LFA_WriteUns32_LE( file, (XMP_Uns32) this->newSize - 8 ); // validated in changesAndSize() above - LFA_WriteUns32_LE( file, this->containerType ); - -} - -void ContainerChunk::release() -{ - // free subchunks - Chunk* curChunk; - while( ! this->children.empty() ) - { - curChunk = this->children.back(); - delete curChunk; - this->children.pop_back(); - } -} - -ContainerChunk::~ContainerChunk() -{ - this->release(); // free resources -} - -// XMP CHUNK /////////////////////////////////////////////// -// a) create - -// a) creation -XMPChunk::XMPChunk( ContainerChunk* parent ) : Chunk( parent, chunk_XMP , kChunk_XMP ) -{ - // nothing -} - -// b) parse -XMPChunk::XMPChunk( ContainerChunk* parent, RIFF_MetaHandler* handler ) : Chunk( parent, handler, false, chunk_XMP ) -{ - chunkType = chunk_XMP; - LFA_FileRef file = handler->parent->fileRef; - XMP_Uns8 level = handler->level; - - handler->packetInfo.offset = this->oldPos + 8; - handler->packetInfo.length = (XMP_Int32) this->oldSize - 8; - - handler->xmpPacket.reserve ( handler->packetInfo.length ); - handler->xmpPacket.assign ( handler->packetInfo.length, ' ' ); - LFA_Read ( file, (void*)handler->xmpPacket.data(), handler->packetInfo.length, kLFA_RequireAll ); - - handler->containsXMP = true; // last, after all possible failure - - // pointer for later processing - handler->xmpChunk = this; -} - -void XMPChunk::changesAndSize( RIFF_MetaHandler* handler ) -{ - XMP_Enforce( &handler->xmpPacket != 0 ); - XMP_Enforce( handler->xmpPacket.size() > 0 ); - this->newSize = 8 + handler->xmpPacket.size(); - - XMP_Validate( this->newSize <= 0xFFFFFFFFLL, "no single chunk may be above 4 GB", kXMPErr_InternalFailure ); - - // a complete no-change would have been caught in XMPFiles common code anyway - this->hasChange = true; -} - -void XMPChunk::write( RIFF_MetaHandler* handler, LFA_FileRef file, bool isMainChunk ) -{ - LFA_WriteUns32_LE( file, kChunk_XMP ); - LFA_WriteUns32_LE( file, (XMP_Uns32) this->newSize - 8 ); // validated in changesAndSize() above - LFA_Write( file, handler->xmpPacket.data(), (XMP_Int32)handler->xmpPacket.size() ); -} - -// Value CHUNK /////////////////////////////////////////////// -// a) creation -ValueChunk::ValueChunk( ContainerChunk* parent, std::string value, XMP_Uns32 id ) : Chunk( parent, chunk_VALUE, id ) -{ - this->oldValue = std::string(); - this->SetValue( value ); -} - -// b) parsing -ValueChunk::ValueChunk( ContainerChunk* parent, RIFF_MetaHandler* handler ) : Chunk( parent, handler, false, chunk_VALUE ) -{ - // set value: ----------------- - LFA_FileRef file = handler->parent->fileRef; - XMP_Uns8 level = handler->level; - - // unless changed through reconciliation, assume for now. - // IMPORTANT to stay true to the original (no \0 cleanup or similar) - // since unknown value chunks might not be fully understood, - // hence must be precisely preserved !!! - - XMP_Int32 length = (XMP_Int32) this->oldSize - 8; - this->oldValue.reserve( length ); - this->oldValue.assign( length + 1, '\0' ); - LFA_Read ( file, (void*)this->oldValue.data(), length, kLFA_RequireAll ); - - this->newValue = this->oldValue; - this->newSize = this->oldSize; -} - -void ValueChunk::SetValue( std::string value, bool optionalNUL /* = false */ ) -{ - this->newValue.assign( value ); - if ( (! optionalNUL) || ((value.size() & 1) == 1) ) { - // ! The NUL should be optional in WAV to avoid a parsing bug in Audition 3 - can't handle implicit pad byte. - this->newValue.append( 1, '\0' ); // append zero termination as explicit part of string - } - this->newSize = this->newValue.size() + 8; -} - -void ValueChunk::changesAndSize( RIFF_MetaHandler* handler ) -{ - // Don't simply assign to this->hasChange, it might already be true. - if ( this->newValue.size() != this->oldValue.size() ) { - this->hasChange = true; - } else if ( strncmp ( this->oldValue.c_str(), this->newValue.c_str(), this->newValue.size() ) != 0 ) { - this->hasChange = true; - } -} - -void ValueChunk::write( RIFF_MetaHandler* handler, LFA_FileRef file, bool isMainChunk ) -{ - LFA_WriteUns32_LE( file, this->id ); - LFA_WriteUns32_LE( file, (XMP_Uns32)this->newSize - 8 ); - LFA_Write( file, this->newValue.data() , (XMP_Int32)this->newSize - 8 ); -} - -/* remove value chunk if existing. - return true if it was existing. */ -bool ContainerChunk::removeValue( XMP_Uns32 id ) -{ - valueMap* cm = &this->childmap; - valueMapIter iter = cm->find( id ); - - if( iter == cm->end() ) - return false; //not found - - ValueChunk* propChunk = iter->second; - - // remove from vector (difficult) - chunkVect* cv = &this->children; - chunkVectIter cvIter; - for (cvIter = cv->begin(); cvIter != cv->end(); ++cvIter ) - { - if ( (*cvIter)->id == id ) - break; // found! - } - XMP_Validate( cvIter != cv->end(), "property not found in children vector", kXMPErr_InternalFailure ); - cv->erase( cvIter ); - - // remove from map (easy) - cm->erase( iter ); - - delete propChunk; - return true; // found and removed -} - -/* returns iterator to (first) occurence of this chunk. - iterator to the end of the map if chunk pointer is not found */ -chunkVectIter ContainerChunk::getChild( Chunk* needle ) -{ - chunkVectIter iter; - for( iter = this->children.begin(); iter != this->children.end(); iter++ ) - { - Chunk* temp1 = *iter; - Chunk* temp2 = needle; - if ( (*iter) == needle ) return iter; - } - return this->children.end(); -} - -/* replaces a chunk by a JUNK chunk. - Also frees memory of prior chunk. */ -void ContainerChunk::replaceChildWithJunk( Chunk* child, bool deleteChild ) -{ - chunkVectIter iter = getChild( child ); - if ( iter == this->children.end() ) { - throw new XMP_Error(kXMPErr_InternalFailure, "replaceChildWithJunk: childChunk not found."); - } - - *iter = new JunkChunk ( NULL, child->oldSize ); - if ( deleteChild ) delete child; - - this->hasChange = true; -} - -// JunkChunk /////////////////////////////////////////////////// -// a) creation -JunkChunk::JunkChunk( ContainerChunk* parent, XMP_Int64 size ) : Chunk( parent, chunk_JUNK, kChunk_JUNK ) -{ - XMP_Assert( size >= 8 ); - this->oldSize = size; - this->newSize = size; - this->hasChange = true; -} - -// b) parsing -JunkChunk::JunkChunk( ContainerChunk* parent, RIFF_MetaHandler* handler ) : Chunk( parent, handler, true, chunk_JUNK ) -{ - chunkType = chunk_JUNK; -} - -void JunkChunk::changesAndSize( RIFF_MetaHandler* handler ) -{ - this->newSize = this->oldSize; // optimization at a later stage - XMP_Validate( this->newSize <= 0xFFFFFFFFLL, "no single chunk may be above 4 GB", kXMPErr_InternalFailure ); - if ( this->id == kChunk_JUNQ ) this->hasChange = true; // Force ID change to JUNK. -} - -// zeroBuffer, etc to write out empty native padding -const static XMP_Uns32 kZeroBufferSize64K = 64 * 1024; -static XMP_Uns8 kZeroes64K [ kZeroBufferSize64K ]; // C semantics guarantee zero initialization. - -void JunkChunk::write( RIFF_MetaHandler* handler, LFA_FileRef file, bool isMainChunk ) -{ - LFA_WriteUns32_LE( file, kChunk_JUNK ); // write JUNK, never JUNQ - XMP_Enforce( this->newSize < 0xFFFFFFFF ); - XMP_Enforce( this->newSize >= 8 ); // minimum size of any chunk - XMP_Uns32 innerSize = (XMP_Uns32)this->newSize - 8; - LFA_WriteUns32_LE( file, innerSize ); - - // write out in 64K chunks - while ( innerSize > kZeroBufferSize64K ) - { - LFA_Write( file, kZeroes64K , kZeroBufferSize64K ); - innerSize -= kZeroBufferSize64K; - } - LFA_Write( file, kZeroes64K , innerSize ); -} - -} // namespace RIFF diff --git a/source/XMPFiles/FormatSupport/RIFF.hpp b/source/XMPFiles/FormatSupport/RIFF.hpp deleted file mode 100644 index 34b2100..0000000 --- a/source/XMPFiles/FormatSupport/RIFF.hpp +++ /dev/null @@ -1,316 +0,0 @@ -#ifndef __RIFF_hpp__ -#define __RIFF_hpp__ 1 - -// ================================================================================================= -// ADOBE SYSTEMS INCORPORATED -// Copyright 2009 Adobe Systems Incorporated -// 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 "XMP_Environment.h" // ! This must be the first include. -#include <vector> -#include <map> -#include "XMPFiles_Impl.hpp" - -// ahead declaration: -class RIFF_MetaHandler; - -namespace RIFF { - - enum ChunkType { - chunk_GENERAL, //unknown or not relevant - chunk_CONTAINER, - chunk_XMP, - chunk_VALUE, - chunk_JUNK, - NO_CHUNK // used as precessor to first chunk, etc. - }; - - // ahead declarations - class Chunk; - class ContainerChunk; - class ValueChunk; - class XMPChunk; - - // (scope: only used in RIFF_Support and RIFF_Handler.cpp - // ==> no need to overspecify with lengthy names ) - - typedef std::vector<Chunk*> chunkVect; // coulddo: narrow down toValueChunk (could give trouble with JUNK though) - typedef chunkVect::iterator chunkVectIter; // or refactor ?? - - typedef std::vector<ContainerChunk*> containerVect; - typedef containerVect::iterator containerVectIter; - - typedef std::map<XMP_Uns32,ValueChunk*> valueMap; - typedef valueMap::iterator valueMapIter; - - - // format chunks+types - const XMP_Uns32 kChunk_RIFF = 0x46464952; - const XMP_Uns32 kType_AVI_ = 0x20495641; - const XMP_Uns32 kType_AVIX = 0x58495641; - const XMP_Uns32 kType_WAVE = 0x45564157; - - const XMP_Uns32 kChunk_JUNK = 0x4B4E554A; - const XMP_Uns32 kChunk_JUNQ = 0x514E554A; - - // other container chunks - const XMP_Uns32 kChunk_LIST = 0x5453494C; - const XMP_Uns32 kType_INFO = 0x4F464E49; - const XMP_Uns32 kType_Tdat = 0x74616454; - - // other relevant chunks - const XMP_Uns32 kChunk_XMP = 0x584D505F; // "_PMX" - - // relevant for Index Correction - // LIST: - const XMP_Uns32 kType_hdrl = 0x6C726468; - const XMP_Uns32 kType_strl = 0x6C727473; - const XMP_Uns32 kChunk_indx = 0x78646E69; - const XMP_Uns32 kChunk_ixXX = 0x58587869; - const XMP_Uns32 kType_movi = 0x69766F6D; - - //should occur only in AVI - const XMP_Uns32 kChunk_Cr8r = 0x72387243; - const XMP_Uns32 kChunk_PrmL = 0x4C6D7250; - - //should occur only in WAV - const XMP_Uns32 kChunk_DISP = 0x50534944; - const XMP_Uns32 kChunk_bext = 0x74786562; - - // LIST/INFO constants - const XMP_Uns32 kPropChunkIART = 0x54524149; - const XMP_Uns32 kPropChunkICMT = 0x544D4349; - const XMP_Uns32 kPropChunkICOP = 0x504F4349; - const XMP_Uns32 kPropChunkICRD = 0x44524349; - const XMP_Uns32 kPropChunkIENG = 0x474E4549; - const XMP_Uns32 kPropChunkIGNR = 0x524E4749; - const XMP_Uns32 kPropChunkINAM = 0x4D414E49; - const XMP_Uns32 kPropChunkISFT = 0x54465349; - const XMP_Uns32 kPropChunkIARL = 0x4C524149; - - const XMP_Uns32 kPropChunkIMED = 0x44454D49; - const XMP_Uns32 kPropChunkISRF = 0x46525349; - const XMP_Uns32 kPropChunkICMS = 0x4C524149; - const XMP_Uns32 kPropChunkIPRD = 0x534D4349; - const XMP_Uns32 kPropChunkISRC = 0x44525049; - const XMP_Uns32 kPropChunkITCH = 0x43525349; - - const XMP_Uns32 kPropChunk_tc_O =0x4F5F6374; - const XMP_Uns32 kPropChunk_tc_A =0x415F6374; - const XMP_Uns32 kPropChunk_rn_O =0x4F5F6E72; - const XMP_Uns32 kPropChunk_rn_A =0x415F6E72; - - /////////////////////////////////////////////////////////////// - - enum PropType { // from a simplified, opinionated legacy angle - prop_SIMPLE, - prop_TIMEVALUE, - prop_LOCALIZED_TEXT, - prop_ARRAYITEM, // ( here: a solitary one) - }; - - struct Mapping { - XMP_Uns32 chunkID; - const char* ns; - const char* prop; - PropType propType; - }; - - // bext Mappings, piece-by-piece: - static Mapping bextDescription = { 0, kXMP_NS_BWF, "description", prop_SIMPLE }; - static Mapping bextOriginator = { 0, kXMP_NS_BWF, "originator", prop_SIMPLE }; - static Mapping bextOriginatorRef = { 0, kXMP_NS_BWF, "originatorReference", prop_SIMPLE }; - static Mapping bextOriginationDate = { 0, kXMP_NS_BWF, "originationDate", prop_SIMPLE }; - static Mapping bextOriginationTime = { 0, kXMP_NS_BWF, "originationTime", prop_SIMPLE }; - static Mapping bextTimeReference = { 0, kXMP_NS_BWF, "timeReference", prop_SIMPLE }; - static Mapping bextVersion = { 0, kXMP_NS_BWF, "version", prop_SIMPLE }; - static Mapping bextUMID = { 0, kXMP_NS_BWF, "umid", prop_SIMPLE }; - static Mapping bextCodingHistory = { 0, kXMP_NS_BWF, "codingHistory", prop_SIMPLE }; - - // LIST:INFO properties - static Mapping listInfoProps[] = { - // reconciliations CS4 and before: - { kPropChunkIART, kXMP_NS_DM, "artist" , prop_SIMPLE }, - { kPropChunkICMT, kXMP_NS_DM, "logComment" , prop_SIMPLE }, - { kPropChunkICOP, kXMP_NS_DC, "rights" , prop_LOCALIZED_TEXT }, - { kPropChunkICRD, kXMP_NS_XMP, "CreateDate" , prop_SIMPLE }, - { kPropChunkIENG, kXMP_NS_DM, "engineer" , prop_SIMPLE }, - { kPropChunkIGNR, kXMP_NS_DM, "genre" , prop_SIMPLE }, - { kPropChunkINAM, kXMP_NS_DC, "title" , prop_LOCALIZED_TEXT }, // ( was wrongly dc:album in pre-CS4) - { kPropChunkISFT, kXMP_NS_XMP, "CreatorTool", prop_SIMPLE }, - - // RIFF/*/LIST/INFO properties, new in CS5, both AVI and WAV - - { kPropChunkIMED, kXMP_NS_DC, "source" , prop_SIMPLE }, - { kPropChunkISRF, kXMP_NS_DC, "type" , prop_ARRAYITEM }, - // TO ENABLE { kPropChunkIARL, kXMP_NS_DC, "subject" , prop_SIMPLE }, // array !! (not x-default language alternative) - //{ kPropChunkICMS, to be decided, "" , prop_SIMPLE }, - //{ kPropChunkIPRD, to be decided, "" , prop_SIMPLE }, - //{ kPropChunkISRC, to be decided, "" , prop_SIMPLE }, - //{ kPropChunkITCH, to be decided, "" , prop_SIMPLE }, - - { 0, 0, 0 } // sentinel - }; - - static Mapping listTdatProps[] = { - // reconciliations CS4 and before: - { kPropChunk_tc_O, kXMP_NS_DM, "startTimecode" , prop_TIMEVALUE }, // special: must end up in dm:timeValue child - { kPropChunk_tc_A, kXMP_NS_DM, "altTimecode" , prop_TIMEVALUE }, // special: must end up in dm:timeValue child - { kPropChunk_rn_O, kXMP_NS_DM, "tapeName" , prop_SIMPLE }, - { kPropChunk_rn_A, kXMP_NS_DM, "altTapeName" , prop_SIMPLE }, - { 0, 0, 0 } // sentinel - }; - - // ================================================================================================= - // ImportCr8rItems - // =============== - #pragma pack ( push, 1 ) - struct PrmLBoxContent { - XMP_Uns32 magic; - XMP_Uns32 size; - XMP_Uns16 verAPI; - XMP_Uns16 verCode; - XMP_Uns32 exportType; - XMP_Uns16 MacVRefNum; - XMP_Uns32 MacParID; - char filePath[260]; - }; - - enum { kExportTypeMovie = 0, kExportTypeStill = 1, kExportTypeAudio = 2, kExportTypeCustom = 3 }; - - struct Cr8rBoxContent { - XMP_Uns32 magic; - XMP_Uns32 size; - XMP_Uns16 majorVer; - XMP_Uns16 minorVer; - XMP_Uns32 creatorCode; - XMP_Uns32 appleEvent; - char fileExt[16]; - char appOptions[16]; - char appName[32]; - }; - #pragma pack ( pop ) - - // static getter, determines appropriate chunkType (peeking)and returns - // the respective constructor. It's the caller's responsibility to - // delete obtained chunk. - Chunk* getChunk ( ContainerChunk* parent, RIFF_MetaHandler* handler ); - - class Chunk - { - public: - ChunkType chunkType; // set by constructor - ContainerChunk* parent; // 0 on top-level - - XMP_Uns32 id; // the first four bytes, first byte of highest value - XMP_Int64 oldSize; // actual chunk size INCLUDING the 8/12 header bytes, - XMP_Int64 oldPos; // file position of this chunk - - // both set as part of changesAndSize() - XMP_Int64 newSize; - bool hasChange; - bool needSizeFix; // used in changesAndSize() only - - // Constructors /////////////////////// - // parsing - Chunk( ContainerChunk* parent, RIFF_MetaHandler* handler, bool skip, ChunkType c /*= chunk_GENERAL*/ ); - // ad-hoc creation - Chunk( ContainerChunk* parent, ChunkType c, XMP_Uns32 id ); - - /* returns true, if something has changed in chunk (which needs specific write-out, - this->newSize is expected to be set by this routine */ - virtual void changesAndSize( RIFF_MetaHandler* handler ); - virtual std::string toString(XMP_Uns8 level = 0); - virtual void write( RIFF_MetaHandler* handler, LFA_FileRef file, bool isMainChunk = false ); - - virtual ~Chunk(); - - }; // class Chunk - - class XMPChunk : public Chunk - { - public: - XMPChunk( ContainerChunk* parent ); - XMPChunk( ContainerChunk* parent, RIFF_MetaHandler* handler ); - - void changesAndSize( RIFF_MetaHandler* handler ); - void write( RIFF_MetaHandler* handler, LFA_FileRef file, bool isMainChunk = false ); - - }; - - // any chunk, whose value should be stored, e.g. LIST:INFO, LIST:Tdat - class ValueChunk : public Chunk - { - public: - std::string oldValue, newValue; - - // for ad-hoc creation (upon write) - ValueChunk( ContainerChunk* parent, std::string value, XMP_Uns32 id ); - - // for parsing - ValueChunk( ContainerChunk* parent, RIFF_MetaHandler* handler ); - - enum { kNULisOptional = true }; - - void SetValue( std::string value, bool optionalNUL = false ); - void changesAndSize( RIFF_MetaHandler* handler ); - void write( RIFF_MetaHandler* handler, LFA_FileRef file, bool isMainChunk = false ); - - // own destructor not needed. - }; - - // relevant (level 1) JUNQ and JUNK chunks... - class JunkChunk : public Chunk - { - public: - // construction - JunkChunk( ContainerChunk* parent, XMP_Int64 size ); - // parsing - JunkChunk( ContainerChunk* parent, RIFF_MetaHandler* handler ); - - // own destructor not needed. - - void changesAndSize( RIFF_MetaHandler* handler ); - void write( RIFF_MetaHandler* handler, LFA_FileRef file, bool isMainChunk = false ); - }; - - - class ContainerChunk : public Chunk - { - public: - XMP_Uns32 containerType; // e.g. kType_INFO as in "LIST:INFO" - - chunkVect children; // used for cleanup/destruction, ordering... - valueMap childmap; // only for efficient *value* access (inside LIST), *not* used for other containers - - // construct - ContainerChunk( ContainerChunk* parent, XMP_Uns32 id, XMP_Uns32 containerType ); - // parse - ContainerChunk( ContainerChunk* parent, RIFF_MetaHandler* handler ); - - bool removeValue( XMP_Uns32 id ); - - /* returns iterator to (first) occurence of this chunk. - iterator to the end of the map if chunk pointer is not found */ - chunkVectIter getChild( Chunk* needle ); - - void replaceChildWithJunk( Chunk* child, bool deleteChild = true ); - - void changesAndSize( RIFF_MetaHandler* handler ); - std::string toString(XMP_Uns8 level = 0); - void write( RIFF_MetaHandler* handler, LFA_FileRef file, bool isMainChunk = false ); - - // destroy - void release(); // used by destructor and on error in constructor - ~ContainerChunk(); - - }; // class ContainerChunk - -} // namespace RIFF - - -#endif // __RIFF_hpp__ diff --git a/source/XMPFiles/FormatSupport/RIFF_Support.cpp b/source/XMPFiles/FormatSupport/RIFF_Support.cpp deleted file mode 100644 index fe7e568..0000000 --- a/source/XMPFiles/FormatSupport/RIFF_Support.cpp +++ /dev/null @@ -1,930 +0,0 @@ -// ================================================================================================= -// ADOBE SYSTEMS INCORPORATED -// Copyright 2008 Adobe Systems Incorporated -// 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. -// ================================================================================================= - -#define MIN(a, b) ((a) < (b) ? (a) : (b)) - -// must have access to handler class fields... -#include "RIFF.hpp" -#include "RIFF_Handler.hpp" -#include "RIFF_Support.hpp" -#include "Reconcile_Impl.hpp" - -using namespace RIFF; -namespace RIFF { - -// The minimum BEXT chunk size should be 610 (incl. 8 byte header/size field) -XMP_Int32 MIN_BEXT_SIZE = 610; // = > 8 + ( 256+32+32+10+8+4+4+2+64+190+0 ) - -// An assumed secure max value of 100 MB. -XMP_Int32 MAX_BEXT_SIZE = 100 * 1024 * 1024; - -// CR8R, PrmL have fixed sizes -XMP_Int32 CR8R_SIZE = 0x5C; -XMP_Int32 PRML_SIZE = 0x122; - -static const char* sHexChars = "0123456789ABCDEF"; - -// Encode a string of raw data bytes into a HexString (w/o spaces, i.e. "DEADBEEF"). -// No insertation/acceptance of whitespace/linefeeds. No output/tolerance of lowercase. -// returns true, if *all* characters returned are zero (or if 0 bytes are returned). -static bool EncodeToHexString ( XMP_StringPtr rawStr, - XMP_StringLen rawLen, - std::string* encodedStr ) -{ - bool allZero = true; // assume for now - - if ( (rawStr == 0) && (rawLen != 0) ) - XMP_Throw ( "EncodeToHexString: null rawStr", kXMPErr_BadParam ); - if ( encodedStr == 0 ) - XMP_Throw ( "EncodeToHexString: null encodedStr", kXMPErr_BadParam ); - - encodedStr->erase(); - if ( rawLen == 0 ) return allZero; - encodedStr->reserve ( rawLen * 2 ); - - for( XMP_Uns32 i = 0; i < rawLen; i++ ) - { - // first, second nibble - XMP_Uns8 first = rawStr[i] >> 4; - XMP_Uns8 second = rawStr[i] & 0xF; - - if ( allZero && (( first != 0 ) || (second != 0))) - allZero = false; - - encodedStr->append( 1, sHexChars[first] ); - encodedStr->append( 1, sHexChars[second] ); - } - - return allZero; -} // EncodeToHexString - -// ------------------------------------------------------------------------------------------------- -// DecodeFromHexString -// ---------------- -// -// Decode a hex string to raw data bytes. -// * Input must be all uppercase and w/o any whitespace, strictly (0-9A-Z)* (i.e. "DEADBEEF0099AABC") -// * No insertation/acceptance of whitespace/linefeeds. -// * bNo use/tolerance of lowercase. -// * Number of bytes in the encoded String must be even. -// * returns true if everything went well, false if illegal (non 0-9A-F) character encountered - -static bool DecodeFromHexString ( XMP_StringPtr encodedStr, - XMP_StringLen encodedLen, - std::string* rawStr ) -{ - if ( (encodedLen % 2) != 0 ) - return false; - rawStr->erase(); - if ( encodedLen == 0 ) return true; - rawStr->reserve ( encodedLen / 2 ); - - for( XMP_Uns32 i = 0; i < encodedLen; ) - { - XMP_Uns8 upperNibble = encodedStr[i]; - if ( (upperNibble < 48) || ( (upperNibble > 57 ) && ( upperNibble < 65 ) ) || (upperNibble > 70) ) - return false; - if ( upperNibble >= 65 ) - upperNibble -= 7; // shift A-F area adjacent to 0-9 - upperNibble -= 48; // 'shift' to a value [0..15] - upperNibble = ( upperNibble << 4 ); - i++; - - XMP_Uns8 lowerNibble = encodedStr[i]; - if ( (lowerNibble < 48) || ( (lowerNibble > 57 ) && ( lowerNibble < 65 ) ) || (lowerNibble > 70) ) - return false; - if ( lowerNibble >= 65 ) - lowerNibble -= 7; // shift A-F area adjacent to 0-9 - lowerNibble -= 48; // 'shift' to a value [0..15] - i++; - - rawStr->append ( 1, (upperNibble + lowerNibble) ); - } - return true; -} // DecodeFromHexString - -// Converts input string to an ascii output string -// - terminates at first 0 -// - replaces all non ascii with 0x3F ('?') -// - produces up to maxOut characters (note that several UTF-8 character bytes can 'melt' to one byte '?' in ascii.) -static XMP_StringLen convertToASCII( XMP_StringPtr input, XMP_StringLen inputLen, std::string* output, XMP_StringLen maxOutputLen ) -{ - if ( (input == 0) && (inputLen != 0) ) - XMP_Throw ( "convertToASCII: null input string", kXMPErr_BadParam ); - if ( output == 0) - XMP_Throw ( "convertToASCII: null output string", kXMPErr_BadParam ); - if ( maxOutputLen == 0) - XMP_Throw ( "convertToASCII: zero maxOutputLen chars", kXMPErr_BadParam ); - - output->reserve(inputLen); - output->erase(); - - bool isUTF8 = ReconcileUtils::IsUTF8( input, inputLen ); - XMP_StringLen outputLen = 0; - - for ( XMP_Uns32 i=0; i < inputLen; i++ ) - { - XMP_Uns8 c = (XMP_Uns8) input[i]; - if ( c == 0 ) // early 0 termination, leave. - break; - if ( c > 127 ) // uft-8 multi-byte sequence. - { - if ( isUTF8 ) // skip all high bytes - { - // how many bytes in this ? - if ( c >= 0xC2 && c <= 0xDF ) - i+=1; // 2-byte sequence - else if ( c >= 0xE0 && c <= 0xEF ) - i+=2; // 3-byte sequence - else if ( c >= 0xF0 && c <= 0xF4 ) - i+=3; // 4-byte sequence - else - continue; //invalid sequence, look for next 'low' byte .. - } // thereafter and 'else': just append a question mark: - output->append( 1, '?' ); - } - else // regular valid ascii. 1 byte. - { - output->append( 1, input[i] ); - } - outputLen++; - if ( outputLen >= maxOutputLen ) - break; // (may be even or even greater due to UFT-8 multi-byte jumps) - } - - return outputLen; -} - -/** - * ensures that native property gets returned as UTF-8 (may or mayn not already be UTF-8) - * - also takes care of "moot padding" (pre-mature zero termination) - * - propertyExists: it is important to know if there as an existing, non zero property - * even (in the event of serverMode) it is not actually returned, but an empty string instead. - */ -static std::string nativePropertyToUTF8 ( XMP_StringPtr cstring, XMP_StringLen maxSize, bool* propertyExists ) -{ - // the value might be properly 0-terminated, prematurely or not - // at all, hence scan through to find actual size - XMP_StringLen size = 0; - for ( size = 0; size < maxSize; size++ ) - { - if ( cstring[size] == 0 ) - break; - } - - (*propertyExists) = ( size > 0 ); - - std::string utf8(""); - if ( ReconcileUtils::IsUTF8( cstring, size ) ) - utf8 = std::string( cstring, size ); //use utf8 directly - else - { - if ( ! ignoreLocalText ) - { - #if ! UNIX_ENV // n/a anyway, since always ignoreLocalText on Unix - ReconcileUtils::LocalToUTF8( cstring, size, &utf8 ); - #endif - } - } - return utf8; -} - -// reads maxSize bytes from file (not "up to", exactly fullSize) -// puts it into a string, sets respective tree property -static std::string getBextField ( const char* data, XMP_Uns32 offset, XMP_Uns32 maxSize ) -{ - if (data == 0) - XMP_Throw ( "getBextField: null data pointer", kXMPErr_BadParam ); - if ( maxSize == 0) - XMP_Throw ( "getBextField: maxSize must be greater than 0", kXMPErr_BadParam ); - - std::string r; - convertToASCII( data+offset, maxSize, &r, maxSize ); - return r; -} - -static void importBextChunkToXMP( RIFF_MetaHandler* handler, ValueChunk* bextChunk ) -{ - // if there's a bext chunk, there is data... - handler->containsXMP = true; // very important for treatment on caller level - - XMP_Enforce( bextChunk->oldSize >= MIN_BEXT_SIZE ); - XMP_Enforce( bextChunk->oldSize < MAX_BEXT_SIZE ); - - const char* data = bextChunk->oldValue.data(); - std::string value; - - // register bext namespace: - SXMPMeta::RegisterNamespace( kXMP_NS_BWF, "bext:", 0 ); - - // bextDescription ------------------------------------------------ - value = getBextField( data, 0, 256 ); - if ( value.size() > 0 ) - handler->xmpObj.SetProperty( bextDescription.ns, bextDescription.prop, value.c_str() ); - - // bextOriginator ------------------------------------------------- - value = getBextField( data, 256, 32 ); - if ( value.size() > 0 ) - handler->xmpObj.SetProperty( bextOriginator.ns , bextOriginator.prop, value.c_str() ); - - // bextOriginatorRef ---------------------------------------------- - value = getBextField( data, 256+32, 32 ); - if ( value.size() > 0 ) - handler->xmpObj.SetProperty( bextOriginatorRef.ns , bextOriginatorRef.prop, value.c_str() ); - - // bextOriginationDate -------------------------------------------- - value = getBextField( data, 256+32+32, 10 ); - if ( value.size() > 0 ) - handler->xmpObj.SetProperty( bextOriginationDate.ns , bextOriginationDate.prop, value.c_str() ); - - // bextOriginationTime -------------------------------------------- - value = getBextField( data, 256+32+32+10, 8 ); - if ( value.size() > 0 ) - handler->xmpObj.SetProperty( bextOriginationTime.ns , bextOriginationTime.prop, value.c_str() ); - - // bextTimeReference ---------------------------------------------- - // thanx to nice byte order, all 8 bytes can be read as one: - XMP_Uns64 timeReferenceFull = GetUns64LE( &(data[256+32+32+10+8 ] ) ); - value.erase(); - SXMPUtils::ConvertFromInt64( timeReferenceFull, "%llu", &value ); - handler->xmpObj.SetProperty( bextTimeReference.ns, bextTimeReference.prop, value ); - - // bextVersion ---------------------------------------------------- - XMP_Uns16 bwfVersion = GetUns16LE( &(data[256+32+32+10+8+8] ) ); - value.erase(); - SXMPUtils::ConvertFromInt( bwfVersion, "", &value ); - handler->xmpObj.SetProperty( bextVersion.ns, bextVersion.prop, value ); - - // bextUMID ------------------------------------------------------- - // binary string is already in memory, must convert to hex string - std::string umidString; - bool allZero = EncodeToHexString( &(data[256+32+32+10+8+8+2]), 64, &umidString ); - if (! allZero ) - handler->xmpObj.SetProperty( bextUMID.ns, bextUMID.prop, umidString ); - - // bextCodingHistory ---------------------------------------------- - bool hasCodingHistory = bextChunk->oldSize > MIN_BEXT_SIZE; - - if ( hasCodingHistory ) - { - XMP_StringLen codingHistorySize = (XMP_StringLen) (bextChunk->oldSize - MIN_BEXT_SIZE); - std::string codingHistory; - convertToASCII( &data[MIN_BEXT_SIZE-8], codingHistorySize, &codingHistory, codingHistorySize ); - if (! codingHistory.empty() ) - handler->xmpObj.SetProperty( bextCodingHistory.ns, bextCodingHistory.prop, codingHistory ); - } -} // importBextChunkToXMP - -static void importPrmLToXMP( RIFF_MetaHandler* handler, ValueChunk* prmlChunk ) -{ - bool haveXMP = false; - - XMP_Enforce( prmlChunk->oldSize == PRML_SIZE ); - PrmLBoxContent rawPrmL; - XMP_Assert ( sizeof ( rawPrmL ) == PRML_SIZE - 8 ); // double check tight packing. - XMP_Assert ( sizeof ( rawPrmL.filePath ) == 260 ); - memcpy ( &rawPrmL, prmlChunk->oldValue.data(), sizeof (rawPrmL) ); - - if ( rawPrmL.magic != 0xBEEFCAFE ) { - Flip4 ( &rawPrmL.exportType ); // The only numeric field that we care about. - } - - rawPrmL.filePath[259] = 0; // Ensure a terminating nul. - if ( rawPrmL.filePath[0] != 0 ) { - if ( rawPrmL.filePath[0] == '/' ) { - haveXMP = true; - handler->xmpObj.SetStructField ( kXMP_NS_CreatorAtom, "macAtom", - kXMP_NS_CreatorAtom, "posixProjectPath", rawPrmL.filePath ); - } else if ( XMP_LitNMatch ( rawPrmL.filePath, "\\\\?\\", 4 ) ) { - haveXMP = true; - handler->xmpObj.SetStructField ( kXMP_NS_CreatorAtom, "windowsAtom", - kXMP_NS_CreatorAtom, "uncProjectPath", rawPrmL.filePath ); - } - } - - const char * exportStr = 0; - switch ( rawPrmL.exportType ) { - case kExportTypeMovie : exportStr = "movie"; break; - case kExportTypeStill : exportStr = "still"; break; - case kExportTypeAudio : exportStr = "audio"; break; - case kExportTypeCustom : exportStr = "custom"; break; - } - if ( exportStr != 0 ) { - haveXMP = true; - handler->xmpObj.SetStructField ( kXMP_NS_DM, "projectRef", kXMP_NS_DM, "type", exportStr ); - } - - handler->containsXMP |= haveXMP; // mind the '|=' -} // importCr8rToXMP - -static void importCr8rToXMP( RIFF_MetaHandler* handler, ValueChunk* cr8rChunk ) -{ - bool haveXMP = false; - - XMP_Enforce( cr8rChunk->oldSize == CR8R_SIZE ); - Cr8rBoxContent rawCr8r; - XMP_Assert ( sizeof ( rawCr8r ) == CR8R_SIZE - 8 ); // double check tight packing. - memcpy ( &rawCr8r, cr8rChunk->oldValue.data(), sizeof (rawCr8r) ); - - if ( rawCr8r.magic != 0xBEEFCAFE ) { - Flip4 ( &rawCr8r.creatorCode ); // The only numeric fields that we care about. - Flip4 ( &rawCr8r.appleEvent ); - } - - std::string fieldPath; - - SXMPUtils::ComposeStructFieldPath ( kXMP_NS_CreatorAtom, "macAtom", kXMP_NS_CreatorAtom, "applicationCode", &fieldPath ); - if ( rawCr8r.creatorCode != 0 ) { - haveXMP = true; - handler->xmpObj.SetProperty_Int64 ( kXMP_NS_CreatorAtom, fieldPath.c_str(), (XMP_Int64)rawCr8r.creatorCode ); // ! Unsigned trickery. - } - - SXMPUtils::ComposeStructFieldPath ( kXMP_NS_CreatorAtom, "macAtom", kXMP_NS_CreatorAtom, "invocationAppleEvent", &fieldPath ); - if ( rawCr8r.appleEvent != 0 ) { - haveXMP = true; - handler->xmpObj.SetProperty_Int64 ( kXMP_NS_CreatorAtom, fieldPath.c_str(), (XMP_Int64)rawCr8r.appleEvent ); // ! Unsigned trickery. - } - - rawCr8r.fileExt[15] = 0; // Ensure a terminating nul. - if ( rawCr8r.fileExt[0] != 0 ) { - haveXMP = true; - handler->xmpObj.SetStructField ( kXMP_NS_CreatorAtom, "windowsAtom", kXMP_NS_CreatorAtom, "extension", rawCr8r.fileExt ); - } - - rawCr8r.appOptions[15] = 0; // Ensure a terminating nul. - if ( rawCr8r.appOptions[0] != 0 ) { - haveXMP = true; - handler->xmpObj.SetStructField ( kXMP_NS_CreatorAtom, "windowsAtom", kXMP_NS_CreatorAtom, "invocationFlags", rawCr8r.appOptions ); - } - - rawCr8r.appName[31] = 0; // Ensure a terminating nul. - if ( rawCr8r.appName[0] != 0 ) { - haveXMP = true; - handler->xmpObj.SetProperty ( kXMP_NS_XMP, "CreatorTool", rawCr8r.appName ); - } - - handler->containsXMP |= haveXMP; // mind the '|=' -} // importCr8rToXMP - - -static void importListChunkToXMP( RIFF_MetaHandler* handler, ContainerChunk* listChunk, Mapping mapping[], bool xmpHasPriority ) -{ - valueMap* cm = &listChunk->childmap; - for (int p=0; mapping[p].chunkID != 0; p++) // go through legacy chunks - { - valueMapIter result = cm->find(mapping[p].chunkID); - if( result != cm->end() ) // if value found - { - ValueChunk* propChunk = result->second; - - bool propertyExists = false; - std::string utf8 = nativePropertyToUTF8( - propChunk->oldValue.c_str(), - (XMP_StringLen)propChunk->oldValue.size(), &propertyExists ); - - if ( utf8.size() > 0 ) // if property is not-empty, set Property - { - switch ( mapping[p].propType ) - { - case prop_TIMEVALUE: - if ( xmpHasPriority && - handler->xmpObj.DoesStructFieldExist( mapping[p].ns, mapping[p].prop, kXMP_NS_DM, "timeValue" )) - break; // skip if XMP has precedence and exists - handler->xmpObj.SetStructField( mapping[p].ns, mapping[p].prop, - kXMP_NS_DM, "timeValue", utf8.c_str() ); - break; - case prop_LOCALIZED_TEXT: - if ( xmpHasPriority && handler->xmpObj.GetLocalizedText( mapping[p].ns , - mapping[p].prop, "" , "x-default", 0, 0, 0 )) - break; // skip if XMP has precedence and exists - handler->xmpObj.SetLocalizedText( mapping[p].ns , mapping[p].prop, - "" , "x-default" , utf8.c_str() ); - if ( mapping[p].chunkID == kPropChunkINAM ) - handler->hasListInfoINAM = true; // needs to be known for special 3-way merge around dc:title - break; - case prop_ARRAYITEM: - if ( xmpHasPriority && - handler->xmpObj.DoesArrayItemExist( mapping[p].ns, mapping[p].prop, 1 )) - break; // skip if XMP has precedence and exists - handler->xmpObj.DeleteProperty( mapping[p].ns, mapping[p].prop ); - handler->xmpObj.AppendArrayItem( mapping[p].ns, mapping[p].prop, kXMP_PropValueIsArray, utf8.c_str(), kXMP_NoOptions ); - break; - case prop_SIMPLE: - if ( xmpHasPriority && - handler->xmpObj.DoesPropertyExist( mapping[p].ns, mapping[p].prop )) - break; // skip if XMP has precedence and exists - handler->xmpObj.SetProperty( mapping[p].ns, mapping[p].prop, utf8.c_str() ); - break; - default: - XMP_Throw( "internal error" , kXMPErr_InternalFailure ); - } - - handler->containsXMP = true; // very important for treatment on caller level - } - else if ( ! propertyExists) // otherwise remove it. - { // [2389942] don't, if legacy value is existing but non-retrievable (due to server mode) - switch ( mapping[p].propType ) - { - case prop_TIMEVALUE: - if ( (!xmpHasPriority) && // forward deletion only if XMP has no priority - handler->xmpObj.DoesPropertyExist( mapping[p].ns, mapping[p].prop )) - handler->xmpObj.DeleteProperty( mapping[p].ns, mapping[p].prop ); - break; - case prop_LOCALIZED_TEXT: - if ( (!xmpHasPriority) && // forward deletion only if XMP has no priority - handler->xmpObj.DoesPropertyExist( mapping[p].ns, mapping[p].prop )) - handler->xmpObj.DeleteLocalizedText( mapping[p].ns, mapping[p].prop, "", "x-default" ); - break; - case prop_ARRAYITEM: - case prop_SIMPLE: - if ( (!xmpHasPriority) && // forward deletion only if XMP has no priority - handler->xmpObj.DoesPropertyExist( mapping[p].ns, mapping[p].prop )) - handler->xmpObj.DeleteProperty( mapping[p].ns, mapping[p].prop ); - break; - default: - XMP_Throw( "internal error" , kXMPErr_InternalFailure ); - } - } - } - } // for -} -void importProperties( RIFF_MetaHandler* handler ) -{ - bool hasDigest = handler->xmpObj.GetProperty( kXMP_NS_WAV, "NativeDigest", NULL , NULL ); - if ( hasDigest ) - { - // remove! since it now becomse a 'new' handler file - handler->xmpObj.DeleteProperty( kXMP_NS_WAV, "NativeDigest" ); - } - - // BWF Bext extension chunk ----------------------------------------------- - if ( handler->parent->format == kXMP_WAVFile && // applies only to WAV - handler->bextChunk != 0 ) //skip if no BEXT chunk found. - { - importBextChunkToXMP( handler, handler->bextChunk ); - } - - // PrmL chunk -------------------------------------------------------------- - if ( handler->prmlChunk != 0 && handler->prmlChunk->oldSize == PRML_SIZE ) - { - importPrmLToXMP( handler, handler->prmlChunk ); - } - - // Cr8r chunk -------------------------------------------------------------- - if ( handler->cr8rChunk != 0 && handler->cr8rChunk->oldSize == CR8R_SIZE ) - { - importCr8rToXMP( handler, handler->cr8rChunk ); - } - - // LIST:INFO -------------------------------------------------------------- - if ( handler->listInfoChunk != 0) //skip if no LIST:INFO chunk found. - importListChunkToXMP( handler, handler->listInfoChunk, listInfoProps, hasDigest ); - - // LIST:Tdat -------------------------------------------------------------- - if ( handler->listTdatChunk != 0) - importListChunkToXMP( handler, handler->listTdatChunk, listTdatProps, hasDigest ); - - // DISP (do last, higher priority than INAM ) ----------------------------- - bool takeXMP = false; // assume for now - if ( hasDigest ) - { - std::string actualLang, value; - bool r = handler->xmpObj.GetLocalizedText( kXMP_NS_DC, "title", "" , "x-default" , &actualLang, &value, NULL ); - if ( r && (actualLang == "x-default") ) takeXMP = true; - } - - if ( (!takeXMP) && handler->dispChunk != 0) //skip if no LIST:INFO chunk found. - { - std::string* value = &handler->dispChunk->oldValue; - if ( value->size() >= 4 ) // ignore contents if file too small - { - XMP_StringPtr cstring = value->c_str(); - XMP_StringLen size = (XMP_StringLen) value->size(); - - size -= 4; // skip first four bytes known to contain constant - cstring += 4; - - bool propertyExists = false; - std::string utf8 = nativePropertyToUTF8( cstring, size, &propertyExists ); - - if ( utf8.size() > 0 ) - { - handler->xmpObj.SetLocalizedText( kXMP_NS_DC, "title", "" , "x-default" , utf8.c_str() ); - handler->containsXMP = true; // very important for treatment on caller level - } - else - { - // found as part of [2389942] - // forward deletion may only happen if no LIST:INFO/INAM is present: - if ( ! handler->hasListInfoINAM && - ! propertyExists ) // ..[2389942]part2: and if truly no legacy property - { // (not just an unreadable one due to ServerMode). - handler->xmpObj.DeleteProperty( kXMP_NS_DC, "title" ); - } - } - } // if size sufficient - } // handler->dispChunk - -} // importProperties - -//////////////////////////////////////////////////////////////////////////////// -// EXPORT -//////////////////////////////////////////////////////////////////////////////// - -void relocateWronglyPlacedXMPChunk( RIFF_MetaHandler* handler ) -{ - LFA_FileRef file = handler->parent->fileRef; - RIFF::containerVect *rc = &handler->riffChunks; - RIFF::ContainerChunk* lastChunk = rc->at( rc->size()-1 ); - - // 1) XMPPacket - // needChunk exists but is not in lastChunk ? - if ( - handler->xmpChunk != 0 && // XMP Chunk existing? - (XMP_Uns32)rc->size() > 1 && // more than 1 top-level chunk (otherwise pointless) - lastChunk->getChild( handler->xmpChunk ) == lastChunk->children.end() // not already in last chunk? - ) - { - RIFF::ContainerChunk* cur; - chunkVectIter child; - XMP_Int32 chunkNo; - - // find and relocate to last chunk: - for ( chunkNo = (XMP_Int32)rc->size()-2 ; chunkNo >= 0; chunkNo-- ) // ==> start with second-last chunk - { - cur = rc->at(chunkNo); - child = cur->getChild( handler->xmpChunk ); - if ( child != cur->children.end() ) // found? - break; - } // for - - if ( chunkNo < 0 ) // already in place? nothing left to do. - return; - - lastChunk->children.push_back( *child ); // nb: order matters! - cur->replaceChildWithJunk( *child, false ); - cur->hasChange = true; // [2414649] initialize early-on i.e: here - } // if -} // relocateWronglyPlacedXMPChunk - -// writes to buffer up to max size, -// 0 termination only if shorter than maxSize -// converts down to ascii -static void setBextField ( std::string* value, XMP_Uns8* data, XMP_Uns32 offset, XMP_Uns32 maxSize ) -{ - XMP_Validate( value != 0, "setBextField: null value string pointer", kXMPErr_BadParam ); - XMP_Validate( data != 0, "setBextField: null data value", kXMPErr_BadParam ); - XMP_Validate( maxSize > 0, "setBextField: maxSize must be greater than 0", kXMPErr_BadParam ); - - std::string ascii; - XMP_StringLen actualSize = convertToASCII( value->data(), (XMP_StringLen) value->size() , &ascii , maxSize ); - strncpy( (char*)(data + offset), ascii.data(), actualSize ); -} - -// add bwf-bext related data to bext chunk, create if not existing yet. -// * in fact, since bext is fully fixed and known, there can be no unknown subchunks worth keeping: -// * prepare bext chunk in buffer -// * value changed/created if needed only, otherways remove chunk -// * remove bext-mapped properties from xmp (non-redundant storage) -// note: ValueChunk**: adress of pointer to allow changing the pointer itself (i.e. chunk creation) -static void exportXMPtoBextChunk( RIFF_MetaHandler* handler, ValueChunk** bextChunk ) -{ - // register bext namespace ( if there was no import, this is news, otherwise harmless moot) - SXMPMeta::RegisterNamespace( kXMP_NS_BWF, "bext:", 0 ); - - bool chunkUsed = false; // assume for now - SXMPMeta* xmp = &handler->xmpObj; - - // prepare buffer, need to know CodingHistory size as the only variable - XMP_Int32 bextBufferSize = MIN_BEXT_SIZE - 8; // -8 because of header - std::string value; - if ( xmp->GetProperty( bextCodingHistory.ns, bextCodingHistory.prop, &value, kXMP_NoOptions )) - { - bextBufferSize += ((XMP_StringLen)value.size()) + 1 ; // add to size (and a trailing zero) - } - - // create and clear buffer - XMP_Uns8* buffer = new XMP_Uns8[bextBufferSize]; - for (XMP_Int32 i = 0; i < bextBufferSize; i++ ) - buffer[i] = 0; - - // grab props, write into buffer, remove from XMP /////////////////////////// - // bextDescription ------------------------------------------------ - if ( xmp->GetProperty( bextDescription.ns, bextDescription.prop, &value, kXMP_NoOptions ) ) - { - setBextField( &value, (XMP_Uns8*) buffer, 0, 256 ); - xmp->DeleteProperty( bextDescription.ns, bextDescription.prop) ; - chunkUsed = true; - } - // bextOriginator ------------------------------------------------- - if ( xmp->GetProperty( bextOriginator.ns , bextOriginator.prop, &value, kXMP_NoOptions ) ) - { - setBextField( &value, (XMP_Uns8*) buffer, 256, 32 ); - xmp->DeleteProperty( bextOriginator.ns , bextOriginator.prop ); - chunkUsed = true; - } - // bextOriginatorRef ---------------------------------------------- - if ( xmp->GetProperty( bextOriginatorRef.ns , bextOriginatorRef.prop, &value, kXMP_NoOptions ) ) - { - setBextField( &value, (XMP_Uns8*) buffer, 256+32, 32 ); - xmp->DeleteProperty( bextOriginatorRef.ns , bextOriginatorRef.prop ); - chunkUsed = true; - } - // bextOriginationDate -------------------------------------------- - if ( xmp->GetProperty( bextOriginationDate.ns , bextOriginationDate.prop, &value, kXMP_NoOptions ) ) - { - setBextField( &value, (XMP_Uns8*) buffer, 256+32+32, 10 ); - xmp->DeleteProperty( bextOriginationDate.ns , bextOriginationDate.prop ); - chunkUsed = true; - } - // bextOriginationTime -------------------------------------------- - if ( xmp->GetProperty( bextOriginationTime.ns , bextOriginationTime.prop, &value, kXMP_NoOptions ) ) - { - setBextField( &value, (XMP_Uns8*) buffer, 256+32+32+10, 8 ); - xmp->DeleteProperty( bextOriginationTime.ns , bextOriginationTime.prop ); - chunkUsed = true; - } - // bextTimeReference ---------------------------------------------- - // thanx to friendly byte order, all 8 bytes can be written in one go: - if ( xmp->GetProperty( bextTimeReference.ns, bextTimeReference.prop, &value, kXMP_NoOptions ) ) - { - try - { - XMP_Int64 v = SXMPUtils::ConvertToInt64( value.c_str() ); - PutUns64LE( v, &(buffer[256+32+32+10+8] )); - chunkUsed = true; - } - catch (XMP_Error& e) - { - if ( e.GetID() != kXMPErr_BadParam ) - throw e; // re-throw on any other error - } // 'else' tolerate ( time reference remains 0x00000000 ) - // valid or not, do not store redundantly: - xmp->DeleteProperty( bextTimeReference.ns, bextTimeReference.prop ); - } - - // bextVersion ---------------------------------------------------- - // set version=1, no matter what. - PutUns16LE( 1, &(buffer[256+32+32+10+8+8]) ); - xmp->DeleteProperty( bextVersion.ns, bextVersion.prop ); - - // bextUMID ------------------------------------------------------- - if ( xmp->GetProperty( bextUMID.ns, bextUMID.prop, &value, kXMP_NoOptions ) ) - { - std::string rawStr; - - if ( !DecodeFromHexString( value.data(), (XMP_StringLen) value.size(), &rawStr ) ) - { - delete [] buffer; // important. - XMP_Throw ( "EncodeFromHexString: illegal umid string. Must contain an even number of 0-9 and uppercase A-F chars.", kXMPErr_BadParam ); - } - - // if UMID is smaller/longer than 64 byte for any reason, - // truncate/do a partial write (just like for any other bext property) - - memcpy( (char*) &(buffer[256+32+32+10+8+8+2]), rawStr.data(), MIN( 64, rawStr.size() ) ); - xmp->DeleteProperty( bextUMID.ns, bextUMID.prop ); - chunkUsed = true; - } - - // bextCodingHistory ---------------------------------------------- - if ( xmp->GetProperty( bextCodingHistory.ns, bextCodingHistory.prop, &value, kXMP_NoOptions ) ) - { - std::string ascii; - convertToASCII( value.data(), (XMP_StringLen) value.size() , &ascii, (XMP_StringLen) value.size() ); - strncpy( (char*) &(buffer[MIN_BEXT_SIZE-8]), ascii.data(), ascii.size() ); - xmp->DeleteProperty( bextCodingHistory.ns, bextCodingHistory.prop ); - chunkUsed = true; - } - - // always delete old, recreate if needed - if ( *bextChunk != 0 ) - { - (*bextChunk)->parent->replaceChildWithJunk( *bextChunk ); - (*bextChunk) = 0; // clear direct Chunk pointer - } - - if ( chunkUsed) - *bextChunk = new ValueChunk( handler->riffChunks.at(0), std::string( (char*)buffer, bextBufferSize ), kChunk_bext ); - - delete [] buffer; // important. -} - -static inline void SetBufferedString ( char * dest, const std::string source, size_t limit ) -{ - memset ( dest, 0, limit ); - size_t count = source.size(); - if ( count >= limit ) count = limit - 1; // Ensure a terminating nul. - memcpy ( dest, source.c_str(), count ); -} - -static void exportXMPtoCr8rChunk ( RIFF_MetaHandler* handler, ValueChunk** cr8rChunk ) -{ - const SXMPMeta & xmp = handler->xmpObj; - - // Make sure an existing Cr8r chunk has the proper fixed length. - bool haveOldCr8r = (*cr8rChunk != 0); - if ( haveOldCr8r && ((*cr8rChunk)->oldSize != sizeof(Cr8rBoxContent)+8) ) { - (*cr8rChunk)->parent->replaceChildWithJunk ( *cr8rChunk ); // Wrong length, the existing chunk must be bad. - (*cr8rChunk) = 0; - haveOldCr8r = false; - } - - bool haveNewCr8r = false; - std::string creatorCode, appleEvent, fileExt, appOptions, appName; - - haveNewCr8r |= xmp.GetStructField ( kXMP_NS_CreatorAtom, "macAtom", kXMP_NS_CreatorAtom, "applicationCode", &creatorCode, 0 ); - haveNewCr8r |= xmp.GetStructField ( kXMP_NS_CreatorAtom, "macAtom", kXMP_NS_CreatorAtom, "invocationAppleEvent", &appleEvent, 0 ); - haveNewCr8r |= xmp.GetStructField ( kXMP_NS_CreatorAtom, "windowsAtom", kXMP_NS_CreatorAtom, "extension", &fileExt, 0 ); - haveNewCr8r |= xmp.GetStructField ( kXMP_NS_CreatorAtom, "windowsAtom", kXMP_NS_CreatorAtom, "invocationFlags", &appOptions, 0 ); - haveNewCr8r |= xmp.GetProperty ( kXMP_NS_XMP, "CreatorTool", &appName, 0 ); - - if ( ! haveNewCr8r ) { // Get rid of an existing Cr8r chunk if there is no new XMP. - if ( haveOldCr8r ) { - (*cr8rChunk)->parent->replaceChildWithJunk ( *cr8rChunk ); - *cr8rChunk = 0; - } - return; - } - - if ( ! haveOldCr8r ) { - *cr8rChunk = new ValueChunk ( handler->lastChunk, std::string(), kChunk_Cr8r ); - } - - std::string strValue; - strValue.assign ( (sizeof(Cr8rBoxContent) - 1), '\0' ); // ! Use size-1 because SetValue appends a trailing 0 byte. - (*cr8rChunk)->SetValue ( strValue ); // ! Just get the space available. - XMP_Assert ( (*cr8rChunk)->newValue.size() == sizeof(Cr8rBoxContent) ); - (*cr8rChunk)->hasChange = true; - - Cr8rBoxContent * newCr8r = (Cr8rBoxContent*) (*cr8rChunk)->newValue.data(); - - if ( ! haveOldCr8r ) { - - newCr8r->magic = MakeUns32LE ( 0xBEEFCAFE ); - newCr8r->size = MakeUns32LE ( sizeof(Cr8rBoxContent) ); - newCr8r->majorVer = MakeUns16LE ( 1 ); - - } else { - - const Cr8rBoxContent * oldCr8r = (Cr8rBoxContent*) (*cr8rChunk)->oldValue.data(); - memcpy ( newCr8r, oldCr8r, sizeof(Cr8rBoxContent) ); - if ( GetUns32LE ( &newCr8r->magic ) != 0xBEEFCAFE ) { // Make sure we write LE numbers. - Flip4 ( &newCr8r->magic ); - Flip4 ( &newCr8r->size ); - Flip2 ( &newCr8r->majorVer ); - Flip2 ( &newCr8r->minorVer ); - Flip4 ( &newCr8r->creatorCode ); - Flip4 ( &newCr8r->appleEvent ); - } - - } - - if ( ! creatorCode.empty() ) { - newCr8r->creatorCode = MakeUns32LE ( (XMP_Uns32) strtoul ( creatorCode.c_str(), 0, 0 ) ); - } - - if ( ! appleEvent.empty() ) { - newCr8r->appleEvent = MakeUns32LE ( (XMP_Uns32) strtoul ( appleEvent.c_str(), 0, 0 ) ); - } - - if ( ! fileExt.empty() ) SetBufferedString ( newCr8r->fileExt, fileExt, sizeof ( newCr8r->fileExt ) ); - if ( ! appOptions.empty() ) SetBufferedString ( newCr8r->appOptions, appOptions, sizeof ( newCr8r->appOptions ) ); - if ( ! appName.empty() ) SetBufferedString ( newCr8r->appName, appName, sizeof ( newCr8r->appName ) ); - -} - -static void exportXMPtoListChunk( XMP_Uns32 id, XMP_Uns32 containerType, - RIFF_MetaHandler* handler, ContainerChunk** listChunk, Mapping mapping[]) -{ - // note: ContainerChunk**: adress of pointer to allow changing the pointer itself (i.e. chunk creation) - SXMPMeta* xmp = &handler->xmpObj; - bool listChunkIsNeeded = false; // assume for now - - // ! The NUL is optional in WAV to avoid a parsing bug in Audition 3 - can't handle implicit pad byte. - bool optionalNUL = (handler->parent->format == kXMP_WAVFile); - - for ( int p=0; mapping[p].chunkID != 0; ++p ) { // go through all potential property mappings - - bool propExists = false; - std::string value, actualLang; - - switch ( mapping[p].propType ) { - - // get property. if existing, remove from XMP (to avoid redundant storage) - case prop_TIMEVALUE: - propExists = xmp->GetStructField ( mapping[p].ns, mapping[p].prop, kXMP_NS_DM, "timeValue", &value, 0 ); - break; - - case prop_LOCALIZED_TEXT: - propExists = xmp->GetLocalizedText ( mapping[p].ns, mapping[p].prop, "", "x-default", &actualLang, &value, 0); - if ( actualLang != "x-default" ) propExists = false; // no "x-default" => nothing to reconcile ! - break; - - case prop_ARRAYITEM: - propExists = xmp->GetArrayItem ( mapping[p].ns, mapping[p].prop, 1, &value, 0 ); - break; - - case prop_SIMPLE: - propExists = xmp->GetProperty ( mapping[p].ns, mapping[p].prop, &value, 0 ); - break; - - default: - XMP_Throw ( "internal error", kXMPErr_InternalFailure ); - - } - - if ( ! propExists ) { - - if ( *listChunk != 0 ) (*listChunk)->removeValue ( mapping[p].chunkID ); - - } else { - - listChunkIsNeeded = true; - if ( *listChunk == 0 ) *listChunk = new ContainerChunk ( handler->riffChunks[0], id, containerType ); - - valueMap* cm = &(*listChunk)->childmap; - valueMapIter result = cm->find( mapping[p].chunkID ); - ValueChunk* propChunk = 0; - - if ( result != cm->end() ) { - propChunk = result->second; - } else { - propChunk = new ValueChunk ( *listChunk, std::string(), mapping[p].chunkID ); - } - - propChunk->SetValue ( value.c_str(), optionalNUL ); - - } - - } // for each property - - if ( (! listChunkIsNeeded) && (*listChunk != 0) && ((*listChunk)->children.size() == 0) ) { - (*listChunk)->parent->replaceChildWithJunk ( *listChunk ); - (*listChunk) = 0; // reset direct Chunk pointer - } - -} - -void exportAndRemoveProperties ( RIFF_MetaHandler* handler ) -{ - SXMPMeta xmpObj = handler->xmpObj; - - exportXMPtoCr8rChunk ( handler, &handler->cr8rChunk ); - - // 1/4 BWF Bext extension chunk ----------------------------------------------- - if ( handler->parent->format == kXMP_WAVFile ) { // applies only to WAV - exportXMPtoBextChunk ( handler, &handler->bextChunk ); - } - - // 2/4 DISP chunk - if ( handler->parent->format == kXMP_WAVFile ) { // create for WAVE only - - std::string actualLang, xmpValue; - bool r = xmpObj.GetLocalizedText ( kXMP_NS_DC, "title", "" , "x-default" , &actualLang, &xmpValue, 0 ); - - if ( r && ( actualLang == "x-default" ) ) { // prop exists? - - // the 'right' DISP is lead by a 32 bit low endian 0x0001 - std::string dispValue = std::string( "\x1\0\0\0", 4 ); - dispValue.append ( xmpValue ); - - if ( handler->dispChunk == 0 ) { - handler->dispChunk = new RIFF::ValueChunk ( handler->riffChunks.at(0), std::string(), kChunk_DISP ); - } - - // ! The NUL is optional in WAV to avoid a parsing bug in Audition 3 - can't handle implicit pad byte. - handler->dispChunk->SetValue ( dispValue, ValueChunk::kNULisOptional ); - - } else { // remove Disp Chunk.. - - if ( handler->dispChunk != 0 ) { // ..if existing - ContainerChunk* mainChunk = handler->riffChunks.at(0); - Chunk* needle = handler->dispChunk; - chunkVectIter iter = mainChunk->getChild ( needle ); - if ( iter != mainChunk->children.end() ) { - mainChunk->replaceChildWithJunk ( *iter ); - handler->dispChunk = 0; - mainChunk->hasChange = true; - } - } - - } - - } - - // 3/4 LIST:INFO - exportXMPtoListChunk ( kChunk_LIST, kType_INFO, handler, &handler->listInfoChunk, listInfoProps ); - - // 4/4 LIST:Tdat - exportXMPtoListChunk ( kChunk_LIST, kType_Tdat, handler, &handler->listTdatChunk, listTdatProps ); - -} - -} // namespace RIFF diff --git a/source/XMPFiles/FormatSupport/RIFF_Support.hpp b/source/XMPFiles/FormatSupport/RIFF_Support.hpp deleted file mode 100644 index a0b972b..0000000 --- a/source/XMPFiles/FormatSupport/RIFF_Support.hpp +++ /dev/null @@ -1,42 +0,0 @@ -#ifndef __RIFF_Support_hpp__ -#define __RIFF_Support_hpp__ 1 - -// ================================================================================================= -// ADOBE SYSTEMS INCORPORATED -// Copyright 2009 Adobe Systems Incorporated -// 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 "XMP_Environment.h" // ! This must be the first include. -#include <vector> -#include "XMPFiles_Impl.hpp" - -// ahead declaration: -class RIFF_MetaHandler; - -namespace RIFF { - - // declare ahead - class Chunk; - class ContainerChunk; - class ValueChunk; - class XMPChunk; - - /* This rountines imports the properties found into the - xmp packet. Use after parsing. */ - void importProperties( RIFF_MetaHandler* handler ); - - /* This rountines exports XMP properties to the respective Chunks, - creating those if needed. No writing to file here. */ - void exportAndRemoveProperties( RIFF_MetaHandler* handler ); - - /* will relocated a wrongly placed chunk (one of XMP, LIST:Info, LIST:Tdat= - from RIFF::avix back to main chunk. Chunk itself not touched. */ - void relocateWronglyPlacedXMPChunk( RIFF_MetaHandler* handler ); - -} // namespace RIFF - -#endif // __RIFF_Support_hpp__ diff --git a/source/XMPFiles/FormatSupport/ReconcileIPTC.cpp b/source/XMPFiles/FormatSupport/ReconcileIPTC.cpp deleted file mode 100644 index bef1b11..0000000 --- a/source/XMPFiles/FormatSupport/ReconcileIPTC.cpp +++ /dev/null @@ -1,855 +0,0 @@ -// ================================================================================================= -// ADOBE SYSTEMS INCORPORATED -// Copyright 2006 Adobe Systems Incorporated -// 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 "XMP_Environment.h" // ! This must be the first include. - -#include "Reconcile_Impl.hpp" - -#include <stdio.h> - -#if XMP_WinBuild - #pragma warning ( disable : 4800 ) // forcing value to bool 'true' or 'false' (performance warning) - #pragma warning ( disable : 4996 ) // '...' was declared deprecated -#endif - -// ================================================================================================= -/// \file ReconcileIPTC.cpp -/// \brief Utilities to reconcile between XMP and legacy IPTC and PSIR metadata. -/// -// ================================================================================================= - -// ================================================================================================= -// NormalizeToCR -// ============= - -static inline void NormalizeToCR ( std::string * value ) -{ - char * strPtr = (char*) value->data(); - char * strEnd = strPtr + value->size(); - - for ( ; strPtr < strEnd; ++strPtr ) { - if ( *strPtr == kLF ) *strPtr = kCR; - } - -} // NormalizeToCR - -// ================================================================================================= -// NormalizeToLF -// ============= - -static inline void NormalizeToLF ( std::string * value ) -{ - char * strPtr = (char*) value->data(); - char * strEnd = strPtr + value->size(); - - for ( ; strPtr < strEnd; ++strPtr ) { - if ( *strPtr == kCR ) *strPtr = kLF; - } - -} // NormalizeToLF - -// ================================================================================================= -// ComputeIPTCDigest -// ================= -// -// Compute a 128 bit (16 byte) MD5 digest of the full IPTC block. - -static inline void ComputeIPTCDigest ( const void * iptcPtr, const XMP_Uns32 iptcLen, MD5_Digest * digest ) -{ - MD5_CTX context; - - MD5Init ( &context ); - MD5Update ( &context, (XMP_Uns8*)iptcPtr, iptcLen ); - MD5Final ( *digest, &context ); - -} // ComputeIPTCDigest; - -// ================================================================================================= -// PhotoDataUtils::CheckIPTCDigest -// =============================== - -int PhotoDataUtils::CheckIPTCDigest ( const void * newPtr, const XMP_Uns32 newLen, const void * oldDigest ) -{ - MD5_Digest newDigest; - ComputeIPTCDigest ( newPtr, newLen, &newDigest ); - if ( memcmp ( &newDigest, oldDigest, 16 ) == 0 ) return kDigestMatches; - return kDigestDiffers; - -} // PhotoDataUtils::CheckIPTCDigest - -// ================================================================================================= -// PhotoDataUtils::SetIPTCDigest -// ============================= - -void PhotoDataUtils::SetIPTCDigest ( void * iptcPtr, XMP_Uns32 iptcLen, PSIR_Manager * psir ) -{ - MD5_Digest newDigest; - - ComputeIPTCDigest ( iptcPtr, iptcLen, &newDigest ); - psir->SetImgRsrc ( kPSIR_IPTCDigest, &newDigest, sizeof(newDigest) ); - -} // PhotoDataUtils::SetIPTCDigest - -// ================================================================================================= -// ================================================================================================= - -// ================================================================================================= -// PhotoDataUtils::ImportIPTC_Simple -// ================================= - -void PhotoDataUtils::ImportIPTC_Simple ( const IPTC_Manager & iptc, SXMPMeta * xmp, - XMP_Uns8 id, const char * xmpNS, const char * xmpProp ) -{ - std::string utf8Str; - size_t count = iptc.GetDataSet_UTF8 ( id, &utf8Str ); - - if ( count != 0 ) { - NormalizeToLF ( &utf8Str ); - xmp->SetProperty ( xmpNS, xmpProp, utf8Str.c_str() ); - } - -} // PhotoDataUtils::ImportIPTC_Simple - -// ================================================================================================= -// PhotoDataUtils::ImportIPTC_LangAlt -// ================================== - -void PhotoDataUtils::ImportIPTC_LangAlt ( const IPTC_Manager & iptc, SXMPMeta * xmp, - XMP_Uns8 id, const char * xmpNS, const char * xmpProp ) -{ - std::string utf8Str; - size_t count = iptc.GetDataSet_UTF8 ( id, &utf8Str ); - - if ( count != 0 ) { - NormalizeToLF ( &utf8Str ); - xmp->SetLocalizedText ( xmpNS, xmpProp, "", "x-default", utf8Str.c_str() ); - } - -} // PhotoDataUtils::ImportIPTC_LangAlt - -// ================================================================================================= -// PhotoDataUtils::ImportIPTC_Array -// ================================ - -void PhotoDataUtils::ImportIPTC_Array ( const IPTC_Manager & iptc, SXMPMeta * xmp, - XMP_Uns8 id, const char * xmpNS, const char * xmpProp ) -{ - std::string utf8Str; - size_t count = iptc.GetDataSet ( id, 0 ); - - xmp->DeleteProperty ( xmpNS, xmpProp ); - - XMP_OptionBits arrayForm = kXMP_PropArrayIsUnordered; - if ( XMP_LitMatch ( xmpNS, kXMP_NS_DC ) && XMP_LitMatch ( xmpProp, "creator" ) ) arrayForm = kXMP_PropArrayIsOrdered; - - for ( size_t ds = 0; ds < count; ++ds ) { - (void) iptc.GetDataSet_UTF8 ( id, &utf8Str, ds ); - NormalizeToLF ( &utf8Str ); - xmp->AppendArrayItem ( xmpNS, xmpProp, arrayForm, utf8Str.c_str() ); - } - -} // PhotoDataUtils::ImportIPTC_Array - -// ================================================================================================= -// PhotoDataUtils::ImportIPTC_Date -// =============================== -// -// An IPTC (IIM) date is 8 characters, YYYYMMDD. Include the time portion if it is present. The IPTC -// time is HHMMSSxHHMM, where 'x' is '+' or '-'. Be tolerant of some ill-formed dates and times. -// Apparently some non-Adobe apps put strings like "YYYY-MM-DD" or "HH:MM:SSxHH:MM" in the IPTC. -// Allow a missing time zone portion. - -// *** The date/time handling differs from the MWG 1.0.1 policy, following a proposed tweak to MWG: -// *** Exif DateTimeOriginal <-> XMP exif:DateTimeOriginal -// *** IPTC DateCreated <-> XMP photoshop:DateCreated -// *** Exif DateTimeDigitized <-> IPTC DigitalCreateDate <-> XMP xmp:CreateDate - -void PhotoDataUtils::ImportIPTC_Date ( XMP_Uns8 dateID, const IPTC_Manager & iptc, SXMPMeta * xmp ) -{ - XMP_Uns8 timeID; - XMP_StringPtr xmpNS, xmpProp; - - if ( dateID == kIPTC_DateCreated ) { - timeID = kIPTC_TimeCreated; - xmpNS = kXMP_NS_Photoshop; - xmpProp = "DateCreated"; - } else if ( dateID == kIPTC_DigitalCreateDate ) { - timeID = kIPTC_DigitalCreateTime; - xmpNS = kXMP_NS_XMP; - xmpProp = "CreateDate"; - } else { - XMP_Throw ( "Unrecognized dateID", kXMPErr_BadParam ); - } - - // First gather the date portion. - - IPTC_Manager::DataSetInfo dsInfo; - size_t count = iptc.GetDataSet ( dateID, &dsInfo ); - if ( count == 0 ) return; - - size_t chPos, digits; - XMP_DateTime xmpDate; - memset ( &xmpDate, 0, sizeof(xmpDate) ); - - chPos = 0; - for ( digits = 0; digits < 4; ++digits, ++chPos ) { - if ( (chPos >= dsInfo.dataLen) || (dsInfo.dataPtr[chPos] < '0') || (dsInfo.dataPtr[chPos] > '9') ) break; - xmpDate.year = (xmpDate.year * 10) + (dsInfo.dataPtr[chPos] - '0'); - } - - if ( dsInfo.dataPtr[chPos] == '-' ) ++chPos; - for ( digits = 0; digits < 2; ++digits, ++chPos ) { - if ( (chPos >= dsInfo.dataLen) || (dsInfo.dataPtr[chPos] < '0') || (dsInfo.dataPtr[chPos] > '9') ) break; - xmpDate.month = (xmpDate.month * 10) + (dsInfo.dataPtr[chPos] - '0'); - } - if ( xmpDate.month < 1 ) xmpDate.month = 1; - if ( xmpDate.month > 12 ) xmpDate.month = 12; - - if ( dsInfo.dataPtr[chPos] == '-' ) ++chPos; - for ( digits = 0; digits < 2; ++digits, ++chPos ) { - if ( (chPos >= dsInfo.dataLen) || (dsInfo.dataPtr[chPos] < '0') || (dsInfo.dataPtr[chPos] > '9') ) break; - xmpDate.day = (xmpDate.day * 10) + (dsInfo.dataPtr[chPos] - '0'); - } - if ( xmpDate.day < 1 ) xmpDate.day = 1; - if ( xmpDate.day > 31 ) xmpDate.day = 28; // Close enough. - - if ( chPos != dsInfo.dataLen ) return; // The DataSet is ill-formed. - xmpDate.hasDate = true; - - // Now add the time portion if present. - - count = iptc.GetDataSet ( timeID, &dsInfo ); - if ( count != 0 ) { - - chPos = 0; - for ( digits = 0; digits < 2; ++digits, ++chPos ) { - if ( (chPos >= dsInfo.dataLen) || (dsInfo.dataPtr[chPos] < '0') || (dsInfo.dataPtr[chPos] > '9') ) break; - xmpDate.hour = (xmpDate.hour * 10) + (dsInfo.dataPtr[chPos] - '0'); - } - if ( xmpDate.hour < 0 ) xmpDate.hour = 0; - if ( xmpDate.hour > 23 ) xmpDate.hour = 23; - - if ( dsInfo.dataPtr[chPos] == ':' ) ++chPos; - for ( digits = 0; digits < 2; ++digits, ++chPos ) { - if ( (chPos >= dsInfo.dataLen) || (dsInfo.dataPtr[chPos] < '0') || (dsInfo.dataPtr[chPos] > '9') ) break; - xmpDate.minute = (xmpDate.minute * 10) + (dsInfo.dataPtr[chPos] - '0'); - } - if ( xmpDate.minute < 0 ) xmpDate.minute = 0; - if ( xmpDate.minute > 59 ) xmpDate.minute = 59; - - if ( dsInfo.dataPtr[chPos] == ':' ) ++chPos; - for ( digits = 0; digits < 2; ++digits, ++chPos ) { - if ( (chPos >= dsInfo.dataLen) || (dsInfo.dataPtr[chPos] < '0') || (dsInfo.dataPtr[chPos] > '9') ) break; - xmpDate.second = (xmpDate.second * 10) + (dsInfo.dataPtr[chPos] - '0'); - } - if ( xmpDate.second < 0 ) xmpDate.second = 0; - if ( xmpDate.second > 59 ) xmpDate.second = 59; - - xmpDate.hasTime = true; - - if ( (dsInfo.dataPtr[chPos] != ' ') && (dsInfo.dataPtr[chPos] != 0) ) { // Tolerate a missing TZ. - - if ( dsInfo.dataPtr[chPos] == '+' ) { - xmpDate.tzSign = kXMP_TimeEastOfUTC; - } else if ( dsInfo.dataPtr[chPos] == '-' ) { - xmpDate.tzSign = kXMP_TimeWestOfUTC; - } else if ( chPos != dsInfo.dataLen ) { - return; // The DataSet is ill-formed. - } - - ++chPos; // Move past the time zone sign. - for ( digits = 0; digits < 2; ++digits, ++chPos ) { - if ( (chPos >= dsInfo.dataLen) || (dsInfo.dataPtr[chPos] < '0') || (dsInfo.dataPtr[chPos] > '9') ) break; - xmpDate.tzHour = (xmpDate.tzHour * 10) + (dsInfo.dataPtr[chPos] - '0'); - } - if ( xmpDate.tzHour < 0 ) xmpDate.tzHour = 0; - if ( xmpDate.tzHour > 23 ) xmpDate.tzHour = 23; - - if ( dsInfo.dataPtr[chPos] == ':' ) ++chPos; - for ( digits = 0; digits < 2; ++digits, ++chPos ) { - if ( (chPos >= dsInfo.dataLen) || (dsInfo.dataPtr[chPos] < '0') || (dsInfo.dataPtr[chPos] > '9') ) break; - xmpDate.tzMinute = (xmpDate.tzMinute * 10) + (dsInfo.dataPtr[chPos] - '0'); - } - if ( xmpDate.tzMinute < 0 ) xmpDate.tzMinute = 0; - if ( xmpDate.tzMinute > 59 ) xmpDate.tzMinute = 59; - - if ( chPos != dsInfo.dataLen ) return; // The DataSet is ill-formed. - xmpDate.hasTimeZone = true; - - } - - } - - // Finally, set the XMP property. - - xmp->SetProperty_Date ( xmpNS, xmpProp, xmpDate ); - -} // PhotoDataUtils::ImportIPTC_Date - -// ================================================================================================= -// ImportIPTC_IntellectualGenre -// ============================ -// -// Import DataSet 2:04. In the IIM this is a 3 digit number, a colon, and an optional text name. -// Even though the number is the more formal part, the IPTC4XMP rule is that the name is imported to -// XMP and the number is dropped. Also, even though IIMv4.1 says that 2:04 is repeatable, the XMP -// property to which it is mapped is simple. - -static void ImportIPTC_IntellectualGenre ( const IPTC_Manager & iptc, SXMPMeta * xmp ) -{ - std::string utf8Str; - size_t count = iptc.GetDataSet_UTF8 ( kIPTC_IntellectualGenre, &utf8Str ); - - if ( count == 0 ) return; - NormalizeToLF ( &utf8Str ); - - XMP_StringPtr namePtr = utf8Str.c_str() + 4; - - if ( utf8Str.size() <= 4 ) { - // No name in the IIM. Look up the number in our list of known genres. - int i; - XMP_StringPtr numPtr = utf8Str.c_str(); - for ( i = 0; kIntellectualGenreMappings[i].refNum != 0; ++i ) { - if ( strncmp ( numPtr, kIntellectualGenreMappings[i].refNum, 3 ) == 0 ) break; - } - if ( kIntellectualGenreMappings[i].refNum == 0 ) return; - namePtr = kIntellectualGenreMappings[i].name; - } - - xmp->SetProperty ( kXMP_NS_IPTCCore, "IntellectualGenre", namePtr ); - -} // ImportIPTC_IntellectualGenre - -// ================================================================================================= -// ImportIPTC_SubjectCode -// ====================== -// -// Import all 2:12 DataSets into an unordered array. In the IIM each DataSet is composed of 5 colon -// separated sections: a provider name, an 8 digit reference number, and 3 optional names for the -// levels of the reference number hierarchy. The IPTC4XMP mapping rule is that only the reference -// number is imported to XMP. - -static void ImportIPTC_SubjectCode ( const IPTC_Manager & iptc, SXMPMeta * xmp ) -{ - std::string utf8Str; - size_t count = iptc.GetDataSet_UTF8 ( kIPTC_SubjectCode, 0 ); - - for ( size_t ds = 0; ds < count; ++ds ) { - - (void) iptc.GetDataSet_UTF8 ( kIPTC_SubjectCode, &utf8Str, ds ); - - char * refNumPtr = (char*) utf8Str.c_str(); - for ( ; (*refNumPtr != ':') && (*refNumPtr != 0); ++refNumPtr ) {} - if ( *refNumPtr == 0 ) continue; // This DataSet is ill-formed. - - char * refNumEnd = refNumPtr + 1; - for ( ; (*refNumEnd != ':') && (*refNumEnd != 0); ++refNumEnd ) {} - if ( (refNumEnd - refNumPtr) != 8 ) continue; // This DataSet is ill-formed. - *refNumEnd = 0; // Ensure a terminating nul for the reference number portion. - - xmp->AppendArrayItem ( kXMP_NS_IPTCCore, "SubjectCode", kXMP_PropArrayIsUnordered, refNumPtr ); - - } - -} // ImportIPTC_SubjectCode - -// ================================================================================================= -// PhotoDataUtils::Import2WayIPTC -// ============================== - -void PhotoDataUtils::Import2WayIPTC ( const IPTC_Manager & iptc, SXMPMeta * xmp, int iptcDigestState ) -{ - if ( iptcDigestState == kDigestMatches ) return; // Ignore the IPTC if the digest matches. - - std::string oldStr, newStr; - IPTC_Writer oldIPTC; - - if ( iptcDigestState == kDigestDiffers ) { - PhotoDataUtils::ExportIPTC ( *xmp, &oldIPTC ); // Predict old IPTC DataSets based on the existing XMP. - } - - size_t newCount; - IPTC_Manager::DataSetInfo newInfo, oldInfo; - - for ( size_t i = 0; kKnownDataSets[i].id != 255; ++i ) { - - const DataSetCharacteristics & thisDS = kKnownDataSets[i]; - if ( thisDS.mapForm >= kIPTC_Map3Way ) continue; // The mapping is handled elsewhere, or not at all. - - bool haveXMP = xmp->DoesPropertyExist ( thisDS.xmpNS, thisDS.xmpProp ); - newCount = PhotoDataUtils::GetNativeInfo ( iptc, thisDS.id, iptcDigestState, haveXMP, &newInfo ); - if ( newCount == 0 ) continue; // GetNativeInfo returns 0 for ignored local text. - - if ( iptcDigestState == kDigestMissing ) { - if ( haveXMP ) continue; // Keep the existing XMP. - } else if ( ! PhotoDataUtils::IsValueDifferent ( iptc, oldIPTC, thisDS.id ) ) { - continue; // Don't import values that match the previous export. - } - - // The IPTC wins. Delete any existing XMP and import the DataSet. - - xmp->DeleteProperty ( thisDS.xmpNS, thisDS.xmpProp ); - - try { // Don't let errors with one stop the others. - - switch ( thisDS.mapForm ) { - - case kIPTC_MapSimple : - ImportIPTC_Simple ( iptc, xmp, thisDS.id, thisDS.xmpNS, thisDS.xmpProp ); - break; - - case kIPTC_MapLangAlt : - ImportIPTC_LangAlt ( iptc, xmp, thisDS.id, thisDS.xmpNS, thisDS.xmpProp ); - break; - - case kIPTC_MapArray : - ImportIPTC_Array ( iptc, xmp, thisDS.id, thisDS.xmpNS, thisDS.xmpProp ); - break; - - case kIPTC_MapSpecial : - if ( thisDS.id == kIPTC_DateCreated ) { - PhotoDataUtils::ImportIPTC_Date ( thisDS.id, iptc, xmp ); - } else if ( thisDS.id == kIPTC_IntellectualGenre ) { - ImportIPTC_IntellectualGenre ( iptc, xmp ); - } else if ( thisDS.id == kIPTC_SubjectCode ) { - ImportIPTC_SubjectCode ( iptc, xmp ); - } else { - XMP_Assert ( false ); // Catch mapping errors. - } - break; - - } - - } catch ( ... ) { - - // Do nothing, let other imports proceed. - // ? Notify client? - - } - - } - -} // PhotoDataUtils::Import2WayIPTC - -// ================================================================================================= -// PhotoDataUtils::ImportPSIR -// ========================== -// -// There are only 2 standalone Photoshop image resources for XMP properties: -// 1034 - Copyright Flag - 0/1 Boolean mapped to xmpRights:Marked. -// 1035 - Copyright URL - Local OS text mapped to xmpRights:WebStatement. - -// ! Photoshop does not use a true/false/missing model for PSIR 1034. Instead it essentially uses a -// ! yes/don't-know model when importing. A missing or 0 value for PSIR 1034 cause xmpRights:Marked -// ! to be deleted. - -void PhotoDataUtils::ImportPSIR ( const PSIR_Manager & psir, SXMPMeta * xmp, int iptcDigestState ) -{ - PSIR_Manager::ImgRsrcInfo rsrcInfo; - bool import; - - if ( iptcDigestState == kDigestMatches ) return; - - try { // Don't let errors with one stop the others. - import = psir.GetImgRsrc ( kPSIR_CopyrightFlag, &rsrcInfo ); - if ( import ) import = (! xmp->DoesPropertyExist ( kXMP_NS_XMP_Rights, "Marked" )); - if ( import && (rsrcInfo.dataLen == 1) && (*((XMP_Uns8*)rsrcInfo.dataPtr) != 0) ) { - xmp->SetProperty_Bool ( kXMP_NS_XMP_Rights, "Marked", true ); - } - } catch ( ... ) { - // Do nothing, let other imports proceed. - // ? Notify client? - } - - try { // Don't let errors with one stop the others. - import = psir.GetImgRsrc ( kPSIR_CopyrightURL, &rsrcInfo ); - if ( import ) import = (! xmp->DoesPropertyExist ( kXMP_NS_XMP_Rights, "WebStatement" )); - if ( import ) { - std::string utf8; - if ( ReconcileUtils::IsUTF8 ( rsrcInfo.dataPtr, rsrcInfo.dataLen ) ) { - utf8.assign ( (char*)rsrcInfo.dataPtr, rsrcInfo.dataLen ); - } else if ( ! ignoreLocalText ) { - ReconcileUtils::LocalToUTF8 ( rsrcInfo.dataPtr, rsrcInfo.dataLen, &utf8 ); - } else { - import = false; // Inhibit the SetProperty call. - } - if ( import ) xmp->SetProperty ( kXMP_NS_XMP_Rights, "WebStatement", utf8.c_str() ); - } - } catch ( ... ) { - // Do nothing, let other imports proceed. - // ? Notify client? - } - -} // PhotoDataUtils::ImportPSIR; - -// ================================================================================================= -// ================================================================================================= - -// ================================================================================================= -// ExportIPTC_Simple -// ================= - -static void ExportIPTC_Simple ( const SXMPMeta & xmp, IPTC_Manager * iptc, - const char * xmpNS, const char * xmpProp, XMP_Uns8 id ) -{ - std::string value; - XMP_OptionBits xmpFlags; - - bool found = xmp.GetProperty ( xmpNS, xmpProp, &value, &xmpFlags ); - if ( ! found ) { - iptc->DeleteDataSet ( id ); - return; - } - - if ( ! XMP_PropIsSimple ( xmpFlags ) ) return; // ? Complain? Delete the DataSet? - - NormalizeToCR ( &value ); - - size_t iptcCount = iptc->GetDataSet ( id, 0 ); - if ( iptcCount > 1 ) iptc->DeleteDataSet ( id ); - - iptc->SetDataSet_UTF8 ( id, value.c_str(), (XMP_Uns32)value.size(), 0 ); // ! Don't append a 2nd DataSet! - -} // ExportIPTC_Simple - -// ================================================================================================= -// ExportIPTC_LangAlt -// ================== - -static void ExportIPTC_LangAlt ( const SXMPMeta & xmp, IPTC_Manager * iptc, - const char * xmpNS, const char * xmpProp, XMP_Uns8 id ) -{ - std::string value; - XMP_OptionBits xmpFlags; - - bool found = xmp.GetProperty ( xmpNS, xmpProp, 0, &xmpFlags ); - if ( ! found ) { - iptc->DeleteDataSet ( id ); - return; - } - - if ( ! XMP_ArrayIsAltText ( xmpFlags ) ) return; // ? Complain? Delete the DataSet? - - found = xmp.GetLocalizedText ( xmpNS, xmpProp, "", "x-default", 0, &value, 0 ); - if ( ! found ) { - iptc->DeleteDataSet ( id ); - return; - } - - NormalizeToCR ( &value ); - - size_t iptcCount = iptc->GetDataSet ( id, 0 ); - if ( iptcCount > 1 ) iptc->DeleteDataSet ( id ); - - iptc->SetDataSet_UTF8 ( id, value.c_str(), (XMP_Uns32)value.size(), 0 ); // ! Don't append a 2nd DataSet! - -} // ExportIPTC_LangAlt - -// ================================================================================================= -// ExportIPTC_Array -// ================ -// -// Array exporting needs a bit of care to preserve the detection of XMP-only updates. If the current -// XMP and IPTC array sizes differ, delete the entire IPTC and append all new values. If they match, -// set the individual values in order - which lets SetDataSet apply its no-change optimization. - -static void ExportIPTC_Array ( const SXMPMeta & xmp, IPTC_Manager * iptc, - const char * xmpNS, const char * xmpProp, XMP_Uns8 id ) -{ - std::string value; - XMP_OptionBits xmpFlags; - - bool found = xmp.GetProperty ( xmpNS, xmpProp, 0, &xmpFlags ); - if ( ! found ) { - iptc->DeleteDataSet ( id ); - return; - } - - if ( ! XMP_PropIsArray ( xmpFlags ) ) return; // ? Complain? Delete the DataSet? - - XMP_Index xmpCount = xmp.CountArrayItems ( xmpNS, xmpProp ); - XMP_Index iptcCount = (XMP_Index) iptc->GetDataSet ( id, 0 ); - - if ( xmpCount != iptcCount ) iptc->DeleteDataSet ( id ); - - for ( XMP_Index ds = 0; ds < xmpCount; ++ds ) { // ! XMP arrays are indexed from 1, IPTC from 0. - - (void) xmp.GetArrayItem ( xmpNS, xmpProp, ds+1, &value, &xmpFlags ); - if ( ! XMP_PropIsSimple ( xmpFlags ) ) continue; // ? Complain? - - NormalizeToCR ( &value ); - - iptc->SetDataSet_UTF8 ( id, value.c_str(), (XMP_Uns32)value.size(), ds ); // ! Appends if necessary. - - } - -} // ExportIPTC_Array - -// ================================================================================================= -// ExportIPTC_IntellectualGenre -// ============================ -// -// Export DataSet 2:04. In the IIM this is a 3 digit number, a colon, and a text name. Even though -// the number is the more formal part, the IPTC4XMP rule is that the name is imported to XMP and the -// number is dropped. Also, even though IIMv4.1 says that 2:04 is repeatable, the XMP property to -// which it is mapped is simple. Look up the XMP value in a list of known genres to get the number. - -static void ExportIPTC_IntellectualGenre ( const SXMPMeta & xmp, IPTC_Manager * iptc ) -{ - std::string xmpValue; - XMP_OptionBits xmpFlags; - - bool found = xmp.GetProperty ( kXMP_NS_IPTCCore, "IntellectualGenre", &xmpValue, &xmpFlags ); - if ( ! found ) { - iptc->DeleteDataSet ( kIPTC_IntellectualGenre ); - return; - } - - if ( ! XMP_PropIsSimple ( xmpFlags ) ) return; // ? Complain? Delete the DataSet? - - NormalizeToCR ( &xmpValue ); - - int i; - XMP_StringPtr namePtr = xmpValue.c_str(); - for ( i = 0; kIntellectualGenreMappings[i].name != 0; ++i ) { - if ( strcmp ( namePtr, kIntellectualGenreMappings[i].name ) == 0 ) break; - } - if ( kIntellectualGenreMappings[i].name == 0 ) return; // Not a known genre, don't export it. - - std::string iimValue = kIntellectualGenreMappings[i].refNum; - iimValue += ':'; - iimValue += xmpValue; - - size_t iptcCount = iptc->GetDataSet ( kIPTC_IntellectualGenre, 0 ); - if ( iptcCount > 1 ) iptc->DeleteDataSet ( kIPTC_IntellectualGenre ); - - iptc->SetDataSet_UTF8 ( kIPTC_IntellectualGenre, iimValue.c_str(), (XMP_Uns32)iimValue.size(), 0 ); // ! Don't append a 2nd DataSet! - -} // ExportIPTC_IntellectualGenre - -// ================================================================================================= -// ExportIPTC_SubjectCode -// ====================== -// -// Export 2:12 DataSets from an unordered array. In the IIM each DataSet is composed of 5 colon -// separated sections: a provider name, an 8 digit reference number, and 3 optional names for the -// levels of the reference number hierarchy. The IPTC4XMP mapping rule is that only the reference -// number is imported to XMP. We export with a fixed provider of "IPTC" and no optional names. - -static void ExportIPTC_SubjectCode ( const SXMPMeta & xmp, IPTC_Manager * iptc ) -{ - std::string xmpValue, iimValue; - XMP_OptionBits xmpFlags; - - bool found = xmp.GetProperty ( kXMP_NS_IPTCCore, "SubjectCode", 0, &xmpFlags ); - if ( ! found ) { - iptc->DeleteDataSet ( kIPTC_SubjectCode ); - return; - } - - if ( ! XMP_PropIsArray ( xmpFlags ) ) return; // ? Complain? Delete the DataSet? - - XMP_Index xmpCount = xmp.CountArrayItems ( kXMP_NS_IPTCCore, "SubjectCode" ); - XMP_Index iptcCount = (XMP_Index) iptc->GetDataSet ( kIPTC_SubjectCode, 0 ); - - if ( xmpCount != iptcCount ) iptc->DeleteDataSet ( kIPTC_SubjectCode ); - - for ( XMP_Index ds = 0; ds < xmpCount; ++ds ) { // ! XMP arrays are indexed from 1, IPTC from 0. - - (void) xmp.GetArrayItem ( kXMP_NS_IPTCCore, "SubjectCode", ds+1, &xmpValue, &xmpFlags ); - if ( ! XMP_PropIsSimple ( xmpFlags ) ) continue; // ? Complain? - if ( xmpValue.size() != 8 ) continue; // ? Complain? - - iimValue = "IPTC:"; - iimValue += xmpValue; - iimValue += ":::"; // Add the separating colons for the empty name portions. - - iptc->SetDataSet_UTF8 ( kIPTC_SubjectCode, iimValue.c_str(), (XMP_Uns32)iimValue.size(), ds ); // ! Appends if necessary. - - } - -} // ExportIPTC_SubjectCode - -// ================================================================================================= -// ExportIPTC_Date -// =============== -// -// The IPTC date and time are "YYYYMMDD" and "HHMMSSxHHMM" where 'x' is '+' or '-'. Export the IPTC -// time only if already present, or if the XMP has a time portion. - -// *** The date/time handling differs from the MWG 1.0 policy, following a proposed tweak to MWG: -// *** Exif DateTimeOriginal <-> IPTC DateCreated <-> XMP photoshop:DateCreated -// *** Exif DateTimeDigitized <-> IPTC DigitalCreateDate <-> XMP xmp:CreateDate - -static void ExportIPTC_Date ( XMP_Uns8 dateID, const SXMPMeta & xmp, IPTC_Manager * iptc ) -{ - XMP_Uns8 timeID; - XMP_StringPtr xmpNS, xmpProp; - - if ( dateID == kIPTC_DateCreated ) { - timeID = kIPTC_TimeCreated; - xmpNS = kXMP_NS_Photoshop; - xmpProp = "DateCreated"; - } else if ( dateID == kIPTC_DigitalCreateDate ) { - timeID = kIPTC_DigitalCreateTime; - xmpNS = kXMP_NS_XMP; - xmpProp = "CreateDate"; - } else { - XMP_Throw ( "Unrecognized dateID", kXMPErr_BadParam ); - } - - iptc->DeleteDataSet ( dateID ); // ! Either the XMP does not exist and we want to - iptc->DeleteDataSet ( timeID ); // ! delete the IPTC, or we're replacing the IPTC. - - XMP_DateTime xmpValue; - bool found = xmp.GetProperty_Date ( xmpNS, xmpProp, &xmpValue, 0 ); - if ( ! found ) return; - - char iimValue[16]; // AUDIT: Big enough for "YYYYMMDD" (8) and "HHMMSS+HHMM" (11). - - // Set the IIM date portion as YYYYMMDD with zeroes for unknown parts. - - snprintf ( iimValue, sizeof(iimValue), "%04d%02d%02d", // AUDIT: Use of sizeof(iimValue) is safe. - xmpValue.year, xmpValue.month, xmpValue.day ); - - iptc->SetDataSet_UTF8 ( dateID, iimValue, 8 ); - - // Set the IIM time portion as HHMMSS+HHMM (or -HHMM). Allow a missing time zone. - - if ( xmpValue.hasTimeZone ) { - snprintf ( iimValue, sizeof(iimValue), "%02d%02d%02d%c%02d%02d", // AUDIT: Use of sizeof(iimValue) is safe. - xmpValue.hour, xmpValue.minute, xmpValue.second, - ((xmpValue.tzSign == kXMP_TimeWestOfUTC) ? '-' : '+'), xmpValue.tzHour, xmpValue.tzMinute ); - iptc->SetDataSet_UTF8 ( timeID, iimValue, 11 ); - } else if ( xmpValue.hasTime ) { - snprintf ( iimValue, sizeof(iimValue), "%02d%02d%02d", // AUDIT: Use of sizeof(iimValue) is safe. - xmpValue.hour, xmpValue.minute, xmpValue.second ); - iptc->SetDataSet_UTF8 ( timeID, iimValue, 6 ); - } else { - iptc->DeleteDataSet ( timeID ); - } - -} // ExportIPTC_Date - -// ================================================================================================= -// PhotoDataUtils::ExportIPTC -// ========================== - -void PhotoDataUtils::ExportIPTC ( const SXMPMeta & xmp, IPTC_Manager * iptc ) -{ - - for ( size_t i = 0; kKnownDataSets[i].id != 255; ++i ) { - - try { // Don't let errors with one stop the others. - - const DataSetCharacteristics & thisDS = kKnownDataSets[i]; - if ( thisDS.mapForm >= kIPTC_UnmappedText ) continue; - - switch ( thisDS.mapForm ) { - - case kIPTC_MapSimple : - ExportIPTC_Simple ( xmp, iptc, thisDS.xmpNS, thisDS.xmpProp, thisDS.id ); - break; - - case kIPTC_MapLangAlt : - ExportIPTC_LangAlt ( xmp, iptc, thisDS.xmpNS, thisDS.xmpProp, thisDS.id ); - break; - - case kIPTC_MapArray : - ExportIPTC_Array ( xmp, iptc, thisDS.xmpNS, thisDS.xmpProp, thisDS.id ); - break; - - case kIPTC_MapSpecial : - if ( thisDS.id == kIPTC_DateCreated ) { - ExportIPTC_Date ( thisDS.id, xmp, iptc ); - } else if ( thisDS.id == kIPTC_IntellectualGenre ) { - ExportIPTC_IntellectualGenre ( xmp, iptc ); - } else if ( thisDS.id == kIPTC_SubjectCode ) { - ExportIPTC_SubjectCode ( xmp, iptc ); - } else { - XMP_Assert ( false ); // Catch mapping errors. - } - break; - - case kIPTC_Map3Way : // The 3 way case is special for import, not for export. - if ( thisDS.id == kIPTC_DigitalCreateDate ) { - // ! Special case: Don't create IIM DigitalCreateDate. This can avoid PSD - // ! full rewrite due to new mapping from xmp:CreateDate. - if ( iptc->GetDataSet ( thisDS.id, 0 ) > 0 ) ExportIPTC_Date ( thisDS.id, xmp, iptc ); - } else if ( thisDS.id == kIPTC_Creator ) { - ExportIPTC_Array ( xmp, iptc, kXMP_NS_DC, "creator", kIPTC_Creator ); - } else if ( thisDS.id == kIPTC_CopyrightNotice ) { - ExportIPTC_LangAlt ( xmp, iptc, kXMP_NS_DC, "rights", kIPTC_CopyrightNotice ); - } else if ( thisDS.id == kIPTC_Description ) { - ExportIPTC_LangAlt ( xmp, iptc, kXMP_NS_DC, "description", kIPTC_Description ); - } else { - XMP_Assert ( false ); // Catch mapping errors. - } - - } - - } catch ( ... ) { - - // Do nothing, let other exports proceed. - // ? Notify client? - - } - - } - -} // PhotoDataUtils::ExportIPTC; - -// ================================================================================================= -// PhotoDataUtils::ExportPSIR -// ========================== -// -// There are only 2 standalone Photoshop image resources for XMP properties: -// 1034 - Copyright Flag - 0/1 Boolean mapped to xmpRights:Marked. -// 1035 - Copyright URL - Local OS text mapped to xmpRights:WebStatement. - -// ! Photoshop does not use a true/false/missing model for PSIR 1034. Instead it is always written, -// ! a missing xmpRights:Marked results in 0 for PSIR 1034. - -// ! We don't bother with the CR<->LF normalization for xmpRights:WebStatement. Very little chance -// ! of having a raw CR character in a URI. - -void PhotoDataUtils::ExportPSIR ( const SXMPMeta & xmp, PSIR_Manager * psir ) -{ - bool found; - std::string utf8Value; - - try { // Don't let errors with one stop the others. - bool copyrighted = false; - found = xmp.GetProperty ( kXMP_NS_XMP_Rights, "Marked", &utf8Value, 0 ); - if ( found ) copyrighted = SXMPUtils::ConvertToBool ( utf8Value ); - psir->SetImgRsrc ( kPSIR_CopyrightFlag, ©righted, 1 ); - } catch ( ... ) { - // Do nothing, let other exports proceed. - // ? Notify client? - } - - try { // Don't let errors with one stop the others. - found = xmp.GetProperty ( kXMP_NS_XMP_Rights, "WebStatement", &utf8Value, 0 ); - if ( ! found ) { - psir->DeleteImgRsrc ( kPSIR_CopyrightURL ); - } else if ( ! ignoreLocalText ) { - std::string localValue; - ReconcileUtils::UTF8ToLocal ( utf8Value.c_str(), utf8Value.size(), &localValue ); - psir->SetImgRsrc ( kPSIR_CopyrightURL, localValue.c_str(), (XMP_Uns32)localValue.size() ); - } else if ( ReconcileUtils::IsASCII ( utf8Value.c_str(), utf8Value.size() ) ) { - psir->SetImgRsrc ( kPSIR_CopyrightURL, utf8Value.c_str(), (XMP_Uns32)utf8Value.size() ); - } else { - psir->DeleteImgRsrc ( kPSIR_CopyrightURL ); - } - } catch ( ... ) { - // Do nothing, let other exports proceed. - // ? Notify client? - } - -} // PhotoDataUtils::ExportPSIR; diff --git a/source/XMPFiles/FormatSupport/ReconcileLegacy.cpp b/source/XMPFiles/FormatSupport/ReconcileLegacy.cpp deleted file mode 100644 index 78eeaa4..0000000 --- a/source/XMPFiles/FormatSupport/ReconcileLegacy.cpp +++ /dev/null @@ -1,185 +0,0 @@ -// ================================================================================================= -// ADOBE SYSTEMS INCORPORATED -// Copyright 2006 Adobe Systems Incorporated -// 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 "XMP_Environment.h" // ! This must be the first include. - -#include "ReconcileLegacy.hpp" -#include "Reconcile_Impl.hpp" - -// ================================================================================================= -/// \file ReconcileLegacy.cpp -/// \brief Top level parts of utilities to reconcile between XMP and legacy metadata forms such as -/// TIFF/Exif and IPTC. -/// -// ================================================================================================= - -// ================================================================================================= -// ImportPhotoData -// =============== -// -// Import legacy metadata for JPEG, TIFF, and Photoshop files into the XMP. The caller must have -// already done the file specific processing to select the appropriate sources of the TIFF stream, -// the Photoshop image resources, and the IPTC. - -#define SaveExifTag(ns,prop) \ - if ( xmp->DoesPropertyExist ( ns, prop ) ) SXMPUtils::DuplicateSubtree ( *xmp, &savedExif, ns, prop ) -#define RestoreExifTag(ns,prop) \ - if ( savedExif.DoesPropertyExist ( ns, prop ) ) SXMPUtils::DuplicateSubtree ( savedExif, xmp, ns, prop ) - -void ImportPhotoData ( const TIFF_Manager & exif, - const IPTC_Manager & iptc, - const PSIR_Manager & psir, - int iptcDigestState, - SXMPMeta * xmp, - XMP_OptionBits options /* = 0 */ ) -{ - bool haveXMP = XMP_OptionIsSet ( options, k2XMP_FileHadXMP ); - bool haveExif = XMP_OptionIsSet ( options, k2XMP_FileHadExif ); - bool haveIPTC = XMP_OptionIsSet ( options, k2XMP_FileHadIPTC ); - - // Save some new Exif writebacks that can be XMP-only from older versions, delete all of the - // XMP's tiff: and exif: namespaces (they should only reflect native Exif), then put back the - // saved writebacks (which might get replaced by the native Exif values in the Import calls). - // The value of exif:ISOSpeedRatings is saved for special case handling of ISO over 65535. - - SXMPMeta savedExif; - - SaveExifTag ( kXMP_NS_EXIF, "DateTimeOriginal" ); - SaveExifTag ( kXMP_NS_EXIF, "GPSLatitude" ); - SaveExifTag ( kXMP_NS_EXIF, "GPSLongitude" ); - SaveExifTag ( kXMP_NS_EXIF, "GPSTimeStamp" ); - SaveExifTag ( kXMP_NS_EXIF, "GPSAltitude" ); - SaveExifTag ( kXMP_NS_EXIF, "GPSAltitudeRef" ); - SaveExifTag ( kXMP_NS_EXIF, "ISOSpeedRatings" ); - - SXMPUtils::RemoveProperties ( xmp, kXMP_NS_TIFF, 0, kXMPUtil_DoAllProperties ); - SXMPUtils::RemoveProperties ( xmp, kXMP_NS_EXIF, 0, kXMPUtil_DoAllProperties ); - - RestoreExifTag ( kXMP_NS_EXIF, "DateTimeOriginal" ); - RestoreExifTag ( kXMP_NS_EXIF, "GPSLatitude" ); - RestoreExifTag ( kXMP_NS_EXIF, "GPSLongitude" ); - RestoreExifTag ( kXMP_NS_EXIF, "GPSTimeStamp" ); - RestoreExifTag ( kXMP_NS_EXIF, "GPSAltitude" ); - RestoreExifTag ( kXMP_NS_EXIF, "GPSAltitudeRef" ); - RestoreExifTag ( kXMP_NS_EXIF, "ISOSpeedRatings" ); - - // Not obvious here, but the logic in PhotoDataUtils follows the MWG reader guidelines. - - PhotoDataUtils::ImportPSIR ( psir, xmp, iptcDigestState ); - - if ( haveIPTC ) PhotoDataUtils::Import2WayIPTC ( iptc, xmp, iptcDigestState ); - if ( haveExif ) PhotoDataUtils::Import2WayExif ( exif, xmp, iptcDigestState ); - - if ( haveExif | haveIPTC ) PhotoDataUtils::Import3WayItems ( exif, iptc, xmp, iptcDigestState ); - - // If photoshop:DateCreated does not exist try to create it from exif:DateTimeOriginal. - - if ( ! xmp->DoesPropertyExist ( kXMP_NS_Photoshop, "DateCreated" ) ) { - std::string exifValue; - bool haveExifDTO = xmp->GetProperty ( kXMP_NS_EXIF, "DateTimeOriginal", &exifValue, 0 ); - if ( haveExifDTO ) xmp->SetProperty ( kXMP_NS_Photoshop, "DateCreated", exifValue.c_str() ); - } - -} // ImportPhotoData - -// ================================================================================================= -// ExportPhotoData -// =============== - -void ExportPhotoData ( XMP_FileFormat destFormat, - SXMPMeta * xmp, - TIFF_Manager * exif, // Pass 0 if not wanted. - IPTC_Manager * iptc, // Pass 0 if not wanted. - PSIR_Manager * psir, // Pass 0 if not wanted. - XMP_OptionBits options /* = 0 */ ) -{ - XMP_Assert ( (destFormat == kXMP_JPEGFile) || (destFormat == kXMP_TIFFFile) || (destFormat == kXMP_PhotoshopFile) ); - - // Do not write IPTC-IIM or PSIR in DNG files (which are a variant of TIFF). - - if ( (destFormat == kXMP_TIFFFile) && (exif != 0) && - exif->GetTag ( kTIFF_PrimaryIFD, kTIFF_DNGVersion, 0 ) ) { - - iptc = 0; // These prevent calls to ExportIPTC and ExportPSIR. - psir = 0; - - exif->DeleteTag ( kTIFF_PrimaryIFD, kTIFF_IPTC ); // These remove any existing IPTC and PSIR. - exif->DeleteTag ( kTIFF_PrimaryIFD, kTIFF_PSIR ); - - } - - // Export the individual metadata items to the non-XMP forms. Set the IPTC digest whether or not - // it changed, it might not have been present or correct before. - - bool iptcChanged = false; // Save explicitly, internal flag is reset by UpdateMemoryDataSets. - - void * iptcPtr = 0; - XMP_Uns32 iptcLen = 0; - - if ( iptc != 0 ) { - PhotoDataUtils::ExportIPTC ( *xmp, iptc ); - iptcChanged = iptc->IsChanged(); - if ( iptcChanged ) iptc->UpdateMemoryDataSets(); - iptcLen = iptc->GetBlockInfo ( &iptcPtr ); - if ( psir != 0 ) PhotoDataUtils::SetIPTCDigest ( iptcPtr, iptcLen, psir ); - } - - if ( exif != 0 ) PhotoDataUtils::ExportExif ( xmp, exif ); - if ( psir != 0 ) PhotoDataUtils::ExportPSIR ( *xmp, psir ); - - // Now update the non-XMP collections of metadata according to the file format. Do not update - // the XMP here, that is done in the file handlers after deciding if an XMP-only in-place - // update should be done. - // - JPEG has the IPTC in PSIR 1028, the Exif and PSIR are marker segments. - // - TIFF has the IPTC and PSIR in primary IFD tags. - // - PSD has everything in PSIRs. - - if ( destFormat == kXMP_JPEGFile ) { - - if ( iptcChanged && (psir != 0) ) psir->SetImgRsrc ( kPSIR_IPTC, iptcPtr, iptcLen ); - - } else if ( destFormat == kXMP_TIFFFile ) { - - XMP_Assert ( exif != 0 ); - - if ( iptcChanged ) exif->SetTag ( kTIFF_PrimaryIFD, kTIFF_IPTC, kTIFF_UndefinedType, iptcLen, iptcPtr ); - - if ( (psir != 0) && psir->IsChanged() ) { - void* psirPtr; - XMP_Uns32 psirLen = psir->UpdateMemoryResources ( &psirPtr ); - exif->SetTag ( kTIFF_PrimaryIFD, kTIFF_PSIR, kTIFF_UndefinedType, psirLen, psirPtr ); - } - - } else if ( destFormat == kXMP_PhotoshopFile ) { - - XMP_Assert ( psir != 0 ); - - if ( iptcChanged ) psir->SetImgRsrc ( kPSIR_IPTC, iptcPtr, iptcLen ); - - if ( (exif != 0) && exif->IsChanged() ) { - void* exifPtr; - XMP_Uns32 exifLen = exif->UpdateMemoryStream ( &exifPtr ); - psir->SetImgRsrc ( kPSIR_Exif, exifPtr, exifLen ); - } - - } - - // Strip the tiff: and exif: namespaces from the XMP, we're done with them. Save the Exif - // ISOSpeedRatings if any of the values are over 0xFFFF, the native tag is SHORT. Lower level - // code already kept or stripped the XMP form. - - SXMPMeta savedExif; - SaveExifTag ( kXMP_NS_EXIF, "ISOSpeedRatings" ); - - SXMPUtils::RemoveProperties ( xmp, kXMP_NS_TIFF, 0, kXMPUtil_DoAllProperties ); - SXMPUtils::RemoveProperties ( xmp, kXMP_NS_EXIF, 0, kXMPUtil_DoAllProperties ); - - RestoreExifTag ( kXMP_NS_EXIF, "ISOSpeedRatings" ); - -} // ExportPhotoData diff --git a/source/XMPFiles/FormatSupport/ReconcileLegacy.hpp b/source/XMPFiles/FormatSupport/ReconcileLegacy.hpp deleted file mode 100644 index 59918cf..0000000 --- a/source/XMPFiles/FormatSupport/ReconcileLegacy.hpp +++ /dev/null @@ -1,272 +0,0 @@ -#ifndef __ReconcileLegacy_hpp__ -#define __ReconcileLegacy_hpp__ 1 - -// ================================================================================================= -// ADOBE SYSTEMS INCORPORATED -// Copyright 2006 Adobe Systems Incorporated -// 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 "XMP_Environment.h" // ! This must be the first include. - -#include "TIFF_Support.hpp" -#include "PSIR_Support.hpp" -#include "IPTC_Support.hpp" - -// ================================================================================================= -/// \file ReconcileLegacy.hpp -/// \brief Utilities to reconcile between XMP and photo metadata forms such as TIFF/Exif and IPTC. -/// -// ================================================================================================= - -// ImportPhotoData imports TIFF/Exif and IPTC metadata from JPEG, TIFF, and Photoshop files into -// XMP. The caller must have already done the file specific processing to select the appropriate -// sources of the TIFF stream, the Photoshop image resources, and the IPTC. -// -// The reconciliation logic used here is based on the Metadata Working Group guidelines. This is a -// simpler approach than used previously - which was modeled after historical Photoshop behavior. - -enum { // Bits for the options to ImportJTPtoXMP. - k2XMP_FileHadXMP = 0x0001, // Set if the file had an XMP packet. - k2XMP_FileHadIPTC = 0x0002, // Set if the file had legacy IPTC. - k2XMP_FileHadExif = 0x0004 // Set if the file had legacy Exif. -}; - -extern void ImportPhotoData ( const TIFF_Manager & exif, - const IPTC_Manager & iptc, - const PSIR_Manager & psir, - int iptcDigestState, - SXMPMeta * xmp, - XMP_OptionBits options = 0 ); - -// ExportPhotoData exports XMP into TIFF/Exif and IPTC metadata for JPEG, TIFF, and Photoshop files. - -extern void ExportPhotoData ( XMP_FileFormat destFormat, - SXMPMeta * xmp, - TIFF_Manager * exif, // Pass 0 if not wanted. - IPTC_Manager * iptc, // Pass 0 if not wanted. - PSIR_Manager * psir, // Pass 0 if not wanted. - XMP_OptionBits options = 0 ); - -// *** Mapping notes need revision for MWG related changes. - -// ================================================================================================= -// Summary of TIFF/Exif mappings to XMP -// ==================================== -// -// The mapping for each tag is driven mainly by the tag ID, and secondarily by the type. E.g. there -// is no blanket rule that all ASCII tags are mapped to simple strings in XMP. Some, such as -// SubSecTime or GPSLatitudeRef, are combined with other tags; others, like Flash, are reformated. -// However, most tags are in fact mapped in an obvious manner based on their type and count. -// -// Photoshop practice has been to truncate ASCII tags at the first NUL, not supporting the TIFF -// specification's notion of multi-part ASCII values. -// -// Rational values are mapped to XMP as "num/denom". -// -// The tags of UNDEFINED type that are mapped to XMP text are either special cases like ExifVersion -// or the strings with an explicit encoding like UserComment. -// -// Latitude and logitude are mapped to XMP as "DDD,MM,SSk" or "DDD,MM.mmk"; k is N, S, E, or W. -// -// Flash struct in XMP separates the Fired, Return, Mode, Function, and RedEyeMode portions of the -// Exif value. Fired, Function, and RedEyeMode are Boolean; Return and Mode are integers. -// -// The OECF/SFR, CFA, and DeviceSettings tables are described in the XMP spec. -// -// Instead of iterating through all tags in the various IFDs, it is probably more efficient to have -// explicit processing for the tags that get special treatment, and a static table listing those -// that get mapped by type and count. The type and count processing will verify that the actual -// type and count are as expected, if not the tag is ignored. -// -// Here are the primary (0th) IFD tags that get special treatment: -// -// 270, 33432 - ASCII mapped to alt-text['x-default'] -// 306 - DateTime master -// 315 - ASCII mapped to text seq[1] -// -// Here are the primary (0th) IFD tags that get mapped by type and count: -// -// 256, 257, 258, 259, 262, 271, 272, 274, 277, 282, 283, 284, 296, 301, 305, 318, 319, -// 529, 530, 531, 532 -// -// Here are the Exif IFD tags that get special treatment: -// -// 34856, 41484 - OECF/SFR table -// 36864, 40960 - 4 ASCII chars to text -// 36867, 36868 - DateTime master -// 37121 - 4 UInt8 to integer seq -// 37385 - Flash struct -// 37510 - explicitly encoded text to alt-text['x-default'] -// 41728, 41729 - UInt8 to integer -// 41730 - CFA table -// 41995 - DeviceSettings table -// -// Here are the Exif IFD tags that get mapped by type and count: -// -// 33434, 33437, 34850, 34852, 34855, 37122, 37377, 37378, 37379, 37380, 37381, 37382, 37383, 37384, -// 37386, 37396, 40961, 40962, 40963, 40964, 41483, 41486, 41487, 41488, 41492, 41493, 41495, 41985, -// 41986, 41987, 41988, 41989, 41990, 41991, 41992, 41993, 41994, 41996, 42016 -// -// Here are the GPS IFD tags that get special treatment: -// -// 0 - 4 UInt8 to text "n.n.n.n" -// 2, 4, 20, 22 - Latitude or longitude master -// 7 - special DateTime master, the time part -// 27, 28 - explicitly encoded text -// -// Here are the GPS IFD tags that get mapped by type and count: -// -// 5, 6, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 23, 24, 25, 26, 30 -// ================================================================================================= - -// *** What about the Camera Raw tags that MDKit maps: -// *** 0xFDE8, 0xFDE9, 0xFDEA, 0xFE4C, 0xFE4D, 0xFE4E, 0xFE4F, 0xFE50, 0xFE51, 0xFE52, 0xFE53, -// *** 0xFE54, 0xFE55, 0xFE56, 0xFE57, 0xFE58 - -// ================================================================================================= -// Summary of TIFF/Exif mappings from XMP -// ====================================== -// -// Only a small number of properties are written back from XMP to TIFF/Exif. Most of the TIFF/Exif -// tags mapped into XMP are information about the image or capture process, not things that users -// should be editing. The tags that can be edited and written back to TIFF/Exif are: -// -// 270, 274, 282, 283, 296, 305, 306, 315, 33432; 36867, 36868, 37510, 40964 -// ================================================================================================= - -// ================================================================================================= -// Details of TIFF/Exif mappings -// ============================= -// -// General (primary and thumbnail, 0th and 1st) IFD tags -// tag TIFF type count Name XMP mapping -// -// 256 SHORTorLONG 1 ImageWidth integer -// 257 SHORTorLONG 1 ImageLength integer -// 258 SHORT 3 BitsPerSample integer seq -// 259 SHORT 1 Compression integer -// 262 SHORT 1 PhotometricInterpretation integer -// 270 ASCII Any ImageDescription text, dc:description['x-default'] -// 271 ASCII Any Make text -// 272 ASCII Any Model text -// 274 SHORT 1 Orientation integer -// 277 SHORT 1 SamplesPerPixel integer -// 282 RATIONAL 1 XResolution rational -// 283 RATIONAL 1 YResolution rational -// 284 SHORT 1 PlanarConfiguration integer -// 296 SHORT 1 ResolutionUnit integer -// 301 SHORT 3*256 TransferFunction integer seq -// 305 ASCII Any Software text, xmp:CreatorTool -// 306 ASCII 20 DateTime date, master of 37520, xmp:DateTime -// 315 ASCII Any Artist text, dc:creator[1] -// 318 RATIONAL 2 WhitePoint rational seq -// 319 RATIONAL 6 PrimaryChromaticities rational seq -// 529 RATIONAL 3 YCbCrCoefficients rational seq -// 530 SHORT 2 YCbCrSubSampling integer seq -// 531 SHORT 1 YCbCrPositioning integer -// 532 RATIONAL 6 ReferenceBlackWhite rational seq -// 33432 ASCII Any Copyright text, dc:rights['x-default'] -// -// Exif IFD tags -// tag TIFF type count Name XMP mapping -// -// 33434 RATIONAL 1 ExposureTime rational -// 33437 RATIONAL 1 FNumber rational -// 34850 SHORT 1 ExposureProgram integer -// 34852 ASCII Any SpectralSensitivity text -// 34855 SHORT Any ISOSpeedRatings integer seq -// 34856 UNDEFINED Any OECF OECF/SFR table -// 36864 UNDEFINED 4 ExifVersion text, Exif has 4 ASCII chars -// 36867 ASCII 20 DateTimeOriginal date, master of 37521 -// 36868 ASCII 20 DateTimeDigitized date, master of 37522 -// 37121 UNDEFINED 4 ComponentsConfiguration integer seq, Exif has 4 UInt8 -// 37122 RATIONAL 1 CompressedBitsPerPixel rational -// 37377 SRATIONAL 1 ShutterSpeedValue rational -// 37378 RATIONAL 1 ApertureValue rational -// 37379 SRATIONAL 1 BrightnessValue rational -// 37380 SRATIONAL 1 ExposureBiasValue rational -// 37381 RATIONAL 1 MaxApertureValue rational -// 37382 RATIONAL 1 SubjectDistance rational -// 37383 SHORT 1 MeteringMode integer -// 37384 SHORT 1 LightSource integer -// 37385 SHORT 1 Flash Flash struct -// 37386 RATIONAL 1 FocalLength rational -// 37396 SHORT 2..4 SubjectArea integer seq -// 37510 UNDEFINED Any UserComment text, explicit encoding, exif:UserComment['x-default] -// 37520 ASCII Any SubSecTime date, with 306 -// 37521 ASCII Any SubSecTimeOriginal date, with 36867 -// 37522 ASCII Any SubSecTimeDigitized date, with 36868 -// 40960 UNDEFINED 4 FlashpixVersion text, Exif has 4 ASCII chars -// 40961 SHORT 1 ColorSpace integer -// 40962 SHORTorLONG 1 PixelXDimension integer -// 40963 SHORTorLONG 1 PixelYDimension integer -// 40964 ASCII 13 RelatedSoundFile text -// 41483 RATIONAL 1 FlashEnergy rational -// 41484 UNDEFINED Any SpatialFrequencyResponse OECF/SFR table -// 41486 RATIONAL 1 FocalPlaneXResolution rational -// 41487 RATIONAL 1 FocalPlaneYResolution rational -// 41488 SHORT 1 FocalPlaneResolutionUnit integer -// 41492 SHORT 2 SubjectLocation integer seq -// 41493 RATIONAL 1 ExposureIndex rational -// 41495 SHORT 1 SensingMethod integer -// 41728 UNDEFINED 1 FileSource integer, Exif has UInt8 -// 41729 UNDEFINED 1 SceneType integer, Exif has UInt8 -// 41730 UNDEFINED Any CFAPattern CFA table -// 41985 SHORT 1 CustomRendered integer -// 41986 SHORT 1 ExposureMode integer -// 41987 SHORT 1 WhiteBalance integer -// 41988 RATIONAL 1 DigitalZoomRatio rational -// 41989 SHORT 1 FocalLengthIn35mmFilm integer -// 41990 SHORT 1 SceneCaptureType integer -// 41991 SHORT 1 GainControl integer -// 41992 SHORT 1 Contrast integer -// 41993 SHORT 1 Saturation integer -// 41994 SHORT 1 Sharpness integer -// 41995 UNDEFINED Any DeviceSettingDescription DeviceSettings table -// 41996 SHORT 1 SubjectDistanceRange integer -// 42016 ASCII 33 ImageUniqueID text -// -// GPS IFD tags -// tag TIFF type count Name XMP mapping -// -// 0 BYTE 4 GPSVersionID text, "n.n.n.n", Exif has 4 UInt8 -// 1 ASCII 2 GPSLatitudeRef latitude, with 2 -// 2 RATIONAL 3 GPSLatitude latitude, master of 2 -// 3 ASCII 2 GPSLongitudeRef longitude, with 4 -// 4 RATIONAL 3 GPSLongitude longitude, master of 3 -// 5 BYTE 1 GPSAltitudeRef integer -// 6 RATIONAL 1 GPSAltitude rational -// 7 RATIONAL 3 GPSTimeStamp date, master of 29 -// 8 ASCII Any GPSSatellites text -// 9 ASCII 2 GPSStatus text -// 10 ASCII 2 GPSMeasureMode text -// 11 RATIONAL 1 GPSDOP rational -// 12 ASCII 2 GPSSpeedRef text -// 13 RATIONAL 1 GPSSpeed rational -// 14 ASCII 2 GPSTrackRef text -// 15 RATIONAL 1 GPSTrack rational -// 16 ASCII 2 GPSImgDirectionRef text -// 17 RATIONAL 1 GPSImgDirection rational -// 18 ASCII Any GPSMapDatum text -// 19 ASCII 2 GPSDestLatitudeRef latitude, with 20 -// 20 RATIONAL 3 GPSDestLatitude latitude, master of 19 -// 21 ASCII 2 GPSDestLongitudeRef longitude, with 22 -// 22 RATIONAL 3 GPSDestLongitude logitude, master of 21 -// 23 ASCII 2 GPSDestBearingRef text -// 24 RATIONAL 1 GPSDestBearing rational -// 25 ASCII 2 GPSDestDistanceRef text -// 26 RATIONAL 1 GPSDestDistance rational -// 27 UNDEFINED Any GPSProcessingMethod text, explicit encoding -// 28 UNDEFINED Any GPSAreaInformation text, explicit encoding -// 29 ASCII 11 GPSDateStamp date, with 29 -// 30 SHORT 1 GPSDifferential integer -// -// ================================================================================================= - -// ================================================================================================= - -#endif // #ifndef __ReconcileLegacy_hpp__ diff --git a/source/XMPFiles/FormatSupport/ReconcileTIFF.cpp b/source/XMPFiles/FormatSupport/ReconcileTIFF.cpp deleted file mode 100644 index 892c683..0000000 --- a/source/XMPFiles/FormatSupport/ReconcileTIFF.cpp +++ /dev/null @@ -1,2934 +0,0 @@ -// ================================================================================================= -// ADOBE SYSTEMS INCORPORATED -// Copyright 2006 Adobe Systems Incorporated -// 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 "XMP_Environment.h" // ! This must be the first include. - -#include "Reconcile_Impl.hpp" - -#include "UnicodeConversions.hpp" - -#include <stdio.h> -#if XMP_WinBuild - #define snprintf _snprintf -#endif - -#if XMP_WinBuild - #pragma warning ( disable : 4146 ) // unary minus operator applied to unsigned type - #pragma warning ( disable : 4800 ) // forcing value to bool 'true' or 'false' - #pragma warning ( disable : 4996 ) // '...' was declared deprecated -#endif - -// ================================================================================================= -/// \file ReconcileTIFF.cpp -/// \brief Utilities to reconcile between XMP and legacy TIFF/Exif metadata. -/// -// ================================================================================================= - -// ================================================================================================= - -// ================================================================================================= -// Tables of the TIFF/Exif tags that are mapped into XMP. For the most part, the tags have obvious -// mappings based on their IFD, tag number, type and count. These tables do not list tags that are -// mapped as subsidiary parts of others, e.g. TIFF SubSecTime or GPS Info GPSDateStamp. Tags that -// have special mappings are marked by having an empty string for the XMP property name. - -// ! These tables have the tags listed in the order of tables 3, 4, 5, and 12 of Exif 2.2, with the -// ! exception of ImageUniqueID (which is listed at the end of the Exif mappings). This order is -// ! very important to consistent checking of the legacy status. The NativeDigest properties list -// ! all possible mapped tags in this order. The NativeDigest strings are compared as a whole, so -// ! the same tags listed in a different order would compare as different. - -// ! The sentinel tag value can't be 0, that is a valid GPS Info tag, 0xFFFF is unused so far. - -enum { - kExport_Never = 0, // Never export. - kExport_Always = 1, // Add, modify, or delete. - kExport_NoDelete = 2, // Add or modify, do not delete if no XMP. - kExport_InjectOnly = 3 // Add tag if new, never modify or delete existing values. -}; - -struct TIFF_MappingToXMP { - XMP_Uns16 id; - XMP_Uns16 type; - XMP_Uns32 count; // Zero means any. - XMP_Uns8 exportMode; - const char * name; // The name of the mapped XMP property. The namespace is implicit. -}; - -enum { kAnyCount = 0 }; - -static const TIFF_MappingToXMP sPrimaryIFDMappings[] = { // A blank name indicates a special mapping. - { /* 256 */ kTIFF_ImageWidth, kTIFF_ShortOrLongType, 1, kExport_Never, "ImageWidth" }, - { /* 257 */ kTIFF_ImageLength, kTIFF_ShortOrLongType, 1, kExport_Never, "ImageLength" }, - { /* 258 */ kTIFF_BitsPerSample, kTIFF_ShortType, 3, kExport_Never, "BitsPerSample" }, - { /* 259 */ kTIFF_Compression, kTIFF_ShortType, 1, kExport_Never, "Compression" }, - { /* 262 */ kTIFF_PhotometricInterpretation, kTIFF_ShortType, 1, kExport_Never, "PhotometricInterpretation" }, - { /* 274 */ kTIFF_Orientation, kTIFF_ShortType, 1, kExport_NoDelete, "Orientation" }, - { /* 277 */ kTIFF_SamplesPerPixel, kTIFF_ShortType, 1, kExport_Never, "SamplesPerPixel" }, - { /* 284 */ kTIFF_PlanarConfiguration, kTIFF_ShortType, 1, kExport_Never, "PlanarConfiguration" }, - { /* 530 */ kTIFF_YCbCrSubSampling, kTIFF_ShortType, 2, kExport_Never, "YCbCrSubSampling" }, - { /* 531 */ kTIFF_YCbCrPositioning, kTIFF_ShortType, 1, kExport_Never, "YCbCrPositioning" }, - { /* 282 */ kTIFF_XResolution, kTIFF_RationalType, 1, kExport_NoDelete, "XResolution" }, - { /* 283 */ kTIFF_YResolution, kTIFF_RationalType, 1, kExport_NoDelete, "YResolution" }, - { /* 296 */ kTIFF_ResolutionUnit, kTIFF_ShortType, 1, kExport_NoDelete, "ResolutionUnit" }, - { /* 301 */ kTIFF_TransferFunction, kTIFF_ShortType, 3*256, kExport_Never, "TransferFunction" }, - { /* 318 */ kTIFF_WhitePoint, kTIFF_RationalType, 2, kExport_Never, "WhitePoint" }, - { /* 319 */ kTIFF_PrimaryChromaticities, kTIFF_RationalType, 6, kExport_Never, "PrimaryChromaticities" }, - { /* 529 */ kTIFF_YCbCrCoefficients, kTIFF_RationalType, 3, kExport_Never, "YCbCrCoefficients" }, - { /* 532 */ kTIFF_ReferenceBlackWhite, kTIFF_RationalType, 6, kExport_Never, "ReferenceBlackWhite" }, - { /* 306 */ kTIFF_DateTime, kTIFF_ASCIIType, 20, kExport_Always, "" }, // ! Has a special mapping. - { /* 270 */ kTIFF_ImageDescription, kTIFF_ASCIIType, kAnyCount, kExport_Always, "" }, // ! Has a special mapping. - { /* 271 */ kTIFF_Make, kTIFF_ASCIIType, kAnyCount, kExport_InjectOnly, "Make" }, - { /* 272 */ kTIFF_Model, kTIFF_ASCIIType, kAnyCount, kExport_InjectOnly, "Model" }, - { /* 305 */ kTIFF_Software, kTIFF_ASCIIType, kAnyCount, kExport_Always, "Software" }, // Has alias to xmp:CreatorTool. - { /* 315 */ kTIFF_Artist, kTIFF_ASCIIType, kAnyCount, kExport_Always, "" }, // ! Has a special mapping. - { /* 33432 */ kTIFF_Copyright, kTIFF_ASCIIType, kAnyCount, kExport_Always, "" }, // ! Has a special mapping. - { 0xFFFF, 0, 0, 0 } // ! Must end with sentinel. -}; - -// ! A special need, easier than looking up the entry in sExifIFDMappings: -static const TIFF_MappingToXMP kISOSpeedMapping = { kTIFF_ISOSpeedRatings, kTIFF_ShortType, kAnyCount, kExport_InjectOnly, "ISOSpeedRatings" }; - -static const TIFF_MappingToXMP sExifIFDMappings[] = { - { /* 36864 */ kTIFF_ExifVersion, kTIFF_UndefinedType, 4, kExport_InjectOnly, "" }, // ! Has a special mapping. - { /* 40960 */ kTIFF_FlashpixVersion, kTIFF_UndefinedType, 4, kExport_Never, "" }, // ! Has a special mapping. - { /* 40961 */ kTIFF_ColorSpace, kTIFF_ShortType, 1, kExport_InjectOnly, "ColorSpace" }, - { /* 37121 */ kTIFF_ComponentsConfiguration, kTIFF_UndefinedType, 4, kExport_InjectOnly, "" }, // ! Has a special mapping. - { /* 37122 */ kTIFF_CompressedBitsPerPixel, kTIFF_RationalType, 1, kExport_InjectOnly, "CompressedBitsPerPixel" }, - { /* 40962 */ kTIFF_PixelXDimension, kTIFF_ShortOrLongType, 1, kExport_InjectOnly, "PixelXDimension" }, - { /* 40963 */ kTIFF_PixelYDimension, kTIFF_ShortOrLongType, 1, kExport_InjectOnly, "PixelYDimension" }, - { /* 37510 */ kTIFF_UserComment, kTIFF_UndefinedType, kAnyCount, kExport_Always, "" }, // ! Has a special mapping. - { /* 40964 */ kTIFF_RelatedSoundFile, kTIFF_ASCIIType, kAnyCount, kExport_Always, "RelatedSoundFile" }, // ! Exif spec says count of 13. - { /* 36867 */ kTIFF_DateTimeOriginal, kTIFF_ASCIIType, 20, kExport_Always, "" }, // ! Has a special mapping. - { /* 36868 */ kTIFF_DateTimeDigitized, kTIFF_ASCIIType, 20, kExport_Always, "" }, // ! Has a special mapping. - { /* 33434 */ kTIFF_ExposureTime, kTIFF_RationalType, 1, kExport_InjectOnly, "ExposureTime" }, - { /* 33437 */ kTIFF_FNumber, kTIFF_RationalType, 1, kExport_InjectOnly, "FNumber" }, - { /* 34850 */ kTIFF_ExposureProgram, kTIFF_ShortType, 1, kExport_InjectOnly, "ExposureProgram" }, - { /* 34852 */ kTIFF_SpectralSensitivity, kTIFF_ASCIIType, kAnyCount, kExport_InjectOnly, "SpectralSensitivity" }, - { /* 34855 */ kTIFF_ISOSpeedRatings, kTIFF_ShortType, kAnyCount, kExport_InjectOnly, "" }, // ! Has a special mapping. - { /* 34856 */ kTIFF_OECF, kTIFF_UndefinedType, kAnyCount, kExport_Never, "" }, // ! Has a special mapping. - { /* 37377 */ kTIFF_ShutterSpeedValue, kTIFF_SRationalType, 1, kExport_InjectOnly, "ShutterSpeedValue" }, - { /* 37378 */ kTIFF_ApertureValue, kTIFF_RationalType, 1, kExport_InjectOnly, "ApertureValue" }, - { /* 37379 */ kTIFF_BrightnessValue, kTIFF_SRationalType, 1, kExport_InjectOnly, "BrightnessValue" }, - { /* 37380 */ kTIFF_ExposureBiasValue, kTIFF_SRationalType, 1, kExport_InjectOnly, "ExposureBiasValue" }, - { /* 37381 */ kTIFF_MaxApertureValue, kTIFF_RationalType, 1, kExport_InjectOnly, "MaxApertureValue" }, - { /* 37382 */ kTIFF_SubjectDistance, kTIFF_RationalType, 1, kExport_InjectOnly, "SubjectDistance" }, - { /* 37383 */ kTIFF_MeteringMode, kTIFF_ShortType, 1, kExport_InjectOnly, "MeteringMode" }, - { /* 37384 */ kTIFF_LightSource, kTIFF_ShortType, 1, kExport_InjectOnly, "LightSource" }, - { /* 37385 */ kTIFF_Flash, kTIFF_ShortType, 1, kExport_InjectOnly, "" }, // ! Has a special mapping. - { /* 37386 */ kTIFF_FocalLength, kTIFF_RationalType, 1, kExport_InjectOnly, "FocalLength" }, - { /* 37396 */ kTIFF_SubjectArea, kTIFF_ShortType, kAnyCount, kExport_Never, "SubjectArea" }, // ! Actually 2..4. - { /* 41483 */ kTIFF_FlashEnergy, kTIFF_RationalType, 1, kExport_InjectOnly, "FlashEnergy" }, - { /* 41484 */ kTIFF_SpatialFrequencyResponse, kTIFF_UndefinedType, kAnyCount, kExport_InjectOnly, "" }, // ! Has a special mapping. - { /* 41486 */ kTIFF_FocalPlaneXResolution, kTIFF_RationalType, 1, kExport_InjectOnly, "FocalPlaneXResolution" }, - { /* 41487 */ kTIFF_FocalPlaneYResolution, kTIFF_RationalType, 1, kExport_InjectOnly, "FocalPlaneYResolution" }, - { /* 41488 */ kTIFF_FocalPlaneResolutionUnit, kTIFF_ShortType, 1, kExport_InjectOnly, "FocalPlaneResolutionUnit" }, - { /* 41492 */ kTIFF_SubjectLocation, kTIFF_ShortType, 2, kExport_Never, "SubjectLocation" }, - { /* 41493 */ kTIFF_ExposureIndex, kTIFF_RationalType, 1, kExport_InjectOnly, "ExposureIndex" }, - { /* 41495 */ kTIFF_SensingMethod, kTIFF_ShortType, 1, kExport_InjectOnly, "SensingMethod" }, - { /* 41728 */ kTIFF_FileSource, kTIFF_UndefinedType, 1, kExport_InjectOnly, "" }, // ! Has a special mapping. - { /* 41729 */ kTIFF_SceneType, kTIFF_UndefinedType, 1, kExport_InjectOnly, "" }, // ! Has a special mapping. - { /* 41730 */ kTIFF_CFAPattern, kTIFF_UndefinedType, kAnyCount, kExport_InjectOnly, "" }, // ! Has a special mapping. - { /* 41985 */ kTIFF_CustomRendered, kTIFF_ShortType, 1, kExport_Never, "CustomRendered" }, - { /* 41986 */ kTIFF_ExposureMode, kTIFF_ShortType, 1, kExport_InjectOnly, "ExposureMode" }, - { /* 41987 */ kTIFF_WhiteBalance, kTIFF_ShortType, 1, kExport_InjectOnly, "WhiteBalance" }, - { /* 41988 */ kTIFF_DigitalZoomRatio, kTIFF_RationalType, 1, kExport_InjectOnly, "DigitalZoomRatio" }, - { /* 41989 */ kTIFF_FocalLengthIn35mmFilm, kTIFF_ShortType, 1, kExport_InjectOnly, "FocalLengthIn35mmFilm" }, - { /* 41990 */ kTIFF_SceneCaptureType, kTIFF_ShortType, 1, kExport_InjectOnly, "SceneCaptureType" }, - { /* 41991 */ kTIFF_GainControl, kTIFF_ShortType, 1, kExport_InjectOnly, "GainControl" }, - { /* 41992 */ kTIFF_Contrast, kTIFF_ShortType, 1, kExport_InjectOnly, "Contrast" }, - { /* 41993 */ kTIFF_Saturation, kTIFF_ShortType, 1, kExport_InjectOnly, "Saturation" }, - { /* 41994 */ kTIFF_Sharpness, kTIFF_ShortType, 1, kExport_InjectOnly, "Sharpness" }, - { /* 41995 */ kTIFF_DeviceSettingDescription, kTIFF_UndefinedType, kAnyCount, kExport_InjectOnly, "" }, // ! Has a special mapping. - { /* 41996 */ kTIFF_SubjectDistanceRange, kTIFF_ShortType, 1, kExport_InjectOnly, "SubjectDistanceRange" }, - { /* 42016 */ kTIFF_ImageUniqueID, kTIFF_ASCIIType, 33, kExport_InjectOnly, "ImageUniqueID" }, - { 0xFFFF, 0, 0, 0 } // ! Must end with sentinel. -}; - -static const TIFF_MappingToXMP sGPSInfoIFDMappings[] = { - { /* 0 */ kTIFF_GPSVersionID, kTIFF_ByteType, 4, kExport_InjectOnly, "" }, // ! Has a special mapping. - { /* 2 */ kTIFF_GPSLatitude, kTIFF_RationalType, 3, kExport_Always, "" }, // ! Has a special mapping. - { /* 4 */ kTIFF_GPSLongitude, kTIFF_RationalType, 3, kExport_Always, "" }, // ! Has a special mapping. - { /* 5 */ kTIFF_GPSAltitudeRef, kTIFF_ByteType, 1, kExport_Always, "GPSAltitudeRef" }, - { /* 6 */ kTIFF_GPSAltitude, kTIFF_RationalType, 1, kExport_Always, "GPSAltitude" }, - { /* 7 */ kTIFF_GPSTimeStamp, kTIFF_RationalType, 3, kExport_Always, "" }, // ! Has a special mapping. - { /* 8 */ kTIFF_GPSSatellites, kTIFF_ASCIIType, kAnyCount, kExport_InjectOnly, "GPSSatellites" }, - { /* 9 */ kTIFF_GPSStatus, kTIFF_ASCIIType, 2, kExport_InjectOnly, "GPSStatus" }, - { /* 10 */ kTIFF_GPSMeasureMode, kTIFF_ASCIIType, 2, kExport_InjectOnly, "GPSMeasureMode" }, - { /* 11 */ kTIFF_GPSDOP, kTIFF_RationalType, 1, kExport_InjectOnly, "GPSDOP" }, - { /* 12 */ kTIFF_GPSSpeedRef, kTIFF_ASCIIType, 2, kExport_InjectOnly, "GPSSpeedRef" }, - { /* 13 */ kTIFF_GPSSpeed, kTIFF_RationalType, 1, kExport_InjectOnly, "GPSSpeed" }, - { /* 14 */ kTIFF_GPSTrackRef, kTIFF_ASCIIType, 2, kExport_InjectOnly, "GPSTrackRef" }, - { /* 15 */ kTIFF_GPSTrack, kTIFF_RationalType, 1, kExport_InjectOnly, "GPSTrack" }, - { /* 16 */ kTIFF_GPSImgDirectionRef, kTIFF_ASCIIType, 2, kExport_InjectOnly, "GPSImgDirectionRef" }, - { /* 17 */ kTIFF_GPSImgDirection, kTIFF_RationalType, 1, kExport_InjectOnly, "GPSImgDirection" }, - { /* 18 */ kTIFF_GPSMapDatum, kTIFF_ASCIIType, kAnyCount, kExport_InjectOnly, "GPSMapDatum" }, - { /* 20 */ kTIFF_GPSDestLatitude, kTIFF_RationalType, 3, kExport_InjectOnly, "" }, // ! Has a special mapping. - { /* 22 */ kTIFF_GPSDestLongitude, kTIFF_RationalType, 3, kExport_InjectOnly, "" }, // ! Has a special mapping. - { /* 23 */ kTIFF_GPSDestBearingRef, kTIFF_ASCIIType, 2, kExport_InjectOnly, "GPSDestBearingRef" }, - { /* 24 */ kTIFF_GPSDestBearing, kTIFF_RationalType, 1, kExport_InjectOnly, "GPSDestBearing" }, - { /* 25 */ kTIFF_GPSDestDistanceRef, kTIFF_ASCIIType, 2, kExport_InjectOnly, "GPSDestDistanceRef" }, - { /* 26 */ kTIFF_GPSDestDistance, kTIFF_RationalType, 1, kExport_InjectOnly, "GPSDestDistance" }, - { /* 27 */ kTIFF_GPSProcessingMethod, kTIFF_UndefinedType, kAnyCount, kExport_InjectOnly, "" }, // ! Has a special mapping. - { /* 28 */ kTIFF_GPSAreaInformation, kTIFF_UndefinedType, kAnyCount, kExport_InjectOnly, "" }, // ! Has a special mapping. - { /* 30 */ kTIFF_GPSDifferential, kTIFF_ShortType, 1, kExport_InjectOnly, "GPSDifferential" }, - { 0xFFFF, 0, 0, 0 } // ! Must end with sentinel. -}; - -// ================================================================================================= - -static void // ! Needed by Import2WayExif -ExportTIFF_Date ( const SXMPMeta & xmp, const char * xmpNS, const char * xmpProp, TIFF_Manager * tiff, XMP_Uns16 mainID ); - -// ================================================================================================= - -static XMP_Uns32 GatherInt ( const char * strPtr, size_t count ) -{ - XMP_Uns32 value = 0; - const char * strEnd = strPtr + count; - - while ( strPtr < strEnd ) { - char ch = *strPtr; - if ( (ch < '0') || (ch > '9') ) break; - value = value*10 + (ch - '0'); - ++strPtr; - } - - return value; - -} // GatherInt - -// ================================================================================================= - -static void TrimTrailingSpaces ( TIFF_Manager::TagInfo * info ) -{ - if ( info->dataLen == 0 ) return; - XMP_Assert ( info->dataPtr != 0 ); - - char * firstChar = (char*)info->dataPtr; - char * lastChar = firstChar + info->dataLen - 1; - - if ( (*lastChar != ' ') && (*lastChar != 0) ) return; // Nothing to do. - - while ( (firstChar <= lastChar) && ((*lastChar == ' ') || (*lastChar == 0)) ) --lastChar; - - XMP_Assert ( (lastChar == firstChar-1) || - ((lastChar >= firstChar) && (*lastChar != ' ') && (*lastChar != 0)) ); - - ++lastChar; - XMP_Uns32 newLen = (XMP_Uns32)(lastChar - firstChar) + 1; - XMP_Assert ( newLen <= info->dataLen ); - - *lastChar = 0; - info->dataLen = newLen; - -} // TrimTrailingSpaces - -// ================================================================================================= - -bool PhotoDataUtils::GetNativeInfo ( const TIFF_Manager & exif, XMP_Uns8 ifd, XMP_Uns16 id, TIFF_Manager::TagInfo * info ) -{ - bool haveExif = exif.GetTag ( ifd, id, info ); - - if ( haveExif ) { - - XMP_Uns32 i; - char * chPtr; - - XMP_Assert ( (info->dataPtr != 0) || (info->dataLen == 0) ); // Null pointer requires zero length. - - bool isDate = ((id == kTIFF_DateTime) || (id == kTIFF_DateTimeOriginal) || (id == kTIFF_DateTimeOriginal)); - - for ( i = 0, chPtr = (char*)info->dataPtr; i < info->dataLen; ++i, ++chPtr ) { - if ( isDate && (*chPtr == ':') ) continue; // Ignore colons, empty dates have spaces and colons. - if ( (*chPtr != ' ') && (*chPtr != 0) ) break; // Break if the Exif value is non-empty. - } - - if ( i == info->dataLen ) { - haveExif = false; // Ignore empty Exif. - } else { - TrimTrailingSpaces ( info ); - if ( info->dataLen == 0 ) haveExif = false; - } - - } - - return haveExif; - -} // PhotoDataUtils::GetNativeInfo - -// ================================================================================================= - -size_t PhotoDataUtils::GetNativeInfo ( const IPTC_Manager & iptc, XMP_Uns8 id, int digestState, bool haveXMP, IPTC_Manager::DataSetInfo * info ) -{ - size_t iptcCount = 0; - - if ( (digestState == kDigestDiffers) || ((digestState == kDigestMissing) && (! haveXMP)) ) { - iptcCount = iptc.GetDataSet ( id, info ); - } - - if ( ignoreLocalText && (iptcCount > 0) && (! iptc.UsingUTF8()) ) { - // Check to see if the new value(s) should be ignored. - size_t i; - IPTC_Manager::DataSetInfo tmpInfo; - for ( i = 0; i < iptcCount; ++i ) { - (void) iptc.GetDataSet ( id, &tmpInfo, i ); - if ( ReconcileUtils::IsASCII ( tmpInfo.dataPtr, tmpInfo.dataLen ) ) break; - } - if ( i == iptcCount ) iptcCount = 0; // Return 0 if value(s) should be ignored. - } - - return iptcCount; - -} // PhotoDataUtils::GetNativeInfo - -// ================================================================================================= - -bool PhotoDataUtils::IsValueDifferent ( const TIFF_Manager::TagInfo & exifInfo, const std::string & xmpValue, std::string * exifValue ) -{ - if ( exifInfo.dataLen == 0 ) return false; // Ignore empty Exif values. - - if ( ReconcileUtils::IsUTF8 ( exifInfo.dataPtr, exifInfo.dataLen ) ) { // ! Note that ASCII is UTF-8. - exifValue->assign ( (char*)exifInfo.dataPtr, exifInfo.dataLen ); - } else { - if ( ignoreLocalText ) return false; - ReconcileUtils::LocalToUTF8 ( exifInfo.dataPtr, exifInfo.dataLen, exifValue ); - } - - return (*exifValue != xmpValue); - -} // PhotoDataUtils::IsValueDifferent - -// ================================================================================================= - -bool PhotoDataUtils::IsValueDifferent ( const IPTC_Manager & newIPTC, const IPTC_Manager & oldIPTC, XMP_Uns8 id ) -{ - IPTC_Manager::DataSetInfo newInfo; - size_t newCount = newIPTC.GetDataSet ( id, &newInfo ); - if ( newCount == 0 ) return false; // Ignore missing new IPTC values. - - IPTC_Manager::DataSetInfo oldInfo; - size_t oldCount = oldIPTC.GetDataSet ( id, &oldInfo ); - if ( oldCount == 0 ) return true; // Missing old IPTC values differ. - - if ( newCount != oldCount ) return true; - - std::string oldStr, newStr; - - for ( newCount = 0; newCount < oldCount; ++newCount ) { - - if ( ignoreLocalText & (! newIPTC.UsingUTF8()) ) { // Check to see if the new value should be ignored. - (void) newIPTC.GetDataSet ( id, &newInfo, newCount ); - if ( ! ReconcileUtils::IsASCII ( newInfo.dataPtr, newInfo.dataLen ) ) continue; - } - - (void) newIPTC.GetDataSet_UTF8 ( id, &newStr, newCount ); - (void) oldIPTC.GetDataSet_UTF8 ( id, &oldStr, newCount ); - if ( newStr.size() == 0 ) continue; // Ignore empty new IPTC. - if ( newStr != oldStr ) break; - - } - - return ( newCount != oldCount ); // Not different if all values matched. - -} // PhotoDataUtils::IsValueDifferent - -// ================================================================================================= -// ================================================================================================= - -// ================================================================================================= -// ImportSingleTIFF_Short -// ====================== - -static void -ImportSingleTIFF_Short ( const TIFF_Manager::TagInfo & tagInfo, const bool nativeEndian, - SXMPMeta * xmp, const char * xmpNS, const char * xmpProp ) -{ - try { // Don't let errors with one stop the others. - - XMP_Uns16 binValue = *((XMP_Uns16*)tagInfo.dataPtr); - if ( ! nativeEndian ) binValue = Flip2 ( binValue ); - - char strValue[20]; - snprintf ( strValue, sizeof(strValue), "%hu", binValue ); // AUDIT: Using sizeof(strValue) is safe. - - xmp->SetProperty ( xmpNS, xmpProp, strValue ); - - } catch ( ... ) { - // Do nothing, let other imports proceed. - // ? Notify client? - } - -} // ImportSingleTIFF_Short - -// ================================================================================================= -// ImportSingleTIFF_Long -// ===================== - -static void -ImportSingleTIFF_Long ( const TIFF_Manager::TagInfo & tagInfo, const bool nativeEndian, - SXMPMeta * xmp, const char * xmpNS, const char * xmpProp ) -{ - try { // Don't let errors with one stop the others. - - XMP_Uns32 binValue = *((XMP_Uns32*)tagInfo.dataPtr); - if ( ! nativeEndian ) binValue = Flip4 ( binValue ); - - char strValue[20]; - snprintf ( strValue, sizeof(strValue), "%lu", (unsigned long)binValue ); // AUDIT: Using sizeof(strValue) is safe. - - xmp->SetProperty ( xmpNS, xmpProp, strValue ); - - } catch ( ... ) { - // Do nothing, let other imports proceed. - // ? Notify client? - } - -} // ImportSingleTIFF_Long - -// ================================================================================================= -// ImportSingleTIFF_Rational -// ========================= - -static void -ImportSingleTIFF_Rational ( const TIFF_Manager::TagInfo & tagInfo, const bool nativeEndian, - SXMPMeta * xmp, const char * xmpNS, const char * xmpProp ) -{ - try { // Don't let errors with one stop the others. - - XMP_Uns32 * binPtr = (XMP_Uns32*)tagInfo.dataPtr; - XMP_Uns32 binNum = binPtr[0]; - XMP_Uns32 binDenom = binPtr[1]; - if ( ! nativeEndian ) { - binNum = Flip4 ( binNum ); - binDenom = Flip4 ( binDenom ); - } - - char strValue[40]; - snprintf ( strValue, sizeof(strValue), "%lu/%lu", (unsigned long)binNum, (unsigned long)binDenom ); // AUDIT: Using sizeof(strValue) is safe. - - xmp->SetProperty ( xmpNS, xmpProp, strValue ); - - } catch ( ... ) { - // Do nothing, let other imports proceed. - // ? Notify client? - } - -} // ImportSingleTIFF_Rational - -// ================================================================================================= -// ImportSingleTIFF_SRational -// ========================== - -static void -ImportSingleTIFF_SRational ( const TIFF_Manager::TagInfo & tagInfo, const bool nativeEndian, - SXMPMeta * xmp, const char * xmpNS, const char * xmpProp ) -{ - try { // Don't let errors with one stop the others. - - XMP_Int32 * binPtr = (XMP_Int32*)tagInfo.dataPtr; - XMP_Int32 binNum = binPtr[0]; - XMP_Int32 binDenom = binPtr[1]; - if ( ! nativeEndian ) { - Flip4 ( &binNum ); - Flip4 ( &binDenom ); - } - - char strValue[40]; - snprintf ( strValue, sizeof(strValue), "%ld/%ld", (unsigned long)binNum, (unsigned long)binDenom ); // AUDIT: Using sizeof(strValue) is safe. - - xmp->SetProperty ( xmpNS, xmpProp, strValue ); - - } catch ( ... ) { - // Do nothing, let other imports proceed. - // ? Notify client? - } - -} // ImportSingleTIFF_SRational - -// ================================================================================================= -// ImportSingleTIFF_ASCII -// ====================== - -static void -ImportSingleTIFF_ASCII ( const TIFF_Manager::TagInfo & tagInfo, - SXMPMeta * xmp, const char * xmpNS, const char * xmpProp ) -{ - try { // Don't let errors with one stop the others. - - TrimTrailingSpaces ( (TIFF_Manager::TagInfo*) &tagInfo ); - if ( tagInfo.dataLen == 0 ) return; // Ignore empty tags. - - const char * chPtr = (const char *)tagInfo.dataPtr; - const bool hasNul = (chPtr[tagInfo.dataLen-1] == 0); - const bool isUTF8 = ReconcileUtils::IsUTF8 ( chPtr, tagInfo.dataLen ); - - if ( isUTF8 && hasNul ) { - xmp->SetProperty ( xmpNS, xmpProp, chPtr ); - } else { - std::string strValue; - if ( isUTF8 ) { - strValue.assign ( chPtr, tagInfo.dataLen ); - } else { - if ( ignoreLocalText ) return; - ReconcileUtils::LocalToUTF8 ( chPtr, tagInfo.dataLen, &strValue ); - } - xmp->SetProperty ( xmpNS, xmpProp, strValue.c_str() ); - } - - } catch ( ... ) { - // Do nothing, let other imports proceed. - // ? Notify client? - } - -} // ImportSingleTIFF_ASCII - -// ================================================================================================= -// ImportSingleTIFF_Byte -// ===================== - -static void -ImportSingleTIFF_Byte ( const TIFF_Manager::TagInfo & tagInfo, - SXMPMeta * xmp, const char * xmpNS, const char * xmpProp ) -{ - try { // Don't let errors with one stop the others. - - XMP_Uns8 binValue = *((XMP_Uns8*)tagInfo.dataPtr); - - char strValue[20]; - snprintf ( strValue, sizeof(strValue), "%hu", binValue ); // AUDIT: Using sizeof(strValue) is safe. - - xmp->SetProperty ( xmpNS, xmpProp, strValue ); - - } catch ( ... ) { - // Do nothing, let other imports proceed. - // ? Notify client? - } - -} // ImportSingleTIFF_Byte - -// ================================================================================================= -// ImportSingleTIFF_SByte -// ====================== - -static void -ImportSingleTIFF_SByte ( const TIFF_Manager::TagInfo & tagInfo, - SXMPMeta * xmp, const char * xmpNS, const char * xmpProp ) -{ - try { // Don't let errors with one stop the others. - - XMP_Int8 binValue = *((XMP_Int8*)tagInfo.dataPtr); - - char strValue[20]; - snprintf ( strValue, sizeof(strValue), "%hd", binValue ); // AUDIT: Using sizeof(strValue) is safe. - - xmp->SetProperty ( xmpNS, xmpProp, strValue ); - - } catch ( ... ) { - // Do nothing, let other imports proceed. - // ? Notify client? - } - -} // ImportSingleTIFF_SByte - -// ================================================================================================= -// ImportSingleTIFF_SShort -// ======================= - -static void -ImportSingleTIFF_SShort ( const TIFF_Manager::TagInfo & tagInfo, const bool nativeEndian, - SXMPMeta * xmp, const char * xmpNS, const char * xmpProp ) -{ - try { // Don't let errors with one stop the others. - - XMP_Int16 binValue = *((XMP_Int16*)tagInfo.dataPtr); - if ( ! nativeEndian ) Flip2 ( &binValue ); - - char strValue[20]; - snprintf ( strValue, sizeof(strValue), "%hd", binValue ); // AUDIT: Using sizeof(strValue) is safe. - - xmp->SetProperty ( xmpNS, xmpProp, strValue ); - - } catch ( ... ) { - // Do nothing, let other imports proceed. - // ? Notify client? - } - -} // ImportSingleTIFF_SShort - -// ================================================================================================= -// ImportSingleTIFF_SLong -// ====================== - -static void -ImportSingleTIFF_SLong ( const TIFF_Manager::TagInfo & tagInfo, const bool nativeEndian, - SXMPMeta * xmp, const char * xmpNS, const char * xmpProp ) -{ - try { // Don't let errors with one stop the others. - - XMP_Int32 binValue = *((XMP_Int32*)tagInfo.dataPtr); - if ( ! nativeEndian ) Flip4 ( &binValue ); - - char strValue[20]; - snprintf ( strValue, sizeof(strValue), "%ld", (long)binValue ); // AUDIT: Using sizeof(strValue) is safe. - - xmp->SetProperty ( xmpNS, xmpProp, strValue ); - - } catch ( ... ) { - // Do nothing, let other imports proceed. - // ? Notify client? - } - -} // ImportSingleTIFF_SLong - -// ================================================================================================= -// ImportSingleTIFF_Float -// ====================== - -static void -ImportSingleTIFF_Float ( const TIFF_Manager::TagInfo & tagInfo, const bool nativeEndian, - SXMPMeta * xmp, const char * xmpNS, const char * xmpProp ) -{ - try { // Don't let errors with one stop the others. - - float binValue = *((float*)tagInfo.dataPtr); - if ( ! nativeEndian ) Flip4 ( &binValue ); - - xmp->SetProperty_Float ( xmpNS, xmpProp, binValue ); - - } catch ( ... ) { - // Do nothing, let other imports proceed. - // ? Notify client? - } - -} // ImportSingleTIFF_Float - -// ================================================================================================= -// ImportSingleTIFF_Double -// ======================= - -static void -ImportSingleTIFF_Double ( const TIFF_Manager::TagInfo & tagInfo, const bool nativeEndian, - SXMPMeta * xmp, const char * xmpNS, const char * xmpProp ) -{ - try { // Don't let errors with one stop the others. - - double binValue = *((double*)tagInfo.dataPtr); - if ( ! nativeEndian ) Flip8 ( &binValue ); - - xmp->SetProperty_Float ( xmpNS, xmpProp, binValue ); // ! Yes, SetProperty_Float. - - } catch ( ... ) { - // Do nothing, let other imports proceed. - // ? Notify client? - } - -} // ImportSingleTIFF_Double - -// ================================================================================================= -// ImportSingleTIFF -// ================ - -static void -ImportSingleTIFF ( const TIFF_Manager::TagInfo & tagInfo, const bool nativeEndian, - SXMPMeta * xmp, const char * xmpNS, const char * xmpProp ) -{ - - // We've got a tag to map to XMP, decide how based on actual type and the expected count. Using - // the actual type eliminates a ShortOrLong case. Using the expected count is needed to know - // whether to create an XMP array. The actual count for an array could be 1. Put the most - // common cases first for better iCache utilization. - - switch ( tagInfo.type ) { - - case kTIFF_ShortType : - ImportSingleTIFF_Short ( tagInfo, nativeEndian, xmp, xmpNS, xmpProp ); - break; - - case kTIFF_LongType : - ImportSingleTIFF_Long ( tagInfo, nativeEndian, xmp, xmpNS, xmpProp ); - break; - - case kTIFF_RationalType : - ImportSingleTIFF_Rational ( tagInfo, nativeEndian, xmp, xmpNS, xmpProp ); - break; - - case kTIFF_SRationalType : - ImportSingleTIFF_SRational ( tagInfo, nativeEndian, xmp, xmpNS, xmpProp ); - break; - - case kTIFF_ASCIIType : - ImportSingleTIFF_ASCII ( tagInfo, xmp, xmpNS, xmpProp ); - break; - - case kTIFF_ByteType : - ImportSingleTIFF_Byte ( tagInfo, xmp, xmpNS, xmpProp ); - break; - - case kTIFF_SByteType : - ImportSingleTIFF_SByte ( tagInfo, xmp, xmpNS, xmpProp ); - break; - - case kTIFF_SShortType : - ImportSingleTIFF_SShort ( tagInfo, nativeEndian, xmp, xmpNS, xmpProp ); - break; - - case kTIFF_SLongType : - ImportSingleTIFF_SLong ( tagInfo, nativeEndian, xmp, xmpNS, xmpProp ); - break; - - case kTIFF_FloatType : - ImportSingleTIFF_Float ( tagInfo, nativeEndian, xmp, xmpNS, xmpProp ); - break; - - case kTIFF_DoubleType : - ImportSingleTIFF_Double ( tagInfo, nativeEndian, xmp, xmpNS, xmpProp ); - break; - - } - -} // ImportSingleTIFF - -// ================================================================================================= -// ================================================================================================= - -// ================================================================================================= -// ImportArrayTIFF_Short -// ===================== - -static void -ImportArrayTIFF_Short ( const TIFF_Manager::TagInfo & tagInfo, const bool nativeEndian, - SXMPMeta * xmp, const char * xmpNS, const char * xmpProp ) -{ - try { // Don't let errors with one stop the others. - - XMP_Uns16 * binPtr = (XMP_Uns16*)tagInfo.dataPtr; - - xmp->DeleteProperty ( xmpNS, xmpProp ); // ! Don't keep appending, create a new array. - - for ( size_t i = 0; i < tagInfo.count; ++i, ++binPtr ) { - - XMP_Uns16 binValue = *binPtr; - if ( ! nativeEndian ) binValue = Flip2 ( binValue ); - - char strValue[20]; - snprintf ( strValue, sizeof(strValue), "%hu", binValue ); // AUDIT: Using sizeof(strValue) is safe. - - xmp->AppendArrayItem ( xmpNS, xmpProp, kXMP_PropArrayIsOrdered, strValue ); - - } - - } catch ( ... ) { - // Do nothing, let other imports proceed. - // ? Notify client? - } - -} // ImportArrayTIFF_Short - -// ================================================================================================= -// ImportArrayTIFF_Long -// ==================== - -static void -ImportArrayTIFF_Long ( const TIFF_Manager::TagInfo & tagInfo, const bool nativeEndian, - SXMPMeta * xmp, const char * xmpNS, const char * xmpProp ) -{ - try { // Don't let errors with one stop the others. - - XMP_Uns32 * binPtr = (XMP_Uns32*)tagInfo.dataPtr; - - xmp->DeleteProperty ( xmpNS, xmpProp ); // ! Don't keep appending, create a new array. - - for ( size_t i = 0; i < tagInfo.count; ++i, ++binPtr ) { - - XMP_Uns32 binValue = *binPtr; - if ( ! nativeEndian ) binValue = Flip4 ( binValue ); - - char strValue[20]; - snprintf ( strValue, sizeof(strValue), "%lu", (unsigned long)binValue ); // AUDIT: Using sizeof(strValue) is safe. - - xmp->AppendArrayItem ( xmpNS, xmpProp, kXMP_PropArrayIsOrdered, strValue ); - - } - - } catch ( ... ) { - // Do nothing, let other imports proceed. - // ? Notify client? - } - -} // ImportArrayTIFF_Long - -// ================================================================================================= -// ImportArrayTIFF_Rational -// ======================== - -static void -ImportArrayTIFF_Rational ( const TIFF_Manager::TagInfo & tagInfo, const bool nativeEndian, - SXMPMeta * xmp, const char * xmpNS, const char * xmpProp ) -{ - try { // Don't let errors with one stop the others. - - XMP_Uns32 * binPtr = (XMP_Uns32*)tagInfo.dataPtr; - - xmp->DeleteProperty ( xmpNS, xmpProp ); // ! Don't keep appending, create a new array. - - for ( size_t i = 0; i < tagInfo.count; ++i, binPtr += 2 ) { - - XMP_Uns32 binNum = binPtr[0]; - XMP_Uns32 binDenom = binPtr[1]; - if ( ! nativeEndian ) { - binNum = Flip4 ( binNum ); - binDenom = Flip4 ( binDenom ); - } - - char strValue[40]; - snprintf ( strValue, sizeof(strValue), "%lu/%lu", (unsigned long)binNum, (unsigned long)binDenom ); // AUDIT: Using sizeof(strValue) is safe. - - xmp->AppendArrayItem ( xmpNS, xmpProp, kXMP_PropArrayIsOrdered, strValue ); - - } - - } catch ( ... ) { - // Do nothing, let other imports proceed. - // ? Notify client? - } - -} // ImportArrayTIFF_Rational - -// ================================================================================================= -// ImportArrayTIFF_SRational -// ========================= - -static void -ImportArrayTIFF_SRational ( const TIFF_Manager::TagInfo & tagInfo, const bool nativeEndian, - SXMPMeta * xmp, const char * xmpNS, const char * xmpProp ) -{ - try { // Don't let errors with one stop the others. - - XMP_Int32 * binPtr = (XMP_Int32*)tagInfo.dataPtr; - - xmp->DeleteProperty ( xmpNS, xmpProp ); // ! Don't keep appending, create a new array. - - for ( size_t i = 0; i < tagInfo.count; ++i, binPtr += 2 ) { - - XMP_Int32 binNum = binPtr[0]; - XMP_Int32 binDenom = binPtr[1]; - if ( ! nativeEndian ) { - Flip4 ( &binNum ); - Flip4 ( &binDenom ); - } - - char strValue[40]; - snprintf ( strValue, sizeof(strValue), "%ld/%ld", (long)binNum, (long)binDenom ); // AUDIT: Using sizeof(strValue) is safe. - - xmp->AppendArrayItem ( xmpNS, xmpProp, kXMP_PropArrayIsOrdered, strValue ); - - } - - } catch ( ... ) { - // Do nothing, let other imports proceed. - // ? Notify client? - } - -} // ImportArrayTIFF_SRational - -// ================================================================================================= -// ImportArrayTIFF_ASCII -// ===================== - -static void -ImportArrayTIFF_ASCII ( const TIFF_Manager::TagInfo & tagInfo, - SXMPMeta * xmp, const char * xmpNS, const char * xmpProp ) -{ - try { // Don't let errors with one stop the others. - - TrimTrailingSpaces ( (TIFF_Manager::TagInfo*) &tagInfo ); - if ( tagInfo.dataLen == 0 ) return; // Ignore empty tags. - - const char * chPtr = (const char *)tagInfo.dataPtr; - const char * chEnd = chPtr + tagInfo.dataLen; - const bool hasNul = (chPtr[tagInfo.dataLen-1] == 0); - const bool isUTF8 = ReconcileUtils::IsUTF8 ( chPtr, tagInfo.dataLen ); - - std::string strValue; - - if ( (! isUTF8) || (! hasNul) ) { - if ( isUTF8 ) { - strValue.assign ( chPtr, tagInfo.dataLen ); - } else { - if ( ignoreLocalText ) return; - ReconcileUtils::LocalToUTF8 ( chPtr, tagInfo.dataLen, &strValue ); - } - chPtr = strValue.c_str(); - chEnd = chPtr + strValue.size(); - } - - xmp->DeleteProperty ( xmpNS, xmpProp ); // ! Don't keep appending, create a new array. - - for ( ; chPtr < chEnd; chPtr += (strlen(chPtr) + 1) ) { - xmp->AppendArrayItem ( xmpNS, xmpProp, kXMP_PropArrayIsOrdered, chPtr ); - } - - } catch ( ... ) { - // Do nothing, let other imports proceed. - // ? Notify client? - } - -} // ImportArrayTIFF_ASCII - -// ================================================================================================= -// ImportArrayTIFF_Byte -// ==================== - -static void -ImportArrayTIFF_Byte ( const TIFF_Manager::TagInfo & tagInfo, - SXMPMeta * xmp, const char * xmpNS, const char * xmpProp ) -{ - try { // Don't let errors with one stop the others. - - XMP_Uns8 * binPtr = (XMP_Uns8*)tagInfo.dataPtr; - - xmp->DeleteProperty ( xmpNS, xmpProp ); // ! Don't keep appending, create a new array. - - for ( size_t i = 0; i < tagInfo.count; ++i, ++binPtr ) { - - XMP_Uns8 binValue = *binPtr; - - char strValue[20]; - snprintf ( strValue, sizeof(strValue), "%hu", binValue ); // AUDIT: Using sizeof(strValue) is safe. - - xmp->AppendArrayItem ( xmpNS, xmpProp, kXMP_PropArrayIsOrdered, strValue ); - - } - - } catch ( ... ) { - // Do nothing, let other imports proceed. - // ? Notify client? - } - -} // ImportArrayTIFF_Byte - -// ================================================================================================= -// ImportArrayTIFF_SByte -// ===================== - -static void -ImportArrayTIFF_SByte ( const TIFF_Manager::TagInfo & tagInfo, - SXMPMeta * xmp, const char * xmpNS, const char * xmpProp ) -{ - try { // Don't let errors with one stop the others. - - XMP_Int8 * binPtr = (XMP_Int8*)tagInfo.dataPtr; - - xmp->DeleteProperty ( xmpNS, xmpProp ); // ! Don't keep appending, create a new array. - - for ( size_t i = 0; i < tagInfo.count; ++i, ++binPtr ) { - - XMP_Int8 binValue = *binPtr; - - char strValue[20]; - snprintf ( strValue, sizeof(strValue), "%hd", binValue ); // AUDIT: Using sizeof(strValue) is safe. - - xmp->AppendArrayItem ( xmpNS, xmpProp, kXMP_PropArrayIsOrdered, strValue ); - - } - - } catch ( ... ) { - // Do nothing, let other imports proceed. - // ? Notify client? - } - -} // ImportArrayTIFF_SByte - -// ================================================================================================= -// ImportArrayTIFF_SShort -// ====================== - -static void -ImportArrayTIFF_SShort ( const TIFF_Manager::TagInfo & tagInfo, const bool nativeEndian, - SXMPMeta * xmp, const char * xmpNS, const char * xmpProp ) -{ - try { // Don't let errors with one stop the others. - - XMP_Int16 * binPtr = (XMP_Int16*)tagInfo.dataPtr; - - xmp->DeleteProperty ( xmpNS, xmpProp ); // ! Don't keep appending, create a new array. - - for ( size_t i = 0; i < tagInfo.count; ++i, ++binPtr ) { - - XMP_Int16 binValue = *binPtr; - if ( ! nativeEndian ) Flip2 ( &binValue ); - - char strValue[20]; - snprintf ( strValue, sizeof(strValue), "%hd", binValue ); // AUDIT: Using sizeof(strValue) is safe. - - xmp->AppendArrayItem ( xmpNS, xmpProp, kXMP_PropArrayIsOrdered, strValue ); - - } - - } catch ( ... ) { - // Do nothing, let other imports proceed. - // ? Notify client? - } - -} // ImportArrayTIFF_SShort - -// ================================================================================================= -// ImportArrayTIFF_SLong -// ===================== - -static void -ImportArrayTIFF_SLong ( const TIFF_Manager::TagInfo & tagInfo, const bool nativeEndian, - SXMPMeta * xmp, const char * xmpNS, const char * xmpProp ) -{ - try { // Don't let errors with one stop the others. - - XMP_Int32 * binPtr = (XMP_Int32*)tagInfo.dataPtr; - - xmp->DeleteProperty ( xmpNS, xmpProp ); // ! Don't keep appending, create a new array. - - for ( size_t i = 0; i < tagInfo.count; ++i, ++binPtr ) { - - XMP_Int32 binValue = *binPtr; - if ( ! nativeEndian ) Flip4 ( &binValue ); - - char strValue[20]; - snprintf ( strValue, sizeof(strValue), "%ld", (long)binValue ); // AUDIT: Using sizeof(strValue) is safe. - - xmp->AppendArrayItem ( xmpNS, xmpProp, kXMP_PropArrayIsOrdered, strValue ); - - } - - } catch ( ... ) { - // Do nothing, let other imports proceed. - // ? Notify client? - } - -} // ImportArrayTIFF_SLong - -// ================================================================================================= -// ImportArrayTIFF_Float -// ===================== - -static void -ImportArrayTIFF_Float ( const TIFF_Manager::TagInfo & tagInfo, const bool nativeEndian, - SXMPMeta * xmp, const char * xmpNS, const char * xmpProp ) -{ - try { // Don't let errors with one stop the others. - - float * binPtr = (float*)tagInfo.dataPtr; - - xmp->DeleteProperty ( xmpNS, xmpProp ); // ! Don't keep appending, create a new array. - - for ( size_t i = 0; i < tagInfo.count; ++i, ++binPtr ) { - - float binValue = *binPtr; - if ( ! nativeEndian ) Flip4 ( &binValue ); - - std::string strValue; - SXMPUtils::ConvertFromFloat ( binValue, "", &strValue ); - - xmp->AppendArrayItem ( xmpNS, xmpProp, kXMP_PropArrayIsOrdered, strValue.c_str() ); - - } - - } catch ( ... ) { - // Do nothing, let other imports proceed. - // ? Notify client? - } - -} // ImportArrayTIFF_Float - -// ================================================================================================= -// ImportArrayTIFF_Double -// ====================== - -static void -ImportArrayTIFF_Double ( const TIFF_Manager::TagInfo & tagInfo, const bool nativeEndian, - SXMPMeta * xmp, const char * xmpNS, const char * xmpProp ) -{ - try { // Don't let errors with one stop the others. - - double * binPtr = (double*)tagInfo.dataPtr; - - xmp->DeleteProperty ( xmpNS, xmpProp ); // ! Don't keep appending, create a new array. - - for ( size_t i = 0; i < tagInfo.count; ++i, ++binPtr ) { - - double binValue = *binPtr; - if ( ! nativeEndian ) Flip8 ( &binValue ); - - std::string strValue; - SXMPUtils::ConvertFromFloat ( binValue, "", &strValue ); // ! Yes, ConvertFromFloat. - - xmp->AppendArrayItem ( xmpNS, xmpProp, kXMP_PropArrayIsOrdered, strValue.c_str() ); - - } - - } catch ( ... ) { - // Do nothing, let other imports proceed. - // ? Notify client? - } - -} // ImportArrayTIFF_Double - -// ================================================================================================= -// ImportArrayTIFF -// =============== - -static void -ImportArrayTIFF ( const TIFF_Manager::TagInfo & tagInfo, const bool nativeEndian, - SXMPMeta * xmp, const char * xmpNS, const char * xmpProp ) -{ - - // We've got a tag to map to XMP, decide how based on actual type and the expected count. Using - // the actual type eliminates a ShortOrLong case. Using the expected count is needed to know - // whether to create an XMP array. The actual count for an array could be 1. Put the most - // common cases first for better iCache utilization. - - switch ( tagInfo.type ) { - - case kTIFF_ShortType : - ImportArrayTIFF_Short ( tagInfo, nativeEndian, xmp, xmpNS, xmpProp ); - break; - - case kTIFF_LongType : - ImportArrayTIFF_Long ( tagInfo, nativeEndian, xmp, xmpNS, xmpProp ); - break; - - case kTIFF_RationalType : - ImportArrayTIFF_Rational ( tagInfo, nativeEndian, xmp, xmpNS, xmpProp ); - break; - - case kTIFF_SRationalType : - ImportArrayTIFF_SRational ( tagInfo, nativeEndian, xmp, xmpNS, xmpProp ); - break; - - case kTIFF_ASCIIType : - ImportArrayTIFF_ASCII ( tagInfo, xmp, xmpNS, xmpProp ); - break; - - case kTIFF_ByteType : - ImportArrayTIFF_Byte ( tagInfo, xmp, xmpNS, xmpProp ); - break; - - case kTIFF_SByteType : - ImportArrayTIFF_SByte ( tagInfo, xmp, xmpNS, xmpProp ); - break; - - case kTIFF_SShortType : - ImportArrayTIFF_SShort ( tagInfo, nativeEndian, xmp, xmpNS, xmpProp ); - break; - - case kTIFF_SLongType : - ImportArrayTIFF_SLong ( tagInfo, nativeEndian, xmp, xmpNS, xmpProp ); - break; - - case kTIFF_FloatType : - ImportArrayTIFF_Float ( tagInfo, nativeEndian, xmp, xmpNS, xmpProp ); - break; - - case kTIFF_DoubleType : - ImportArrayTIFF_Double ( tagInfo, nativeEndian, xmp, xmpNS, xmpProp ); - break; - - } - -} // ImportArrayTIFF - -// ================================================================================================= -// ImportTIFF_CheckStandardMapping -// =============================== - -static bool -ImportTIFF_CheckStandardMapping ( const TIFF_Manager::TagInfo & tagInfo, const TIFF_MappingToXMP & mapInfo ) -{ - XMP_Assert ( (kTIFF_ByteType <= tagInfo.type) && (tagInfo.type <= kTIFF_LastType) ); - XMP_Assert ( mapInfo.type <= kTIFF_LastType ); - - if ( (tagInfo.type < kTIFF_ByteType) || (tagInfo.type > kTIFF_LastType) ) return false; - - if ( tagInfo.type != mapInfo.type ) { - if ( mapInfo.type != kTIFF_ShortOrLongType ) return false; - if ( (tagInfo.type != kTIFF_ShortType) && (tagInfo.type != kTIFF_LongType) ) return false; - } - - if ( (tagInfo.count != mapInfo.count) && // Maybe there is a problem because the counts don't match. - // (mapInfo.count != kAnyCount) && ... don't need this because of the new check below ... - (mapInfo.count == 1) ) return false; // Be tolerant of mismatch in expected array size. - - return true; - -} // ImportTIFF_CheckStandardMapping - -// ================================================================================================= -// ImportTIFF_StandardMappings -// =========================== - -static void -ImportTIFF_StandardMappings ( XMP_Uns8 ifd, const TIFF_Manager & tiff, SXMPMeta * xmp ) -{ - const bool nativeEndian = tiff.IsNativeEndian(); - TIFF_Manager::TagInfo tagInfo; - - const TIFF_MappingToXMP * mappings = 0; - const char * xmpNS = 0; - - if ( ifd == kTIFF_PrimaryIFD ) { - mappings = sPrimaryIFDMappings; - xmpNS = kXMP_NS_TIFF; - } else if ( ifd == kTIFF_ExifIFD ) { - mappings = sExifIFDMappings; - xmpNS = kXMP_NS_EXIF; - } else if ( ifd == kTIFF_GPSInfoIFD ) { - mappings = sGPSInfoIFDMappings; - xmpNS = kXMP_NS_EXIF; // ! Yes, the GPS Info tags go into the exif: namespace. - } else { - XMP_Throw ( "Invalid IFD for standard mappings", kXMPErr_InternalFailure ); - } - - for ( size_t i = 0; mappings[i].id != 0xFFFF; ++i ) { - - try { // Don't let errors with one stop the others. - - const TIFF_MappingToXMP & mapInfo = mappings[i]; - const bool mapSingle = ((mapInfo.count == 1) || (mapInfo.type == kTIFF_ASCIIType)); - - if ( mapInfo.name[0] == 0 ) continue; // Skip special mappings, handled higher up. - - bool found = tiff.GetTag ( ifd, mapInfo.id, &tagInfo ); - if ( ! found ) continue; - - XMP_Assert ( tagInfo.type != kTIFF_UndefinedType ); // These must have a special mapping. - if ( tagInfo.type == kTIFF_UndefinedType ) continue; - if ( ! ImportTIFF_CheckStandardMapping ( tagInfo, mapInfo ) ) continue; - - if ( mapSingle ) { - ImportSingleTIFF ( tagInfo, nativeEndian, xmp, xmpNS, mapInfo.name ); - } else { - ImportArrayTIFF ( tagInfo, nativeEndian, xmp, xmpNS, mapInfo.name ); - } - - } catch ( ... ) { - - // Do nothing, let other imports proceed. - // ? Notify client? - - } - - } - -} // ImportTIFF_StandardMappings - -// ================================================================================================= -// ================================================================================================= - -// ================================================================================================= -// ImportTIFF_Date -// =============== -// -// Convert an Exif 2.2 master date/time tag plus associated fractional seconds to an XMP date/time. -// The Exif date/time part is a 20 byte ASCII value formatted as "YYYY:MM:DD HH:MM:SS" with a -// terminating nul. Any of the numeric portions can be blanks if unknown. The fractional seconds -// are a nul terminated ASCII string with possible space padding. They are literally the fractional -// part, the digits that would be to the right of the decimal point. - -static void -ImportTIFF_Date ( const TIFF_Manager & tiff, const TIFF_Manager::TagInfo & dateInfo, - SXMPMeta * xmp, const char * xmpNS, const char * xmpProp ) -{ - XMP_Uns16 secID; - switch ( dateInfo.id ) { - case kTIFF_DateTime : secID = kTIFF_SubSecTime; break; - case kTIFF_DateTimeOriginal : secID = kTIFF_SubSecTimeOriginal; break; - case kTIFF_DateTimeDigitized : secID = kTIFF_SubSecTimeDigitized; break; - } - - try { // Don't let errors with one stop the others. - - if ( (dateInfo.type != kTIFF_ASCIIType) || (dateInfo.count != 20) ) return; - - const char * dateStr = (const char *) dateInfo.dataPtr; - if ( (dateStr[4] != ':') || (dateStr[7] != ':') || - (dateStr[10] != ' ') || (dateStr[13] != ':') || (dateStr[16] != ':') ) return; - - XMP_DateTime binValue; - - binValue.year = GatherInt ( &dateStr[0], 4 ); - binValue.month = GatherInt ( &dateStr[5], 2 ); - binValue.day = GatherInt ( &dateStr[8], 2 ); - if ( (binValue.year != 0) | (binValue.month != 0) | (binValue.day != 0) ) binValue.hasDate = true; - - binValue.hour = GatherInt ( &dateStr[11], 2 ); - binValue.minute = GatherInt ( &dateStr[14], 2 ); - binValue.second = GatherInt ( &dateStr[17], 2 ); - binValue.nanoSecond = 0; // Get the fractional seconds later. - if ( (binValue.hour != 0) | (binValue.minute != 0) | (binValue.second != 0) ) binValue.hasTime = true; - - binValue.tzSign = 0; // ! Separate assignment, avoid VS integer truncation warning. - binValue.tzHour = binValue.tzMinute = 0; - binValue.hasTimeZone = false; // Exif times have no zone. - - // *** Consider looking at the TIFF/EP TimeZoneOffset tag? - - TIFF_Manager::TagInfo secInfo; - bool found = tiff.GetTag ( kTIFF_ExifIFD, secID, &secInfo ); // ! Subseconds are all in the Exif IFD. - - if ( found && (secInfo.type == kTIFF_ASCIIType) ) { - const char * fracPtr = (const char *) secInfo.dataPtr; - binValue.nanoSecond = GatherInt ( fracPtr, secInfo.dataLen ); - size_t digits = 0; - for ( ; (('0' <= *fracPtr) && (*fracPtr <= '9')); ++fracPtr ) ++digits; - for ( ; digits < 9; ++digits ) binValue.nanoSecond *= 10; - if ( binValue.nanoSecond != 0 ) binValue.hasTime = true; - } - - xmp->SetProperty_Date ( xmpNS, xmpProp, binValue ); - - } catch ( ... ) { - // Do nothing, let other imports proceed. - // ? Notify client? - } - -} // ImportTIFF_Date - -// ================================================================================================= -// ImportTIFF_LocTextASCII -// ======================= - -static void -ImportTIFF_LocTextASCII ( const TIFF_Manager & tiff, XMP_Uns8 ifd, XMP_Uns16 tagID, - SXMPMeta * xmp, const char * xmpNS, const char * xmpProp ) -{ - try { // Don't let errors with one stop the others. - - TIFF_Manager::TagInfo tagInfo; - - bool found = tiff.GetTag ( ifd, tagID, &tagInfo ); - if ( (! found) || (tagInfo.type != kTIFF_ASCIIType) ) return; - - const char * chPtr = (const char *)tagInfo.dataPtr; - const bool hasNul = (chPtr[tagInfo.dataLen-1] == 0); - const bool isUTF8 = ReconcileUtils::IsUTF8 ( chPtr, tagInfo.dataLen ); - - if ( isUTF8 && hasNul ) { - xmp->SetLocalizedText ( xmpNS, xmpProp, "", "x-default", chPtr ); - } else { - std::string strValue; - if ( isUTF8 ) { - strValue.assign ( chPtr, tagInfo.dataLen ); - } else { - if ( ignoreLocalText ) return; - ReconcileUtils::LocalToUTF8 ( chPtr, tagInfo.dataLen, &strValue ); - } - xmp->SetLocalizedText ( xmpNS, xmpProp, "", "x-default", strValue.c_str() ); - } - - } catch ( ... ) { - // Do nothing, let other imports proceed. - // ? Notify client? - } - -} // ImportTIFF_LocTextASCII - -// ================================================================================================= -// ImportTIFF_EncodedString -// ======================== - -static void -ImportTIFF_EncodedString ( const TIFF_Manager & tiff, const TIFF_Manager::TagInfo & tagInfo, - SXMPMeta * xmp, const char * xmpNS, const char * xmpProp, bool isLangAlt = false ) -{ - try { // Don't let errors with one stop the others. - - std::string strValue; - - bool ok = tiff.DecodeString ( tagInfo.dataPtr, tagInfo.dataLen, &strValue ); - if ( ! ok ) return; - - if ( ! isLangAlt ) { - xmp->SetProperty ( xmpNS, xmpProp, strValue.c_str() ); - } else { - xmp->SetLocalizedText ( xmpNS, xmpProp, "", "x-default", strValue.c_str() ); - } - - } catch ( ... ) { - // Do nothing, let other imports proceed. - // ? Notify client? - } - -} // ImportTIFF_EncodedString - -// ================================================================================================= -// ImportTIFF_Flash -// ================ - -static void -ImportTIFF_Flash ( const TIFF_Manager::TagInfo & tagInfo, bool nativeEndian, - SXMPMeta * xmp, const char * xmpNS, const char * xmpProp ) -{ - try { // Don't let errors with one stop the others. - - XMP_Uns16 binValue = *((XMP_Uns16*)tagInfo.dataPtr); - if ( ! nativeEndian ) binValue = Flip2 ( binValue ); - - bool fired = (bool)(binValue & 1); // Avoid implicit 0/1 conversion. - int rtrn = (binValue >> 1) & 3; - int mode = (binValue >> 3) & 3; - bool function = (bool)((binValue >> 5) & 1); - bool redEye = (bool)((binValue >> 6) & 1); - - static const char * sTwoBits[] = { "0", "1", "2", "3" }; - - xmp->SetStructField ( kXMP_NS_EXIF, "Flash", kXMP_NS_EXIF, "Fired", (fired ? kXMP_TrueStr : kXMP_FalseStr) ); - xmp->SetStructField ( kXMP_NS_EXIF, "Flash", kXMP_NS_EXIF, "Return", sTwoBits[rtrn] ); - xmp->SetStructField ( kXMP_NS_EXIF, "Flash", kXMP_NS_EXIF, "Mode", sTwoBits[mode] ); - xmp->SetStructField ( kXMP_NS_EXIF, "Flash", kXMP_NS_EXIF, "Function", (function ? kXMP_TrueStr : kXMP_FalseStr) ); - xmp->SetStructField ( kXMP_NS_EXIF, "Flash", kXMP_NS_EXIF, "RedEyeMode", (redEye ? kXMP_TrueStr : kXMP_FalseStr) ); - - } catch ( ... ) { - // Do nothing, let other imports proceed. - // ? Notify client? - } - -} // ImportTIFF_Flash - -// ================================================================================================= -// ImportTIFF_OECFTable -// ==================== -// -// Although the XMP for the OECF and SFR tables is the same, the Exif is not. The OECF table has -// signed rational values and the SFR table has unsigned. - -static void -ImportTIFF_OECFTable ( const TIFF_Manager::TagInfo & tagInfo, bool nativeEndian, - SXMPMeta * xmp, const char * xmpNS, const char * xmpProp ) -{ - try { // Don't let errors with one stop the others. - - const XMP_Uns8 * bytePtr = (XMP_Uns8*)tagInfo.dataPtr; - const XMP_Uns8 * byteEnd = bytePtr + tagInfo.dataLen; - - XMP_Uns16 columns = *((XMP_Uns16*)bytePtr); - XMP_Uns16 rows = *((XMP_Uns16*)(bytePtr+2)); - if ( ! nativeEndian ) { - columns = Flip2 ( columns ); - rows = Flip2 ( rows ); - } - - char buffer[40]; - - snprintf ( buffer, sizeof(buffer), "%d", columns ); // AUDIT: Use of sizeof(buffer) is safe. - xmp->SetStructField ( xmpNS, xmpProp, kXMP_NS_EXIF, "Columns", buffer ); - snprintf ( buffer, sizeof(buffer), "%d", rows ); // AUDIT: Use of sizeof(buffer) is safe. - xmp->SetStructField ( xmpNS, xmpProp, kXMP_NS_EXIF, "Rows", buffer ); - - std::string arrayPath; - - SXMPUtils::ComposeStructFieldPath ( xmpNS, xmpProp, kXMP_NS_EXIF, "Names", &arrayPath ); - - bytePtr += 4; // Move to the list of names. - for ( size_t i = columns; i > 0; --i ) { - size_t nameLen = strlen((XMP_StringPtr)bytePtr) + 1; // ! Include the terminating nul. - if ( (bytePtr + nameLen) > byteEnd ) { xmp->DeleteProperty ( xmpNS, xmpProp ); return; }; - xmp->AppendArrayItem ( xmpNS, arrayPath.c_str(), kXMP_PropArrayIsOrdered, (XMP_StringPtr)bytePtr ); - bytePtr += nameLen; - } - - if ( (byteEnd - bytePtr) != (8 * columns * rows) ) { xmp->DeleteProperty ( xmpNS, xmpProp ); return; }; // Make sure the values are present. - SXMPUtils::ComposeStructFieldPath ( xmpNS, xmpProp, kXMP_NS_EXIF, "Values", &arrayPath ); - - XMP_Int32 * binPtr = (XMP_Int32*)bytePtr; - for ( size_t i = (columns * rows); i > 0; --i, binPtr += 2 ) { - - XMP_Int32 binNum = binPtr[0]; - XMP_Int32 binDenom = binPtr[1]; - if ( ! nativeEndian ) { - Flip4 ( &binNum ); - Flip4 ( &binDenom ); - } - - snprintf ( buffer, sizeof(buffer), "%ld/%ld", (long)binNum, (long)binDenom ); // AUDIT: Use of sizeof(buffer) is safe. - - xmp->AppendArrayItem ( xmpNS, arrayPath.c_str(), kXMP_PropArrayIsOrdered, buffer ); - - } - - return; - - } catch ( ... ) { - // Do nothing, let other imports proceed. - // ? Notify client? - } - -} // ImportTIFF_OECFTable - -// ================================================================================================= -// ImportTIFF_SFRTable -// =================== -// -// Although the XMP for the OECF and SFR tables is the same, the Exif is not. The OECF table has -// signed rational values and the SFR table has unsigned. - -static void -ImportTIFF_SFRTable ( const TIFF_Manager::TagInfo & tagInfo, bool nativeEndian, - SXMPMeta * xmp, const char * xmpNS, const char * xmpProp ) -{ - try { // Don't let errors with one stop the others. - - const XMP_Uns8 * bytePtr = (XMP_Uns8*)tagInfo.dataPtr; - const XMP_Uns8 * byteEnd = bytePtr + tagInfo.dataLen; - - XMP_Uns16 columns = *((XMP_Uns16*)bytePtr); - XMP_Uns16 rows = *((XMP_Uns16*)(bytePtr+2)); - if ( ! nativeEndian ) { - columns = Flip2 ( columns ); - rows = Flip2 ( rows ); - } - - char buffer[40]; - - snprintf ( buffer, sizeof(buffer), "%d", columns ); // AUDIT: Use of sizeof(buffer) is safe. - xmp->SetStructField ( xmpNS, xmpProp, kXMP_NS_EXIF, "Columns", buffer ); - snprintf ( buffer, sizeof(buffer), "%d", rows ); // AUDIT: Use of sizeof(buffer) is safe. - xmp->SetStructField ( xmpNS, xmpProp, kXMP_NS_EXIF, "Rows", buffer ); - - std::string arrayPath; - - SXMPUtils::ComposeStructFieldPath ( xmpNS, xmpProp, kXMP_NS_EXIF, "Names", &arrayPath ); - - bytePtr += 4; // Move to the list of names. - for ( size_t i = columns; i > 0; --i ) { - size_t nameLen = strlen((XMP_StringPtr)bytePtr) + 1; // ! Include the terminating nul. - if ( (bytePtr + nameLen) > byteEnd ) { xmp->DeleteProperty ( xmpNS, xmpProp ); return; }; - xmp->AppendArrayItem ( xmpNS, arrayPath.c_str(), kXMP_PropArrayIsOrdered, (XMP_StringPtr)bytePtr ); - bytePtr += nameLen; - } - - if ( (byteEnd - bytePtr) != (8 * columns * rows) ) { xmp->DeleteProperty ( xmpNS, xmpProp ); return; }; // Make sure the values are present. - SXMPUtils::ComposeStructFieldPath ( xmpNS, xmpProp, kXMP_NS_EXIF, "Values", &arrayPath ); - - XMP_Uns32 * binPtr = (XMP_Uns32*)bytePtr; - for ( size_t i = (columns * rows); i > 0; --i, binPtr += 2 ) { - - XMP_Uns32 binNum = binPtr[0]; - XMP_Uns32 binDenom = binPtr[1]; - if ( ! nativeEndian ) { - binNum = Flip4 ( binNum ); - binDenom = Flip4 ( binDenom ); - } - - snprintf ( buffer, sizeof(buffer), "%lu/%lu", (unsigned long)binNum, (unsigned long)binDenom ); // AUDIT: Use of sizeof(buffer) is safe. - - xmp->AppendArrayItem ( xmpNS, arrayPath.c_str(), kXMP_PropArrayIsOrdered, buffer ); - - } - - return; - - } catch ( ... ) { - // Do nothing, let other imports proceed. - // ? Notify client? - } - -} // ImportTIFF_SFRTable - -// ================================================================================================= -// ImportTIFF_CFATable -// =================== - -static void -ImportTIFF_CFATable ( const TIFF_Manager::TagInfo & tagInfo, bool nativeEndian, - SXMPMeta * xmp, const char * xmpNS, const char * xmpProp ) -{ - try { // Don't let errors with one stop the others. - - const XMP_Uns8 * bytePtr = (XMP_Uns8*)tagInfo.dataPtr; - const XMP_Uns8 * byteEnd = bytePtr + tagInfo.dataLen; - - XMP_Uns16 columns = *((XMP_Uns16*)bytePtr); - XMP_Uns16 rows = *((XMP_Uns16*)(bytePtr+2)); - if ( ! nativeEndian ) { - columns = Flip2 ( columns ); - rows = Flip2 ( rows ); - } - - char buffer[20]; - std::string arrayPath; - - snprintf ( buffer, sizeof(buffer), "%d", columns ); // AUDIT: Use of sizeof(buffer) is safe. - xmp->SetStructField ( xmpNS, xmpProp, kXMP_NS_EXIF, "Columns", buffer ); - snprintf ( buffer, sizeof(buffer), "%d", rows ); // AUDIT: Use of sizeof(buffer) is safe. - xmp->SetStructField ( xmpNS, xmpProp, kXMP_NS_EXIF, "Rows", buffer ); - - bytePtr += 4; // Move to the matrix of values. - if ( (byteEnd - bytePtr) != (columns * rows) ) goto BadExif; // Make sure the values are present. - - SXMPUtils::ComposeStructFieldPath ( xmpNS, xmpProp, kXMP_NS_EXIF, "Values", &arrayPath ); - - for ( size_t i = (columns * rows); i > 0; --i, ++bytePtr ) { - snprintf ( buffer, sizeof(buffer), "%hu", *bytePtr ); // AUDIT: Use of sizeof(buffer) is safe. - xmp->AppendArrayItem ( xmpNS, arrayPath.c_str(), kXMP_PropArrayIsOrdered, buffer ); - } - - return; - - BadExif: // Ignore the tag if the table is ill-formed. - xmp->DeleteProperty ( xmpNS, xmpProp ); - return; - - } catch ( ... ) { - // Do nothing, let other imports proceed. - // ? Notify client? - } - -} // ImportTIFF_CFATable - -// ================================================================================================= -// ImportTIFF_DSDTable -// =================== - -static void -ImportTIFF_DSDTable ( const TIFF_Manager & tiff, const TIFF_Manager::TagInfo & tagInfo, - SXMPMeta * xmp, const char * xmpNS, const char * xmpProp ) -{ - try { // Don't let errors with one stop the others. - - const XMP_Uns8 * bytePtr = (XMP_Uns8*)tagInfo.dataPtr; - const XMP_Uns8 * byteEnd = bytePtr + tagInfo.dataLen; - - XMP_Uns16 columns = *((XMP_Uns16*)bytePtr); - XMP_Uns16 rows = *((XMP_Uns16*)(bytePtr+2)); - if ( ! tiff.IsNativeEndian() ) { - columns = Flip2 ( columns ); - rows = Flip2 ( rows ); - } - - char buffer[20]; - - snprintf ( buffer, sizeof(buffer), "%d", columns ); // AUDIT: Use of sizeof(buffer) is safe. - xmp->SetStructField ( xmpNS, xmpProp, kXMP_NS_EXIF, "Columns", buffer ); - snprintf ( buffer, sizeof(buffer), "%d", rows ); // AUDIT: Use of sizeof(buffer) is safe. - xmp->SetStructField ( xmpNS, xmpProp, kXMP_NS_EXIF, "Rows", buffer ); - - std::string arrayPath; - SXMPUtils::ComposeStructFieldPath ( xmpNS, xmpProp, kXMP_NS_EXIF, "Settings", &arrayPath ); - - bytePtr += 4; // Move to the list of settings. - UTF16Unit * utf16Ptr = (UTF16Unit*)bytePtr; - UTF16Unit * utf16End = (UTF16Unit*)byteEnd; - - std::string utf8; - - // Figure 17 in the Exif 2.2 spec is unclear. It has counts for rows and columns, but the - // settings are listed as 1..n, not as a rectangular matrix. So, ignore the counts and copy - // strings until the end of the Exif value. - - while ( utf16Ptr < utf16End ) { - - size_t nameLen = 0; - while ( utf16Ptr[nameLen] != 0 ) ++nameLen; - ++nameLen; // ! Include the terminating nul. - if ( (utf16Ptr + nameLen) > utf16End ) goto BadExif; - - try { - FromUTF16 ( utf16Ptr, nameLen, &utf8, tiff.IsBigEndian() ); - } catch ( ... ) { - goto BadExif; // Ignore the tag if there are conversion errors. - } - - xmp->AppendArrayItem ( xmpNS, arrayPath.c_str(), kXMP_PropArrayIsOrdered, utf8.c_str() ); - - utf16Ptr += nameLen; - - } - - return; - - BadExif: // Ignore the tag if the table is ill-formed. - xmp->DeleteProperty ( xmpNS, xmpProp ); - return; - - } catch ( ... ) { - // Do nothing, let other imports proceed. - // ? Notify client? - } - -} // ImportTIFF_DSDTable - -// ================================================================================================= -// ImportTIFF_GPSCoordinate -// ======================== - -static void -ImportTIFF_GPSCoordinate ( const TIFF_Manager & tiff, const TIFF_Manager::TagInfo & posInfo, - SXMPMeta * xmp, const char * xmpNS, const char * xmpProp ) -{ - try { // Don't let errors with one stop the others. - - const bool nativeEndian = tiff.IsNativeEndian(); - - XMP_Uns16 refID = posInfo.id - 1; // ! The GPS refs and coordinates are all tag n and n+1. - TIFF_Manager::TagInfo refInfo; - bool found = tiff.GetTag ( kTIFF_GPSInfoIFD, refID, &refInfo ); - if ( (! found) || (refInfo.type != kTIFF_ASCIIType) || (refInfo.count != 2) ) return; - char ref = *((char*)refInfo.dataPtr); - - XMP_Uns32 * binPtr = (XMP_Uns32*)posInfo.dataPtr; - XMP_Uns32 degNum = binPtr[0]; - XMP_Uns32 degDenom = binPtr[1]; - XMP_Uns32 minNum = binPtr[2]; - XMP_Uns32 minDenom = binPtr[3]; - XMP_Uns32 secNum = binPtr[4]; - XMP_Uns32 secDenom = binPtr[5]; - if ( ! nativeEndian ) { - degNum = Flip4 ( degNum ); - degDenom = Flip4 ( degDenom ); - minNum = Flip4 ( minNum ); - minDenom = Flip4 ( minDenom ); - secNum = Flip4 ( secNum ); - secDenom = Flip4 ( secDenom ); - } - - char buffer[40]; - - if ( (degDenom == 1) && (minDenom == 1) && (secDenom == 1) ) { - - snprintf ( buffer, sizeof(buffer), "%lu,%lu,%lu%c", (unsigned long)degNum, (unsigned long)minNum, (unsigned long)secNum, ref ); // AUDIT: Using sizeof(buffer is safe. - - } else { - - XMP_Uns32 maxDenom = degDenom; - if ( minDenom > degDenom ) maxDenom = minDenom; - if ( secDenom > degDenom ) maxDenom = secDenom; - - int fracDigits = 1; - while ( maxDenom > 10 ) { ++fracDigits; maxDenom = maxDenom/10; } - - double temp = (double)degNum / (double)degDenom; - double degrees = (double)((XMP_Uns32)temp); // Just the integral number of degrees. - double minutes = ((temp - degrees) * 60.0) + - ((double)minNum / (double)minDenom) + - (((double)secNum / (double)secDenom) / 60.0); - - snprintf ( buffer, sizeof(buffer), "%.0f,%.*f%c", degrees, fracDigits, minutes, ref ); // AUDIT: Using sizeof(buffer is safe. - - } - - xmp->SetProperty ( xmpNS, xmpProp, buffer ); - - } catch ( ... ) { - // Do nothing, let other imports proceed. - // ? Notify client? - } - -} // ImportTIFF_GPSCoordinate - -// ================================================================================================= -// ImportTIFF_GPSTimeStamp -// ======================= - -static void -ImportTIFF_GPSTimeStamp ( const TIFF_Manager & tiff, const TIFF_Manager::TagInfo & timeInfo, - SXMPMeta * xmp, const char * xmpNS, const char * xmpProp ) -{ - try { // Don't let errors with one stop the others. - - const bool nativeEndian = tiff.IsNativeEndian(); - - bool haveDate; - TIFF_Manager::TagInfo dateInfo; - haveDate = tiff.GetTag ( kTIFF_GPSInfoIFD, kTIFF_GPSDateStamp, &dateInfo ); - if ( ! haveDate ) haveDate = tiff.GetTag ( kTIFF_ExifIFD, kTIFF_DateTimeOriginal, &dateInfo ); - if ( ! haveDate ) haveDate = tiff.GetTag ( kTIFF_ExifIFD, kTIFF_DateTimeDigitized, &dateInfo ); - if ( ! haveDate ) return; - - const char * dateStr = (const char *) dateInfo.dataPtr; - if ( (dateStr[4] != ':') || (dateStr[7] != ':') ) return; - if ( (dateStr[10] != 0) && (dateStr[10] != ' ') ) return; - - XMP_Uns32 * binPtr = (XMP_Uns32*)timeInfo.dataPtr; - XMP_Uns32 hourNum = binPtr[0]; - XMP_Uns32 hourDenom = binPtr[1]; - XMP_Uns32 minNum = binPtr[2]; - XMP_Uns32 minDenom = binPtr[3]; - XMP_Uns32 secNum = binPtr[4]; - XMP_Uns32 secDenom = binPtr[5]; - if ( ! nativeEndian ) { - hourNum = Flip4 ( hourNum ); - hourDenom = Flip4 ( hourDenom ); - minNum = Flip4 ( minNum ); - minDenom = Flip4 ( minDenom ); - secNum = Flip4 ( secNum ); - secDenom = Flip4 ( secDenom ); - } - - double fHour, fMin, fSec, fNano, temp; - fSec = (double)secNum / (double)secDenom; - temp = (double)minNum / (double)minDenom; - fMin = (double)((XMP_Uns32)temp); - fSec += (temp - fMin) * 60.0; - temp = (double)hourNum / (double)hourDenom; - fHour = (double)((XMP_Uns32)temp); - fSec += (temp - fHour) * 3600.0; - temp = (double)((XMP_Uns32)fSec); - fNano = ((fSec - temp) * (1000.0*1000.0*1000.0)) + 0.5; // Try to avoid n999... problems. - fSec = temp; - - XMP_DateTime binStamp; - binStamp.year = GatherInt ( dateStr, 4 ); - binStamp.month = GatherInt ( dateStr+5, 2 ); - binStamp.day = GatherInt ( dateStr+8, 2 ); - binStamp.hour = (XMP_Int32)fHour; - binStamp.minute = (XMP_Int32)fMin; - binStamp.second = (XMP_Int32)fSec; - binStamp.nanoSecond = (XMP_Int32)fNano; - binStamp.hasTimeZone = true; // Exif GPS TimeStamp is implicitly UTC. - binStamp.tzSign = kXMP_TimeIsUTC; - binStamp.tzHour = binStamp.tzMinute = 0; - - xmp->SetProperty_Date ( xmpNS, xmpProp, binStamp ); - - } catch ( ... ) { - // Do nothing, let other imports proceed. - // ? Notify client? - } - -} // ImportTIFF_GPSTimeStamp - -// ================================================================================================= -// ================================================================================================= - -// ================================================================================================= -// PhotoDataUtils::Import2WayExif -// ============================== -// -// Import the TIFF/Exif tags that have 2 way mappings to XMP, i.e. no correspondence to IPTC. -// These are always imported for the tiff: and exif: namespaces, but not for others. - -void -PhotoDataUtils::Import2WayExif ( const TIFF_Manager & exif, SXMPMeta * xmp, int iptcDigestState ) -{ - const bool nativeEndian = exif.IsNativeEndian(); - - bool found, foundFromXMP; - TIFF_Manager::TagInfo tagInfo; - - ImportTIFF_StandardMappings ( kTIFF_PrimaryIFD, exif, xmp ); - ImportTIFF_StandardMappings ( kTIFF_ExifIFD, exif, xmp ); - ImportTIFF_StandardMappings ( kTIFF_GPSInfoIFD, exif, xmp ); - - // ----------------------------------------------------------------- - // Fixup erroneous files that have a negative value for GPSAltitude. - - found = exif.GetTag ( kTIFF_GPSInfoIFD, kTIFF_GPSAltitude, &tagInfo ); - if ( found && (tagInfo.type == kTIFF_RationalType) && (tagInfo.count == 1) ) { - - XMP_Uns32 num = exif.GetUns32 ( tagInfo.dataPtr ); - XMP_Uns32 denom = exif.GetUns32 ( (XMP_Uns8*)tagInfo.dataPtr + 4 ); - bool numNeg = num >> 31; - bool denomNeg = denom >> 31; - - if ( (numNeg != denomNeg) || numNeg ) { // Does the GPSAltitude look negative? - if ( denomNeg ) { - denom = -denom; - num = -num; - numNeg = num >> 31; - } - if ( numNeg ) { - char buffer [32]; - num = -num; - snprintf ( buffer, sizeof(buffer), "%lu/%lu", (unsigned long) num, (unsigned long) denom ); // AUDIT: Using sizeof(buffer) is safe. - xmp->SetProperty ( kXMP_NS_EXIF, "GPSAltitude", buffer ); - xmp->SetProperty ( kXMP_NS_EXIF, "GPSAltitudeRef", "1" ); - } - } - - } - - // --------------------------------------------------------------- - // Import DateTimeOriginal and DateTime if the XMP doss not exist. - - found = exif.GetTag ( kTIFF_ExifIFD, kTIFF_DateTimeOriginal, &tagInfo ); - foundFromXMP = xmp->DoesPropertyExist ( kXMP_NS_EXIF, "DateTimeOriginal" ); - - if ( found && (! foundFromXMP) && (tagInfo.type == kTIFF_ASCIIType) ) { - ImportTIFF_Date ( exif, tagInfo, xmp, kXMP_NS_EXIF, "DateTimeOriginal" ); - } - - found = exif.GetTag ( kTIFF_PrimaryIFD, kTIFF_DateTime, &tagInfo ); - foundFromXMP = xmp->DoesPropertyExist ( kXMP_NS_XMP, "ModifyDate" ); - - if ( found && (! foundFromXMP) && (tagInfo.type == kTIFF_ASCIIType) ) { - ImportTIFF_Date ( exif, tagInfo, xmp, kXMP_NS_XMP, "ModifyDate" ); - } - - // ---------------------------------------------------- - // Import the Exif IFD tags that have special mappings. - - // 34855 ISOSpeedRatings has special cases for ISO over 65535. The tag is SHORT, some cameras - // omit the tag and some write 65535, all put the real ISO in MakerNote - which ACR might - // extract and leave in the XMP. There are 3 import cases: - // 1. No native tag: Leave existing XMP. - // 2. All of the native values are under 65535: Clear any XMP and import. - // 3. One or more of the native values are 65535: Leave existing XMP, else import. - - found = exif.GetTag ( kTIFF_ExifIFD, kTIFF_ISOSpeedRatings, &tagInfo ); - if ( found && (tagInfo.type == kTIFF_ShortType) && (tagInfo.count > 0) ) { - - bool keepXMP = false; - XMP_Uns16 * itemPtr = (XMP_Uns16*) tagInfo.dataPtr; - for ( XMP_Uns32 i = 0; i < tagInfo.count; ++i, ++itemPtr ) { - if ( *itemPtr == 0xFFFF ) { keepXMP = true; break; } // ! Don't care about BE or LF, same either way. - } - - if ( ! keepXMP ) xmp->DeleteProperty ( kXMP_NS_EXIF, "ISOSpeedRatings" ); - - if ( ! xmp->DoesPropertyExist ( kXMP_NS_EXIF, "ISOSpeedRatings" ) ) { - ImportArrayTIFF ( tagInfo, exif.IsNativeEndian(), xmp, kXMP_NS_EXIF, "ISOSpeedRatings" ); - } - - } - - // 36864 ExifVersion is 4 "undefined" ASCII characters. - found = exif.GetTag ( kTIFF_ExifIFD, kTIFF_ExifVersion, &tagInfo ); - if ( found && (tagInfo.type == kTIFF_UndefinedType) && (tagInfo.count == 4) ) { - char str[5]; - *((XMP_Uns32*)str) = *((XMP_Uns32*)tagInfo.dataPtr); - str[4] = 0; - xmp->SetProperty ( kXMP_NS_EXIF, "ExifVersion", str ); - } - - // 40960 FlashpixVersion is 4 "undefined" ASCII characters. - found = exif.GetTag ( kTIFF_ExifIFD, kTIFF_FlashpixVersion, &tagInfo ); - if ( found && (tagInfo.type == kTIFF_UndefinedType) && (tagInfo.count == 4) ) { - char str[5]; - *((XMP_Uns32*)str) = *((XMP_Uns32*)tagInfo.dataPtr); - str[4] = 0; - xmp->SetProperty ( kXMP_NS_EXIF, "FlashpixVersion", str ); - } - - // 37121 ComponentsConfiguration is an array of 4 "undefined" UInt8 bytes. - found = exif.GetTag ( kTIFF_ExifIFD, kTIFF_ComponentsConfiguration, &tagInfo ); - if ( found && (tagInfo.type == kTIFF_UndefinedType) && (tagInfo.count == 4) ) { - ImportArrayTIFF_Byte ( tagInfo, xmp, kXMP_NS_EXIF, "ComponentsConfiguration" ); - } - - // 37510 UserComment is a string with explicit encoding. - found = exif.GetTag ( kTIFF_ExifIFD, kTIFF_UserComment, &tagInfo ); - if ( found ) { - ImportTIFF_EncodedString ( exif, tagInfo, xmp, kXMP_NS_EXIF, "UserComment", true /* isLangAlt */ ); - } - - // 34856 OECF is an OECF/SFR table. - found = exif.GetTag ( kTIFF_ExifIFD, kTIFF_OECF, &tagInfo ); - if ( found ) { - ImportTIFF_OECFTable ( tagInfo, nativeEndian, xmp, kXMP_NS_EXIF, "OECF" ); - } - - // 37385 Flash is a UInt16 collection of bit fields and is mapped to a struct in XMP. - found = exif.GetTag ( kTIFF_ExifIFD, kTIFF_Flash, &tagInfo ); - if ( found && (tagInfo.type == kTIFF_ShortType) && (tagInfo.count == 1) ) { - ImportTIFF_Flash ( tagInfo, nativeEndian, xmp, kXMP_NS_EXIF, "Flash" ); - } - - // 41484 SpatialFrequencyResponse is an OECF/SFR table. - found = exif.GetTag ( kTIFF_ExifIFD, kTIFF_SpatialFrequencyResponse, &tagInfo ); - if ( found ) { - ImportTIFF_SFRTable ( tagInfo, nativeEndian, xmp, kXMP_NS_EXIF, "SpatialFrequencyResponse" ); - } - - // 41728 FileSource is an "undefined" UInt8. - found = exif.GetTag ( kTIFF_ExifIFD, kTIFF_FileSource, &tagInfo ); - if ( found && (tagInfo.type == kTIFF_UndefinedType) && (tagInfo.count == 1) ) { - ImportSingleTIFF_Byte ( tagInfo, xmp, kXMP_NS_EXIF, "FileSource" ); - } - - // 41729 SceneType is an "undefined" UInt8. - found = exif.GetTag ( kTIFF_ExifIFD, kTIFF_SceneType, &tagInfo ); - if ( found && (tagInfo.type == kTIFF_UndefinedType) && (tagInfo.count == 1) ) { - ImportSingleTIFF_Byte ( tagInfo, xmp, kXMP_NS_EXIF, "SceneType" ); - } - - // 41730 CFAPattern is a custom table. - found = exif.GetTag ( kTIFF_ExifIFD, kTIFF_CFAPattern, &tagInfo ); - if ( found ) { - ImportTIFF_CFATable ( tagInfo, nativeEndian, xmp, kXMP_NS_EXIF, "CFAPattern" ); - } - - // 41995 DeviceSettingDescription is a custom table. - found = exif.GetTag ( kTIFF_ExifIFD, kTIFF_DeviceSettingDescription, &tagInfo ); - if ( found ) { - ImportTIFF_DSDTable ( exif, tagInfo, xmp, kXMP_NS_EXIF, "DeviceSettingDescription" ); - } - - // -------------------------------------------------------- - // Import the GPS Info IFD tags that have special mappings. - - // 0 GPSVersionID is 4 UInt8 bytes and mapped as "n.n.n.n". - found = exif.GetTag ( kTIFF_GPSInfoIFD, kTIFF_GPSVersionID, &tagInfo ); - if ( found && (tagInfo.type == kTIFF_ByteType) && (tagInfo.count == 4) ) { - const char * strIn = (const char *) tagInfo.dataPtr; - char strOut[8]; - strOut[0] = strIn[0]; - strOut[2] = strIn[1]; - strOut[4] = strIn[2]; - strOut[6] = strIn[3]; - strOut[1] = strOut[3] = strOut[5] = '.'; - strOut[7] = 0; - xmp->SetProperty ( kXMP_NS_EXIF, "GPSVersionID", strOut ); - } - - // 2 GPSLatitude is a GPS coordinate master. - found = exif.GetTag ( kTIFF_GPSInfoIFD, kTIFF_GPSLatitude, &tagInfo ); - if ( found ) { - ImportTIFF_GPSCoordinate ( exif, tagInfo, xmp, kXMP_NS_EXIF, "GPSLatitude" ); - } - - // 4 GPSLongitude is a GPS coordinate master. - found = exif.GetTag ( kTIFF_GPSInfoIFD, kTIFF_GPSLongitude, &tagInfo ); - if ( found ) { - ImportTIFF_GPSCoordinate ( exif, tagInfo, xmp, kXMP_NS_EXIF, "GPSLongitude" ); - } - - // 7 GPSTimeStamp is a UTC time as 3 rationals, mated with the optional GPSDateStamp. - found = exif.GetTag ( kTIFF_GPSInfoIFD, kTIFF_GPSTimeStamp, &tagInfo ); - if ( found && (tagInfo.type == kTIFF_RationalType) && (tagInfo.count == 3) ) { - ImportTIFF_GPSTimeStamp ( exif, tagInfo, xmp, kXMP_NS_EXIF, "GPSTimeStamp" ); - } - - // 20 GPSDestLatitude is a GPS coordinate master. - found = exif.GetTag ( kTIFF_GPSInfoIFD, kTIFF_GPSDestLatitude, &tagInfo ); - if ( found ) { - ImportTIFF_GPSCoordinate ( exif, tagInfo, xmp, kXMP_NS_EXIF, "GPSDestLatitude" ); - } - - // 22 GPSDestLongitude is a GPS coordinate master. - found = exif.GetTag ( kTIFF_GPSInfoIFD, kTIFF_GPSDestLongitude, &tagInfo ); - if ( found ) { - ImportTIFF_GPSCoordinate ( exif, tagInfo, xmp, kXMP_NS_EXIF, "GPSDestLongitude" ); - } - - // 27 GPSProcessingMethod is a string with explicit encoding. - found = exif.GetTag ( kTIFF_GPSInfoIFD, kTIFF_GPSProcessingMethod, &tagInfo ); - if ( found ) { - ImportTIFF_EncodedString ( exif, tagInfo, xmp, kXMP_NS_EXIF, "GPSProcessingMethod" ); - } - - // 28 GPSAreaInformation is a string with explicit encoding. - found = exif.GetTag ( kTIFF_GPSInfoIFD, kTIFF_GPSAreaInformation, &tagInfo ); - if ( found ) { - ImportTIFF_EncodedString ( exif, tagInfo, xmp, kXMP_NS_EXIF, "GPSAreaInformation" ); - } - -} // PhotoDataUtils::Import2WayExif - -// ================================================================================================= -// Import3WayDateTime -// ================== - -static void Import3WayDateTime ( XMP_Uns16 exifTag, const TIFF_Manager & exif, const IPTC_Manager & iptc, - SXMPMeta * xmp, int iptcDigestState, const IPTC_Manager & oldIPTC ) -{ - XMP_Uns8 iptcDS; - XMP_StringPtr xmpNS, xmpProp; - - if ( exifTag == kTIFF_DateTimeOriginal ) { - iptcDS = kIPTC_DateCreated; - xmpNS = kXMP_NS_Photoshop; - xmpProp = "DateCreated"; - } else if ( exifTag == kTIFF_DateTimeDigitized ) { - iptcDS = kIPTC_DigitalCreateDate; - xmpNS = kXMP_NS_XMP; - xmpProp = "CreateDate"; - } else { - XMP_Throw ( "Unrecognized dateID", kXMPErr_BadParam ); - } - - size_t iptcCount; - bool haveXMP, haveExif, haveIPTC; // ! These are manipulated to simplify MWG-compliant logic. - std::string xmpValue, exifValue, iptcValue; - TIFF_Manager::TagInfo exifInfo; - IPTC_Manager::DataSetInfo iptcInfo; - - // Get the basic info about available values. - haveXMP = xmp->GetProperty ( xmpNS, xmpProp, &xmpValue, 0 ); - iptcCount = PhotoDataUtils::GetNativeInfo ( iptc, iptcDS, iptcDigestState, haveXMP, &iptcInfo ); - haveIPTC = (iptcCount > 0); - XMP_Assert ( (iptcDigestState == kDigestMatches) ? (! haveIPTC) : true ); - haveExif = (! haveXMP) && (! haveIPTC) && PhotoDataUtils::GetNativeInfo ( exif, kTIFF_ExifIFD, exifTag, &exifInfo ); - XMP_Assert ( (! (haveExif & haveXMP)) & (! (haveExif & haveIPTC)) ); - - if ( haveIPTC ) { - - PhotoDataUtils::ImportIPTC_Date ( iptcDS, iptc, xmp ); - - } else if ( haveExif && (exifInfo.type == kTIFF_ASCIIType) ) { - - // Only import the Exif form if the non-TZ information differs from the XMP. - - TIFF_FileWriter exifFromXMP; - TIFF_Manager::TagInfo infoFromXMP; - - ExportTIFF_Date ( *xmp, xmpNS, xmpProp, &exifFromXMP, exifTag ); - bool foundFromXMP = exifFromXMP.GetTag ( kTIFF_ExifIFD, exifTag, &infoFromXMP ); - - if ( (! foundFromXMP) || (exifInfo.dataLen != infoFromXMP.dataLen) || - (! XMP_LitNMatch ( (char*)exifInfo.dataPtr, (char*)infoFromXMP.dataPtr, exifInfo.dataLen )) ) { - ImportTIFF_Date ( exif, exifInfo, xmp, xmpNS, xmpProp ); - } - - } - -} // Import3WayDateTime - -// ================================================================================================= -// PhotoDataUtils::Import3WayItems -// =============================== -// -// Handle the imports that involve all 3 of Exif, IPTC, and XMP. There are only 4 properties with -// 3-way mappings, copyright, description, creator, and date/time. Following the MWG guidelines, -// this general policy is applied separately to each: -// -// If the new IPTC digest differs from the stored digest (favor IPTC over Exif and XMP) -// If the IPTC value differs from the predicted old IPTC value -// Import the IPTC value, including deleting the XMP -// Else if the Exif is non-empty and differs from the XMP -// Import the Exif value (does not delete existing XMP) -// Else if the stored IPTC digest is missing (favor Exif over IPTC, or IPTC over missing XMP) -// If the Exif is non-empty and differs from the XMP -// Import the Exif value (does not delete existing XMP) -// Else if the XMP is missing and the Exif is missing or empty -// Import the IPTC value -// Else (the new IPTC digest matches the stored digest - ignore the IPTC) -// If the Exif is non-empty and differs from the XMP -// Import the Exif value (does not delete existing XMP) -// -// Note that missing or empty Exif will never cause existing XMP to be deleted. This is a pragmatic -// choice to improve compatibility with pre-MWG software. There are few Exif-only editors for these -// 3-way properties, there are important existing IPTC-only editors. - -// ------------------------------------------------------------------------------------------------- - -void PhotoDataUtils::Import3WayItems ( const TIFF_Manager & exif, const IPTC_Manager & iptc, SXMPMeta * xmp, int iptcDigestState ) -{ - size_t iptcCount; - - bool haveXMP, haveExif, haveIPTC; // ! These are manipulated to simplify MWG-compliant logic. - std::string xmpValue, exifValue, iptcValue; - - TIFF_Manager::TagInfo exifInfo; - IPTC_Manager::DataSetInfo iptcInfo; - - IPTC_Writer oldIPTC; - if ( iptcDigestState == kDigestDiffers ) { - PhotoDataUtils::ExportIPTC ( *xmp, &oldIPTC ); // Predict old IPTC DataSets based on the existing XMP. - } - - // --------------------------------------------------------------------------------- - // Process the copyright. Replace internal nuls in the Exif to "merge" the portions. - - // Get the basic info about available values. - haveXMP = xmp->GetLocalizedText ( kXMP_NS_DC, "rights", "", "x-default", 0, &xmpValue, 0 ); - iptcCount = PhotoDataUtils::GetNativeInfo ( iptc, kIPTC_CopyrightNotice, iptcDigestState, haveXMP, &iptcInfo ); - haveIPTC = (iptcCount > 0); - XMP_Assert ( (iptcDigestState == kDigestMatches) ? (! haveIPTC) : true ); - haveExif = (! haveXMP) && (! haveIPTC) && PhotoDataUtils::GetNativeInfo ( exif, kTIFF_PrimaryIFD, kTIFF_Copyright, &exifInfo ); - XMP_Assert ( (! (haveExif & haveXMP)) & (! (haveExif & haveIPTC)) ); - - if ( haveExif && (exifInfo.dataLen > 1) ) { // Replace internal nul characters with linefeed. - for ( XMP_Uns32 i = 0; i < exifInfo.dataLen-1; ++i ) { - if ( ((char*)exifInfo.dataPtr)[i] == 0 ) ((char*)exifInfo.dataPtr)[i] = 0x0A; - } - } - - if ( haveIPTC ) { - PhotoDataUtils::ImportIPTC_LangAlt ( iptc, xmp, kIPTC_CopyrightNotice, kXMP_NS_DC, "rights" ); - } else if ( haveExif && PhotoDataUtils::IsValueDifferent ( exifInfo, xmpValue, &exifValue ) ) { - xmp->SetLocalizedText ( kXMP_NS_DC, "rights", "", "x-default", exifValue.c_str() ); - } - - // ------------------------ - // Process the description. - - // Get the basic info about available values. - haveXMP = xmp->GetLocalizedText ( kXMP_NS_DC, "description", "", "x-default", 0, &xmpValue, 0 ); - iptcCount = PhotoDataUtils::GetNativeInfo ( iptc, kIPTC_Description, iptcDigestState, haveXMP, &iptcInfo ); - haveIPTC = (iptcCount > 0); - XMP_Assert ( (iptcDigestState == kDigestMatches) ? (! haveIPTC) : true ); - haveExif = (! haveXMP) && (! haveIPTC) && PhotoDataUtils::GetNativeInfo ( exif, kTIFF_PrimaryIFD, kTIFF_ImageDescription, &exifInfo ); - XMP_Assert ( (! (haveExif & haveXMP)) & (! (haveExif & haveIPTC)) ); - - if ( haveIPTC ) { - PhotoDataUtils::ImportIPTC_LangAlt ( iptc, xmp, kIPTC_Description, kXMP_NS_DC, "description" ); - } else if ( haveExif && PhotoDataUtils::IsValueDifferent ( exifInfo, xmpValue, &exifValue ) ) { - xmp->SetLocalizedText ( kXMP_NS_DC, "description", "", "x-default", exifValue.c_str() ); - } - - // ------------------------------------------------------------------------------------------- - // Process the creator. The XMP and IPTC are arrays, the Exif is a semicolon separated string. - - // Get the basic info about available values. - haveXMP = xmp->DoesPropertyExist ( kXMP_NS_DC, "creator" ); - haveExif = PhotoDataUtils::GetNativeInfo ( exif, kTIFF_PrimaryIFD, kTIFF_Artist, &exifInfo ); - iptcCount = PhotoDataUtils::GetNativeInfo ( iptc, kIPTC_Creator, iptcDigestState, haveXMP, &iptcInfo ); - haveIPTC = (iptcCount > 0); - XMP_Assert ( (iptcDigestState == kDigestMatches) ? (! haveIPTC) : true ); - haveExif = (! haveXMP) && (! haveIPTC) && PhotoDataUtils::GetNativeInfo ( exif, kTIFF_PrimaryIFD, kTIFF_Artist, &exifInfo ); - XMP_Assert ( (! (haveExif & haveXMP)) & (! (haveExif & haveIPTC)) ); - - if ( haveIPTC ) { - PhotoDataUtils::ImportIPTC_Array ( iptc, xmp, kIPTC_Creator, kXMP_NS_DC, "creator" ); - } else if ( haveExif && PhotoDataUtils::IsValueDifferent ( exifInfo, xmpValue, &exifValue ) ) { - SXMPUtils::SeparateArrayItems ( xmp, kXMP_NS_DC, "creator", kXMP_PropArrayIsOrdered, exifValue ); - } - - // ------------------------------------------------------------------------------ - // Process DateTimeDigitized; DateTimeOriginal and DateTime are 2-way. - // *** Exif DateTimeOriginal <-> XMP exif:DateTimeOriginal - // *** IPTC DateCreated <-> XMP photoshop:DateCreated - // *** Exif DateTimeDigitized <-> IPTC DigitalCreateDate <-> XMP xmp:CreateDate - // *** TIFF DateTime <-> XMP xmp:ModifyDate - - Import3WayDateTime ( kTIFF_DateTimeDigitized, exif, iptc, xmp, iptcDigestState, oldIPTC ); - -} // PhotoDataUtils::Import3WayItems - -// ================================================================================================= -// ================================================================================================= - -// ================================================================================================= -// ExportSingleTIFF -// ================ -// -// This is only called when the XMP exists and will be exported. And only for standard mappings. - -// ! Only implemented for the types known to be needed. - -static void -ExportSingleTIFF ( TIFF_Manager * tiff, XMP_Uns8 ifd, const TIFF_MappingToXMP & mapInfo, - bool nativeEndian, const std::string & xmpValue ) -{ - XMP_Assert ( (mapInfo.count == 1) || (mapInfo.type == kTIFF_ASCIIType) ); - XMP_Assert ( mapInfo.name[0] != 0 ); // Must be a standard mapping. - - char nextChar; // Used to make sure sscanf consumes all of the string. - - switch ( mapInfo.type ) { - - case kTIFF_ByteType : { - unsigned short binValue; - int items = sscanf ( xmpValue.c_str(), "%hu%c", &binValue, &nextChar ); // AUDIT: Using xmpValue.c_str() is safe. - if ( items != 1 ) return; // ? complain? notify client? - tiff->SetTag_Byte ( ifd, mapInfo.id, (XMP_Uns8)binValue ); - break; - } - - case kTIFF_ShortType : { - unsigned long binValue; - int items = sscanf ( xmpValue.c_str(), "%lu%c", &binValue, &nextChar ); // AUDIT: Using xmpValue.c_str() is safe. - if ( items != 1 ) return; // ? complain? notify client? - tiff->SetTag_Short ( ifd, mapInfo.id, (XMP_Uns16)binValue ); - break; - } - - case kTIFF_ShortOrLongType : { - unsigned long binValue; - int items = sscanf ( xmpValue.c_str(), "%lu%c", &binValue, &nextChar ); // AUDIT: Using xmpValue.c_str() is safe. - if ( items != 1 ) return; // ? complain? notify client? - if ( binValue <= 0xFFFF ) { - tiff->SetTag_Short ( ifd, mapInfo.id, (XMP_Uns16)binValue ); - } else { - tiff->SetTag_Long ( ifd, mapInfo.id, (XMP_Uns32)binValue ); - } - break; - } - - case kTIFF_RationalType : { // The XMP is formatted as "num/denom". - unsigned long num, denom; - int items = sscanf ( xmpValue.c_str(), "%lu/%lu%c", &num, &denom, &nextChar ); // AUDIT: Using xmpValue.c_str() is safe. - if ( items != 2 ) { - if ( items != 1 ) return; // ? complain? notify client? - denom = 1; // The XMP was just an integer, assume a denominator of 1. - } - tiff->SetTag_Rational ( ifd, mapInfo.id, (XMP_Uns32)num, (XMP_Uns32)denom ); - break; - } - - case kTIFF_SRationalType : { // The XMP is formatted as "num/denom". - signed long num, denom; - int items = sscanf ( xmpValue.c_str(), "%ld/%ld%c", &num, &denom, &nextChar ); // AUDIT: Using xmpValue.c_str() is safe. - if ( items != 2 ) { - if ( items != 1 ) return; // ? complain? notify client? - denom = 1; // The XMP was just an integer, assume a denominator of 1. - } - tiff->SetTag_SRational ( ifd, mapInfo.id, (XMP_Int32)num, (XMP_Int32)denom ); - break; - } - - case kTIFF_ASCIIType : - tiff->SetTag ( ifd, mapInfo.id, kTIFF_ASCIIType, (XMP_Uns32)(xmpValue.size()+1), xmpValue.c_str() ); - break; - - default: - XMP_Assert ( false ); // Force a debug assert for unexpected types. - - } - -} // ExportSingleTIFF - -// ================================================================================================= -// ExportArrayTIFF -// ================ -// -// This is only called when the XMP exists and will be exported. And only for standard mappings. - -// ! Only implemented for the types known to be needed. - -static void -ExportArrayTIFF ( TIFF_Manager * tiff, XMP_Uns8 ifd, const TIFF_MappingToXMP & mapInfo, bool nativeEndian, - const SXMPMeta & xmp, const char * xmpNS, const char * xmpArray ) -{ - XMP_Assert ( (mapInfo.count != 1) && (mapInfo.type != kTIFF_ASCIIType) ); - XMP_Assert ( mapInfo.name[0] != 0 ); // Must be a standard mapping. - XMP_Assert ( mapInfo.type == kTIFF_ShortType ); // ! So far ISOSpeedRatings is the only standard array export. - XMP_Assert ( xmp.DoesPropertyExist ( xmpNS, xmpArray ) ); - - if ( mapInfo.type != kTIFF_ShortType ) return; // ! So far ISOSpeedRatings is the only standard array export. - - size_t arraySize = xmp.CountArrayItems ( xmpNS, xmpArray ); - if ( arraySize == 0 ) { - tiff->DeleteTag ( ifd, mapInfo.id ); - return; - } - - std::vector<XMP_Uns16> vecValue; - vecValue.assign ( arraySize, 0 ); - XMP_Uns16 * binPtr = (XMP_Uns16*) &vecValue[0]; - - std::string itemPath; - XMP_Int32 int32; - XMP_Uns16 uns16; - for ( size_t i = 1; i <= arraySize; ++i, ++binPtr ) { - SXMPUtils::ComposeArrayItemPath ( xmpNS, xmpArray, (XMP_Index)i, &itemPath ); - xmp.GetProperty_Int ( xmpNS, itemPath.c_str(), &int32, 0 ); - uns16 = (XMP_Uns16)int32; - if ( ! nativeEndian ) uns16 = Flip2 ( uns16 ); - *binPtr = uns16; - } - - tiff->SetTag ( ifd, mapInfo.id, kTIFF_ShortType, (XMP_Uns32)arraySize, &vecValue[0] ); - -} // ExportArrayTIFF - -// ================================================================================================= -// ExportTIFF_StandardMappings -// =========================== - -static void -ExportTIFF_StandardMappings ( XMP_Uns8 ifd, TIFF_Manager * tiff, const SXMPMeta & xmp ) -{ - const bool nativeEndian = tiff->IsNativeEndian(); - TIFF_Manager::TagInfo tagInfo; - std::string xmpValue; - XMP_OptionBits xmpForm; - - const TIFF_MappingToXMP * mappings = 0; - const char * xmpNS = 0; - - if ( ifd == kTIFF_PrimaryIFD ) { - mappings = sPrimaryIFDMappings; - xmpNS = kXMP_NS_TIFF; - } else if ( ifd == kTIFF_ExifIFD ) { - mappings = sExifIFDMappings; - xmpNS = kXMP_NS_EXIF; - } else if ( ifd == kTIFF_GPSInfoIFD ) { - mappings = sGPSInfoIFDMappings; - xmpNS = kXMP_NS_EXIF; // ! Yes, the GPS Info tags are in the exif: namespace. - } else { - XMP_Throw ( "Invalid IFD for standard mappings", kXMPErr_InternalFailure ); - } - - for ( size_t i = 0; mappings[i].id != 0xFFFF; ++i ) { - - try { // Don't let errors with one stop the others. - - const TIFF_MappingToXMP & mapInfo = mappings[i]; - - if ( mapInfo.exportMode == kExport_Never ) continue; - if ( mapInfo.name[0] == 0 ) continue; // Skip special mappings, handled higher up. - - bool haveTIFF = tiff->GetTag ( ifd, mapInfo.id, &tagInfo ); - if ( haveTIFF && (mapInfo.exportMode == kExport_InjectOnly) ) continue; - - bool haveXMP = xmp.GetProperty ( xmpNS, mapInfo.name, &xmpValue, &xmpForm ); - if ( ! haveXMP ) { - - if ( haveTIFF && (mapInfo.exportMode == kExport_Always) ) tiff->DeleteTag ( ifd, mapInfo.id ); - - } else { - - XMP_Assert ( tagInfo.type != kTIFF_UndefinedType ); // These must have a special mapping. - if ( tagInfo.type == kTIFF_UndefinedType ) continue; - - const bool mapSingle = ((mapInfo.count == 1) || (mapInfo.type == kTIFF_ASCIIType)); - if ( mapSingle ) { - if ( ! XMP_PropIsSimple ( xmpForm ) ) continue; // ? Notify client? - ExportSingleTIFF ( tiff, ifd, mapInfo, nativeEndian, xmpValue ); - } else { - if ( ! XMP_PropIsArray ( xmpForm ) ) continue; // ? Notify client? - ExportArrayTIFF ( tiff, ifd, mapInfo, nativeEndian, xmp, xmpNS, mapInfo.name ); - } - - } - - } catch ( ... ) { - - // Do nothing, let other imports proceed. - // ? Notify client? - - } - - } - -} // ExportTIFF_StandardMappings - -// ================================================================================================= -// ExportTIFF_Date -// =============== -// -// Convert an XMP date/time to an Exif 2.2 master date/time tag plus associated fractional seconds. -// The Exif date/time part is a 20 byte ASCII value formatted as "YYYY:MM:DD HH:MM:SS" with a -// terminating nul. The fractional seconds are a nul terminated ASCII string with possible space -// padding. They are literally the fractional part, the digits that would be to the right of the -// decimal point. - -static void -ExportTIFF_Date ( const SXMPMeta & xmp, const char * xmpNS, const char * xmpProp, TIFF_Manager * tiff, XMP_Uns16 mainID ) -{ - XMP_Uns8 mainIFD = kTIFF_ExifIFD; - XMP_Uns16 fracID; - switch ( mainID ) { - case kTIFF_DateTime : mainIFD = kTIFF_PrimaryIFD; fracID = kTIFF_SubSecTime; break; - case kTIFF_DateTimeOriginal : fracID = kTIFF_SubSecTimeOriginal; break; - case kTIFF_DateTimeDigitized : fracID = kTIFF_SubSecTimeDigitized; break; - } - - try { // Don't let errors with one stop the others. - - std::string xmpStr; - bool foundXMP = xmp.GetProperty ( xmpNS, xmpProp, &xmpStr, 0 ); - if ( ! foundXMP ) { - tiff->DeleteTag ( mainIFD, mainID ); - tiff->DeleteTag ( kTIFF_ExifIFD, fracID ); // ! The subseconds are always in the Exif IFD. - return; - } - - // Format using all of the numbers. Then overwrite blanks for missing fields. The fields - // missing from the XMP are detected with length checks: YYYY-MM-DDThh:mm:ss - // < 18 - no seconds - // < 15 - no minutes - // < 12 - no hours - // < 9 - no day - // < 6 - no month - // < 1 - no year - - XMP_DateTime xmpBin; - SXMPUtils::ConvertToDate ( xmpStr.c_str(), &xmpBin ); - - char buffer[24]; - snprintf ( buffer, sizeof(buffer), "%04d:%02d:%02d %02d:%02d:%02d", // AUDIT: Use of sizeof(buffer) is safe. - xmpBin.year, xmpBin.month, xmpBin.day, xmpBin.hour, xmpBin.minute, xmpBin.second ); - - size_t xmpLen = xmpStr.size(); - if ( xmpLen < 18 ) { - buffer[17] = buffer[18] = ' '; - if ( xmpLen < 15 ) { - buffer[14] = buffer[15] = ' '; - if ( xmpLen < 12 ) { - buffer[11] = buffer[12] = ' '; - if ( xmpLen < 9 ) { - buffer[8] = buffer[9] = ' '; - if ( xmpLen < 6 ) { - buffer[5] = buffer[6] = ' '; - if ( xmpLen < 1 ) { - buffer[0] = buffer[1] = buffer[2] = buffer[3] = ' '; - } - } - } - } - } - } - - tiff->SetTag_ASCII ( mainIFD, mainID, buffer ); - - if ( xmpBin.nanoSecond == 0 ) { - - tiff->DeleteTag ( kTIFF_ExifIFD, fracID ); - - } else { - - snprintf ( buffer, sizeof(buffer), "%09d", xmpBin.nanoSecond ); // AUDIT: Use of sizeof(buffer) is safe. - for ( size_t i = strlen(buffer)-1; i > 0; --i ) { - if ( buffer[i] != '0' ) break; - buffer[i] = 0; // Strip trailing zero digits. - } - - tiff->SetTag_ASCII ( kTIFF_ExifIFD, fracID, buffer ); // ! The subseconds are always in the Exif IFD. - - } - - } catch ( ... ) { - // Do nothing, let other exports proceed. - // ? Notify client? - } - -} // ExportTIFF_Date - -// ================================================================================================= -// ExportTIFF_ArrayASCII -// ===================== -// -// Catenate all of the XMP array values into a string. Use a "; " separator for Artist, nul for others. - -static void -ExportTIFF_ArrayASCII ( const SXMPMeta & xmp, const char * xmpNS, const char * xmpProp, - TIFF_Manager * tiff, XMP_Uns8 ifd, XMP_Uns16 id ) -{ - try { // Don't let errors with one stop the others. - - std::string itemValue, fullValue; - XMP_OptionBits xmpFlags; - - bool foundXMP = xmp.GetProperty ( xmpNS, xmpProp, 0, &xmpFlags ); - if ( ! foundXMP ) { - tiff->DeleteTag ( ifd, id ); - return; - } - - if ( ! XMP_PropIsArray ( xmpFlags ) ) return; // ? Complain? Delete the tag? - - if ( id == kTIFF_Artist ) { - SXMPUtils::CatenateArrayItems ( xmp, xmpNS, xmpProp, 0, 0, kXMP_PropArrayIsOrdered, &fullValue ); - fullValue += '\x0'; // ! Need explicit final nul for SetTag below. - } else { - size_t count = xmp.CountArrayItems ( xmpNS, xmpProp ); - for ( size_t i = 1; i <= count; ++i ) { // ! XMP arrays are indexed from 1. - (void) xmp.GetArrayItem ( xmpNS, xmpProp, (XMP_Index)i, &itemValue, &xmpFlags ); - if ( ! XMP_PropIsSimple ( xmpFlags ) ) continue; // ? Complain? - fullValue.append ( itemValue ); - fullValue.append ( 1, '\x0' ); - } - } - - tiff->SetTag ( ifd, id, kTIFF_ASCIIType, (XMP_Uns32)fullValue.size(), fullValue.c_str() ); // ! Already have trailing nul. - - } catch ( ... ) { - // Do nothing, let other exports proceed. - // ? Notify client? - } - -} // ExportTIFF_ArrayASCII - -// ================================================================================================= -// ExportTIFF_LocTextASCII -// ====================== - -static void -ExportTIFF_LocTextASCII ( const SXMPMeta & xmp, const char * xmpNS, const char * xmpProp, - TIFF_Manager * tiff, XMP_Uns8 ifd, XMP_Uns16 id ) -{ - try { // Don't let errors with one stop the others. - - std::string xmpValue; - - bool foundXMP = xmp.GetLocalizedText ( xmpNS, xmpProp, "", "x-default", 0, &xmpValue, 0 ); - if ( ! foundXMP ) { - tiff->DeleteTag ( ifd, id ); - return; - } - - tiff->SetTag ( ifd, id, kTIFF_ASCIIType, (XMP_Uns32)( xmpValue.size()+1 ), xmpValue.c_str() ); - - } catch ( ... ) { - // Do nothing, let other exports proceed. - // ? Notify client? - } - -} // ExportTIFF_LocTextASCII - -// ================================================================================================= -// ExportTIFF_EncodedString -// ======================== - -static void -ExportTIFF_EncodedString ( const SXMPMeta & xmp, const char * xmpNS, const char * xmpProp, - TIFF_Manager * tiff, XMP_Uns8 ifd, XMP_Uns16 id, bool isLangAlt = false ) -{ - try { // Don't let errors with one stop the others. - - std::string xmpValue; - XMP_OptionBits xmpFlags; - - bool foundXMP = xmp.GetProperty ( xmpNS, xmpProp, &xmpValue, &xmpFlags ); - if ( ! foundXMP ) { - tiff->DeleteTag ( ifd, id ); - return; - } - - if ( ! isLangAlt ) { - if ( ! XMP_PropIsSimple ( xmpFlags ) ) return; // ? Complain? Delete the tag? - } else { - if ( ! XMP_ArrayIsAltText ( xmpFlags ) ) return; // ? Complain? Delete the tag? - bool ok = xmp.GetLocalizedText ( xmpNS, xmpProp, "", "x-default", 0, &xmpValue, 0 ); - if ( ! ok ) return; // ? Complain? Delete the tag? - } - - XMP_Uns8 encoding = kTIFF_EncodeASCII; - for ( size_t i = 0; i < xmpValue.size(); ++i ) { - if ( (XMP_Uns8)xmpValue[i] >= 0x80 ) { - encoding = kTIFF_EncodeUnicode; - break; - } - } - - tiff->SetTag_EncodedString ( ifd, id, xmpValue.c_str(), encoding ); - - } catch ( ... ) { - // Do nothing, let other exports proceed. - // ? Notify client? - } - -} // ExportTIFF_EncodedString - -// ================================================================================================= -// ExportTIFF_GPSCoordinate -// ======================== -// -// The XMP format is either "deg,min,secR" or "deg,min.fracR", where 'R' is the reference direction, -// 'N', 'S', 'E', or 'W'. The location gets output as ( deg/1, min/1, sec/1 ) for the first form, -// and ( deg/1, minFrac/denom, 0/1 ) for the second form. - -// ! We arbitrarily limit the number of fractional minute digits to 6 to avoid overflow in the -// ! combined numerator. But we don't otherwise check for overflow or range errors. - -static void -ExportTIFF_GPSCoordinate ( const SXMPMeta & xmp, const char * xmpNS, const char * xmpProp, - TIFF_Manager * tiff, XMP_Uns8 ifd, XMP_Uns16 _id ) -{ - XMP_Uns16 refID = _id-1; // ! The GPS refs and locations are all tag N-1 and N pairs. - XMP_Uns16 locID = _id; - - XMP_Assert ( (locID & 1) == 0 ); - - try { // Don't let errors with one stop the others. - - std::string xmpValue; - XMP_OptionBits xmpFlags; - - bool foundXMP = xmp.GetProperty ( xmpNS, xmpProp, &xmpValue, &xmpFlags ); - if ( ! foundXMP ) { - tiff->DeleteTag ( ifd, refID ); - tiff->DeleteTag ( ifd, locID ); - return; - } - - if ( ! XMP_PropIsSimple ( xmpFlags ) ) return; - - const char * chPtr = xmpValue.c_str(); - - XMP_Uns32 deg=0, minNum=0, minDenom=1, sec=0; - - for ( ; ('0' <= *chPtr) && (*chPtr <= '9'); ++chPtr ) deg = deg*10 + (*chPtr - '0'); - if ( *chPtr != ',' ) return; // Bad XMP string. - ++chPtr; // Skip the comma. - - for ( ; ('0' <= *chPtr) && (*chPtr <= '9'); ++chPtr ) minNum = minNum*10 + (*chPtr - '0'); - if ( (*chPtr != ',') && (*chPtr != '.') ) return; // Bad XMP string. - - if ( *chPtr == ',' ) { - - ++chPtr; // Skip the comma. - for ( ; ('0' <= *chPtr) && (*chPtr <= '9'); ++chPtr ) sec = sec*10 + (*chPtr - '0'); - - } else { - - XMP_Assert ( *chPtr == '.' ); - ++chPtr; // Skip the period. - for ( ; ('0' <= *chPtr) && (*chPtr <= '9'); ++chPtr ) { - if ( minDenom > 100*1000 ) continue; // Don't accumulate any more digits. - minDenom *= 10; - minNum = minNum*10 + (*chPtr - '0'); - } - - } - - if ( *(chPtr+1) != 0 ) return; // Bad XMP string. - - char ref[2]; - ref[0] = *chPtr; - ref[1] = 0; - - tiff->SetTag ( ifd, refID, kTIFF_ASCIIType, 2, &ref[0] ); - - XMP_Uns32 loc[6]; - tiff->PutUns32 ( deg, &loc[0] ); - tiff->PutUns32 ( 1, &loc[1] ); - tiff->PutUns32 ( minNum, &loc[2] ); - tiff->PutUns32 ( minDenom, &loc[3] ); - tiff->PutUns32 ( sec, &loc[4] ); - tiff->PutUns32 ( 1, &loc[5] ); - - tiff->SetTag ( ifd, locID, kTIFF_RationalType, 3, &loc[0] ); - - } catch ( ... ) { - // Do nothing, let other exports proceed. - // ? Notify client? - } - -} // ExportTIFF_GPSCoordinate - -// ================================================================================================= -// ExportTIFF_GPSTimeStamp -// ======================= -// -// The Exif is in 2 tags, GPSTimeStamp and GPSDateStamp. The time is 3 rationals for the hour, minute, -// and second in UTC. The date is a nul terminated string "YYYY:MM:DD". - -static const double kBillion = 1000.0*1000.0*1000.0; -static const double mMaxSec = 4.0*kBillion - 1.0; - -static void -ExportTIFF_GPSTimeStamp ( const SXMPMeta & xmp, const char * xmpNS, const char * xmpProp, TIFF_Manager * tiff ) -{ - - try { // Don't let errors with one stop the others. - - XMP_DateTime binXMP; - bool foundXMP = xmp.GetProperty_Date ( xmpNS, xmpProp, &binXMP, 0 ); - if ( ! foundXMP ) { - tiff->DeleteTag ( kTIFF_GPSInfoIFD, kTIFF_GPSTimeStamp ); - tiff->DeleteTag ( kTIFF_GPSInfoIFD, kTIFF_GPSDateStamp ); - return; - } - - SXMPUtils::ConvertToUTCTime ( &binXMP ); - - XMP_Uns32 exifTime[6]; - tiff->PutUns32 ( binXMP.hour, &exifTime[0] ); - tiff->PutUns32 ( 1, &exifTime[1] ); - tiff->PutUns32 ( binXMP.minute, &exifTime[2] ); - tiff->PutUns32 ( 1, &exifTime[3] ); - if ( binXMP.nanoSecond == 0 ) { - tiff->PutUns32 ( binXMP.second, &exifTime[4] ); - tiff->PutUns32 ( 1, &exifTime[5] ); - } else { - double fSec = (double)binXMP.second + ((double)binXMP.nanoSecond / kBillion ); - XMP_Uns32 denom = 1000*1000; // Choose microsecond resolution by default. - TIFF_Manager::TagInfo oldInfo; - bool hadExif = tiff->GetTag ( kTIFF_GPSInfoIFD, kTIFF_GPSTimeStamp, &oldInfo ); - if ( hadExif && (oldInfo.type == kTIFF_RationalType) && (oldInfo.count == 3) ) { - XMP_Uns32 oldDenom = tiff->GetUns32 ( &(((XMP_Uns32*)oldInfo.dataPtr)[5]) ); - if ( oldDenom != 1 ) denom = oldDenom; - } - fSec *= denom; - while ( fSec > mMaxSec ) { fSec /= 10; denom /= 10; } - tiff->PutUns32 ( (XMP_Uns32)fSec, &exifTime[4] ); - tiff->PutUns32 ( denom, &exifTime[5] ); - } - tiff->SetTag ( kTIFF_GPSInfoIFD, kTIFF_GPSTimeStamp, kTIFF_RationalType, 3, &exifTime[0] ); - - char exifDate[16]; // AUDIT: Long enough, only need 11. - snprintf ( exifDate, 12, "%04d:%02d:%02d", binXMP.year, binXMP.month, binXMP.day ); - if ( exifDate[10] == 0 ) { // Make sure there is no value overflow. - tiff->SetTag ( kTIFF_GPSInfoIFD, kTIFF_GPSDateStamp, kTIFF_ASCIIType, 11, exifDate ); - } - - } catch ( ... ) { - // Do nothing, let other exports proceed. - // ? Notify client? - } - -} // ExportTIFF_GPSTimeStamp - -// ================================================================================================= -// ================================================================================================= - -// ================================================================================================= -// PhotoDataUtils::ExportExif -// ========================== - -void -PhotoDataUtils::ExportExif ( SXMPMeta * xmp, TIFF_Manager * exif ) -{ - bool haveXMP, haveExif; - std::string xmpValue; - XMP_Int32 int32; - XMP_Uns8 uns8; - - // Do all of the table driven standard exports. - - ExportTIFF_StandardMappings ( kTIFF_PrimaryIFD, exif, *xmp ); - ExportTIFF_StandardMappings ( kTIFF_ExifIFD, exif, *xmp ); - ExportTIFF_StandardMappings ( kTIFF_GPSInfoIFD, exif, *xmp ); - - // Export dc:description to TIFF ImageDescription, and exif:UserComment to EXIF UserComment. - - // *** This is not following the MWG guidelines. The policy here tries to be more backward compatible. - - ExportTIFF_LocTextASCII ( *xmp, kXMP_NS_DC, "description", - exif, kTIFF_PrimaryIFD, kTIFF_ImageDescription ); - - ExportTIFF_EncodedString ( *xmp, kXMP_NS_EXIF, "UserComment", - exif, kTIFF_ExifIFD, kTIFF_UserComment, true /* isLangAlt */ ); - - // Export all of the date/time tags. - // ! Special case: Don't create Exif DateTimeDigitized. This can avoid PSD full rewrite due to - // ! new mapping from xmp:CreateDate. - - if ( exif->GetTag ( kTIFF_ExifIFD, kTIFF_DateTimeDigitized, 0 ) ) { - ExportTIFF_Date ( *xmp, kXMP_NS_XMP, "CreateDate", exif, kTIFF_DateTimeDigitized ); - } - - ExportTIFF_Date ( *xmp, kXMP_NS_EXIF, "DateTimeOriginal", exif, kTIFF_DateTimeOriginal ); - ExportTIFF_Date ( *xmp, kXMP_NS_XMP, "ModifyDate", exif, kTIFF_DateTime ); - - // 34855 ISOSpeedRatings has special cases for ISO over 65535. The tag is SHORT, some cameras - // omit the tag and some write 65535, all put the real ISO in MakerNote - which ACR might - // extract and leave in the XMP. There are 2 export cases: - // 1. No XMP property, or one or more of the XMP values are over 65535: - // Leave both the XMP and native tag alone. - // 1. Have XMP property and all of the XMP values are under 65535: - // Leave existing native tag, else export; strip the XMP either way. - - haveXMP = xmp->DoesPropertyExist ( kXMP_NS_EXIF, "ISOSpeedRatings" ); - if ( haveXMP ) { - - XMP_Index i, count; - std::string isoValue; - bool haveHighISO = false; - - for ( i = 1, count = xmp->CountArrayItems ( kXMP_NS_EXIF, "ISOSpeedRatings" ); i <= count; ++i ) { - xmp->GetArrayItem ( kXMP_NS_EXIF, "ISOSpeedRatings", i, &isoValue, 0 ); - if ( SXMPUtils::ConvertToInt ( isoValue.c_str() ) > 0xFFFF ) { haveHighISO = true; break; } - } - - if ( ! haveHighISO ) { - haveExif = exif->GetTag ( kTIFF_ExifIFD, kTIFF_ISOSpeedRatings, 0 ); - if ( ! haveExif ) { // ISOSpeedRatings has an inject-only mapping. - ExportArrayTIFF ( exif, kTIFF_ExifIFD, kISOSpeedMapping, exif->IsNativeEndian(), *xmp, kXMP_NS_EXIF, "ISOSpeedRatings" ); - } - xmp->DeleteProperty ( kXMP_NS_EXIF, "ISOSpeedRatings"); - } - - } - - // Export the remaining TIFF, Exif, and GPS IFD tags. - - ExportTIFF_ArrayASCII ( *xmp, kXMP_NS_DC, "creator", exif, kTIFF_PrimaryIFD, kTIFF_Artist ); - - ExportTIFF_LocTextASCII ( *xmp, kXMP_NS_DC, "rights", exif, kTIFF_PrimaryIFD, kTIFF_Copyright ); - - haveXMP = xmp->GetProperty ( kXMP_NS_EXIF, "ExifVersion", &xmpValue, 0 ); - if ( haveXMP && (xmpValue.size() == 4) && (! exif->GetTag ( kTIFF_ExifIFD, kTIFF_ExifVersion, 0 )) ) { - // 36864 ExifVersion is 4 "undefined" ASCII characters. - exif->SetTag ( kTIFF_ExifIFD, kTIFF_ExifVersion, kTIFF_UndefinedType, 4, xmpValue.data() ); - } - - haveXMP = xmp->DoesPropertyExist ( kXMP_NS_EXIF, "ComponentsConfiguration" ); - if ( haveXMP && (xmp->CountArrayItems ( kXMP_NS_EXIF, "ComponentsConfiguration" ) == 4) && - (! exif->GetTag ( kTIFF_ExifIFD, kTIFF_ComponentsConfiguration, 0 )) ) { - // 37121 ComponentsConfiguration is an array of 4 "undefined" UInt8 bytes. - XMP_Uns8 compConfig[4]; - xmp->GetProperty_Int ( kXMP_NS_EXIF, "ComponentsConfiguration[1]", &int32, 0 ); - compConfig[0] = (XMP_Uns8)int32; - xmp->GetProperty_Int ( kXMP_NS_EXIF, "ComponentsConfiguration[2]", &int32, 0 ); - compConfig[1] = (XMP_Uns8)int32; - xmp->GetProperty_Int ( kXMP_NS_EXIF, "ComponentsConfiguration[3]", &int32, 0 ); - compConfig[2] = (XMP_Uns8)int32; - xmp->GetProperty_Int ( kXMP_NS_EXIF, "ComponentsConfiguration[4]", &int32, 0 ); - compConfig[3] = (XMP_Uns8)int32; - exif->SetTag ( kTIFF_ExifIFD, kTIFF_ComponentsConfiguration, kTIFF_UndefinedType, 4, &compConfig[0] ); - } - - haveXMP = xmp->DoesPropertyExist ( kXMP_NS_EXIF, "Flash" ); - if ( haveXMP && (! exif->GetTag ( kTIFF_ExifIFD, kTIFF_Flash, 0 )) ) { - // 37385 Flash is a UInt16 collection of bit fields and is mapped to a struct in XMP. - XMP_Uns16 binFlash = 0; - bool field; - haveXMP = xmp->GetProperty_Bool ( kXMP_NS_EXIF, "Flash/exif:Fired", &field, 0 ); - if ( haveXMP & field ) binFlash |= 0x0001; - haveXMP = xmp->GetProperty_Int ( kXMP_NS_EXIF, "Flash/exif:Return", &int32, 0 ); - if ( haveXMP ) binFlash |= (int32 & 3) << 1; - haveXMP = xmp->GetProperty_Int ( kXMP_NS_EXIF, "Flash/exif:Mode", &int32, 0 ); - if ( haveXMP ) binFlash |= (int32 & 3) << 3; - haveXMP = xmp->GetProperty_Bool ( kXMP_NS_EXIF, "Flash/exif:Function", &field, 0 ); - if ( haveXMP & field ) binFlash |= 0x0020; - haveXMP = xmp->GetProperty_Bool ( kXMP_NS_EXIF, "Flash/exif:RedEyeMode", &field, 0 ); - if ( haveXMP & field ) binFlash |= 0x0040; - exif->SetTag_Short ( kTIFF_ExifIFD, kTIFF_Flash, binFlash ); - } - - haveXMP = xmp->GetProperty_Int ( kXMP_NS_EXIF, "FileSource", &int32, 0 ); - if ( haveXMP && (! exif->GetTag ( kTIFF_ExifIFD, kTIFF_FileSource, 0 )) ) { - // 41728 FileSource is an "undefined" UInt8. - uns8 = (XMP_Uns8)int32; - exif->SetTag ( kTIFF_ExifIFD, kTIFF_FileSource, kTIFF_UndefinedType, 1, &uns8 ); - } - - haveXMP = xmp->GetProperty_Int ( kXMP_NS_EXIF, "SceneType", &int32, 0 ); - if ( haveXMP && (! exif->GetTag ( kTIFF_ExifIFD, kTIFF_SceneType, 0 )) ) { - // 41729 SceneType is an "undefined" UInt8. - uns8 = (XMP_Uns8)int32; - exif->SetTag ( kTIFF_ExifIFD, kTIFF_SceneType, kTIFF_UndefinedType, 1, &uns8 ); - } - - // *** Deferred inject-only tags: SpatialFrequencyResponse, DeviceSettingDescription, CFAPattern - - haveXMP = xmp->GetProperty ( kXMP_NS_EXIF, "GPSVersionID", &xmpValue, 0 ); // This is inject-only. - if ( haveXMP && (xmpValue.size() == 7) && (! exif->GetTag ( kTIFF_GPSInfoIFD, kTIFF_GPSVersionID, 0 )) ) { - char gpsID[4]; // 0 GPSVersionID is 4 UInt8 bytes and mapped in XMP as "n.n.n.n". - gpsID[0] = xmpValue[0]; - gpsID[1] = xmpValue[2]; - gpsID[2] = xmpValue[4]; - gpsID[3] = xmpValue[6]; - exif->SetTag ( kTIFF_GPSInfoIFD, kTIFF_GPSVersionID, kTIFF_ByteType, 4, &gpsID[0] ); - } - - ExportTIFF_GPSCoordinate ( *xmp, kXMP_NS_EXIF, "GPSLatitude", exif, kTIFF_GPSInfoIFD, kTIFF_GPSLatitude ); - - ExportTIFF_GPSCoordinate ( *xmp, kXMP_NS_EXIF, "GPSLongitude", exif, kTIFF_GPSInfoIFD, kTIFF_GPSLongitude ); - - ExportTIFF_GPSTimeStamp ( *xmp, kXMP_NS_EXIF, "GPSTimeStamp", exif ); - - // The following GPS tags are inject-only. - - haveXMP = xmp->DoesPropertyExist ( kXMP_NS_EXIF, "GPSDestLatitude" ); - if ( haveXMP && (! exif->GetTag ( kTIFF_GPSInfoIFD, kTIFF_GPSDestLatitude, 0 )) ) { - ExportTIFF_GPSCoordinate ( *xmp, kXMP_NS_EXIF, "GPSDestLatitude", exif, kTIFF_GPSInfoIFD, kTIFF_GPSDestLatitude ); - } - - haveXMP = xmp->DoesPropertyExist ( kXMP_NS_EXIF, "GPSDestLongitude" ); - if ( haveXMP && (! exif->GetTag ( kTIFF_GPSInfoIFD, kTIFF_GPSDestLongitude, 0 )) ) { - ExportTIFF_GPSCoordinate ( *xmp, kXMP_NS_EXIF, "GPSDestLongitude", exif, kTIFF_GPSInfoIFD, kTIFF_GPSDestLongitude ); - } - - haveXMP = xmp->GetProperty ( kXMP_NS_EXIF, "GPSProcessingMethod", &xmpValue, 0 ); - if ( haveXMP && (! xmpValue.empty()) && (! exif->GetTag ( kTIFF_GPSInfoIFD, kTIFF_GPSProcessingMethod, 0 )) ) { - // 27 GPSProcessingMethod is a string with explicit encoding. - ExportTIFF_EncodedString ( *xmp, kXMP_NS_EXIF, "GPSProcessingMethod", exif, kTIFF_GPSInfoIFD, kTIFF_GPSProcessingMethod ); - } - - haveXMP = xmp->GetProperty ( kXMP_NS_EXIF, "GPSAreaInformation", &xmpValue, 0 ); - if ( haveXMP && (! xmpValue.empty()) && (! exif->GetTag ( kTIFF_GPSInfoIFD, kTIFF_GPSAreaInformation, 0 )) ) { - // 28 GPSAreaInformation is a string with explicit encoding. - ExportTIFF_EncodedString ( *xmp, kXMP_NS_EXIF, "GPSAreaInformation", exif, kTIFF_GPSInfoIFD, kTIFF_GPSAreaInformation ); - } - -} // PhotoDataUtils::ExportExif diff --git a/source/XMPFiles/FormatSupport/Reconcile_Impl.cpp b/source/XMPFiles/FormatSupport/Reconcile_Impl.cpp deleted file mode 100644 index 7d27769..0000000 --- a/source/XMPFiles/FormatSupport/Reconcile_Impl.cpp +++ /dev/null @@ -1,406 +0,0 @@ -// ================================================================================================= -// ADOBE SYSTEMS INCORPORATED -// Copyright 2006 Adobe Systems Incorporated -// 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 "XMP_Environment.h" // ! This must be the first include. - -#include "Reconcile_Impl.hpp" - -#include "UnicodeConversions.hpp" - -#if XMP_WinBuild -#elif XMP_MacBuild - #include "UnicodeConverter.h" -#endif - -// ================================================================================================= -/// \file Reconcile_Impl.cpp -/// \brief Implementation utilities for the photo metadata reconciliation support. -/// -// ================================================================================================= - -// ================================================================================================= -// ReconcileUtils::IsASCII -// ======================= -// -// See if a string is 7 bit ASCII. - -bool ReconcileUtils::IsASCII ( const void * textPtr, size_t textLen ) -{ - - for ( const XMP_Uns8 * textPos = (XMP_Uns8*)textPtr; textLen > 0; --textLen, ++textPos ) { - if ( *textPos >= 0x80 ) return false; - } - - return true; - -} // ReconcileUtils::IsASCII - -// ================================================================================================= -// ReconcileUtils::IsUTF8 -// ====================== -// -// See if a string contains valid UTF-8. Allow nul bytes, they can appear inside of multi-part Exif -// strings. We don't use CodePoint_from_UTF8_Multi in UnicodeConversions because it throws an -// exception for non-Unicode and we don't need to actually compute the code points. - -bool ReconcileUtils::IsUTF8 ( const void * textPtr, size_t textLen ) -{ - const XMP_Uns8 * textPos = (XMP_Uns8*)textPtr; - const XMP_Uns8 * textEnd = textPos + textLen; - - while ( textPos < textEnd ) { - - if ( *textPos < 0x80 ) { - - ++textPos; // ASCII is UTF-8, tolerate nuls. - - } else { - - // ------------------------------------------------------------------------------------- - // We've got a multibyte UTF-8 character. The first byte has the number of bytes as the - // number of high order 1 bits. The remaining bytes must have 1 and 0 as the top 2 bits. - - #if 0 // *** This might be a more effcient way to count the bytes. - static XMP_Uns8 kByteCounts[16] = { 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 2, 2, 3, 4 }; - size_t bytesNeeded = kByteCounts [ *textPos >> 4 ]; - if ( (bytesNeeded < 2) || ((bytesNeeded == 4) && ((*textPos & 0x08) != 0)) ) return false; - if ( (textPos + bytesNeeded) > textEnd ) return false; - #endif - - size_t bytesNeeded = 0; // Count the high order 1 bits in the first byte. - for ( XMP_Uns8 temp = *textPos; temp > 0x7F; temp = temp << 1 ) ++bytesNeeded; - // *** Consider CPU-specific assembly inline, e.g. cntlzw on PowerPC. - - if ( (bytesNeeded < 2) || (bytesNeeded > 4) || ((textPos+bytesNeeded) > textEnd) ) return false; - - for ( --bytesNeeded, ++textPos; bytesNeeded > 0; --bytesNeeded, ++textPos ) { - if ( (*textPos >> 6) != 2 ) return false; - } - - } - - } - - return true; // ! Returns true for empty strings. - -} // ReconcileUtils::IsUTF8 - -// ================================================================================================= -// UTF8ToHostEncoding -// ================== - -#if XMP_WinBuild - - void ReconcileUtils::UTF8ToWinEncoding ( UINT codePage, const XMP_Uns8 * utf8Ptr, size_t utf8Len, std::string * host ) - { - - std::string utf16; // WideCharToMultiByte wants native UTF-16. - ToUTF16Native ( (UTF8Unit*)utf8Ptr, utf8Len, &utf16 ); - - LPCWSTR utf16Ptr = (LPCWSTR) utf16.c_str(); - size_t utf16Len = utf16.size() / 2; - - int hostLen = WideCharToMultiByte ( codePage, 0, utf16Ptr, (int)utf16Len, 0, 0, 0, 0 ); - host->assign ( hostLen, ' ' ); // Allocate space for the results. - - (void) WideCharToMultiByte ( codePage, 0, utf16Ptr, (int)utf16Len, (LPSTR)host->data(), hostLen, 0, 0 ); - XMP_Assert ( hostLen == host->size() ); - - } // UTF8ToWinEncoding - -#elif XMP_MacBuild - - void ReconcileUtils::UTF8ToMacEncoding ( XMP_Uns16 macScript, XMP_Uns16 macLang, const XMP_Uns8 * utf8Ptr, size_t utf8Len, std::string * host ) - { - OSStatus err; - - TextEncoding destEncoding; - if ( macLang == langUnspecified ) macLang = kTextLanguageDontCare; - err = UpgradeScriptInfoToTextEncoding ( macScript, macLang, kTextRegionDontCare, 0, &destEncoding ); - if ( err != noErr ) XMP_Throw ( "UpgradeScriptInfoToTextEncoding failed", kXMPErr_ExternalFailure ); - - UnicodeMapping mappingInfo; - mappingInfo.mappingVersion = kUnicodeUseLatestMapping; - mappingInfo.otherEncoding = GetTextEncodingBase ( destEncoding ); - mappingInfo.unicodeEncoding = CreateTextEncoding ( kTextEncodingUnicodeDefault, - kUnicodeNoSubset, kUnicodeUTF8Format ); - - UnicodeToTextInfo converterInfo; - err = CreateUnicodeToTextInfo ( &mappingInfo, &converterInfo ); - if ( err != noErr ) XMP_Throw ( "CreateUnicodeToTextInfo failed", kXMPErr_ExternalFailure ); - - try { // ! Need to call DisposeUnicodeToTextInfo before exiting. - - OptionBits convFlags = kUnicodeUseFallbacksMask | - kUnicodeLooseMappingsMask | kUnicodeDefaultDirectionMask; - ByteCount bytesRead, bytesWritten; - - enum { kBufferLen = 1000 }; // Ought to be enough in practice, without using too much stack. - char buffer [kBufferLen]; - - host->reserve ( utf8Len ); // As good a guess as any. - - while ( utf8Len > 0 ) { - // Ignore all errors from ConvertFromUnicodeToText. It returns info like "output - // buffer full" or "use substitution" as errors. - err = ConvertFromUnicodeToText ( converterInfo, utf8Len, (UniChar*)utf8Ptr, convFlags, - 0, 0, 0, 0, kBufferLen, &bytesRead, &bytesWritten, buffer ); - if ( bytesRead == 0 ) break; // Make sure forward progress happens. - host->append ( &buffer[0], bytesWritten ); - utf8Ptr += bytesRead; - utf8Len -= bytesRead; - } - - DisposeUnicodeToTextInfo ( &converterInfo ); - - } catch ( ... ) { - - DisposeUnicodeToTextInfo ( &converterInfo ); - throw; - - } - - } // UTF8ToMacEncoding - -#elif XMP_UNIXBuild - - // ! Does not exist, must not be called, for Generic UNIX builds. - -#endif - -// ================================================================================================= -// ReconcileUtils::UTF8ToLocal -// =========================== - -void ReconcileUtils::UTF8ToLocal ( const void * _utf8Ptr, size_t utf8Len, std::string * local ) -{ - const XMP_Uns8* utf8Ptr = (XMP_Uns8*)_utf8Ptr; - - local->erase(); - - if ( ReconcileUtils::IsASCII ( utf8Ptr, utf8Len ) ) { - local->assign ( (const char *)utf8Ptr, utf8Len ); - return; - } - - #if XMP_WinBuild - - UTF8ToWinEncoding ( CP_ACP, utf8Ptr, utf8Len, local ); - - #elif XMP_MacBuild - - UTF8ToMacEncoding ( smSystemScript, kTextLanguageDontCare, utf8Ptr, utf8Len, local ); - - #elif XMP_UNIXBuild - - XMP_Throw ( "Generic UNIX does not have conversions between local and Unicode", kXMPErr_Unavailable ); - - #endif - -} // ReconcileUtils::UTF8ToLocal - -// ================================================================================================= -// ReconcileUtils::UTF8ToLatin1 -// ============================ - -void ReconcileUtils::UTF8ToLatin1 ( const void * _utf8Ptr, size_t utf8Len, std::string * latin1 ) -{ - const XMP_Uns8* utf8Ptr = (XMP_Uns8*)_utf8Ptr; - const XMP_Uns8* utf8End = utf8Ptr + utf8Len; - - latin1->erase(); - latin1->reserve ( utf8Len ); // As good a guess as any, at least enough, exact for ASCII. - - bool inBadRun = false; - - while ( utf8Ptr < utf8End ) { - - if ( *utf8Ptr <= 0x7F ) { - - (*latin1) += (char)*utf8Ptr; // Have an ASCII character. - inBadRun = false; - ++utf8Ptr; - - } else if ( utf8Ptr == (utf8End - 1) ) { - - inBadRun = false; - ++utf8Ptr; // Ignore a bad end to the UTF-8. - - } else { - - XMP_Assert ( (utf8End - utf8Ptr) >= 2 ); - XMP_Uns16 ch16 = GetUns16BE ( utf8Ptr ); // A Latin-1 80..FF is 2 UTF-8 bytes. - - if ( (0xC280 <= ch16) && (ch16 <= 0xC2BF) ) { - - (*latin1) += (char)(ch16 & 0xFF); // UTF-8 C280..C2BF are Latin-1 80..BF. - inBadRun = false; - utf8Ptr += 2; - - } else if ( (0xC380 <= ch16) && (ch16 <= 0xC3BF) ) { - - (*latin1) += (char)((ch16 & 0xFF) + 0x40); // UTF-8 C380..C3BF are Latin-1 C0..FF. - inBadRun = false; - utf8Ptr += 2; - - } else { - - if ( ! inBadRun ) { - inBadRun = true; - (*latin1) += "(?)"; // Mark the run of out of scope UTF-8. - } - - ++utf8Ptr; // Skip the presumably well-formed UTF-8 character. - while ( (utf8Ptr < utf8End) && ((*utf8Ptr & 0xC0) == 0x80) ) ++utf8Ptr; - - } - - } - - } - - XMP_Assert ( utf8Ptr == utf8End ); - -} // ReconcileUtils::UTF8ToLatin1 - -// ================================================================================================= -// HostEncodingToUTF8 -// ================== - -#if XMP_WinBuild - - void ReconcileUtils::WinEncodingToUTF8 ( UINT codePage, const XMP_Uns8 * hostPtr, size_t hostLen, std::string * utf8 ) - { - - int utf16Len = MultiByteToWideChar ( codePage, 0, (LPCSTR)hostPtr, (int)hostLen, 0, 0 ); - std::vector<UTF16Unit> utf16 ( utf16Len, 0 ); // MultiByteToWideChar returns native UTF-16. - - (void) MultiByteToWideChar ( codePage, 0, (LPCSTR)hostPtr, (int)hostLen, (LPWSTR)&utf16[0], utf16Len ); - FromUTF16Native ( &utf16[0], (int)utf16Len, utf8 ); - - } // WinEncodingToUTF8 - -#elif XMP_MacBuild - - void ReconcileUtils::MacEncodingToUTF8 ( XMP_Uns16 macScript, XMP_Uns16 macLang, const XMP_Uns8 * hostPtr, size_t hostLen, std::string * utf8 ) - { - OSStatus err; - - TextEncoding srcEncoding; - if ( macLang == langUnspecified ) macLang = kTextLanguageDontCare; - err = UpgradeScriptInfoToTextEncoding ( macScript, macLang, kTextRegionDontCare, 0, &srcEncoding ); - if ( err != noErr ) XMP_Throw ( "UpgradeScriptInfoToTextEncoding failed", kXMPErr_ExternalFailure ); - - UnicodeMapping mappingInfo; - mappingInfo.mappingVersion = kUnicodeUseLatestMapping; - mappingInfo.otherEncoding = GetTextEncodingBase ( srcEncoding ); - mappingInfo.unicodeEncoding = CreateTextEncoding ( kTextEncodingUnicodeDefault, - kUnicodeNoSubset, kUnicodeUTF8Format ); - - TextToUnicodeInfo converterInfo; - err = CreateTextToUnicodeInfo ( &mappingInfo, &converterInfo ); - if ( err != noErr ) XMP_Throw ( "CreateTextToUnicodeInfo failed", kXMPErr_ExternalFailure ); - - try { // ! Need to call DisposeTextToUnicodeInfo before exiting. - - ByteCount bytesRead, bytesWritten; - - enum { kBufferLen = 1000 }; // Ought to be enough in practice, without using too much stack. - char buffer [kBufferLen]; - - utf8->reserve ( hostLen ); // As good a guess as any. - - while ( hostLen > 0 ) { - // Ignore all errors from ConvertFromTextToUnicode. It returns info like "output - // buffer full" or "use substitution" as errors. - err = ConvertFromTextToUnicode ( converterInfo, hostLen, hostPtr, kNilOptions, - 0, 0, 0, 0, kBufferLen, &bytesRead, &bytesWritten, (UniChar*)buffer ); - if ( bytesRead == 0 ) break; // Make sure forward progress happens. - utf8->append ( &buffer[0], bytesWritten ); - hostPtr += bytesRead; - hostLen -= bytesRead; - } - - DisposeTextToUnicodeInfo ( &converterInfo ); - - } catch ( ... ) { - - DisposeTextToUnicodeInfo ( &converterInfo ); - throw; - - } - - } // MacEncodingToUTF8 - -#elif XMP_UNIXBuild - - // ! Does not exist, must not be called, for Generic UNIX builds. - -#endif - -// ================================================================================================= -// ReconcileUtils::LocalToUTF8 -// =========================== - -void ReconcileUtils::LocalToUTF8 ( const void * _localPtr, size_t localLen, std::string * utf8 ) -{ - const XMP_Uns8* localPtr = (XMP_Uns8*)_localPtr; - - utf8->erase(); - - if ( ReconcileUtils::IsASCII ( localPtr, localLen ) ) { - utf8->assign ( (const char *)localPtr, localLen ); - return; - } - - #if XMP_WinBuild - - WinEncodingToUTF8 ( CP_ACP, localPtr, localLen, utf8 ); - - #elif XMP_MacBuild - - MacEncodingToUTF8 ( smSystemScript, kTextLanguageDontCare, localPtr, localLen, utf8 ); - - #elif XMP_UNIXBuild - - XMP_Throw ( "Generic UNIX does not have conversions between local and Unicode", kXMPErr_Unavailable ); - - #endif - -} // ReconcileUtils::LocalToUTF8 - -// ================================================================================================= -// ReconcileUtils::Latin1ToUTF8 -// ============================ - -void ReconcileUtils::Latin1ToUTF8 ( const void * _latin1Ptr, size_t latin1Len, std::string * utf8 ) -{ - const XMP_Uns8* latin1Ptr = (XMP_Uns8*)_latin1Ptr; - const XMP_Uns8* latin1End = latin1Ptr + latin1Len; - - utf8->erase(); - utf8->reserve ( latin1Len ); // As good a guess as any, exact for ASCII. - - for ( ; latin1Ptr < latin1End; ++latin1Ptr ) { - - XMP_Uns8 ch8 = *latin1Ptr; - - if ( ch8 <= 0x7F ) { - (*utf8) += (char)ch8; // Have an ASCII character. - } else if ( ch8 <= 0xBF ) { - (*utf8) += 0xC2; // Latin-1 80..BF are UTF-8 C280..C2BF. - (*utf8) += (char)ch8; - } else { - (*utf8) += 0xC3; // Latin-1 C0..FF are UTF-8 C380..C3BF. - (*utf8) += (char)(ch8 - 0x40); - } - - } - -} // ReconcileUtils::Latin1ToUTF8 diff --git a/source/XMPFiles/FormatSupport/Reconcile_Impl.hpp b/source/XMPFiles/FormatSupport/Reconcile_Impl.hpp deleted file mode 100644 index 19ea865..0000000 --- a/source/XMPFiles/FormatSupport/Reconcile_Impl.hpp +++ /dev/null @@ -1,95 +0,0 @@ -#ifndef __Reconcile_Impl_hpp__ -#define __Reconcile_Impl_hpp__ 1 - -// ================================================================================================= -// ADOBE SYSTEMS INCORPORATED -// Copyright 2006 Adobe Systems Incorporated -// 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 "XMP_Environment.h" // ! This must be the first include. - -#include "ReconcileLegacy.hpp" -#include "MD5.h" - -// ================================================================================================= -/// \file Reconcile_Impl.hpp -/// \brief Implementation utilities for the legacy metadata reconciliation support. -/// -// ================================================================================================= - -typedef XMP_Uns8 MD5_Digest[16]; // ! Should be in MD5.h. - -enum { - kDigestMissing = -1, // A partial import is done, existing XMP is left alone. - kDigestDiffers = 0, // A full import is done, existing XMP is deleted or replaced. - kDigestMatches = +1 // No importing is done. -}; - -namespace ReconcileUtils { - - // *** These ought to be with the Unicode conversions. - - static const char * kHexDigits = "0123456789ABCDEF"; - - bool IsASCII ( const void * _textPtr, size_t textLen ); - bool IsUTF8 ( const void * _textPtr, size_t textLen ); - - void UTF8ToLocal ( const void * _utf8Ptr, size_t utf8Len, std::string * local ); - void UTF8ToLatin1 ( const void * _utf8Ptr, size_t utf8Len, std::string * latin1 ); - void LocalToUTF8 ( const void * _localPtr, size_t localLen, std::string * utf8 ); - void Latin1ToUTF8 ( const void * _latin1Ptr, size_t latin1Len, std::string * utf8 ); - - #if XMP_WinBuild - void UTF8ToWinEncoding ( UINT codePage, const XMP_Uns8 * utf8Ptr, size_t utf8Len, std::string * host ); - void WinEncodingToUTF8 ( UINT codePage, const XMP_Uns8 * hostPtr, size_t hostLen, std::string * utf8 ); - #elif XMP_MacBuild - void UTF8ToMacEncoding ( XMP_Uns16 macScript, XMP_Uns16 macLang, const XMP_Uns8 * utf8Ptr, size_t utf8Len, std::string * host ); - void MacEncodingToUTF8 ( XMP_Uns16 macScript, XMP_Uns16 macLang, const XMP_Uns8 * hostPtr, size_t hostLen, std::string * utf8 ); - #endif - -}; // ReconcileUtils - -namespace PhotoDataUtils { - - int CheckIPTCDigest ( const void * newPtr, const XMP_Uns32 newLen, const void * oldDigest ); - void SetIPTCDigest ( void * iptcPtr, XMP_Uns32 iptcLen, PSIR_Manager * psir ); - - bool GetNativeInfo ( const TIFF_Manager & exif, XMP_Uns8 ifd, XMP_Uns16 id, TIFF_Manager::TagInfo * info ); - size_t GetNativeInfo ( const IPTC_Manager & iptc, XMP_Uns8 id, int digestState, - bool haveXMP, IPTC_Manager::DataSetInfo * info ); - - bool IsValueDifferent ( const TIFF_Manager::TagInfo & exifInfo, - const std::string & xmpValue, std::string * exifValue ); - bool IsValueDifferent ( const IPTC_Manager & newIPTC, const IPTC_Manager & oldIPTC, XMP_Uns8 id ); - - void ImportPSIR ( const PSIR_Manager & psir, SXMPMeta * xmp, int iptcDigestState ); - - void Import2WayIPTC ( const IPTC_Manager & iptc, SXMPMeta * xmp, int iptcDigestState ); - void Import2WayExif ( const TIFF_Manager & exif, SXMPMeta * xmp, int iptcDigestState ); - - void Import3WayItems ( const TIFF_Manager & exif, const IPTC_Manager & iptc, SXMPMeta * xmp, int iptcDigestState ); - - void ExportPSIR ( const SXMPMeta & xmp, PSIR_Manager * psir ); - void ExportIPTC ( const SXMPMeta & xmp, IPTC_Manager * iptc ); - void ExportExif ( SXMPMeta * xmp, TIFF_Manager * exif ); - - // These need to be exposed for use in Import3WayItem: - - void ImportIPTC_Simple ( const IPTC_Manager & iptc, SXMPMeta * xmp, - XMP_Uns8 id, const char * xmpNS, const char * xmpProp ); - - void ImportIPTC_LangAlt ( const IPTC_Manager & iptc, SXMPMeta * xmp, - XMP_Uns8 id, const char * xmpNS, const char * xmpProp ); - - void ImportIPTC_Array ( const IPTC_Manager & iptc, SXMPMeta * xmp, - XMP_Uns8 id, const char * xmpNS, const char * xmpProp ); - - void ImportIPTC_Date ( XMP_Uns8 dateID, const IPTC_Manager & iptc, SXMPMeta * xmp ); - -}; // PhotoDataUtils - -#endif // __Reconcile_Impl_hpp__ diff --git a/source/XMPFiles/FormatSupport/SWF_Support.cpp b/source/XMPFiles/FormatSupport/SWF_Support.cpp deleted file mode 100644 index d9b8115..0000000 --- a/source/XMPFiles/FormatSupport/SWF_Support.cpp +++ /dev/null @@ -1,844 +0,0 @@ -// ================================================================================================= -// ADOBE SYSTEMS INCORPORATED -// Copyright 2007 Adobe Systems Incorporated -// 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 "SWF_Support.hpp" -#include "zlib.h" - - -namespace SWF_Support -{ - - // ============================================================================================= - - static int CalcHeaderSize ( IO::InputStream* inputStream ) - { - int size = 0; - - try { - - XMP_Uns8 buffer[1]; - long bytesRead = inputStream->Read ( buffer, 1 ); - if ( bytesRead != 1 ) return 0; - - // The RECT datatype has a variable size depending on the packed bit-values -> rectDatatypeSize = size in bytes - int bits = int(buffer[0] >> 3); - int bytes = ((5 + (4 * bits)) / 8) + 1; - - size = 12 + bytes; - - } catch ( ... ) { - - return 0; - } - - inputStream->Skip ( size - inputStream->GetCurrentPos() ); - return size; - - } // CalcHeaderSize - - // ============================================================================================= - - static unsigned long CheckTag ( IO::InputStream* inputStream, TagState& inOutTagState, TagData& inOutTagData ) - { - unsigned long ret = 0; - XMP_Uns8 * buffer = 0; - - try { - - if ( inOutTagData.id == SWF_TAG_ID_METADATA ) { - - buffer = new XMP_Uns8[inOutTagData.len]; - XMP_Uns32 bytes = inputStream->Read ( buffer, inOutTagData.len ); - - XMP_Assert ( bytes == inOutTagData.len ); - - inOutTagState.xmpPos = inOutTagData.pos + inOutTagData.offset; - inOutTagState.xmpLen = inOutTagData.len; - inOutTagData.xmp = true; - - inOutTagState.xmpPacket.assign ( (char*)buffer, inOutTagData.len ); - - delete[] buffer; - buffer = 0; - ret = inOutTagState.xmpLen; - - } - - } catch ( ... ) { - - if ( buffer != 0 ) { - delete[] buffer; - buffer = 0; - } - - } - - if ( buffer != 0 ) { - delete[] buffer; - buffer = 0; - } - - return ret; - - } // CheckTag - - // ============================================================================================= - - bool HasMetadata ( IO::InputStream* inputStream, TagState& tagState ) - { - - XMP_Uns32 flags = ReadFileAttrFlags ( inputStream ); - tagState.fileAttrFlags = flags; - return ((flags & SWF_METADATA_FLAG) == SWF_METADATA_FLAG); - - } // HasMetadata - - // ============================================================================================= - - XMP_Uns32 ReadFileAttrFlags ( IO::InputStream* inputStream ) - { - XMP_Uns8 buffer[4]; - - XMP_Uns32 bytes = inputStream->Read ( buffer, 4 ); - XMP_Assert ( bytes == 4 ); - - return GetUns32LE ( buffer ); - - } // ReadFileAttrFlags - - // ============================================================================================= - - long OpenSWF ( IO::InputStream *inputStream, TagState & inOutTagState ) - { - XMP_Uns64 pos = 0; - long name; - XMP_Uns32 len; - - inOutTagState.headerSize = CalcHeaderSize ( inputStream ); - pos = inOutTagState.headerSize; - - // read first and following chunks - bool running = true; - while ( running ) { - running = ReadTag ( inputStream, inOutTagState, &name, &len, pos ); - if ( inOutTagState.cachingFile ) { - if ( (! inOutTagState.hasXMP) || (inOutTagState.xmpLen > 0) ) running = false; - } - } - - return (long) inOutTagState.tags.size(); - - } // OpenSWF - - // ============================================================================================= - - bool ReadTag ( IO::InputStream* inputStream, TagState & inOutTagState, - long* tagType, XMP_Uns32* tagLength, XMP_Uns64& inOutPosition ) - { - - try { - - XMP_Uns64 startPosition = inOutPosition; - long bytesRead; - XMP_Uns8 buffer[4]; - - bytesRead = inputStream->Read ( buffer, 2 ); - if ( bytesRead != 2 ) return false; - - inOutPosition += 2; - XMP_Uns16 code = GetUns16LE ( buffer ); - *tagType = code >> 6; - *tagLength = code & 0x3f; - - bool longTag = false; - - if ( *tagLength == 0x3f ) { - longTag = true; - bytesRead = inputStream->Read ( buffer, 4 ); - if ( bytesRead != 4 ) return false; - inOutPosition += 4; - *tagLength = GetUns32LE ( buffer ); - } - - inOutPosition += *tagLength; - - TagData newTag; - - newTag.pos = startPosition; - newTag.len = *tagLength; - newTag.id = *tagType; - newTag.offset = ( (! longTag) ? 2 : 6 ); - - // we cannot check valid XMP within the handler - // provide validating XMP by invoking XMPCore - // check tag for XMP - if ( newTag.id == SWF_TAG_ID_METADATA ) { - newTag.xmp = true; - inOutTagState.xmpTag = newTag; - CheckTag ( inputStream, inOutTagState, newTag ); - if ( ! inOutTagState.hasFileAttrTag ) inOutTagState.hasXMP = true; - } - - //store FileAttribute Tag - if ( newTag.id == SWF_TAG_ID_FILEATTRIBUTES ) { - inOutTagState.hasFileAttrTag = true; - inOutTagState.fileAttrTag = newTag; - inOutTagState.hasXMP = HasMetadata ( inputStream, inOutTagState ); - //decreasing since stream moved on within HasMetadata function - *tagLength -= 4; - } - - //store tag in vector to process later - inOutTagState.tags.push_back ( newTag ); - - //seek to next tag - if ( ! newTag.xmp ) inputStream->Skip ( *tagLength ); - if ( inputStream->IsEOF() ) return false; - - } catch ( ... ) { - - return false; - - } - - return true; - - } // ReadTag - - // ============================================================================================= - - bool WriteXMPTag ( LFA_FileRef fileRef, XMP_Uns32 len, const char* inBuffer ) - { - bool ret = false; - - XMP_Uns16 code = MakeUns16LE ( (SWF_TAG_ID_METADATA << 6) | 0x3F ); - XMP_Uns32 length = MakeUns32LE ( len ); - - try { - LFA_Write (fileRef, &code, 2 ); - LFA_Write (fileRef, &length, 4 ); - LFA_Write (fileRef, inBuffer, len ); - ret = true; - } catch ( ... ) {} - - return ret; - - } // WriteXMPTag - - // ============================================================================================= - - bool CopyHeader ( LFA_FileRef sourceRef, LFA_FileRef destRef, const TagState& tagState ) - { - try { - int headerSize = tagState.headerSize; - LFA_Seek ( sourceRef, 0, SEEK_SET ); - LFA_Copy ( sourceRef, destRef, headerSize ); - return true; - } catch ( ... ) {} - - return false; - - } // CopyHeader - - // ============================================================================================= - - 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 - - // ============================================================================================= - - bool CopyTag ( LFA_FileRef sourceRef, LFA_FileRef destRef, TagData& tag ) - { - - try { - LFA_Seek ( sourceRef, tag.pos, SEEK_SET ); - LFA_Copy ( sourceRef, destRef, (tag.len + tag.offset) ); - return true; - } catch ( ... ) {} - - return false; - - } // CopyTag - - // ============================================================================================= - - bool ReadBuffer ( LFA_FileRef fileRef, XMP_Uns64& pos, XMP_Uns32 len, XMP_Uns8* outBuffer ) - { - - try { - if ( (fileRef == 0) || (outBuffer == 0) ) return false; - LFA_Seek ( fileRef, pos, SEEK_SET ); - long bytesRead = LFA_Read ( fileRef, outBuffer, len ); - return ( (XMP_Uns32)bytesRead == len ); - } catch ( ... ) {} - - return false; - - } // ReadBuffer - - // ============================================================================================= - - bool WriteBuffer ( LFA_FileRef fileRef, XMP_Uns64& pos, XMP_Uns32 len, const char* inBuffer ) - { - - try { - if ( (fileRef == 0) || (inBuffer == 0) ) return false; - LFA_Seek ( fileRef, pos, SEEK_SET ); - LFA_Write ( fileRef, inBuffer, len ); - return true; - } - catch ( ... ) {} - - return false; - - } // WriteBuffer - - // ============================================================================================= - - bool UpdateFileAttrTag ( LFA_FileRef fileRef, const TagData& fileAttrTag, const TagState& tagState ) - { - - try { - XMP_Uns32 flags = tagState.fileAttrFlags; - flags |= SWF_METADATA_FLAG; - return WriteFileAttrFlags ( fileRef, fileAttrTag, flags ); - } catch ( ... ) {} - - return false; - - } // UpdateFileAttrTag - - // ============================================================================================= - - - bool WriteFileAttrFlags ( LFA_FileRef fileRef, const TagData& fileAttrTag, XMP_Uns32 flags ) - { - - try { - XMP_Uns32 bitMask = MakeUns32LE ( flags ); - LFA_Seek ( fileRef, fileAttrTag.pos + fileAttrTag.offset, SEEK_SET ); - LFA_Write ( fileRef, &bitMask, 4 ); - return true; - } catch ( ... ) {} - - return false; - - } // WriteFileAttrFlags - - // ============================================================================================= - // ============================================================================================= - - FileInfo::FileInfo ( LFA_FileRef fileRef, const std::string& origPath ) - { - - this->compressedFile = false; - this->iSize = 0; - this->CheckFormat ( fileRef ); - this->origFilePath.assign ( origPath ); - this->fileRef = fileRef; - - } // FileInfo::FileInfo - - // ============================================================================================= - - 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 FileInfo::IsCompressed() - { - - return this->compressedFile; - - } // FileInfo::IsCompressed - - // ============================================================================================= - - LFA_FileRef FileInfo::Decompress() - { - - if ( ! this->IsCompressed() ) return this->fileRef; - - LFA_FileRef updateRef = 0; - std::string updatePath; - - try { - - CreateTempFile ( this->origFilePath, &updatePath, kCopyMacRsrc ); - updateRef = LFA_Open ( updatePath.c_str(), 'w' ); - this->tmpFilePath.assign ( updatePath ); - - int ret = this->Encode ( this->fileRef, updateRef, FWS, Inf ); - this->tmpFileRef = updateRef; - if ( ret != Z_OK ) XMP_Throw ( "zstream error occured", kXMPErr_ExternalFailure ); - - return this->tmpFileRef; - - } catch ( ... ) { - - LFA_Close ( updateRef ); - LFA_Delete ( updatePath.c_str() ); - return this->fileRef; - - } - - } // FileInfo::Decompress - - // ============================================================================================= - - void FileInfo::Compress ( LFA_FileRef sourceRef, LFA_FileRef destRef ) - { - - if ( this->IsCompressed() ) this->Encode ( sourceRef, destRef, CWS, Def ); - - } // FileInfo::Compress - - // ============================================================================================= - - void FileInfo::Clean() - { - - if ( this->tmpFileRef != 0 ) LFA_Close ( this->tmpFileRef ); - this->tmpFileRef = 0; - this->CleanTempFiles(); - - } // FileInfo::Clean - - // ============================================================================================= - - void FileInfo::CleanTempFiles() - { - - if ( ! this->tmpFilePath.empty() ) { - LFA_Delete ( this->tmpFilePath.c_str() ); - this->tmpFilePath.erase(); - } - - } // FileInfo::CleanTempFiles - - // ============================================================================================= - - int FileInfo::Encode ( LFA_FileRef fileRef, LFA_FileRef updateRef, SWF_MODE swfMode, CompressionFnc cmpFnc ) - { - - LFA_Seek ( updateRef, 0, SEEK_SET ); - - if ( swfMode == CWS ) { - LFA_Write ( updateRef, SWF_C_SIGNATURE_DATA, SWF_SIGNATURE_LEN ); - } else { - XMP_Assert ( swfMode == FWS ); - LFA_Write ( updateRef, SWF_F_SIGNATURE_DATA, SWF_SIGNATURE_LEN ); - } - - LFA_Seek ( fileRef, SWF_SIGNATURE_LEN, SEEK_SET ); - LFA_Seek ( updateRef, SWF_SIGNATURE_LEN, SEEK_SET ); - LFA_Copy ( fileRef, updateRef, 5 ); - - int ret = cmpFnc ( fileRef, updateRef ); - LFA_Flush ( updateRef ); - - return ret; - - } // FileInfo::Encode - - // ============================================================================================= - - 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 - - // ============================================================================================= - - 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 - - // ============================================================================================= - -} // namespace SWF_Support - -// ================================================================================================= -// ================================================================================================= - -namespace IO -{ - - // ============================================================================================= - - void FileInputStream::InitStream() - { - - iEndPos = LFA_Seek ( iFile, 0, SEEK_END ); - iPos = LFA_Seek ( iFile, 0, SEEK_SET ); - - } // FileInputStream::InitStream - - // ============================================================================================= - - XMP_Int32 FileInputStream::Read ( XMP_Uns8* ioBuf, XMP_Int32 len ) - { - if ( IsEOF() ) throw new IOException ( IO::STATUS_EOF ); - - XMP_Int32 bytes = LFA_Read ( iFile, ioBuf, len ); - iPos += bytes; - - return bytes; - - } // FileInputStream::Read - - // ============================================================================================= - - XMP_Int64 FileInputStream::Skip ( XMP_Int64 len ) - { - - if ( IsEOF() ) return 0; - - iPos += len; - XMP_Int64 bytes = LFA_Seek ( iFile, iPos, SEEK_SET ); - return bytes; - - } // FileInputStream::Skip - - // ============================================================================================= - - void FileInputStream::Reset() - { - - iPos = LFA_Seek ( iFile, 0, SEEK_SET ); - - } // FileInputStream::Reset - - // ============================================================================================= - - bool FileInputStream::IsEOF() - { - - return (iPos >= iEndPos); - - } // FileInputStream::IsEOF - - // ============================================================================================= - // ============================================================================================= - - namespace ZIP - { - - // ========================================================================================= - - DeflateInputStream::DeflateInputStream ( LFA_FileRef file, XMP_Int32 bufferLength ) - : FileInputStream(file), iStatus(0), iBufferLength(bufferLength) - { - - InitStream(); - iBuffer = new XMP_Uns8[bufferLength]; - - } // DeflateInputStream::DeflateInputStream - - // ========================================================================================= - - DeflateInputStream::~DeflateInputStream() - { - - inflateEnd ( &iStream ); - delete[] iBuffer; - iBuffer = NULL; - - } // DeflateInputStream::~DeflateInputStream - - // ========================================================================================= - - void DeflateInputStream::InitStream() - { - - iStream.zalloc = Z_NULL; - iStream.zfree = Z_NULL; - iStream.opaque = Z_NULL; - iStream.avail_in = 0; - iStream.next_in = Z_NULL; - iStream.avail_out = 1; - - iStatus = inflateInit ( &iStream ); - if ( iStatus != Z_OK ) throw new ZIPException ( iStatus ); - - } // DeflateInputStream::InitStream - - // ========================================================================================= - - XMP_Int32 DeflateInputStream::Read ( XMP_Uns8* ioBuf, XMP_Int32 len ) - { - - // *** if ( len > iBufferLength ) throw new IOException ( STATUS_BUFFER_OVERFLOW ); - - if( iStream.avail_out != 0 ) { - XMP_Int64 pos = GetCurrentPos(); - iStream.avail_in = FileInputStream::Read ( iBuffer, iBufferLength ); - iPos = pos + len; - iStream.next_in = iBuffer; - } - - iStream.avail_out = len; - iStream.next_out = ioBuf; - - iStatus = inflate ( &iStream, Z_NO_FLUSH ); - - if ( iStatus == Z_MEM_ERROR ) { - inflateEnd ( &iStream ); - throw new ZIPException ( Z_MEM_ERROR ); - } - - return (len - iStream.avail_out); - - } // DeflateInputStream::Read - - // ========================================================================================= - - XMP_Int32 DeflateInputStream::Read ( XMP_Uns8* ioBuf ) - { - - return Read ( ioBuf, iBufferLength ); - - } // DeflateInputStream::Read - - // ========================================================================================= - - void DeflateInputStream::Close() - { - - inflateEnd ( &iStream ); - iPos = 0; - - } // DeflateInputStream::Close - - // ========================================================================================= - - bool DeflateInputStream::IsEOF() - { - - return (iStatus == Z_STREAM_END); - - } // DeflateInputStream::IsEOF - - // ========================================================================================= - - XMP_Int64 DeflateInputStream::Skip ( XMP_Int64 len ) - { - - return Skip ( len, DEFLATE ); - - } // DeflateInputStream::Skip - - // ========================================================================================= - - XMP_Int64 DeflateInputStream::Skip ( XMP_Int64 len, EDeflate deflate ) - { - - switch ( deflate ) { - - case DEFLATE: - { - XMP_Uns8 * buffer = new XMP_Uns8 [ (XMP_Uns32)len ]; - XMP_Int64 bytes = Read ( buffer, (XMP_Uns32)len ); - delete[] buffer; - return bytes; - } - break; - - case DEFLATE_NO: - return FileInputStream::Skip ( len ); - - default: - throw new IOException ( STATUS_SKIP ); - - } - - } // DeflateInputStream::Skip - - // ========================================================================================= - - } // namespace ZIP - - // ============================================================================================= - -} // namespace IO diff --git a/source/XMPFiles/FormatSupport/SWF_Support.hpp b/source/XMPFiles/FormatSupport/SWF_Support.hpp deleted file mode 100644 index db20ab2..0000000 --- a/source/XMPFiles/FormatSupport/SWF_Support.hpp +++ /dev/null @@ -1,254 +0,0 @@ -#ifndef __SWF_Support_hpp__ -#define __SWF_Support_hpp__ 1 - -// ================================================================================================= -// ADOBE SYSTEMS INCORPORATED -// Copyright 2008 Adobe Systems Incorporated -// 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 "XMP_Environment.h" // ! This must be the first include. - -#include "XMPFiles_Impl.hpp" - -#define SWF_SIGNATURE_LEN 3 -#define SWF_C_SIGNATURE_DATA "\x43\x57\x53" -#define SWF_F_SIGNATURE_DATA "\x46\x57\x53" - -#define SWF_TAG_ID_FILEATTRIBUTES 69 -#define SWF_TAG_ID_METADATA 77 -#define SWF_TAG_ID_ENDTAG 0 - -#define SWF_METADATA_FLAG 0x10 -#define SWF_DEFAULT_COMPRESSION_LEVEL Z_DEFAULT_COMPRESSION - -#define SWF_COMPRESSION_BEGIN 8 - -#define CHUNK 16384 - -#include "zlib.h" - -namespace IO -{ - //------------------------------------------------------------------ - //input/output stream declaration - typedef enum {FLUSH, FLUSH_NO} EFlush; - - class InputStream - { - public: - virtual ~InputStream() {}; - virtual XMP_Int32 Read(XMP_Uns8 * ioBuf, XMP_Int32 len) = 0; - virtual XMP_Int64 Skip(XMP_Int64 len) = 0; - virtual void Reset(void) = 0; - virtual void Close(void) = 0; - virtual bool IsEOF(void) = 0; - virtual XMP_Int64 GetCurrentPos(void) = 0; - }; - - - class FileInputStream : public InputStream - { - public: - FileInputStream(LFA_FileRef file) : iFile(file), iPos(0), iEndPos(0) { InitStream(); }; - virtual ~FileInputStream() {}; - - virtual XMP_Int32 Read(XMP_Uns8 * ioBuf, XMP_Int32 len); - virtual XMP_Int64 Skip(XMP_Int64 len); - virtual void Reset(void); - virtual void Close(void) {}; - virtual bool IsEOF(void); - virtual XMP_Int64 GetCurrentPos(void) { return iPos; }; - - protected: - void InitStream(void); - - LFA_FileRef iFile; - XMP_Int64 iPos; - XMP_Int64 iEndPos; - }; - - typedef enum { - STATUS_WRITE, - STATUS_READ, - STATUS_EOF, - STATUS_BUFFER_OVERFLOW, - STATUS_SKIP - } EStatus; - - class IOException - { - public: - IOException(EStatus status) : iStatus(status) {}; - ~IOException(void) {}; - EStatus GetErrorCode(void) { return iStatus; }; - protected: - EStatus iStatus; - - }; - - namespace ZIP - { - typedef enum {DEFLATE, DEFLATE_NO} EDeflate; - - class DeflateInputStream : public FileInputStream - { - public: - DeflateInputStream(LFA_FileRef file, XMP_Int32 bufferLength); - virtual ~DeflateInputStream(); - - virtual XMP_Int32 Read(XMP_Uns8 * ioBuf, XMP_Int32 len); - virtual XMP_Int32 Read(XMP_Uns8 * ioBuf); - virtual void Close(void); - virtual bool IsEOF(void); - virtual XMP_Int64 Skip(XMP_Int64 len); - virtual XMP_Int64 Skip(XMP_Int64 len, EDeflate deflate); - virtual XMP_Int64 GetCurrentPos(void) { return iPos; }; - - - protected: - void InitStream(void); - z_stream iStream; - XMP_Int32 iStatus; - XMP_Uns8 * iBuffer; - XMP_Int32 iBufferLength; - - }; - - class ZIPException - { - public: - ZIPException(XMP_Int32 err) : iErrorCode(err) {}; - ~ZIPException(void) {}; - - XMP_Int32 GetErrorCode(void) { return iErrorCode; }; - - protected: - XMP_Int32 iErrorCode; - - }; - - - } // namespace zip - -} // namespace IO - -namespace SWF_Support -{ - class TagData - { - public: - TagData() : pos(0), len(0), id(0), offset(0), xmp(false) {} - virtual ~TagData() {} - - // Short tag: - // | code/length | data | - // | 2 | val(length) | - // Long tag (data > 63): - // | code/length | length | data | - // | 2 | 4 | val(length) | - XMP_Uns64 pos; // file offset of tag - XMP_Uns32 len; // length of tag data - long id; // tag ID - long offset; // offset of data in tag (short vs. long tag) - bool xmp; // tag with XMP ? - }; - - typedef std::vector<TagData> TagVector; - typedef TagVector::iterator TagIterator; - - class TagState - { - public: - TagState() : xmpPos(0), xmpLen(0), headerSize(0),hasFileAttrTag(false), cachingFile(false), - hasXMP(false), xmpPacket(""), fileAttrFlags(0) {} - virtual ~TagState() {} - - XMP_Uns64 xmpPos; - XMP_Uns32 xmpLen; - TagData xmpTag; - TagVector tags; - XMP_Uns32 headerSize; - TagData fileAttrTag; - XMP_Uns32 fileAttrFlags; - bool hasFileAttrTag; - bool cachingFile; - bool hasXMP; - std::string xmpPacket; - }; - - //compression related data types - - typedef enum swf_mode { CWS, FWS } SWF_MODE; - typedef int (*CompressionFnc)(LFA_FileRef source, LFA_FileRef dest); - - class FileInfo - { - public: - FileInfo(LFA_FileRef fileRef, const std::string & origPath); - virtual ~FileInfo() {} - - bool IsCompressed(); - LFA_FileRef Decompress(); - void Compress(LFA_FileRef sourceRef, LFA_FileRef destRef); - void Clean(); - inline XMP_Uns32 GetSize() { return iSize; } - - private: - std::string tmpFilePath; - std::string origFilePath; - LFA_FileRef fileRef; - bool compressedFile; - XMP_Uns32 iSize; - - //tmp Data - LFA_FileRef tmpFileRef; - - - void CheckFormat(LFA_FileRef fileRef); - void CleanTempFiles(); - - int Encode ( LFA_FileRef fileRef, LFA_FileRef updateRef, SWF_MODE swfMode, CompressionFnc cmpFnc ); - - static int Inf ( LFA_FileRef source, LFA_FileRef dest ); - static int Def ( LFA_FileRef source, LFA_FileRef dest ); - - }; - - long OpenSWF ( IO::InputStream *inputStream, TagState & inOutTagState ); - bool ReadTag ( IO::InputStream * inputStream, TagState & inOutTagState, long * tagType, - XMP_Uns32 * tagLength, XMP_Uns64 & inOutPosition ); - unsigned long CheckTag ( IO::InputStream * inputStream, const TagState& inOutTagState, const TagData& inOutTagData ); - bool HasMetadata(IO::InputStream * inputStream, TagState& tagState); - XMP_Uns32 ReadFileAttrFlags(IO::InputStream * inputStream); - - bool WriteXMPTag ( LFA_FileRef fileRef, XMP_Uns32 len, const char* inBuffer ); - bool CopyHeader ( LFA_FileRef sourceRef, LFA_FileRef destRef, const TagState & tagState ); - bool UpdateHeader ( LFA_FileRef fileRef ); - bool CopyTag ( LFA_FileRef sourceRef, LFA_FileRef destRef, TagData& tag ); - - bool UpdateFileAttrTag(LFA_FileRef fileRef, const TagData& fileAttrTag, const TagState& tagState); - bool WriteFileAttrFlags(LFA_FileRef fileRef, const TagData& fileAttrTag, XMP_Uns32 flags); - - bool ReadBuffer ( LFA_FileRef fileRef, XMP_Uns64& pos, XMP_Uns32 len, XMP_Uns8* outBuffer ); - bool WriteBuffer ( LFA_FileRef fileRef, XMP_Uns64& pos, XMP_Uns32 len, const char* inBuffer ); - - - typedef struct TailBufferDef - { - XMP_Uns64 tailStartPosition; - XMP_Uns64 writePosition; - XMP_Uns64 tailEndPosition; - - TailBufferDef() : tailStartPosition(0), writePosition(0), tailEndPosition(0) {}; - XMP_Uns32 GetTailSize(void) { return static_cast<XMP_Uns32>(tailEndPosition - tailStartPosition); } - } TailBufferDef; - - - -} // namespace SWF_Support - -#endif // __SWF_Support_hpp__ diff --git a/source/XMPFiles/FormatSupport/TIFF_FileWriter.cpp b/source/XMPFiles/FormatSupport/TIFF_FileWriter.cpp deleted file mode 100644 index 3eb6eb2..0000000 --- a/source/XMPFiles/FormatSupport/TIFF_FileWriter.cpp +++ /dev/null @@ -1,1901 +0,0 @@ -// ================================================================================================= -// ADOBE SYSTEMS INCORPORATED -// Copyright 2006 Adobe Systems Incorporated -// 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 "TIFF_Support.hpp" - -// ================================================================================================= -/// \file TIFF_FileWriter.cpp -/// \brief TIFF_FileWriter is used for memory-based read-write access and all file-based access. -/// -/// \c TIFF_FileWriter is used for memory-based read-write access and all file-based access. The -/// main internal data structure is the InternalTagMap, a std::map that uses the tag number as the -/// key and InternalTagInfo as the value. There are 5 of these maps, one for each of the recognized -/// IFDs. The maps contain an entry for each tag in the IFD, whether we capture the data or not. The -/// dataPtr and dataLen fields in the InternalTagInfo are zero if the tag is not captured. -// ================================================================================================= - -// ================================================================================================= -// TIFF_FileWriter::TIFF_FileWriter -// ================================ -// -// Set big endian Get/Put routines so that routines are in place for creating TIFF without a parse. -// Parsing will reset them to the proper endianness for the stream. Big endian is a good default -// since JPEG and PSD files are big endian overall. - -TIFF_FileWriter::TIFF_FileWriter() : changed(false), legacyDeleted(false), memParsed(false), - fileParsed(false), ownedStream(false), memStream(0), tiffLength(0) -{ - - XMP_Uns8 bogusTIFF [kEmptyTIFFLength]; - - bogusTIFF[0] = 0x4D; - bogusTIFF[1] = 0x4D; - bogusTIFF[2] = 0x00; - bogusTIFF[3] = 0x2A; - bogusTIFF[4] = bogusTIFF[5] = bogusTIFF[6] = bogusTIFF[7] = 0x00; - - (void) this->CheckTIFFHeader ( bogusTIFF, sizeof ( bogusTIFF ) ); - -} // TIFF_FileWriter::TIFF_FileWriter - -// ================================================================================================= -// TIFF_FileWriter::~TIFF_FileWriter -// ================================= - -TIFF_FileWriter::~TIFF_FileWriter() -{ - XMP_Assert ( ! (this->memParsed && this->fileParsed) ); - - if ( this->ownedStream ) { - XMP_Assert ( this->memStream != 0 ); - free ( this->memStream ); - } - -} // TIFF_FileWriter::~TIFF_FileWriter - -// ================================================================================================= -// TIFF_FileWriter::DeleteExistingInfo -// =================================== - -void TIFF_FileWriter::DeleteExistingInfo() -{ - XMP_Assert ( ! (this->memParsed && this->fileParsed) ); - - if ( this->ownedStream ) free ( this->memStream ); // ! Current TIFF might be memory-parsed. - this->memStream = 0; - this->tiffLength = 0; - - for ( int ifd = 0; ifd < kTIFF_KnownIFDCount; ++ifd ) this->containedIFDs[ifd].clear(); - - this->changed = false; - this->legacyDeleted = false; - this->memParsed = false; - this->fileParsed = false; - this->ownedStream = false; - -} // TIFF_FileWriter::DeleteExistingInfo - -// ================================================================================================= -// TIFF_FileWriter::PickIFD -// ======================== - -XMP_Uns8 TIFF_FileWriter::PickIFD ( XMP_Uns8 ifd, XMP_Uns16 id ) -{ - if ( ifd > kTIFF_LastRealIFD ) { - if ( ifd != kTIFF_KnownIFD ) XMP_Throw ( "Invalid IFD number", kXMPErr_BadParam ); - XMP_Throw ( "kTIFF_KnownIFD not yet implemented", kXMPErr_Unimplemented ); - // *** Likely to stay unimplemented until there is a client need. - } - - return ifd; - -} // TIFF_FileWriter::PickIFD - -// ================================================================================================= -// TIFF_FileWriter::FindTagInIFD -// ============================= - -const TIFF_FileWriter::InternalTagInfo* TIFF_FileWriter::FindTagInIFD ( XMP_Uns8 ifd, XMP_Uns16 id ) const -{ - ifd = PickIFD ( ifd, id ); - const InternalTagMap& currIFD = this->containedIFDs[ifd].tagMap; - - InternalTagMap::const_iterator tagPos = currIFD.find ( id ); - if ( tagPos == currIFD.end() ) return 0; - return &tagPos->second; - -} // TIFF_FileWriter::FindTagInIFD - -// ================================================================================================= -// TIFF_FileWriter::GetIFD -// ======================= - -bool TIFF_FileWriter::GetIFD ( XMP_Uns8 ifd, TagInfoMap* ifdMap ) const -{ - if ( ifd > kTIFF_LastRealIFD ) XMP_Throw ( "Invalid IFD number", kXMPErr_BadParam ); - const InternalTagMap& currIFD = this->containedIFDs[ifd].tagMap; - - InternalTagMap::const_iterator tagPos = currIFD.begin(); - InternalTagMap::const_iterator tagEnd = currIFD.end(); - - if ( ifdMap != 0 ) ifdMap->clear(); - if ( tagPos == tagEnd ) return false; // Empty IFD. - - if ( ifdMap != 0 ) { - for ( ; tagPos != tagEnd; ++tagPos ) { - const InternalTagInfo& intInfo = tagPos->second; - TagInfo extInfo ( intInfo.id, intInfo.type, intInfo.count, intInfo.dataPtr, intInfo.dataLen ); - (*ifdMap)[intInfo.id] = extInfo; - } - } - - return true; - -} // TIFF_FileWriter::GetIFD - -// ================================================================================================= -// TIFF_FileWriter::GetValueOffset -// =============================== - -XMP_Uns32 TIFF_FileWriter::GetValueOffset ( XMP_Uns8 ifd, XMP_Uns16 id ) const -{ - const InternalTagInfo* thisTag = this->FindTagInIFD ( ifd, id ); - if ( (thisTag == 0) || (thisTag->origDataLen == 0) ) return 0; - - return thisTag->origDataOffset; - -} // TIFF_FileWriter::GetValueOffset - -// ================================================================================================= -// TIFF_FileWriter::GetTag -// ======================= - -bool TIFF_FileWriter::GetTag ( XMP_Uns8 ifd, XMP_Uns16 id, TagInfo* info ) const -{ - const InternalTagInfo* thisTag = this->FindTagInIFD ( ifd, id ); - if ( thisTag == 0 ) return false; - - if ( info != 0 ) { - - info->id = thisTag->id; - info->type = thisTag->type; - info->count = thisTag->dataLen / (XMP_Uns32)kTIFF_TypeSizes[thisTag->type]; - info->dataLen = thisTag->dataLen; - info->dataPtr = (const void*)(thisTag->dataPtr); - - } - - return true; - -} // TIFF_FileWriter::GetTag - -// ================================================================================================= -// TIFF_FileWriter::SetTag -// ======================= - -void TIFF_FileWriter::SetTag ( XMP_Uns8 ifd, XMP_Uns16 id, XMP_Uns16 type, XMP_Uns32 count, const void* clientPtr ) -{ - if ( (type < kTIFF_ByteType) || (type > kTIFF_LastType) ) XMP_Throw ( "Invalid TIFF tag type", kXMPErr_BadParam ); - size_t typeSize = kTIFF_TypeSizes[type]; - size_t fullSize = count * typeSize; - - ifd = PickIFD ( ifd, id ); - InternalTagMap& currIFD = this->containedIFDs[ifd].tagMap; - - InternalTagInfo* tagPtr = 0; - InternalTagMap::iterator tagPos = currIFD.find ( id ); - - if ( tagPos == currIFD.end() ) { - - // The tag does not yet exist, add it. - InternalTagMap::value_type mapValue ( id, InternalTagInfo ( id, type, count, this->fileParsed ) ); - tagPos = currIFD.insert ( tagPos, mapValue ); - tagPtr = &tagPos->second; - - } else { - - tagPtr = &tagPos->second; - - // The tag already exists, make sure the value is actually changing. - if ( (type == tagPtr->type) && (count == tagPtr->count) && - (memcmp ( clientPtr, tagPtr->dataPtr, tagPtr->dataLen ) == 0) ) { - return; // ! The value is unchanged, exit. - } - - tagPtr->FreeData(); // Release any existing data allocation. - - tagPtr->type = type; // These might be changing also. - tagPtr->count = count; - - } - - tagPtr->changed = true; - tagPtr->dataLen = (XMP_Uns32)fullSize; - - if ( fullSize <= 4 ) { - // The data is less than 4 bytes, store it in the smallValue field using native endianness. - tagPtr->dataPtr = (XMP_Uns8*) &tagPtr->smallValue; - } else { - // The data is more than 4 bytes, make a copy. - tagPtr->dataPtr = (XMP_Uns8*) malloc ( fullSize ); - if ( tagPtr->dataPtr == 0 ) XMP_Throw ( "Out of memory", kXMPErr_NoMemory ); - } - memcpy ( tagPtr->dataPtr, clientPtr, fullSize ); // AUDIT: Safe, space guaranteed to be fullSize. - - if ( ! this->nativeEndian ) { - if ( typeSize == 2 ) { - XMP_Uns16* flipPtr = (XMP_Uns16*) tagPtr->dataPtr; - for ( XMP_Uns32 i = 0; i < count; ++i ) Flip2 ( flipPtr[i] ); - } else if ( typeSize == 4 ) { - XMP_Uns32* flipPtr = (XMP_Uns32*) tagPtr->dataPtr; - for ( XMP_Uns32 i = 0; i < count; ++i ) Flip4 ( flipPtr[i] ); - } else if ( typeSize == 8 ) { - XMP_Uns64* flipPtr = (XMP_Uns64*) tagPtr->dataPtr; - for ( XMP_Uns32 i = 0; i < count; ++i ) Flip8 ( flipPtr[i] ); - } - } - - this->containedIFDs[ifd].changed = true; - this->changed = true; - -} // TIFF_FileWriter::SetTag - -// ================================================================================================= -// TIFF_FileWriter::DeleteTag -// ========================== - -void TIFF_FileWriter::DeleteTag ( XMP_Uns8 ifd, XMP_Uns16 id ) -{ - ifd = PickIFD ( ifd, id ); - InternalTagMap& currIFD = this->containedIFDs[ifd].tagMap; - - InternalTagMap::iterator tagPos = currIFD.find ( id ); - if ( tagPos == currIFD.end() ) return; // ! Don't set the changed flags if the tag didn't exist. - - currIFD.erase ( tagPos ); - this->containedIFDs[ifd].changed = true; - this->changed = true; - if ( (ifd != kTIFF_PrimaryIFD) || (id != kTIFF_XMP) ) this->legacyDeleted = true; - -} // TIFF_FileWriter::DeleteTag - -// ================================================================================================= -// TIFF_FileWriter::GetTag_Integer -// =============================== - -bool TIFF_FileWriter::GetTag_Integer ( XMP_Uns8 ifd, XMP_Uns16 id, XMP_Uns32* data ) const -{ - const InternalTagInfo* thisTag = this->FindTagInIFD ( ifd, id ); - if ( thisTag == 0 ) return false; - if ( thisTag->count != 1 ) return false; - - static XMP_Uns32 voidValue; - if ( data == 0 ) data = &voidValue; - - if ( thisTag->type == kTIFF_ShortType ) { - *data = this->GetUns16 ( thisTag->dataPtr ); - } else if ( thisTag->type == kTIFF_LongType ) { - *data = this->GetUns32 ( thisTag->dataPtr ); - } else { - return false; - } - - return true; - -} // TIFF_FileWriter::GetTag_Integer - -// ================================================================================================= -// TIFF_FileWriter::GetTag_Byte -// ============================ - -bool TIFF_FileWriter::GetTag_Byte ( XMP_Uns8 ifd, XMP_Uns16 id, XMP_Uns8* data ) const -{ - const InternalTagInfo* thisTag = this->FindTagInIFD ( ifd, id ); - if ( thisTag == 0 ) return false; - if ( (thisTag->type != kTIFF_ByteType) || (thisTag->dataLen != 1) ) return false; - - if ( data != 0 ) *data = *thisTag->dataPtr; - return true; - -} // TIFF_FileWriter::GetTag_Byte - -// ================================================================================================= -// TIFF_FileWriter::GetTag_SByte -// ============================= - -bool TIFF_FileWriter::GetTag_SByte ( XMP_Uns8 ifd, XMP_Uns16 id, XMP_Int8* data ) const -{ - const InternalTagInfo* thisTag = this->FindTagInIFD ( ifd, id ); - if ( thisTag == 0 ) return false; - if ( (thisTag->type != kTIFF_SByteType) || (thisTag->dataLen != 1) ) return false; - - if ( data != 0 ) *data = *thisTag->dataPtr; - return true; - -} // TIFF_FileWriter::GetTag_SByte - -// ================================================================================================= -// TIFF_FileWriter::GetTag_Short -// ============================= - -bool TIFF_FileWriter::GetTag_Short ( XMP_Uns8 ifd, XMP_Uns16 id, XMP_Uns16* data ) const -{ - const InternalTagInfo* thisTag = this->FindTagInIFD ( ifd, id ); - if ( thisTag == 0 ) return false; - if ( (thisTag->type != kTIFF_ShortType) || (thisTag->dataLen != 2) ) return false; - - if ( data != 0 ) *data = this->GetUns16 ( thisTag->dataPtr ); - return true; - -} // TIFF_FileWriter::GetTag_Short - -// ================================================================================================= -// TIFF_FileWriter::GetTag_SShort -// ============================== - -bool TIFF_FileWriter::GetTag_SShort ( XMP_Uns8 ifd, XMP_Uns16 id, XMP_Int16* data ) const -{ - const InternalTagInfo* thisTag = this->FindTagInIFD ( ifd, id ); - if ( thisTag == 0 ) return false; - if ( (thisTag->type != kTIFF_SShortType) || (thisTag->dataLen != 2) ) return false; - - if ( data != 0 ) *data = (XMP_Int16) this->GetUns16 ( thisTag->dataPtr ); - return true; - -} // TIFF_FileWriter::GetTag_SShort - -// ================================================================================================= -// TIFF_FileWriter::GetTag_Long -// ============================ - -bool TIFF_FileWriter::GetTag_Long ( XMP_Uns8 ifd, XMP_Uns16 id, XMP_Uns32* data ) const -{ - const InternalTagInfo* thisTag = this->FindTagInIFD ( ifd, id ); - if ( thisTag == 0 ) return false; - if ( (thisTag->type != kTIFF_LongType) || (thisTag->dataLen != 4) ) return false; - - if ( data != 0 ) *data = this->GetUns32 ( thisTag->dataPtr ); - return true; - -} // TIFF_FileWriter::GetTag_Long - -// ================================================================================================= -// TIFF_FileWriter::GetTag_SLong -// ============================= - -bool TIFF_FileWriter::GetTag_SLong ( XMP_Uns8 ifd, XMP_Uns16 id, XMP_Int32* data ) const -{ - const InternalTagInfo* thisTag = this->FindTagInIFD ( ifd, id ); - if ( thisTag == 0 ) return false; - if ( (thisTag->type != kTIFF_SLongType) || (thisTag->dataLen != 4) ) return false; - - if ( data != 0 ) *data = (XMP_Int32) this->GetUns32 ( thisTag->dataPtr ); - return true; - -} // TIFF_FileWriter::GetTag_SLong - -// ================================================================================================= -// TIFF_FileWriter::GetTag_Rational -// ================================ - -bool TIFF_FileWriter::GetTag_Rational ( XMP_Uns8 ifd, XMP_Uns16 id, Rational* data ) const -{ - const InternalTagInfo* thisTag = this->FindTagInIFD ( ifd, id ); - if ( (thisTag == 0) || (thisTag->dataPtr == 0) ) return false; - if ( (thisTag->type != kTIFF_RationalType) || (thisTag->dataLen != 8) ) return false; - - if ( data != 0 ) { - XMP_Uns32* dataPtr = (XMP_Uns32*)thisTag->dataPtr; - data->num = this->GetUns32 ( dataPtr ); - data->denom = this->GetUns32 ( dataPtr+1 ); - } - - return true; - -} // TIFF_FileWriter::GetTag_Rational - -// ================================================================================================= -// TIFF_FileWriter::GetTag_SRational -// ================================= - -bool TIFF_FileWriter::GetTag_SRational ( XMP_Uns8 ifd, XMP_Uns16 id, SRational* data ) const -{ - const InternalTagInfo* thisTag = this->FindTagInIFD ( ifd, id ); - if ( (thisTag == 0) || (thisTag->dataPtr == 0) ) return false; - if ( (thisTag->type != kTIFF_SRationalType) || (thisTag->dataLen != 8) ) return false; - - if ( data != 0 ) { - XMP_Uns32* dataPtr = (XMP_Uns32*)thisTag->dataPtr; - data->num = (XMP_Int32) this->GetUns32 ( dataPtr ); - data->denom = (XMP_Int32) this->GetUns32 ( dataPtr+1 ); - } - - return true; - -} // TIFF_FileWriter::GetTag_SRational - -// ================================================================================================= -// TIFF_FileWriter::GetTag_Float -// ============================= - -bool TIFF_FileWriter::GetTag_Float ( XMP_Uns8 ifd, XMP_Uns16 id, float* data ) const -{ - const InternalTagInfo* thisTag = this->FindTagInIFD ( ifd, id ); - if ( thisTag == 0 ) return false; - if ( (thisTag->type != kTIFF_FloatType) || (thisTag->dataLen != 4) ) return false; - - if ( data != 0 ) *data = this->GetFloat ( thisTag->dataPtr ); - return true; - -} // TIFF_FileWriter::GetTag_Float - -// ================================================================================================= -// TIFF_FileWriter::GetTag_Double -// ============================== - -bool TIFF_FileWriter::GetTag_Double ( XMP_Uns8 ifd, XMP_Uns16 id, double* data ) const -{ - const InternalTagInfo* thisTag = this->FindTagInIFD ( ifd, id ); - if ( (thisTag == 0) || (thisTag->dataPtr == 0) ) return false; - if ( (thisTag->type != kTIFF_DoubleType) || (thisTag->dataLen != 8) ) return false; - - if ( data != 0 ) *data = this->GetDouble ( thisTag->dataPtr ); - return true; - -} // TIFF_FileWriter::GetTag_Double - -// ================================================================================================= -// TIFF_FileWriter::GetTag_ASCII -// ============================= - -bool TIFF_FileWriter::GetTag_ASCII ( XMP_Uns8 ifd, XMP_Uns16 id, XMP_StringPtr* dataPtr, XMP_StringLen* dataLen ) const -{ - const InternalTagInfo* thisTag = this->FindTagInIFD ( ifd, id ); - if ( thisTag == 0 ) return false; - if ( (thisTag->dataLen > 4) && (thisTag->dataPtr == 0) ) return false; - if ( thisTag->type != kTIFF_ASCIIType ) return false; - - if ( dataPtr != 0 ) *dataPtr = (XMP_StringPtr)thisTag->dataPtr; - if ( dataLen != 0 ) *dataLen = thisTag->dataLen; - - return true; - -} // TIFF_FileWriter::GetTag_ASCII - -// ================================================================================================= -// TIFF_FileWriter::GetTag_EncodedString -// ===================================== - -bool TIFF_FileWriter::GetTag_EncodedString ( XMP_Uns8 ifd, XMP_Uns16 id, std::string* utf8Str ) const -{ - const InternalTagInfo* thisTag = this->FindTagInIFD ( ifd, id ); - if ( thisTag == 0 ) return false; - if ( thisTag->type != kTIFF_UndefinedType ) return false; - - if ( utf8Str == 0 ) return true; // Return true if the converted string is not wanted. - - bool ok = this->DecodeString ( thisTag->dataPtr, thisTag->dataLen, utf8Str ); - return ok; - -} // TIFF_FileWriter::GetTag_EncodedString - -// ================================================================================================= -// TIFF_FileWriter::SetTag_EncodedString -// ===================================== - -void TIFF_FileWriter::SetTag_EncodedString ( XMP_Uns8 ifd, XMP_Uns16 id, const std::string& utf8Str, XMP_Uns8 encoding ) -{ - std::string encodedStr; - - this->EncodeString ( utf8Str, encoding, &encodedStr ); - this->SetTag ( ifd, id, kTIFF_UndefinedType, (XMP_Uns32)encodedStr.size(), encodedStr.c_str() ); - -} // TIFF_FileWriter::SetTag_EncodedString - -// ================================================================================================= -// TIFF_FileWriter::IsLegacyChanged -// ================================ - -bool TIFF_FileWriter::IsLegacyChanged() -{ - - if ( ! this->changed ) return false; - if ( this->legacyDeleted ) return true; - - for ( int ifd = 0; ifd < kTIFF_KnownIFDCount; ++ifd ) { - - InternalIFDInfo & thisIFD = this->containedIFDs[ifd]; - if ( ! thisIFD.changed ) continue; - - InternalTagMap::iterator tagPos; - InternalTagMap::iterator tagEnd = thisIFD.tagMap.end(); - - for ( tagPos = thisIFD.tagMap.begin(); tagPos != tagEnd; ++tagPos ) { - InternalTagInfo & thisTag = tagPos->second; - if ( thisTag.changed && (thisTag.id != kTIFF_XMP) ) return true; - } - - } - - return false; // Can get here if the XMP tag is the only one changed. - -} // TIFF_FileWriter::IsLegacyChanged - -// ================================================================================================= -// TIFF_FileWriter::ParseMemoryStream -// ================================== - -void TIFF_FileWriter::ParseMemoryStream ( 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 stream and copy it. - - if ( ! copyData ) { - XMP_Assert ( ! this->ownedStream ); - this->memStream = (XMP_Uns8*) data; - } else { - if ( length > 100*1024*1024 ) XMP_Throw ( "Outrageous length for memory-based TIFF", kXMPErr_BadTIFF ); - this->memStream = (XMP_Uns8*) malloc(length); - if ( this->memStream == 0 ) XMP_Throw ( "Out of memory", kXMPErr_NoMemory ); - memcpy ( this->memStream, data, length ); // AUDIT: Safe, malloc'ed length bytes above. - this->ownedStream = true; - } - this->tiffLength = length; - - // Find and process the primary, Exif, GPS, and Interoperability IFDs. - - XMP_Uns32 primaryIFDOffset = this->CheckTIFFHeader ( this->memStream, length ); - - if ( primaryIFDOffset != 0 ) (void) this->ProcessMemoryIFD ( primaryIFDOffset, kTIFF_PrimaryIFD ); - - const InternalTagInfo* exifIFDTag = this->FindTagInIFD ( kTIFF_PrimaryIFD, kTIFF_ExifIFDPointer ); - if ( (exifIFDTag != 0) && (exifIFDTag->type == kTIFF_LongType) && (exifIFDTag->dataLen == 4) ) { - XMP_Uns32 exifOffset = this->GetUns32 ( exifIFDTag->dataPtr ); - (void) this->ProcessMemoryIFD ( exifOffset, kTIFF_ExifIFD ); - } - - const InternalTagInfo* gpsIFDTag = this->FindTagInIFD ( kTIFF_PrimaryIFD, kTIFF_GPSInfoIFDPointer ); - if ( (gpsIFDTag != 0) && (gpsIFDTag->type == kTIFF_LongType) && (gpsIFDTag->dataLen == 4) ) { - XMP_Uns32 gpsOffset = this->GetUns32 ( gpsIFDTag->dataPtr ); - (void) this->ProcessMemoryIFD ( gpsOffset, kTIFF_GPSInfoIFD ); - } - - const InternalTagInfo* interopIFDTag = this->FindTagInIFD ( kTIFF_ExifIFD, kTIFF_InteroperabilityIFDPointer ); - if ( (interopIFDTag != 0) && (interopIFDTag->type == kTIFF_LongType) && (interopIFDTag->dataLen == 4) ) { - XMP_Uns32 interopOffset = this->GetUns32 ( interopIFDTag->dataPtr ); - (void) this->ProcessMemoryIFD ( interopOffset, kTIFF_InteropIFD ); - } - - #if 0 - { - printf ( "\nExiting TIFF_FileWriter::ParseMemoryStream\n" ); - for ( int ifd = 0; ifd < kTIFF_KnownIFDCount; ++ifd ) { - InternalIFDInfo & thisIFD = this->containedIFDs[ifd]; - printf ( "\n IFD %d, count %d, mapped %d, offset %d (0x%X), next IFD %d (0x%X)\n", - ifd, thisIFD.origCount, thisIFD.tagMap.size(), - thisIFD.origDataOffset, thisIFD.origDataOffset, thisIFD.origNextIFD, thisIFD.origNextIFD ); - InternalTagMap::iterator tagPos; - InternalTagMap::iterator tagEnd = thisIFD.tagMap.end(); - for ( tagPos = thisIFD.tagMap.begin(); tagPos != tagEnd; ++tagPos ) { - InternalTagInfo & thisTag = tagPos->second; - printf ( " Tag %d, smallValue 0x%X, origDataLen %d, origDataOffset %d (0x%X)\n", - thisTag.id, thisTag.smallValue, thisTag.origDataLen, thisTag.origDataOffset, thisTag.origDataOffset ); - } - } - printf ( "\n" ); - } - #endif - -} // TIFF_FileWriter::ParseMemoryStream - -// ================================================================================================= -// TIFF_FileWriter::ProcessMemoryIFD -// ================================= - -XMP_Uns32 TIFF_FileWriter::ProcessMemoryIFD ( XMP_Uns32 ifdOffset, XMP_Uns8 ifd ) -{ - InternalIFDInfo& ifdInfo ( this->containedIFDs[ifd] ); - - if ( (ifdOffset < 8) || (ifdOffset > (this->tiffLength - kEmptyIFDLength)) ) { - XMP_Throw ( "Bad IFD offset", kXMPErr_BadTIFF ); - } - - XMP_Uns8* ifdPtr = this->memStream + ifdOffset; - XMP_Uns16 tagCount = this->GetUns16 ( ifdPtr ); - RawIFDEntry* ifdEntries = (RawIFDEntry*)(ifdPtr+2); - - if ( tagCount >= 0x8000 ) XMP_Throw ( "Outrageous IFD count", kXMPErr_BadTIFF ); - if ( (ifdOffset + 2 + tagCount*12 + 4) > this->tiffLength ) XMP_Throw ( "Out of bounds IFD", kXMPErr_BadTIFF ); - - ifdInfo.origIFDOffset = ifdOffset; - ifdInfo.origCount = tagCount; - - for ( size_t i = 0; i < tagCount; ++i ) { - - RawIFDEntry* rawTag = &ifdEntries[i]; - XMP_Uns16 tagType = this->GetUns16 ( &rawTag->type ); - if ( (tagType < kTIFF_ByteType) || (tagType > kTIFF_LastType) ) continue; // Bad type, skip this tag. - - XMP_Uns16 tagID = this->GetUns16 ( &rawTag->id ); - XMP_Uns32 tagCount = this->GetUns32 ( &rawTag->count ); - - InternalTagMap::value_type mapValue ( tagID, InternalTagInfo ( tagID, tagType, tagCount, kIsMemoryBased ) ); - InternalTagMap::iterator newPos = ifdInfo.tagMap.insert ( ifdInfo.tagMap.end(), mapValue ); - InternalTagInfo& mapTag = newPos->second; - - mapTag.dataLen = mapTag.origDataLen = mapTag.count * (XMP_Uns32)kTIFF_TypeSizes[mapTag.type]; - mapTag.smallValue = rawTag->dataOrOffset; // Keep the value or offset in stream byte ordering. - - if ( mapTag.dataLen <= 4 ) { - mapTag.origDataOffset = ifdOffset + 2 + (12 * (XMP_Uns32)i) + 8; // Compute the data offset. - } else { - mapTag.origDataOffset = this->GetUns32 ( &rawTag->dataOrOffset ); // Extract the data offset. - // printf ( "FW_ProcessMemoryIFD tag %d large value @ %.8X\n", mapTag.id, mapTag.dataPtr ); - } - mapTag.dataPtr = this->memStream + mapTag.origDataOffset; - - } - - ifdPtr += (2 + tagCount*12); - ifdInfo.origNextIFD = this->GetUns32 ( ifdPtr ); - - return ifdInfo.origNextIFD; - -} // TIFF_FileWriter::ProcessMemoryIFD - -// ================================================================================================= -// TIFF_FileWriter::ParseFileStream -// ================================ -// -// The buffered I/O model is worth the logic complexity - as opposed to a simple seek/read for each -// part of the TIFF stream. The vast majority of real-world TIFFs have the primary IFD, Exif IFD, -// and all of their interesting tag values within the first 64K of the file. Well, at least before -// we get around to our edit-by-append approach. - -void TIFF_FileWriter::ParseFileStream ( LFA_FileRef fileRef ) -{ - bool ok; - IOBuffer ioBuf; - - this->DeleteExistingInfo(); - this->fileParsed = true; - this->tiffLength = (XMP_Uns32) LFA_Measure ( fileRef ); - if ( this->tiffLength == 0 ) return; - - // Find and process the primary, Exif, GPS, and Interoperability IFDs. - - ioBuf.filePos = LFA_Seek ( fileRef, 0, SEEK_SET ); - ok = CheckFileSpace ( fileRef, &ioBuf, 8 ); - if ( ! ok ) XMP_Throw ( "TIFF too small", kXMPErr_BadTIFF ); - - XMP_Uns32 primaryIFDOffset = this->CheckTIFFHeader ( ioBuf.ptr, this->tiffLength ); - - if ( primaryIFDOffset != 0 ) (void) this->ProcessFileIFD ( kTIFF_PrimaryIFD, primaryIFDOffset, fileRef, &ioBuf ); - - const InternalTagInfo* exifIFDTag = this->FindTagInIFD ( kTIFF_PrimaryIFD, kTIFF_ExifIFDPointer ); - if ( (exifIFDTag != 0) && (exifIFDTag->type == kTIFF_LongType) && (exifIFDTag->count == 1) ) { - XMP_Uns32 exifOffset = this->GetUns32 ( exifIFDTag->dataPtr ); - (void) this->ProcessFileIFD ( kTIFF_ExifIFD, exifOffset, fileRef, &ioBuf ); - } - - const InternalTagInfo* gpsIFDTag = this->FindTagInIFD ( kTIFF_PrimaryIFD, kTIFF_GPSInfoIFDPointer ); - if ( (gpsIFDTag != 0) && (gpsIFDTag->type == kTIFF_LongType) && (gpsIFDTag->count == 1) ) { - XMP_Uns32 gpsOffset = this->GetUns32 ( gpsIFDTag->dataPtr ); - (void) this->ProcessFileIFD ( kTIFF_GPSInfoIFD, gpsOffset, fileRef, &ioBuf ); - } - - const InternalTagInfo* interopIFDTag = this->FindTagInIFD ( kTIFF_ExifIFD, kTIFF_InteroperabilityIFDPointer ); - if ( (interopIFDTag != 0) && (interopIFDTag->type == kTIFF_LongType) && (interopIFDTag->dataLen == 4) ) { - XMP_Uns32 interopOffset = this->GetUns32 ( interopIFDTag->dataPtr ); - (void) this->ProcessFileIFD ( kTIFF_InteropIFD, interopOffset, fileRef, &ioBuf ); - } - - #if 0 - { - printf ( "\nExiting TIFF_FileWriter::ParseFileStream\n" ); - for ( int ifd = 0; ifd < kTIFF_KnownIFDCount; ++ifd ) { - InternalIFDInfo & thisIFD = this->containedIFDs[ifd]; - printf ( "\n IFD %d, count %d, mapped %d, offset %d (0x%X), next IFD %d (0x%X)\n", - ifd, thisIFD.origCount, thisIFD.tagMap.size(), - thisIFD.origDataOffset, thisIFD.origDataOffset, thisIFD.origNextIFD, thisIFD.origNextIFD ); - InternalTagMap::iterator tagPos; - InternalTagMap::iterator tagEnd = thisIFD.tagMap.end(); - for ( tagPos = thisIFD.tagMap.begin(); tagPos != tagEnd; ++tagPos ) { - InternalTagInfo & thisTag = tagPos->second; - printf ( " Tag %d, smallValue 0x%X, origDataLen %d, origDataOffset %d (0x%X)\n", - thisTag.id, thisTag.smallValue, thisTag.origDataLen, thisTag.origDataOffset, thisTag.origDataOffset ); - } - } - printf ( "\n" ); - } - #endif - -} // TIFF_FileWriter::ParseFileStream - -// ================================================================================================= -// TIFF_FileWriter::ProcessFileIFD -// =============================== - -XMP_Uns32 TIFF_FileWriter::ProcessFileIFD ( XMP_Uns8 ifd, XMP_Uns32 ifdOffset, LFA_FileRef fileRef, IOBuffer* ioBuf ) -{ - InternalIFDInfo& ifdInfo ( this->containedIFDs[ifd] ); - - MoveToOffset ( fileRef, ifdOffset, ioBuf ); // Move to the start of the IFD. - - bool ok = CheckFileSpace ( fileRef, ioBuf, 2 ); - if ( ! ok ) XMP_Throw ( "IFD count missing", kXMPErr_BadTIFF ); - XMP_Uns16 tagCount = this->GetUns16 ( ioBuf->ptr ); - - if ( tagCount >= 0x8000 ) XMP_Throw ( "Outrageous IFD count", kXMPErr_BadTIFF ); - if ( (ifdOffset + 2 + tagCount*12 + 4) > this->tiffLength ) XMP_Throw ( "Out of bounds IFD", kXMPErr_BadTIFF ); - - ifdInfo.origIFDOffset = ifdOffset; - ifdInfo.origCount = tagCount; - - // --------------------------------------------------------------------------------------------- - // First create all of the IFD map entries, capturing short values, and get the next IFD offset. - // We're using a std::map for storage, it automatically eliminates duplicates and provides - // sorted output. Plus the "map[key] = value" assignment conveniently keeps the last encountered - // value, following Photoshop's behavior. - - ioBuf->ptr += 2; // Move to the first IFD entry. - - for ( XMP_Uns16 i = 0; i < tagCount; ++i, ioBuf->ptr += 12 ) { - - if ( ! CheckFileSpace ( fileRef, ioBuf, 12 ) ) XMP_Throw ( "EOF within IFD", kXMPErr_BadTIFF ); - - RawIFDEntry* rawTag = (RawIFDEntry*)ioBuf->ptr; - XMP_Uns16 tagType = this->GetUns16 ( &rawTag->type ); - if ( (tagType < kTIFF_ByteType) || (tagType > kTIFF_LastType) ) continue; // Bad type, skip this tag. - - XMP_Uns16 tagID = this->GetUns16 ( &rawTag->id ); - XMP_Uns32 tagCount = this->GetUns32 ( &rawTag->count ); - - InternalTagMap::value_type mapValue ( tagID, InternalTagInfo ( tagID, tagType, tagCount, kIsFileBased ) ); - InternalTagMap::iterator newPos = ifdInfo.tagMap.insert ( ifdInfo.tagMap.end(), mapValue ); - InternalTagInfo& mapTag = newPos->second; - - mapTag.dataLen = mapTag.origDataLen = mapTag.count * (XMP_Uns32)kTIFF_TypeSizes[mapTag.type]; - mapTag.smallValue = rawTag->dataOrOffset; // Keep the value or offset in stream byte ordering. - - if ( mapTag.dataLen <= 4 ) { - mapTag.dataPtr = (XMP_Uns8*) &mapTag.smallValue; - mapTag.origDataOffset = ifdOffset + 2 + (12 * i) + 8; // Compute the data offset. - } else { - mapTag.origDataOffset = this->GetUns32 ( &rawTag->dataOrOffset ); // Extract the data offset. - } - - } - - if ( ! CheckFileSpace ( fileRef, ioBuf, 4 ) ) XMP_Throw ( "EOF at next IFD offset", kXMPErr_BadTIFF ); - ifdInfo.origNextIFD = this->GetUns32 ( ioBuf->ptr ); - - // --------------------------------------------------------------------------------------------- - // Go back over the tag map and extract the data for large recognized tags. This is done in 2 - // passes, in order to lessen the typical amount of I/O. On the first pass make sure we have at - // least 32K of data following the IFD in the buffer, and extract all of the values in that - // portion. This should cover an original file, or the appended values with an appended IFD. - - if ( (ioBuf->limit - ioBuf->ptr) < 32*1024 ) RefillBuffer ( fileRef, ioBuf ); - - InternalTagMap::iterator tagPos = ifdInfo.tagMap.begin(); - InternalTagMap::iterator tagEnd = ifdInfo.tagMap.end(); - - const XMP_Uns16* knownTagPtr = sKnownTags[ifd]; // Points into the ordered recognized tag list. - - XMP_Uns32 bufBegin = (XMP_Uns32)ioBuf->filePos; // TIFF stream bounds for the current buffer. - XMP_Uns32 bufEnd = bufBegin + (XMP_Uns32)ioBuf->len; - - for ( ; tagPos != tagEnd; ++tagPos ) { - - InternalTagInfo* currTag = &tagPos->second; - - if ( currTag->dataLen <= 4 ) continue; // Short values are already in the smallValue field. - while ( *knownTagPtr < currTag->id ) ++knownTagPtr; - if ( *knownTagPtr != currTag->id ) continue; // Skip unrecognized tags. - if ( currTag->dataLen > 1024*1024 ) XMP_Throw ( "Outrageous data length", kXMPErr_BadTIFF ); - - if ( (bufBegin <= currTag->origDataOffset) && ((currTag->origDataOffset + currTag->dataLen) <= bufEnd) ) { - // This value is already fully within the current I/O buffer, copy it. - MoveToOffset ( fileRef, currTag->origDataOffset, ioBuf ); - currTag->dataPtr = (XMP_Uns8*) malloc ( currTag->dataLen ); - if ( currTag->dataPtr == 0 ) XMP_Throw ( "No data block", kXMPErr_NoMemory ); - memcpy ( currTag->dataPtr, ioBuf->ptr, currTag->dataLen ); // AUDIT: Safe, malloc'ed currTag->dataLen bytes above. - } - - } - - // --------------------------------------------------------------------------------------------- - // Now the second large value pass. This will reposition the I/O buffer as necessary. Hopefully - // just once, to pick up the span of data not covered in the first pass. - - tagPos = ifdInfo.tagMap.begin(); // Reset both map/array positions. - knownTagPtr = sKnownTags[ifd]; - - for ( ; tagPos != tagEnd; ++tagPos ) { - - InternalTagInfo* currTag = &tagPos->second; - - if ( (currTag->dataLen <= 4) || (currTag->dataPtr != 0) ) continue; // Done this tag? - while ( *knownTagPtr < currTag->id ) ++knownTagPtr; - if ( *knownTagPtr != currTag->id ) continue; // Skip unrecognized tags. - if ( currTag->dataLen > 1024*1024 ) XMP_Throw ( "Outrageous data length", kXMPErr_BadTIFF ); - - currTag->dataPtr = (XMP_Uns8*) malloc ( currTag->dataLen ); - if ( currTag->dataPtr == 0 ) XMP_Throw ( "No data block", kXMPErr_NoMemory ); - - if ( currTag->dataLen > kIOBufferSize ) { - // This value is bigger than the I/O buffer, read it directly and restore the file position. - LFA_Seek ( fileRef, currTag->origDataOffset, SEEK_SET ); - LFA_Read ( fileRef, currTag->dataPtr, currTag->dataLen, kLFA_RequireAll ); - LFA_Seek ( fileRef, (ioBuf->filePos + ioBuf->len), SEEK_SET ); - } else { - // This value can fit in the I/O buffer, so use that. - MoveToOffset ( fileRef, currTag->origDataOffset, ioBuf ); - ok = CheckFileSpace ( fileRef, ioBuf, currTag->dataLen ); - if ( ! ok ) XMP_Throw ( "EOF in data block", kXMPErr_BadTIFF ); - memcpy ( currTag->dataPtr, ioBuf->ptr, currTag->dataLen ); // AUDIT: Safe, malloc'ed currTag->dataLen bytes above. - } - - } - - // Done, return the next IFD offset. - - return ifdInfo.origNextIFD; - -} // TIFF_FileWriter::ProcessFileIFD - -// ================================================================================================= -// TIFF_FileWriter::IntegrateFromPShop6 -// ==================================== -// -// See comments for ProcessPShop6IFD. - -void TIFF_FileWriter::IntegrateFromPShop6 ( const void * buriedPtr, size_t buriedLen ) -{ - TIFF_MemoryReader buriedExif; - buriedExif.ParseMemoryStream ( buriedPtr, (XMP_Uns32) buriedLen ); - - this->ProcessPShop6IFD ( buriedExif, kTIFF_PrimaryIFD ); - this->ProcessPShop6IFD ( buriedExif, kTIFF_ExifIFD ); - this->ProcessPShop6IFD ( buriedExif, kTIFF_GPSInfoIFD ); - -} // TIFF_FileWriter::IntegrateFromPShop6 - -// ================================================================================================= -// TIFF_FileWriter::CopyTagToMasterIFD -// =================================== -// -// Create a new master IFD entry from a buried Photoshop 6 IFD entry. Don't try to get clever with -// large values, just create a new copy. This preserves a clean separation between the memory-based -// and file-based TIFF processing. - -void* TIFF_FileWriter::CopyTagToMasterIFD ( const TagInfo & ps6Tag, InternalIFDInfo * masterIFD ) -{ - InternalTagMap::value_type mapValue ( ps6Tag.id, InternalTagInfo ( ps6Tag.id, ps6Tag.type, ps6Tag.count, this->fileParsed ) ); - InternalTagMap::iterator newPos = masterIFD->tagMap.insert ( masterIFD->tagMap.end(), mapValue ); - InternalTagInfo& newTag = newPos->second; - - newTag.dataLen = ps6Tag.dataLen; - - if ( newTag.dataLen <= 4 ) { - newTag.dataPtr = (XMP_Uns8*) &newTag.smallValue; - newTag.smallValue = *((XMP_Uns32*)ps6Tag.dataPtr); - } else { - newTag.dataPtr = (XMP_Uns8*) malloc ( newTag.dataLen ); - if ( newTag.dataPtr == 0 ) XMP_Throw ( "Out of memory", kXMPErr_NoMemory ); - memcpy ( newTag.dataPtr, ps6Tag.dataPtr, newTag.dataLen ); // AUDIT: Safe, malloc'ed dataLen bytes above. - } - - newTag.changed = true; // ! See comments with ProcessPShop6IFD. - XMP_Assert ( (newTag.origDataLen == 0) && (newTag.origDataOffset == 0) ); - - masterIFD->changed = true; - - return newPos->second.dataPtr; // ! Return the address within the map entry for small values. - -} // TIFF_FileWriter::CopyTagToMasterIFD - -// ================================================================================================= -// FlipCFATable -// ============ -// -// The CFA pattern table is trivial, a pair of short counts followed by n*m bytes. - -static bool FlipCFATable ( void* voidPtr, XMP_Uns32 tagLen, GetUns16_Proc GetUns16 ) -{ - if ( tagLen < 4 ) return false; - - XMP_Uns16* u16Ptr = (XMP_Uns16*)voidPtr; - - Flip2 ( &u16Ptr[0] ); // Flip the counts to match the master TIFF. - Flip2 ( &u16Ptr[1] ); - - XMP_Uns16 columns = GetUns16 ( &u16Ptr[0] ); // Fetch using the master TIFF's routine. - XMP_Uns16 rows = GetUns16 ( &u16Ptr[1] ); - - if ( tagLen != (XMP_Uns32)(4 + columns*rows) ) return false; - - return true; - -} // FlipCFATable - -// ================================================================================================= -// FlipDSDTable -// ============ -// -// The device settings description table is trivial, a pair of short counts followed by UTF-16 -// strings. So the whole value should be flipped as a sequence of 16 bit items. - -// ! The Exif 2.2 description is a bit garbled. It might be wrong. It would be nice to have a real example. - -static bool FlipDSDTable ( void* voidPtr, XMP_Uns32 tagLen, GetUns16_Proc GetUns16 ) -{ - if ( tagLen < 4 ) return false; - - XMP_Uns16* u16Ptr = (XMP_Uns16*)voidPtr; - for ( size_t i = tagLen/2; i > 0; --i, ++u16Ptr ) Flip2 ( u16Ptr ); - - return true; - -} // FlipDSDTable - -// ================================================================================================= -// FlipOECFSFRTable -// ================ -// -// The OECF and SFR tables have the same layout: -// 2 short counts, columns and rows -// c ASCII strings, null terminated, column names -// c*r rationals - -static bool FlipOECFSFRTable ( void* voidPtr, XMP_Uns32 tagLen, GetUns16_Proc GetUns16 ) -{ - XMP_Uns16* u16Ptr = (XMP_Uns16*)voidPtr; - - Flip2 ( &u16Ptr[0] ); // Flip the data to match the master TIFF. - Flip2 ( &u16Ptr[1] ); - - XMP_Uns16 columns = GetUns16 ( &u16Ptr[0] ); // Fetch using the master TIFF's routine. - XMP_Uns16 rows = GetUns16 ( &u16Ptr[1] ); - - XMP_Uns32 minLen = 4 + columns + (8 * columns * rows); // Minimum legit tag size. - if ( tagLen < minLen ) return false; - - // Compute the start of the rationals from the end of value. No need to walk through the names. - XMP_Uns32* u32Ptr = (XMP_Uns32*) ((XMP_Uns8*)voidPtr + tagLen - (8 * columns * rows)); - - for ( size_t i = 2*columns*rows; i > 0; --i, ++u32Ptr ) Flip4 ( u32Ptr ); - - return true; - -} // FlipOECFSFRTable - -// ================================================================================================= -// TIFF_FileWriter::ProcessPShop6IFD -// ================================= -// -// Photoshop 6 wrote wacky TIFF files that have much of the Exif metadata buried inside of image -// resource 1058, which is itself within tag 34377 in the 0th IFD. This routine moves the buried -// tags up to the parent file. Existing tags are not replaced. -// -// While it is tempting to try to directly use the TIFF_MemoryReader's tweaked IFD info, making that -// visible would compromise implementation separation. Better to pay the modest runtime cost of -// using the official GetIFD method, letting it build the map. -// -// The tags that get moved are marked as being changed, as is the IFD they are moved into, but the -// overall TIFF_FileWriter object is not. We don't want this integration on its own to force a file -// update, but a file update should include these changes. - -// ! Be careful to not move tags that are the nasty Exif explicit offsets, e.g. the Exif or GPS IFD -// ! "pointers". These are tags with a LONG type and count of 1, whose value is an offset into the -// ! buried TIFF stream. We can't reliably plant that offset into the outer IFD structure. - -// ! To make things even more fun, the buried Exif might not have the same endianness as the outer! - -void TIFF_FileWriter::ProcessPShop6IFD ( const TIFF_MemoryReader& buriedExif, XMP_Uns8 ifd ) -{ - bool ok, found; - TagInfoMap ps6IFD; - - found = buriedExif.GetIFD ( ifd, &ps6IFD ); - if ( ! found ) return; - - bool needsFlipping = (this->bigEndian != buriedExif.IsBigEndian()); - - InternalIFDInfo* masterIFD = &this->containedIFDs[ifd]; - - TagInfoMap::const_iterator ps6Pos = ps6IFD.begin(); - TagInfoMap::const_iterator ps6End = ps6IFD.end(); - - for ( ; ps6Pos != ps6End; ++ps6Pos ) { - - // Copy buried tags to the master IFD if they don't already exist there. - - const TagInfo& ps6Tag = ps6Pos->second; - - if ( this->FindTagInIFD ( ifd, ps6Tag.id ) != 0 ) continue; // Keep existing master tags. - if ( needsFlipping && (ps6Tag.id == 37500) ) continue; // Don't copy an unflipped MakerNote. - if ( (ps6Tag.id == kTIFF_ExifIFDPointer) || // Skip the tags that are explicit offsets. - (ps6Tag.id == kTIFF_GPSInfoIFDPointer) || - (ps6Tag.id == kTIFF_JPEGInterchangeFormat) || - (ps6Tag.id == kTIFF_InteroperabilityIFDPointer) ) continue; - - void* voidPtr = this->CopyTagToMasterIFD ( ps6Tag, masterIFD ); - - if ( needsFlipping ) { - switch ( ps6Tag.type ) { - - case kTIFF_ByteType: - case kTIFF_SByteType: - case kTIFF_ASCIIType: - // Nothing more to do. - break; - - case kTIFF_ShortType: - case kTIFF_SShortType: - { - XMP_Uns16* u16Ptr = (XMP_Uns16*)voidPtr; - for ( size_t i = ps6Tag.count; i > 0; --i, ++u16Ptr ) Flip2 ( u16Ptr ); - } - break; - - case kTIFF_LongType: - case kTIFF_SLongType: - case kTIFF_FloatType: - { - XMP_Uns32* u32Ptr = (XMP_Uns32*)voidPtr; - for ( size_t i = ps6Tag.count; i > 0; --i, ++u32Ptr ) Flip4 ( u32Ptr ); - } - break; - - case kTIFF_RationalType: - case kTIFF_SRationalType: - { - XMP_Uns32* ratPtr = (XMP_Uns32*)voidPtr; - for ( size_t i = (2 * ps6Tag.count); i > 0; --i, ++ratPtr ) Flip4 ( ratPtr ); - } - break; - - case kTIFF_DoubleType: - { - XMP_Uns64* u64Ptr = (XMP_Uns64*)voidPtr; - for ( size_t i = ps6Tag.count; i > 0; --i, ++u64Ptr ) Flip8 ( u64Ptr ); - } - break; - - case kTIFF_UndefinedType: - // Fix up the few kinds of special tables that Exif 2.2 defines. - ok = true; // Keep everything that isn't a special table. - if ( ps6Tag.id == kTIFF_CFAPattern ) { - ok = FlipCFATable ( voidPtr, ps6Tag.dataLen, this->GetUns16 ); - } else if ( ps6Tag.id == kTIFF_DeviceSettingDescription ) { - ok = FlipDSDTable ( voidPtr, ps6Tag.dataLen, this->GetUns16 ); - } else if ( (ps6Tag.id == kTIFF_OECF) || (ps6Tag.id == kTIFF_SpatialFrequencyResponse) ) { - ok = FlipOECFSFRTable ( voidPtr, ps6Tag.dataLen, this->GetUns16 ); - } - if ( ! ok ) this->DeleteTag ( ifd, ps6Tag.id ); - break; - - default: - // ? XMP_Throw ( "Unexpected tag type", kXMPErr_InternalFailure ); - this->DeleteTag ( ifd, ps6Tag.id ); - break; - - } - } - - } - -} // TIFF_FileWriter::ProcessPShop6IFD - -// ================================================================================================= -// TIFF_FileWriter::PreflightIFDLinkage -// ==================================== -// -// Preflight special cases for the linkage between IFDs. Three of the IFDs are found through an -// explicit tag, the Exif, GPS, and Interop IFDs. The presence or absence of those IFDs affects the -// presence or absence of the linkage tag, which can affect the IFD containing the linkage tag. The -// thumbnail IFD is chained from the primary IFD, so if the thumbnail IFD is present we make sure -// that the primary IFD isn't empty. - -void TIFF_FileWriter::PreflightIFDLinkage() -{ - - // Do the tag-linked IFDs bottom up, Interop then GPS then Exif. - - if ( this->containedIFDs[kTIFF_InteropIFD].tagMap.empty() ) { - this->DeleteTag ( kTIFF_ExifIFD, kTIFF_InteroperabilityIFDPointer ); - } else if ( ! this->GetTag ( kTIFF_ExifIFD, kTIFF_InteroperabilityIFDPointer, 0 ) ) { - this->SetTag_Long ( kTIFF_ExifIFD, kTIFF_InteroperabilityIFDPointer, 0xABADABAD ); - } - - if ( this->containedIFDs[kTIFF_GPSInfoIFD].tagMap.empty() ) { - this->DeleteTag ( kTIFF_PrimaryIFD, kTIFF_GPSInfoIFDPointer ); - } else if ( ! this->GetTag ( kTIFF_PrimaryIFD, kTIFF_GPSInfoIFDPointer, 0 ) ) { - this->SetTag_Long ( kTIFF_PrimaryIFD, kTIFF_GPSInfoIFDPointer, 0xABADABAD ); - } - - if ( this->containedIFDs[kTIFF_ExifIFD].tagMap.empty() ) { - this->DeleteTag ( kTIFF_PrimaryIFD, kTIFF_ExifIFDPointer ); - } else if ( ! this->GetTag ( kTIFF_PrimaryIFD, kTIFF_ExifIFDPointer, 0 ) ) { - this->SetTag_Long ( kTIFF_PrimaryIFD, kTIFF_ExifIFDPointer, 0xABADABAD ); - } - - // Make sure that the primary IFD is not empty if the thumbnail IFD is not empty. - - if ( this->containedIFDs[kTIFF_PrimaryIFD].tagMap.empty() && - (! this->containedIFDs[kTIFF_TNailIFD].tagMap.empty()) ) { - this->SetTag_Short ( kTIFF_PrimaryIFD, kTIFF_ResolutionUnit, 2 ); // Set Resolution unit to inches. - } - -} // TIFF_FileWriter::PreflightIFDLinkage - -// ================================================================================================= -// TIFF_FileWriter::DetermineVisibleLength -// ======================================= - -XMP_Uns32 TIFF_FileWriter::DetermineVisibleLength() -{ - XMP_Uns32 visibleLength = 8; // Start with the TIFF header size. - - for ( int ifd = 0; ifd < kTIFF_KnownIFDCount; ++ifd ) { - - InternalIFDInfo& ifdInfo ( this->containedIFDs[ifd] ); - size_t tagCount = ifdInfo.tagMap.size(); - if ( tagCount == 0 ) continue; - - visibleLength += (XMP_Uns32)( 6 + (12 * tagCount) ); - - InternalTagMap::iterator tagPos = ifdInfo.tagMap.begin(); - InternalTagMap::iterator tagEnd = ifdInfo.tagMap.end(); - - for ( ; tagPos != tagEnd; ++tagPos ) { - InternalTagInfo & currTag ( tagPos->second ); - if ( currTag.dataLen > 4 ) visibleLength += ((currTag.dataLen + 1) & 0xFFFFFFFE); // ! Round to even lengths. - } - - } - - return visibleLength; - -} // TIFF_FileWriter::DetermineVisibleLength - -// ================================================================================================= -// TIFF_FileWriter::DetermineAppendInfo -// ==================================== - -#ifndef Trace_DetermineAppendInfo - #define Trace_DetermineAppendInfo 0 -#endif - -XMP_Uns32 TIFF_FileWriter::DetermineAppendInfo ( XMP_Uns32 appendedOrigin, - bool appendedIFDs[kTIFF_KnownIFDCount], - XMP_Uns32 newIFDOffsets[kTIFF_KnownIFDCount], - bool appendAll /* = false */ ) -{ - XMP_Uns32 appendedLength = 0; - XMP_Assert ( (appendedOrigin & 1) == 0 ); // Make sure it is even. - - #if Trace_DetermineAppendInfo - { - printf ( "\nEntering TIFF_FileWriter::DetermineAppendInfo%s\n", (appendAll ? ", append all" : "") ); - for ( int ifd = 0; ifd < kTIFF_KnownIFDCount; ++ifd ) { - InternalIFDInfo & thisIFD = this->containedIFDs[ifd]; - printf ( "\n IFD %d, origCount %d, map.size %d, origIFDOffset %d (0x%X), origNextIFD %d (0x%X)", - ifd, thisIFD.origCount, thisIFD.tagMap.size(), - thisIFD.origIFDOffset, thisIFD.origIFDOffset, thisIFD.origNextIFD, thisIFD.origNextIFD ); - if ( thisIFD.changed ) printf ( ", changed" ); - if ( thisIFD.origCount < thisIFD.tagMap.size() ) printf ( ", should get appended" ); - printf ( "\n" ); - InternalTagMap::iterator tagPos; - InternalTagMap::iterator tagEnd = thisIFD.tagMap.end(); - for ( tagPos = thisIFD.tagMap.begin(); tagPos != tagEnd; ++tagPos ) { - InternalTagInfo & thisTag = tagPos->second; - printf ( " Tag %d, smallValue 0x%X, origDataLen %d, origDataOffset %d (0x%X)", - thisTag.id, thisTag.smallValue, thisTag.origDataLen, thisTag.origDataOffset, thisTag.origDataOffset ); - if ( thisTag.changed ) printf ( ", changed" ); - if ( (thisTag.dataLen > thisTag.origDataLen) && (thisTag.dataLen > 4) ) printf ( ", should get appended" ); - printf ( "\n" ); - } - } - printf ( "\n" ); - } - #endif - - // Determine which of the IFDs will be appended. If the Exif, GPS, or Interoperability IFDs are - // appended, set dummy values for their offsets in the "owning" IFD. This must be done first - // since this might cause the owning IFD to grow. - - if ( ! appendAll ) { - for ( int i = 0; i < kTIFF_KnownIFDCount ;++i ) appendedIFDs[i] = false; - } else { - for ( int i = 0; i < kTIFF_KnownIFDCount ;++i ) appendedIFDs[i] = (this->containedIFDs[i].tagMap.size() > 0); - } - - appendedIFDs[kTIFF_InteropIFD] |= (this->containedIFDs[kTIFF_InteropIFD].origCount < - this->containedIFDs[kTIFF_InteropIFD].tagMap.size()); - if ( appendedIFDs[kTIFF_InteropIFD] ) { - this->SetTag_Long ( kTIFF_ExifIFD, kTIFF_InteroperabilityIFDPointer, 0xABADABAD ); - } - - appendedIFDs[kTIFF_GPSInfoIFD] |= (this->containedIFDs[kTIFF_GPSInfoIFD].origCount < - this->containedIFDs[kTIFF_GPSInfoIFD].tagMap.size()); - if ( appendedIFDs[kTIFF_GPSInfoIFD] ) { - this->SetTag_Long ( kTIFF_PrimaryIFD, kTIFF_GPSInfoIFDPointer, 0xABADABAD ); - } - - appendedIFDs[kTIFF_ExifIFD] |= (this->containedIFDs[kTIFF_ExifIFD].origCount < - this->containedIFDs[kTIFF_ExifIFD].tagMap.size()); - if ( appendedIFDs[kTIFF_ExifIFD] ) { - this->SetTag_Long ( kTIFF_PrimaryIFD, kTIFF_ExifIFDPointer, 0xABADABAD ); - } - - appendedIFDs[kTIFF_PrimaryIFD] |= (this->containedIFDs[kTIFF_PrimaryIFD].origCount < - this->containedIFDs[kTIFF_PrimaryIFD].tagMap.size()); - - // The appended data (if any) will be a sequence of an IFD followed by its large values. - // Determine the new offsets for the appended IFDs and tag values, and the total amount of - // appended stuff. - - for ( int ifd = 0; ifd < kTIFF_KnownIFDCount ;++ifd ) { - - InternalIFDInfo& ifdInfo ( this->containedIFDs[ifd] ); - size_t tagCount = ifdInfo.tagMap.size(); - - if ( ! (appendAll | ifdInfo.changed) ) continue; - if ( tagCount == 0 ) continue; - - newIFDOffsets[ifd] = ifdInfo.origIFDOffset; - if ( appendedIFDs[ifd] ) { - newIFDOffsets[ifd] = appendedOrigin + appendedLength; - appendedLength += (XMP_Uns32)( 6 + (12 * tagCount) ); - } - - InternalTagMap::iterator tagPos = ifdInfo.tagMap.begin(); - InternalTagMap::iterator tagEnd = ifdInfo.tagMap.end(); - - for ( ; tagPos != tagEnd; ++tagPos ) { - - InternalTagInfo & currTag ( tagPos->second ); - if ( (! (appendAll | currTag.changed)) || (currTag.dataLen <= 4) ) continue; - - if ( (currTag.dataLen <= currTag.origDataLen) && (! appendAll) ) { - this->PutUns32 ( currTag.origDataOffset, &currTag.smallValue ); // Reuse the old space. - } else { - this->PutUns32 ( (appendedOrigin + appendedLength), &currTag.smallValue ); // Set the appended offset. - appendedLength += ((currTag.dataLen + 1) & 0xFFFFFFFEUL); // Round to an even size. - } - - } - - } - - // If the Exif, GPS, or Interoperability IFDs get appended, update the tag values for their new offsets. - - if ( appendedIFDs[kTIFF_ExifIFD] ) { - this->SetTag_Long ( kTIFF_PrimaryIFD, kTIFF_ExifIFDPointer, newIFDOffsets[kTIFF_ExifIFD] ); - } - if ( appendedIFDs[kTIFF_GPSInfoIFD] ) { - this->SetTag_Long ( kTIFF_PrimaryIFD, kTIFF_GPSInfoIFDPointer, newIFDOffsets[kTIFF_GPSInfoIFD] ); - } - if ( appendedIFDs[kTIFF_InteropIFD] ) { - this->SetTag_Long ( kTIFF_ExifIFD, kTIFF_InteroperabilityIFDPointer, newIFDOffsets[kTIFF_InteropIFD] ); - } - - #if Trace_DetermineAppendInfo - { - printf ( "Exiting TIFF_FileWriter::DetermineAppendInfo\n" ); - for ( int ifd = 0; ifd < kTIFF_KnownIFDCount; ++ifd ) { - InternalIFDInfo & thisIFD = this->containedIFDs[ifd]; - printf ( "\n IFD %d, origCount %d, map.size %d, origIFDOffset %d (0x%X), origNextIFD %d (0x%X)", - ifd, thisIFD.origCount, thisIFD.tagMap.size(), - thisIFD.origIFDOffset, thisIFD.origIFDOffset, thisIFD.origNextIFD, thisIFD.origNextIFD ); - if ( thisIFD.changed ) printf ( ", changed" ); - if ( appendedIFDs[ifd] ) printf ( ", will be appended at %d (0x%X)", newIFDOffsets[ifd], newIFDOffsets[ifd] ); - printf ( "\n" ); - InternalTagMap::iterator tagPos; - InternalTagMap::iterator tagEnd = thisIFD.tagMap.end(); - for ( tagPos = thisIFD.tagMap.begin(); tagPos != tagEnd; ++tagPos ) { - InternalTagInfo & thisTag = tagPos->second; - printf ( " Tag %d, smallValue 0x%X, origDataLen %d, origDataOffset %d (0x%X)", - thisTag.id, thisTag.smallValue, thisTag.origDataLen, thisTag.origDataOffset, thisTag.origDataOffset ); - if ( thisTag.changed ) printf ( ", changed" ); - if ( (thisTag.dataLen > thisTag.origDataLen) && (thisTag.dataLen > 4) ) { - XMP_Uns32 newOffset = this->GetUns32 ( &thisTag.smallValue ); - printf ( ", will be appended at %d (0x%X)", newOffset, newOffset ); - } - printf ( "\n" ); - } - } - printf ( "\n" ); - } - #endif - - return appendedLength; - -} // TIFF_FileWriter::DetermineAppendInfo - -// ================================================================================================= -// TIFF_FileWriter::UpdateMemByAppend -// ================================== -// -// Normally we update TIFF in a conservative "by-append" manner. Changes are written in-place where -// they fit, anything requiring growth is appended to the end and the old space is abandoned. The -// end for memory-based TIFF is the end of the data block, the end for file-based TIFF is the end of -// the file. This update-by-append model has the advantage of not perturbing any hidden offsets, a -// common feature of proprietary MakerNotes. -// -// When doing the update-by-append we're only going to be modifying things that have changed. This -// means IFDs with changed, added, or deleted tags, and large values for changed or added tags. The -// IFDs and tag values are updated in-place if they fit, leaving holes in the stream if the new -// value is smaller than the old. - -// ** Someday we might want to use the FreeOffsets and FreeByteCounts tags to track free space. -// ** Probably not a huge win in practice though, and the TIFF spec says they are not recommended -// ** for general interchange use. - -void TIFF_FileWriter::UpdateMemByAppend ( XMP_Uns8** newStream_out, XMP_Uns32* newLength_out, - bool appendAll /* = false */, XMP_Uns32 extraSpace /* = 0 */ ) -{ - bool appendedIFDs[kTIFF_KnownIFDCount]; - XMP_Uns32 newIFDOffsets[kTIFF_KnownIFDCount]; - XMP_Uns32 appendedOrigin = ((this->tiffLength + 1) & 0xFFFFFFFEUL); // Start at an even offset. - XMP_Uns32 appendedLength = DetermineAppendInfo ( appendedOrigin, appendedIFDs, newIFDOffsets, appendAll ); - - // Allocate the new block of memory for the full stream. Copy the original stream. Write the - // modified IFDs and values. Finally rebuild the internal IFD info and tag map. - - XMP_Uns32 newLength = appendedOrigin + appendedLength; - XMP_Uns8* newStream = (XMP_Uns8*) malloc ( newLength + extraSpace ); - if ( newStream == 0 ) XMP_Throw ( "Out of memory", kXMPErr_NoMemory ); - - memcpy ( newStream, this->memStream, this->tiffLength ); // AUDIT: Safe, malloc'ed newLength bytes above. - if ( this->tiffLength < appendedOrigin ) { - XMP_Assert ( appendedOrigin == (this->tiffLength + 1) ); - newStream[this->tiffLength] = 0; // Clear the pad byte. - } - - try { // We might get exceptions from the next part and must delete newStream on the way out. - - // Write the modified IFDs and values. Rewrite the full IFD from scratch to make sure the - // tags are now unique and sorted. Copy large changed values to their appropriate location. - - XMP_Uns32 appendedOffset = appendedOrigin; - - for ( int ifd = 0; ifd < kTIFF_KnownIFDCount; ++ifd ) { - - InternalIFDInfo& ifdInfo ( this->containedIFDs[ifd] ); - size_t tagCount = ifdInfo.tagMap.size(); - - if ( ! (appendAll | ifdInfo.changed) ) continue; - if ( tagCount == 0 ) continue; - - XMP_Uns8* ifdPtr = newStream + newIFDOffsets[ifd]; - - if ( appendedIFDs[ifd] ) { - XMP_Assert ( newIFDOffsets[ifd] == appendedOffset ); - appendedOffset += (XMP_Uns32)( 6 + (12 * tagCount) ); - } - - this->PutUns16 ( (XMP_Uns16)tagCount, ifdPtr ); - ifdPtr += 2; - - InternalTagMap::iterator tagPos = ifdInfo.tagMap.begin(); - InternalTagMap::iterator tagEnd = ifdInfo.tagMap.end(); - - for ( ; tagPos != tagEnd; ++tagPos ) { - - InternalTagInfo & currTag ( tagPos->second ); - - this->PutUns16 ( currTag.id, ifdPtr ); - ifdPtr += 2; - this->PutUns16 ( currTag.type, ifdPtr ); - ifdPtr += 2; - this->PutUns32 ( currTag.count, ifdPtr ); - ifdPtr += 4; - - *((XMP_Uns32*)ifdPtr) = currTag.smallValue; - - if ( (appendAll | currTag.changed) && (currTag.dataLen > 4) ) { - - XMP_Uns32 valueOffset = this->GetUns32 ( &currTag.smallValue ); - - if ( (currTag.dataLen <= currTag.origDataLen) && (! appendAll) ) { - XMP_Assert ( valueOffset == currTag.origDataOffset ); - } else { - XMP_Assert ( valueOffset == appendedOffset ); - appendedOffset += ((currTag.dataLen + 1) & 0xFFFFFFFEUL); - } - - if ( currTag.dataLen > (newLength - valueOffset) ) XMP_Throw ( "Buffer overrun", kXMPErr_InternalFailure ); - memcpy ( (newStream + valueOffset), currTag.dataPtr, currTag.dataLen ); // AUDIT: Protected by the above check. - if ( (currTag.dataLen & 1) != 0 ) newStream[valueOffset+currTag.dataLen] = 0; - - } - - ifdPtr += 4; - - } - - this->PutUns32 ( ifdInfo.origNextIFD, ifdPtr ); - ifdPtr += 4; - - } - - XMP_Assert ( appendedOffset == newLength ); - - // Back fill the offsets for the primary and thumnbail IFDs, if they are now appended. - - if ( appendedIFDs[kTIFF_PrimaryIFD] ) { - this->PutUns32 ( newIFDOffsets[kTIFF_PrimaryIFD], (newStream + 4) ); - } - - } catch ( ... ) { - - free ( newStream ); - throw; - - } - - *newStream_out = newStream; - *newLength_out = newLength; - -} // TIFF_FileWriter::UpdateMemByAppend - -// ================================================================================================= -// TIFF_FileWriter::UpdateMemByRewrite -// =================================== -// -// Normally we update TIFF in a conservative "by-append" manner. Changes are written in-place where -// they fit, anything requiring growth is appended to the end and the old space is abandoned. The -// end for memory-based TIFF is the end of the data block, the end for file-based TIFF is the end of -// the file. This update-by-append model has the advantage of not perturbing any hidden offsets, a -// common feature of proprietary MakerNotes. -// -// The condenseStream parameter can be used to rewrite the full stream instead of appending. This -// will discard any MakerNote tag and risks breaking offsets that are hidden. This can be necessary -// though to try to make the TIFF fit in a JPEG file. -// -// We don't do most of the actual rewrite here. We set things up so that UpdateMemByAppend can be -// called to append onto a bare TIFF header. Additional hidden offsets are then handled here. -// -// These tags are recognized as being hidden offsets when composing a condensed stream: -// 273 - StripOffsets, lengths in tag 279 -// 288 - FreeOffsets, lengths in tag 289 -// 324 - TileOffsets, lengths in tag 325 -// 330 - SubIFDs, lengths within the IFDs (Plus subIFD values and possible chaining!) -// 513 - JPEGInterchangeFormat, length in tag 514 -// 519 - JPEGQTables, each table is 64 bytes -// 520 - JPEGDCTables, lengths ??? -// 521 - JPEGACTables, lengths ??? -// Some of these will handled and kept, some will be thrown out, some will cause the rewrite to fail. -// -// The hidden offsets for the Exif, GPS, and Interoperability IFDs (tags 34665, 34853, and 40965) -// are handled by the code in DetermineAppendInfo, which is called from UpdateMemByAppend, which is -// called from here. - -// ! So far, a memory-based TIFF rewrite would only be done for the Exif portion of a JPEG file. -// ! In which case we're probably OK to handle JPEGInterchangeFormat (used for compressed thumbnails) -// ! and complain about any of the other hidden offset tags. - -// tag count type - -// 273 n short or long -// 279 n short or long -// 288 n long -// 289 n long -// 324 n long -// 325 n short or long - -// 330 n long - -// 513 1 long -// 514 1 long - -// 519 n long -// 520 n long -// 521 n long - -static XMP_Uns16 kNoGoTags[] = - { - kTIFF_StripOffsets, // 273 *** Should be handled? - kTIFF_StripByteCounts, // 279 *** Should be handled? - kTIFF_FreeOffsets, // 288 *** Should be handled? - kTIFF_FreeByteCounts, // 289 *** Should be handled? - kTIFF_TileOffsets, // 324 *** Should be handled? - kTIFF_TileByteCounts, // 325 *** Should be handled? - kTIFF_SubIFDs, // 330 *** Should be handled? - kTIFF_JPEGQTables, // 519 - kTIFF_JPEGDCTables, // 520 - kTIFF_JPEGACTables, // 521 - 0xFFFF // Must be last as a sentinel. - }; - -static XMP_Uns16 kBanishedTags[] = - { - kTIFF_MakerNote, // *** Should someday support MakerNoteSafety. - 0xFFFF // Must be last as a sentinel. - }; - -struct SimpleHiddenContentInfo { - XMP_Uns8 ifd; - XMP_Uns16 offsetTag, lengthTag; -}; - -struct SimpleHiddenContentLocations { - XMP_Uns32 length, oldOffset, newOffset; - SimpleHiddenContentLocations() : length(0), oldOffset(0), newOffset(0) {}; -}; - -enum { kSimpleHiddenContentCount = 1 }; - -static const SimpleHiddenContentInfo kSimpleHiddenContentInfo [kSimpleHiddenContentCount] = - { - { kTIFF_TNailIFD, kTIFF_JPEGInterchangeFormat, kTIFF_JPEGInterchangeFormatLength } - }; - -// ------------------------------------------------------------------------------------------------- - -void TIFF_FileWriter::UpdateMemByRewrite ( XMP_Uns8** newStream_out, XMP_Uns32* newLength_out ) -{ - const InternalTagInfo* tagInfo; - - // Check for tags that we don't tolerate because they have data we can't (or refuse to) find. - - for ( XMP_Uns8 ifd = 0; ifd < kTIFF_KnownIFDCount; ++ifd ) { - for ( int i = 0; kNoGoTags[i] != 0xFFFF; ++i ) { - tagInfo = this->FindTagInIFD ( ifd, kNoGoTags[i] ); - if ( tagInfo != 0 ) XMP_Throw ( "Tag not tolerated for TIFF rewrite", kXMPErr_Unimplemented ); - } - } - - // Delete unwanted tags. - - for ( XMP_Uns8 ifd = 0; ifd < kTIFF_KnownIFDCount; ++ifd ) { - for ( int i = 0; kBanishedTags[i] != 0xFFFF; ++i ) { - this->DeleteTag ( ifd, kBanishedTags[i] ); - } - } - - // Determine the offsets and additional size for the hidden offset content. Set the offset tags - // to the new offset. - - XMP_Uns32 hiddenContentLength = 0; - XMP_Uns32 hiddenContentOrigin = this->DetermineVisibleLength(); - - SimpleHiddenContentLocations hiddenLocations [kSimpleHiddenContentCount]; - - for ( int i = 0; i < kSimpleHiddenContentCount; ++i ) { - - const SimpleHiddenContentInfo & hiddenInfo ( kSimpleHiddenContentInfo[i] ); - - bool haveLength = this->GetTag_Integer ( hiddenInfo.ifd, hiddenInfo.lengthTag, &hiddenLocations[i].length ); - bool haveOffset = this->GetTag_Integer ( hiddenInfo.ifd, hiddenInfo.offsetTag, &hiddenLocations[i].oldOffset ); - if ( haveLength != haveOffset ) XMP_Throw ( "Unpaired simple hidden content tag", kXMPErr_BadTIFF ); - if ( (! haveLength) || (hiddenLocations[i].length == 0) ) continue; - - hiddenLocations[i].newOffset = hiddenContentOrigin + hiddenContentLength; - this->SetTag_Long ( hiddenInfo.ifd, hiddenInfo.offsetTag, hiddenLocations[i].newOffset ); - hiddenContentLength += ((hiddenLocations[i].length + 1) & 0xFFFFFFFE); // ! Round up for even offsets. - - } - - // Save any old memory stream for the content behind hidden offsets. Setup a bare TIFF header. - - XMP_Uns8* oldStream = this->memStream; - - XMP_Uns8 bareTIFF [8]; - if ( this->bigEndian ) { - bareTIFF[0] = 0x4D; bareTIFF[1] = 0x4D; bareTIFF[2] = 0x00; bareTIFF[3] = 0x2A; - } else { - bareTIFF[0] = 0x49; bareTIFF[1] = 0x49; bareTIFF[2] = 0x2A; bareTIFF[3] = 0x00; - } - *((XMP_Uns32*)&bareTIFF[4]) = 0; - - this->memStream = &bareTIFF[0]; - this->tiffLength = sizeof ( bareTIFF ); - this->ownedStream = false; - - // Call UpdateMemByAppend to write the new stream, telling it to append everything. - - this->UpdateMemByAppend ( newStream_out, newLength_out, true, hiddenContentLength ); - - // Copy the hidden content and update the output stream length; - - XMP_Assert ( *newLength_out == hiddenContentOrigin ); - *newLength_out += hiddenContentLength; - - for ( int i = 0; i < kSimpleHiddenContentCount; ++i ) { - - if ( hiddenLocations[i].length == 0 ) continue; - - XMP_Uns8* srcPtr = oldStream + hiddenLocations[i].oldOffset; - XMP_Uns8* destPtr = *newStream_out + hiddenLocations[i].newOffset; - memcpy ( destPtr, srcPtr, hiddenLocations[i].length ); // AUDIT: Safe copy, not user data, computed length. - - } - -} // TIFF_FileWriter::UpdateMemByRewrite - -// ================================================================================================= -// TIFF_FileWriter::UpdateMemoryStream -// =================================== -// -// Normally we update TIFF in a conservative "by-append" manner. Changes are written in-place where -// they fit, anything requiring growth is appended to the end and the old space is abandoned. The -// end for memory-based TIFF is the end of the data block, the end for file-based TIFF is the end of -// the file. This update-by-append model has the advantage of not perturbing any hidden offsets, a -// common feature of proprietary MakerNotes. -// -// The condenseStream parameter can be used to rewrite the full stream instead of appending. This -// will discard any MakerNote tags and risks breaking offsets that are hidden. This can be necessary -// though to try to make the TIFF fit in a JPEG file. - -XMP_Uns32 TIFF_FileWriter::UpdateMemoryStream ( void** dataPtr, bool condenseStream /* = false */ ) -{ - if ( this->fileParsed ) XMP_Throw ( "Not memory based", kXMPErr_EnforceFailure ); - - if ( ! this->changed ) { - if ( dataPtr != 0 ) *dataPtr = this->memStream; - return this->tiffLength; - } - - this->PreflightIFDLinkage(); - - bool nowEmpty = true; - for ( size_t i = 0; i < kTIFF_KnownIFDCount; ++i ) { - if ( ! this->containedIFDs[i].tagMap.empty() ) { - nowEmpty = false; - break; - } - } - - XMP_Uns8* newStream = 0; - XMP_Uns32 newLength = 0; - - if ( nowEmpty ) { - - this->DeleteExistingInfo(); // Prepare for an empty reparse. - - } else { - - if ( this->tiffLength == 0 ) { // ! An empty parse does set this->memParsed. - condenseStream = true; // Makes "conjured" TIFF take the full rewrite path. - } - - if ( condenseStream ) this->changed = true; // A prior regular call would have cleared this->changed. - - if ( condenseStream ) { - this->UpdateMemByRewrite ( &newStream, &newLength ); - } else { - this->UpdateMemByAppend ( &newStream, &newLength ); - } - - } - - // Parse the revised stream. This is the cleanest way to rebuild the tag map. - - this->ParseMemoryStream ( newStream, newLength, kDoNotCopyData ); - XMP_Assert ( this->tiffLength == newLength ); - this->ownedStream = (newLength > 0); // ! We really do own the new stream, if not empty. - - if ( dataPtr != 0 ) *dataPtr = this->memStream; - return newLength; - -} // TIFF_FileWriter::UpdateMemoryStream - -// ================================================================================================= -// TIFF_FileWriter::UpdateFileStream -// ================================= -// -// Updating a file stream is done in the same general manner as updating a memory stream, the intro -// comments for UpdateMemoryStream largely apply. The file update happens in 3 phases: -// 1. Determine which IFDs will be appended, and the new offsets for the appended IFDs and tags. -// 2. Do the in-place update for the IFDs and tag values that fit. -// 3. Append the IFDs and tag values that grow. -// -// The file being updated must match the file that was previously parsed. Offsets and lengths saved -// when parsing are used to decide if something can be updated in-place or must be appended. - -// *** The general linked structure of TIFF makes it very difficult to process the file in a single -// *** sequential pass. This implementation uses a simple seek/write model for the in-place updates. -// *** In the future we might want to consider creating a map of what gets updated, allowing use of -// *** a possibly more efficient buffered model. - -// ** Someday we might want to use the FreeOffsets and FreeByteCounts tags to track free space. -// ** Probably not a huge win in practice though, and the TIFF spec says they are not recommended -// ** for general interchange use. - -#ifndef Trace_UpdateFileStream - #define Trace_UpdateFileStream 0 -#endif - -void TIFF_FileWriter::UpdateFileStream ( LFA_FileRef fileRef ) -{ - if ( this->memParsed ) XMP_Throw ( "Not file based", kXMPErr_EnforceFailure ); - if ( ! this->changed ) return; - - XMP_Int64 origDataLength = LFA_Measure ( fileRef ); - if ( (origDataLength >> 32) != 0 ) XMP_Throw ( "TIFF files can't exceed 4GB", kXMPErr_BadTIFF ); - - bool appendedIFDs[kTIFF_KnownIFDCount]; - XMP_Uns32 newIFDOffsets[kTIFF_KnownIFDCount]; - - #if Trace_UpdateFileStream - printf ( "\nStarting update of TIFF file stream\n" ); - #endif - - XMP_Uns32 appendedOrigin = (XMP_Uns32)origDataLength; - if ( (appendedOrigin & 1) != 0 ) { - ++appendedOrigin; // Start at an even offset. - LFA_Seek ( fileRef, 0, SEEK_END ); - LFA_Write ( fileRef, "\0", 1 ); - } - - this->PreflightIFDLinkage(); - - XMP_Uns32 appendedLength = DetermineAppendInfo ( appendedOrigin, appendedIFDs, newIFDOffsets ); - if ( appendedLength > (0xFFFFFFFFUL - appendedOrigin) ) XMP_Throw ( "TIFF files can't exceed 4GB", kXMPErr_BadTIFF ); - - // Do the in-place update for the IFDs and tag values that fit. This part does separate seeks - // and writes for the IFDs and values. Things to be updated can be anywhere in the file. - - // *** This might benefit from a map of the in-place updates. This would allow use of a possibly - // *** more efficient sequential I/O model. Could even incorporate the safe update file copying. - - for ( int ifd = 0; ifd < kTIFF_KnownIFDCount; ++ifd ) { - - InternalIFDInfo & thisIFD = this->containedIFDs[ifd]; - if ( ! thisIFD.changed ) continue; - - // In order to get a little bit of locality, write the IFD first then the changed tags that - // have large values and fit in-place. - - if ( ! appendedIFDs[ifd] ) { - #if Trace_UpdateFileStream - printf ( " Updating IFD %d in-place at offset %d (0x%X)\n", ifd, thisIFD.origIFDOffset, thisIFD.origIFDOffset ); - #endif - LFA_Seek ( fileRef, thisIFD.origIFDOffset, SEEK_SET ); - this->WriteFileIFD ( fileRef, thisIFD ); - } - - InternalTagMap::iterator tagPos; - InternalTagMap::iterator tagEnd = thisIFD.tagMap.end(); - - for ( tagPos = thisIFD.tagMap.begin(); tagPos != tagEnd; ++tagPos ) { - InternalTagInfo & thisTag = tagPos->second; - if ( (! thisTag.changed) || (thisTag.dataLen <= 4) || (thisTag.dataLen > thisTag.origDataLen) ) continue; - #if Trace_UpdateFileStream - printf ( " Updating tag %d in IFD %d in-place at offset %d (0x%X)\n", thisTag.id, ifd, thisTag.origDataOffset, thisTag.origDataOffset ); - #endif - LFA_Seek ( fileRef, thisTag.origDataOffset, SEEK_SET ); - LFA_Write ( fileRef, thisTag.dataPtr, thisTag.dataLen ); - } - - } - - // Append the IFDs and tag values that grow. - - XMP_Int64 fileEnd = LFA_Seek ( fileRef, 0, SEEK_END ); - XMP_Assert ( fileEnd == appendedOrigin ); - - for ( int ifd = 0; ifd < kTIFF_KnownIFDCount; ++ifd ) { - - InternalIFDInfo & thisIFD = this->containedIFDs[ifd]; - if ( ! thisIFD.changed ) continue; - - if ( appendedIFDs[ifd] ) { - #if Trace_UpdateFileStream - printf ( " Updating IFD %d by append at offset %d (0x%X)\n", ifd, newIFDOffsets[ifd], newIFDOffsets[ifd] ); - #endif - XMP_Assert ( newIFDOffsets[ifd] == LFA_Measure(fileRef) ); - this->WriteFileIFD ( fileRef, thisIFD ); - } - - InternalTagMap::iterator tagPos; - InternalTagMap::iterator tagEnd = thisIFD.tagMap.end(); - - for ( tagPos = thisIFD.tagMap.begin(); tagPos != tagEnd; ++tagPos ) { - InternalTagInfo & thisTag = tagPos->second; - if ( (! thisTag.changed) || (thisTag.dataLen <= 4) || (thisTag.dataLen <= thisTag.origDataLen) ) continue; - #if Trace_UpdateFileStream - XMP_Uns32 newOffset = this->GetUns32(&thisTag.origDataOffset); - printf ( " Updating tag %d in IFD %d by append at offset %d (0x%X)\n", thisTag.id, ifd, newOffset, newOffset ); - #endif - XMP_Assert ( this->GetUns32(&thisTag.smallValue) == LFA_Measure(fileRef) ); - LFA_Write ( fileRef, thisTag.dataPtr, thisTag.dataLen ); - if ( (thisTag.dataLen & 1) != 0 ) LFA_Write ( fileRef, "\0", 1 ); - } - - } - - // Back-fill the offset for the primary IFD, if it is now appended. - - XMP_Uns32 newOffset; - - if ( appendedIFDs[kTIFF_PrimaryIFD] ) { - this->PutUns32 ( newIFDOffsets[kTIFF_PrimaryIFD], &newOffset ); - #if TraceUpdateFileStream - printf ( " Back-filling offset of primary IFD, pointing to %d (0x%X)\n", newOffset, newOffset ); - #endif - LFA_Seek ( fileRef, 4, SEEK_SET ); - LFA_Write ( fileRef, &newOffset, 4 ); - } - - // Reset the changed flags and original length/offset values. This simulates a reparse of the - // updated file. - - for ( int ifd = 0; ifd < kTIFF_KnownIFDCount; ++ifd ) { - - InternalIFDInfo & thisIFD = this->containedIFDs[ifd]; - if ( ! thisIFD.changed ) continue; - - thisIFD.changed = false; - thisIFD.origCount = (XMP_Uns16)( thisIFD.tagMap.size() ); - thisIFD.origIFDOffset = newIFDOffsets[ifd]; - - InternalTagMap::iterator tagPos; - InternalTagMap::iterator tagEnd = thisIFD.tagMap.end(); - - for ( tagPos = thisIFD.tagMap.begin(); tagPos != tagEnd; ++tagPos ) { - InternalTagInfo & thisTag = tagPos->second; - if ( ! thisTag.changed ) continue; - thisTag.changed = false; - thisTag.origDataLen = thisTag.dataLen; - if ( thisTag.origDataLen > 4 ) thisTag.origDataOffset = this->GetUns32 ( &thisTag.smallValue ); - } - - } - - this->tiffLength = (XMP_Uns32) LFA_Measure ( fileRef ); - LFA_Seek ( fileRef, 0, SEEK_END ); // Can't hurt. - - #if Trace_UpdateFileStream - printf ( "\nFinished update of TIFF file stream\n" ); - #endif - -} // TIFF_FileWriter::UpdateFileStream - -// ================================================================================================= -// TIFF_FileWriter::WriteFileIFD -// ============================= - -void TIFF_FileWriter::WriteFileIFD ( LFA_FileRef fileRef, InternalIFDInfo & thisIFD ) -{ - XMP_Uns16 tagCount; - this->PutUns16 ( (XMP_Uns16)thisIFD.tagMap.size(), &tagCount ); - LFA_Write ( fileRef, &tagCount, 2 ); - - InternalTagMap::iterator tagPos; - InternalTagMap::iterator tagEnd = thisIFD.tagMap.end(); - - for ( tagPos = thisIFD.tagMap.begin(); tagPos != tagEnd; ++tagPos ) { - - InternalTagInfo & thisTag = tagPos->second; - RawIFDEntry ifdEntry; - - this->PutUns16 ( thisTag.id, &ifdEntry.id ); - this->PutUns16 ( thisTag.type, &ifdEntry.type ); - this->PutUns32 ( thisTag.count, &ifdEntry.count ); - ifdEntry.dataOrOffset = thisTag.smallValue; // ! Already in stream endianness. - - LFA_Write ( fileRef, &ifdEntry, sizeof(ifdEntry) ); - XMP_Assert ( sizeof(ifdEntry) == 12 ); - - } - - XMP_Uns32 nextIFD; - this->PutUns32 ( thisIFD.origNextIFD, &nextIFD ); - LFA_Write ( fileRef, &nextIFD, 4 ); - -} // TIFF_FileWriter::WriteFileIFD diff --git a/source/XMPFiles/FormatSupport/TIFF_MemoryReader.cpp b/source/XMPFiles/FormatSupport/TIFF_MemoryReader.cpp deleted file mode 100644 index 316cea0..0000000 --- a/source/XMPFiles/FormatSupport/TIFF_MemoryReader.cpp +++ /dev/null @@ -1,592 +0,0 @@ -// ================================================================================================= -// ADOBE SYSTEMS INCORPORATED -// Copyright 2006 Adobe Systems Incorporated -// 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 "TIFF_Support.hpp" - -// ================================================================================================= -/// \file TIFF_MemoryReader.cpp -/// \brief Implementation of the memory-based read-only TIFF_Manager. -/// -/// The read-only forms of TIFF_Manager are derived from TIFF_Reader. The GetTag methods are common -/// implementations in TIFF_Reader. The parsing code is different in the TIFF_MemoryReader and -/// TIFF_FileReader constructors. There are also separate destructors to release captured info. -/// -/// The read-only implementations use runtime data that is simple tweaks on the stored form. The -/// memory-based reader has one block of data for the whole TIFF stream. The file-based reader has -/// one for each IFD, plus one for the collected non-local data for each IFD. Otherwise the logic -/// is the same in both cases. -/// -/// The count for each IFD is extracted and a pointer set to the first entry in each IFD (serving as -/// a normal C array pointer). The IFD entries are tweaked as follows: -/// -/// \li The id and type fields are converted to native values. -/// \li The count field is converted to a native byte count. -/// \li If the data is not inline the offset is converted to a pointer. -/// -/// The tag values, whether inline or not, are not converted to native values. The values returned -/// from the GetTag methods are converted on the fly. The id, type, and count fields are easier to -/// convert because their types are fixed. They are used more, and more valuable to convert. -// ================================================================================================= - -// ================================================================================================= -// TIFF_MemoryReader::SortIFD -// ========================== -// -// Does a fairly simple minded insertion-like sort. This sort is not going to be optimal for edge -// cases like and IFD with lots of duplicates. - -// *** Might be better done using read and write pointers and two loops. The first loop moves out -// *** of order tags by a modified bubble sort, shifting the middle down one at a time in the loop. -// *** The first loop stops when a duplicate is hit. The second loop continues by moving the tail -// *** entries up to the appropriate slot. - -void TIFF_MemoryReader::SortIFD ( TweakedIFDInfo* thisIFD ) -{ - - XMP_Uns16 tagCount = thisIFD->count; - TweakedIFDEntry* ifdEntries = thisIFD->entries; - XMP_Uns16 prevTag = ifdEntries[0].id; - - for ( size_t i = 1; i < tagCount; ++i ) { - - XMP_Uns16 thisTag = ifdEntries[i].id; - - if ( thisTag > prevTag ) { - - // In proper order. - prevTag = thisTag; - - } else if ( thisTag == prevTag ) { - - // Duplicate tag, keep the 2nd copy, move the tail of the array up, prevTag is unchanged. - memcpy ( &ifdEntries[i-1], &ifdEntries[i], 12*(tagCount-i) ); // AUDIT: Safe, moving tail forward, i >= 1. - --tagCount; - --i; // ! Don't move forward in the array, we've moved the unseen part up. - - } else if ( thisTag < prevTag ) { - - // Out of order, move this tag up, prevTag is unchanged. Might still be a duplicate! - XMP_Int32 j; // ! Need a signed value. - for ( j = (XMP_Int32)i-1; j >= 0; --j ) { - if ( ifdEntries[j].id <= thisTag ) break; - } - - if ( (j >= 0) && (ifdEntries[j].id == thisTag) ) { - - // Out of order duplicate, move it to position j, move the tail of the array up. - ifdEntries[j] = ifdEntries[i]; - memcpy ( &ifdEntries[i], &ifdEntries[i+1], 12*(tagCount-(i+1)) ); // AUDIT: Safe, moving tail forward, i >= 1. - --tagCount; - --i; // ! Don't move forward in the array, we've moved the unseen part up. - - } else { - - // Move the out of order entry to position j+1, move the middle of the array down. - TweakedIFDEntry temp = ifdEntries[i]; - ++j; // ! So the insertion index becomes j. - memcpy ( &ifdEntries[j+1], &ifdEntries[j], 12*(i-j) ); // AUDIT: Safe, moving less than i entries to a location before i. - ifdEntries[j] = temp; - - } - - } - - } - - thisIFD->count = tagCount; // Save the final count. - -} // TIFF_MemoryReader::SortIFD - -// ================================================================================================= -// TIFF_MemoryReader::GetIFD -// ========================= - -bool TIFF_MemoryReader::GetIFD ( XMP_Uns8 ifd, TagInfoMap* ifdMap ) const -{ - if ( ifd > kTIFF_LastRealIFD ) XMP_Throw ( "Invalid IFD requested", kXMPErr_InternalFailure ); - const TweakedIFDInfo* thisIFD = &containedIFDs[ifd]; - - if ( ifdMap != 0 ) ifdMap->clear(); - if ( thisIFD->count == 0 ) return false; - - if ( ifdMap != 0 ) { - - for ( size_t i = 0; i < thisIFD->count; ++i ) { - - TweakedIFDEntry* thisTag = &(thisIFD->entries[i]); - if ( (thisTag->type < kTIFF_ByteType) || (thisTag->type > kTIFF_LastType) ) continue; // Bad type, skip this tag. - - TagInfo info ( thisTag->id, thisTag->type, 0, 0, thisTag->bytes ); - info.count = info.dataLen / (XMP_Uns32)kTIFF_TypeSizes[info.type]; - info.dataPtr = this->GetDataPtr ( thisTag ); - - (*ifdMap)[info.id] = info; - - } - - } - - return true; - -} // TIFF_MemoryReader::GetIFD - -// ================================================================================================= -// TIFF_MemoryReader::FindTagInIFD -// =============================== - -const TIFF_MemoryReader::TweakedIFDEntry* TIFF_MemoryReader::FindTagInIFD ( XMP_Uns8 ifd, XMP_Uns16 id ) const -{ - if ( ifd == kTIFF_KnownIFD ) { - // ... lookup the tag in the known tag map - } - - if ( ifd > kTIFF_LastRealIFD ) XMP_Throw ( "Invalid IFD requested", kXMPErr_InternalFailure ); - const TweakedIFDInfo* thisIFD = &containedIFDs[ifd]; - - if ( thisIFD->count == 0 ) return 0; - - XMP_Uns32 spanLength = thisIFD->count; - const TweakedIFDEntry* spanBegin = &(thisIFD->entries[0]); - - while ( spanLength > 1 ) { - - XMP_Uns32 halfLength = spanLength >> 1; // Since spanLength > 1, halfLength > 0. - const TweakedIFDEntry* spanMiddle = spanBegin + halfLength; - - // There are halfLength entries below spanMiddle, then the spanMiddle entry, then - // spanLength-halfLength-1 entries above spanMiddle (which can be none). - - if ( spanMiddle->id == id ) { - spanBegin = spanMiddle; - break; - } else if ( spanMiddle->id > id ) { - spanLength = halfLength; // Discard the middle. - } else { - spanBegin = spanMiddle; // Keep a valid spanBegin for the return check, don't use spanMiddle+1. - spanLength -= halfLength; - } - - } - - if ( spanBegin->id != id ) spanBegin = 0; - return spanBegin; - -} // TIFF_MemoryReader::FindTagInIFD - -// ================================================================================================= -// TIFF_MemoryReader::GetValueOffset -// ================================= - -XMP_Uns32 TIFF_MemoryReader::GetValueOffset ( XMP_Uns8 ifd, XMP_Uns16 id ) const -{ - const TweakedIFDEntry* thisTag = this->FindTagInIFD ( ifd, id ); - if ( thisTag == 0 ) return 0; - - XMP_Uns8 * valuePtr = (XMP_Uns8*) this->GetDataPtr ( thisTag ); - - return (XMP_Uns32)(valuePtr - this->tiffStream); // ! TIFF streams can't exceed 4GB. - -} // TIFF_MemoryReader::GetValueOffset - -// ================================================================================================= -// TIFF_MemoryReader::GetTag -// ========================= - -bool TIFF_MemoryReader::GetTag ( XMP_Uns8 ifd, XMP_Uns16 id, TagInfo* info ) const -{ - const TweakedIFDEntry* thisTag = this->FindTagInIFD ( ifd, id ); - if ( thisTag == 0 ) return false; - if ( (thisTag->type < kTIFF_ByteType) || (thisTag->type > kTIFF_LastType) ) return false; // Bad type, skip this tag. - - if ( info != 0 ) { - - info->id = thisTag->id; - info->type = thisTag->type; - info->count = thisTag->bytes / (XMP_Uns32)kTIFF_TypeSizes[thisTag->type]; - info->dataLen = thisTag->bytes; - - info->dataPtr = this->GetDataPtr ( thisTag ); - - } - - return true; - -} // TIFF_MemoryReader::GetTag - -// ================================================================================================= -// TIFF_MemoryReader::GetTag_Integer -// ================================= - -bool TIFF_MemoryReader::GetTag_Integer ( XMP_Uns8 ifd, XMP_Uns16 id, XMP_Uns32* data ) const -{ - const TweakedIFDEntry* thisTag = this->FindTagInIFD ( ifd, id ); - if ( thisTag == 0 ) return false; - - if ( data != 0 ) { - if ( thisTag->type == kTIFF_ShortType ) { - if ( thisTag->bytes != 2 ) return false; // Wrong count. - *data = this->GetUns16 ( this->GetDataPtr ( thisTag ) ); - } else if ( thisTag->type == kTIFF_LongType ) { - if ( thisTag->bytes != 4 ) return false; // Wrong count. - *data = this->GetUns32 ( this->GetDataPtr ( thisTag ) ); - } else { - return false; - } - } - - return true; - -} // TIFF_MemoryReader::GetTag_Integer - -// ================================================================================================= -// TIFF_MemoryReader::GetTag_Byte -// ============================== - -bool TIFF_MemoryReader::GetTag_Byte ( XMP_Uns8 ifd, XMP_Uns16 id, XMP_Uns8* data ) const -{ - const TweakedIFDEntry* thisTag = this->FindTagInIFD ( ifd, id ); - if ( thisTag == 0 ) return false; - if ( (thisTag->type != kTIFF_ByteType) || (thisTag->bytes != 1) ) return false; - - if ( data != 0 ) { - *data = * ( (XMP_Uns8*) this->GetDataPtr ( thisTag ) ); - } - - return true; - -} // TIFF_MemoryReader::GetTag_Byte - -// ================================================================================================= -// TIFF_MemoryReader::GetTag_SByte -// =============================== - -bool TIFF_MemoryReader::GetTag_SByte ( XMP_Uns8 ifd, XMP_Uns16 id, XMP_Int8* data ) const -{ - const TweakedIFDEntry* thisTag = this->FindTagInIFD ( ifd, id ); - if ( thisTag == 0 ) return false; - if ( (thisTag->type != kTIFF_SByteType) || (thisTag->bytes != 1) ) return false; - - if ( data != 0 ) { - *data = * ( (XMP_Int8*) this->GetDataPtr ( thisTag ) ); - } - - return true; - -} // TIFF_MemoryReader::GetTag_SByte - -// ================================================================================================= -// TIFF_MemoryReader::GetTag_Short -// =============================== - -bool TIFF_MemoryReader::GetTag_Short ( XMP_Uns8 ifd, XMP_Uns16 id, XMP_Uns16* data ) const -{ - const TweakedIFDEntry* thisTag = this->FindTagInIFD ( ifd, id ); - if ( thisTag == 0 ) return false; - if ( (thisTag->type != kTIFF_ShortType) || (thisTag->bytes != 2) ) return false; - - if ( data != 0 ) { - *data = this->GetUns16 ( this->GetDataPtr ( thisTag ) ); - } - - return true; - -} // TIFF_MemoryReader::GetTag_Short - -// ================================================================================================= -// TIFF_MemoryReader::GetTag_SShort -// ================================ - -bool TIFF_MemoryReader::GetTag_SShort ( XMP_Uns8 ifd, XMP_Uns16 id, XMP_Int16* data ) const -{ - const TweakedIFDEntry* thisTag = this->FindTagInIFD ( ifd, id ); - if ( thisTag == 0 ) return false; - if ( (thisTag->type != kTIFF_SShortType) || (thisTag->bytes != 2) ) return false; - - if ( data != 0 ) { - *data = (XMP_Int16) this->GetUns16 ( this->GetDataPtr ( thisTag ) ); - } - - return true; - -} // TIFF_MemoryReader::GetTag_SShort - -// ================================================================================================= -// TIFF_MemoryReader::GetTag_Long -// ============================== - -bool TIFF_MemoryReader::GetTag_Long ( XMP_Uns8 ifd, XMP_Uns16 id, XMP_Uns32* data ) const -{ - const TweakedIFDEntry* thisTag = this->FindTagInIFD ( ifd, id ); - if ( thisTag == 0 ) return false; - if ( (thisTag->type != kTIFF_LongType) || (thisTag->bytes != 4) ) return false; - - if ( data != 0 ) { - *data = this->GetUns32 ( this->GetDataPtr ( thisTag ) ); - } - - return true; - -} // TIFF_MemoryReader::GetTag_Long - -// ================================================================================================= -// TIFF_MemoryReader::GetTag_SLong -// =============================== - -bool TIFF_MemoryReader::GetTag_SLong ( XMP_Uns8 ifd, XMP_Uns16 id, XMP_Int32* data ) const -{ - const TweakedIFDEntry* thisTag = this->FindTagInIFD ( ifd, id ); - if ( thisTag == 0 ) return false; - if ( (thisTag->type != kTIFF_SLongType) || (thisTag->bytes != 4) ) return false; - - if ( data != 0 ) { - *data = (XMP_Int32) this->GetUns32 ( this->GetDataPtr ( thisTag ) ); - } - - return true; - -} // TIFF_MemoryReader::GetTag_SLong - -// ================================================================================================= -// TIFF_MemoryReader::GetTag_Rational -// ================================== - -bool TIFF_MemoryReader::GetTag_Rational ( XMP_Uns8 ifd, XMP_Uns16 id, Rational* data ) const -{ - const TweakedIFDEntry* thisTag = this->FindTagInIFD ( ifd, id ); - if ( thisTag == 0 ) return false; - if ( (thisTag->type != kTIFF_RationalType) || (thisTag->bytes != 8) ) return false; - - if ( data != 0 ) { - XMP_Uns32* dataPtr = (XMP_Uns32*) this->GetDataPtr ( thisTag ); - data->num = this->GetUns32 ( dataPtr ); - data->denom = this->GetUns32 ( dataPtr+1 ); - } - - return true; - -} // TIFF_MemoryReader::GetTag_Rational - -// ================================================================================================= -// TIFF_MemoryReader::GetTag_SRational -// =================================== - -bool TIFF_MemoryReader::GetTag_SRational ( XMP_Uns8 ifd, XMP_Uns16 id, SRational* data ) const -{ - const TweakedIFDEntry* thisTag = this->FindTagInIFD ( ifd, id ); - if ( thisTag == 0 ) return false; - if ( (thisTag->type != kTIFF_SRationalType) || (thisTag->bytes != 8) ) return false; - - if ( data != 0 ) { - XMP_Uns32* dataPtr = (XMP_Uns32*) this->GetDataPtr ( thisTag ); - data->num = (XMP_Int32) this->GetUns32 ( dataPtr ); - data->denom = (XMP_Int32) this->GetUns32 ( dataPtr+1 ); - } - - return true; - -} // TIFF_MemoryReader::GetTag_SRational - -// ================================================================================================= -// TIFF_MemoryReader::GetTag_Float -// =============================== - -bool TIFF_MemoryReader::GetTag_Float ( XMP_Uns8 ifd, XMP_Uns16 id, float* data ) const -{ - const TweakedIFDEntry* thisTag = this->FindTagInIFD ( ifd, id ); - if ( thisTag == 0 ) return false; - if ( (thisTag->type != kTIFF_FloatType) || (thisTag->bytes != 4) ) return false; - - if ( data != 0 ) { - *data = this->GetFloat ( this->GetDataPtr ( thisTag ) ); - } - - return true; - -} // TIFF_MemoryReader::GetTag_Float - -// ================================================================================================= -// TIFF_MemoryReader::GetTag_Double -// ================================ - -bool TIFF_MemoryReader::GetTag_Double ( XMP_Uns8 ifd, XMP_Uns16 id, double* data ) const -{ - const TweakedIFDEntry* thisTag = this->FindTagInIFD ( ifd, id ); - if ( thisTag == 0 ) return false; - if ( (thisTag->type != kTIFF_DoubleType) || (thisTag->bytes != 8) ) return false; - - if ( data != 0 ) { - double* dataPtr = (double*) this->GetDataPtr ( thisTag ); - *data = this->GetDouble ( dataPtr ); - } - - return true; - -} // TIFF_MemoryReader::GetTag_Double - -// ================================================================================================= -// TIFF_MemoryReader::GetTag_ASCII -// =============================== - -bool TIFF_MemoryReader::GetTag_ASCII ( XMP_Uns8 ifd, XMP_Uns16 id, XMP_StringPtr* dataPtr, XMP_StringLen* dataLen ) const -{ - const TweakedIFDEntry* thisTag = this->FindTagInIFD ( ifd, id ); - if ( thisTag == 0 ) return false; - if ( thisTag->type != kTIFF_ASCIIType ) return false; - - if ( dataPtr != 0 ) { - *dataPtr = (XMP_StringPtr) this->GetDataPtr ( thisTag ); - } - - if ( dataLen != 0 ) *dataLen = thisTag->bytes; - - return true; - -} // TIFF_MemoryReader::GetTag_ASCII - -// ================================================================================================= -// TIFF_MemoryReader::GetTag_EncodedString -// ======================================= - -bool TIFF_MemoryReader::GetTag_EncodedString ( XMP_Uns8 ifd, XMP_Uns16 id, std::string* utf8Str ) const -{ - const TweakedIFDEntry* thisTag = this->FindTagInIFD ( ifd, id ); - if ( thisTag == 0 ) return false; - if ( thisTag->type != kTIFF_UndefinedType ) return false; - - if ( utf8Str == 0 ) return true; // Return true if the converted string is not wanted. - - bool ok = this->DecodeString ( this->GetDataPtr ( thisTag ), thisTag->bytes, utf8Str ); - return ok; - -} // TIFF_MemoryReader::GetTag_EncodedString - -// ================================================================================================= -// TIFF_MemoryReader::ParseMemoryStream -// ==================================== - -// *** Need to tell TIFF/Exif from TIFF/EP, DNG files are the latter. - -void TIFF_MemoryReader::ParseMemoryStream ( const void* data, XMP_Uns32 length, bool copyData /* = true */ ) -{ - // Get rid of any current TIFF. - - if ( this->ownedStream ) free ( this->tiffStream ); - this->ownedStream = false; - this->tiffStream = 0; - this->tiffLength = 0; - - for ( size_t i = 0; i < kTIFF_KnownIFDCount; ++i ) { - this->containedIFDs[i].count = 0; - this->containedIFDs[i].entries = 0; - } - - if ( length == 0 ) return; - - // Allocate space for the full in-memory stream and copy it. - - if ( ! copyData ) { - XMP_Assert ( ! this->ownedStream ); - this->tiffStream = (XMP_Uns8*) data; - } else { - if ( length > 100*1024*1024 ) XMP_Throw ( "Outrageous length for memory-based TIFF", kXMPErr_BadTIFF ); - this->tiffStream = (XMP_Uns8*) malloc(length); - if ( this->tiffStream == 0 ) XMP_Throw ( "Out of memory", kXMPErr_NoMemory ); - memcpy ( this->tiffStream, data, length ); // AUDIT: Safe, malloc'ed length bytes above. - this->ownedStream = true; - } - - this->tiffLength = length; - - // Find and process the primary, Exif, GPS, and Interoperability IFDs. - - XMP_Uns32 primaryIFDOffset = this->CheckTIFFHeader ( this->tiffStream, length ); - XMP_Uns32 tnailIFDOffset = 0; - - if ( primaryIFDOffset != 0 ) tnailIFDOffset = this->ProcessOneIFD ( primaryIFDOffset, kTIFF_PrimaryIFD ); - - // ! Need the thumbnail IFD for checking full Exif APP1 in some JPEG files! - if ( tnailIFDOffset != 0 ) (void) this->ProcessOneIFD ( tnailIFDOffset, kTIFF_TNailIFD ); - - const TweakedIFDEntry* exifIFDTag = this->FindTagInIFD ( kTIFF_PrimaryIFD, kTIFF_ExifIFDPointer ); - if ( (exifIFDTag != 0) && (exifIFDTag->type == kTIFF_LongType) && (exifIFDTag->bytes == 4) ) { - XMP_Uns32 exifOffset = this->GetUns32 ( &exifIFDTag->dataOrPos ); - (void) this->ProcessOneIFD ( exifOffset, kTIFF_ExifIFD ); - } - - const TweakedIFDEntry* gpsIFDTag = this->FindTagInIFD ( kTIFF_PrimaryIFD, kTIFF_GPSInfoIFDPointer ); - if ( (gpsIFDTag != 0) && (gpsIFDTag->type == kTIFF_LongType) && (gpsIFDTag->bytes == 4) ) { - XMP_Uns32 gpsOffset = this->GetUns32 ( &gpsIFDTag->dataOrPos ); - (void) this->ProcessOneIFD ( gpsOffset, kTIFF_GPSInfoIFD ); - } - - const TweakedIFDEntry* interopIFDTag = this->FindTagInIFD ( kTIFF_ExifIFD, kTIFF_InteroperabilityIFDPointer ); - if ( (interopIFDTag != 0) && (interopIFDTag->type == kTIFF_LongType) && (interopIFDTag->bytes == 4) ) { - XMP_Uns32 interopOffset = this->GetUns32 ( &interopIFDTag->dataOrPos ); - (void) this->ProcessOneIFD ( interopOffset, kTIFF_InteropIFD ); - } - -} // TIFF_MemoryReader::ParseMemoryStream - -// ================================================================================================= -// TIFF_MemoryReader::ProcessOneIFD -// ================================ - -XMP_Uns32 TIFF_MemoryReader::ProcessOneIFD ( XMP_Uns32 ifdOffset, XMP_Uns8 ifd ) -{ - TweakedIFDInfo& ifdInfo = this->containedIFDs[ifd]; - - if ( (ifdOffset < 8) || (ifdOffset > (this->tiffLength - kEmptyIFDLength)) ) { - XMP_Throw ( "Bad IFD offset", kXMPErr_BadTIFF ); - } - - XMP_Uns8* ifdPtr = this->tiffStream + ifdOffset; - XMP_Uns16 ifdCount = this->GetUns16 ( ifdPtr ); - TweakedIFDEntry* ifdEntries = (TweakedIFDEntry*)(ifdPtr+2); - - if ( ifdCount >= 0x8000 ) XMP_Throw ( "Outrageous IFD count", kXMPErr_BadTIFF ); - if ( (ifdOffset + 2 + ifdCount*12 + 4) > this->tiffLength ) XMP_Throw ( "Out of bounds IFD", kXMPErr_BadTIFF ); - - ifdInfo.count = ifdCount; - ifdInfo.entries = ifdEntries; - - XMP_Int32 prevTag = -1; // ! The GPS IFD has a tag 0, so we need a signed initial value. - bool needsSorting = false; - for ( size_t i = 0; i < ifdCount; ++i ) { - - TweakedIFDEntry* thisEntry = &ifdEntries[i]; // Tweak the IFD entry to be more useful. - - if ( ! this->nativeEndian ) { - Flip2 ( &thisEntry->id ); - Flip2 ( &thisEntry->type ); - Flip4 ( &thisEntry->bytes ); - } - - if ( thisEntry->id <= prevTag ) needsSorting = true; - prevTag = thisEntry->id; - - if ( (thisEntry->type < kTIFF_ByteType) || (thisEntry->type > kTIFF_LastType) ) continue; // Bad type, skip this tag. - - thisEntry->bytes *= (XMP_Uns32)kTIFF_TypeSizes[thisEntry->type]; - if ( thisEntry->bytes > this->tiffLength ) XMP_Throw ( "Bad TIFF data size", kXMPErr_BadTIFF ); - if ( thisEntry->bytes > 4 ) { - if ( ! this->nativeEndian ) Flip4 ( &thisEntry->dataOrPos ); - } - - } - - ifdPtr += (2 + ifdCount*12); - XMP_Uns32 nextIFDOffset = this->GetUns32 ( ifdPtr ); - - if ( needsSorting ) SortIFD ( &ifdInfo ); // ! Don't perturb the ifdCount used to find the next IFD offset. - - return nextIFDOffset; - -} // TIFF_MemoryReader::ProcessOneIFD - -// ================================================================================================= diff --git a/source/XMPFiles/FormatSupport/TIFF_Support.cpp b/source/XMPFiles/FormatSupport/TIFF_Support.cpp deleted file mode 100644 index 87a96c9..0000000 --- a/source/XMPFiles/FormatSupport/TIFF_Support.cpp +++ /dev/null @@ -1,434 +0,0 @@ -// ================================================================================================= -// ADOBE SYSTEMS INCORPORATED -// Copyright 2006 Adobe Systems Incorporated -// 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 "TIFF_Support.hpp" -#include "EndianUtils.hpp" - -#include "UnicodeConversions.hpp" - -// ================================================================================================= -/// \file TIFF_Support.cpp -/// \brief Manager for parsing and serializing TIFF streams. -/// -// ================================================================================================= - -// ================================================================================================= -// TIFF_Manager::TIFF_Manager -// ========================== - -static bool sFirstCTor = true; - -TIFF_Manager::TIFF_Manager() - : bigEndian(false), nativeEndian(false), - GetUns16(0), GetUns32(0), GetFloat(0), GetDouble(0), - PutUns16(0), PutUns32(0), PutFloat(0), PutDouble(0) -{ - - if ( sFirstCTor ) { - sFirstCTor = false; - #if XMP_DebugBuild - for ( int ifd = 0; ifd < kTIFF_KnownIFDCount; ++ifd ) { // Make sure the known tag arrays are sorted. - for ( const XMP_Uns16* idPtr = sKnownTags[ifd]; *idPtr != 0xFFFF; ++idPtr ) { - XMP_Assert ( *idPtr < *(idPtr+1) ); - } - } - #endif - } - -} // TIFF_Manager::TIFF_Manager - -// ================================================================================================= -// TIFF_Manager::CheckTIFFHeader -// ============================= -// -// Checks the 4 byte TIFF prefix for validity and endianness. Sets the endian flags and the Get -// function pointers. Returns the 0th IFD offset. - -XMP_Uns32 TIFF_Manager::CheckTIFFHeader ( const XMP_Uns8* tiffPtr, XMP_Uns32 length ) -{ - if ( length < kEmptyTIFFLength ) XMP_Throw ( "The TIFF is too small", kXMPErr_BadTIFF ); - - XMP_Uns32 tiffPrefix = (tiffPtr[0] << 24) | (tiffPtr[1] << 16) | (tiffPtr[2] << 8) | (tiffPtr[3]); - - if ( tiffPrefix == kBigEndianPrefix ) { - this->bigEndian = true; - } else if ( tiffPrefix == kLittleEndianPrefix ) { - this->bigEndian = false; - } else { - XMP_Throw ( "Unrecognized TIFF prefix", kXMPErr_BadTIFF ); - } - - this->nativeEndian = (this->bigEndian == kBigEndianHost); - - if ( this->bigEndian ) { - - this->GetUns16 = GetUns16BE; - this->GetUns32 = GetUns32BE; - this->GetFloat = GetFloatBE; - this->GetDouble = GetDoubleBE; - - this->PutUns16 = PutUns16BE; - this->PutUns32 = PutUns32BE; - this->PutFloat = PutFloatBE; - this->PutDouble = PutDoubleBE; - - } else { - - this->GetUns16 = GetUns16LE; - this->GetUns32 = GetUns32LE; - this->GetFloat = GetFloatLE; - this->GetDouble = GetDoubleLE; - - this->PutUns16 = PutUns16LE; - this->PutUns32 = PutUns32LE; - this->PutFloat = PutFloatLE; - this->PutDouble = PutDoubleLE; - - } - - XMP_Uns32 mainIFDOffset = this->GetUns32 ( tiffPtr+4 ); // ! Do this after setting the Get/Put procs! - - if ( mainIFDOffset != 0 ) { // Tolerate empty TIFF even though formally invalid. - if ( (length < (kEmptyTIFFLength + kEmptyIFDLength)) || - (mainIFDOffset < kEmptyTIFFLength) || (mainIFDOffset > (length - kEmptyIFDLength)) ) { - XMP_Throw ( "Invalid primary IFD offset", kXMPErr_BadTIFF ); - } - } - - return mainIFDOffset; - -} // TIFF_Manager::CheckTIFFHeader - -// ================================================================================================= -// TIFF_Manager::SetTag_Integer -// ============================ - -void TIFF_Manager::SetTag_Integer ( XMP_Uns8 ifd, XMP_Uns16 id, XMP_Uns32 data32 ) -{ - - if ( data32 > 0xFFFF ) { - this->SetTag ( ifd, id, kTIFF_LongType, 1, &data32 ); - } else { - XMP_Uns16 data16 = (XMP_Uns16)data32; - this->SetTag ( ifd, id, kTIFF_ShortType, 1, &data16 ); - } - -} // TIFF_Manager::SetTag_Integer - -// ================================================================================================= -// TIFF_Manager::SetTag_Byte -// ========================= - -void TIFF_Manager::SetTag_Byte ( XMP_Uns8 ifd, XMP_Uns16 id, XMP_Uns8 data ) -{ - - this->SetTag ( ifd, id, kTIFF_ByteType, 1, &data ); - -} // TIFF_Manager::SetTag_Byte - -// ================================================================================================= -// TIFF_Manager::SetTag_SByte -// ========================== - -void TIFF_Manager::SetTag_SByte ( XMP_Uns8 ifd, XMP_Uns16 id, XMP_Int8 data ) -{ - - this->SetTag ( ifd, id, kTIFF_SByteType, 1, &data ); - -} // TIFF_Manager::SetTag_SByte - -// ================================================================================================= -// TIFF_Manager::SetTag_Short -// ========================== - -void TIFF_Manager::SetTag_Short ( XMP_Uns8 ifd, XMP_Uns16 id, XMP_Uns16 clientData ) -{ - XMP_Uns16 streamData; - - this->PutUns16 ( clientData, &streamData ); - this->SetTag ( ifd, id, kTIFF_ShortType, 1, &streamData ); - -} // TIFF_Manager::SetTag_Short - -// ================================================================================================= -// TIFF_Manager::SetTag_SShort -// =========================== - - void TIFF_Manager::SetTag_SShort ( XMP_Uns8 ifd, XMP_Uns16 id, XMP_Int16 clientData ) -{ - XMP_Int16 streamData; - - this->PutUns16 ( (XMP_Uns16)clientData, &streamData ); - this->SetTag ( ifd, id, kTIFF_SShortType, 1, &streamData ); - -} // TIFF_Manager::SetTag_SShort - -// ================================================================================================= -// TIFF_Manager::SetTag_Long -// ========================= - - void TIFF_Manager::SetTag_Long ( XMP_Uns8 ifd, XMP_Uns16 id, XMP_Uns32 clientData ) -{ - XMP_Uns32 streamData; - - this->PutUns32 ( clientData, &streamData ); - this->SetTag ( ifd, id, kTIFF_LongType, 1, &streamData ); - -} // TIFF_Manager::SetTag_Long - -// ================================================================================================= -// TIFF_Manager::SetTag_SLong -// ========================== - - void TIFF_Manager::SetTag_SLong ( XMP_Uns8 ifd, XMP_Uns16 id, XMP_Int32 clientData ) -{ - XMP_Int32 streamData; - - this->PutUns32 ( (XMP_Uns32)clientData, &streamData ); - this->SetTag ( ifd, id, kTIFF_SLongType, 1, &streamData ); - -} // TIFF_Manager::SetTag_SLong - -// ================================================================================================= -// TIFF_Manager::SetTag_Rational -// ============================= - -struct StreamRational { XMP_Uns32 num, denom; }; - - void TIFF_Manager::SetTag_Rational ( XMP_Uns8 ifd, XMP_Uns16 id, XMP_Uns32 clientNum, XMP_Uns32 clientDenom ) -{ - StreamRational streamData; - - this->PutUns32 ( clientNum, &streamData.num ); - this->PutUns32 ( clientDenom, &streamData.denom ); - this->SetTag ( ifd, id, kTIFF_RationalType, 1, &streamData ); - -} // TIFF_Manager::SetTag_Rational - -// ================================================================================================= -// TIFF_Manager::SetTag_SRational -// ============================== - - void TIFF_Manager::SetTag_SRational ( XMP_Uns8 ifd, XMP_Uns16 id, XMP_Int32 clientNum, XMP_Int32 clientDenom ) -{ - StreamRational streamData; - - this->PutUns32 ( (XMP_Uns32)clientNum, &streamData.num ); - this->PutUns32 ( (XMP_Uns32)clientDenom, &streamData.denom ); - this->SetTag ( ifd, id, kTIFF_SRationalType, 1, &streamData ); - -} // TIFF_Manager::SetTag_SRational - -// ================================================================================================= -// TIFF_Manager::SetTag_Float -// ========================== - - void TIFF_Manager::SetTag_Float ( XMP_Uns8 ifd, XMP_Uns16 id, float clientData ) -{ - float streamData; - - this->PutFloat ( clientData, &streamData ); - this->SetTag ( ifd, id, kTIFF_FloatType, 1, &streamData ); - -} // TIFF_Manager::SetTag_Float - -// ================================================================================================= -// TIFF_Manager::SetTag_Double -// =========================== - - void TIFF_Manager::SetTag_Double ( XMP_Uns8 ifd, XMP_Uns16 id, double clientData ) -{ - double streamData; - - this->PutDouble ( clientData, &streamData ); - this->SetTag ( ifd, id, kTIFF_DoubleType, 1, &streamData ); - -} // TIFF_Manager::SetTag_Double - -// ================================================================================================= -// TIFF_Manager::SetTag_ASCII -// =========================== - -void TIFF_Manager::SetTag_ASCII ( XMP_Uns8 ifd, XMP_Uns16 id, XMP_StringPtr data ) -{ - - this->SetTag ( ifd, id, kTIFF_ASCIIType, (XMP_Uns32)(strlen(data) + 1), data ); // ! Include trailing nul. - -} // TIFF_Manager::SetTag_ASCII - -// ================================================================================================= -// UTF16_to_UTF8 -// ============= - -static void UTF16_to_UTF8 ( const UTF16Unit * utf16Ptr, size_t utf16Len, bool bigEndian, std::string * outStr ) -{ - UTF16_to_UTF8_Proc ToUTF8 = 0; - if ( bigEndian ) { - ToUTF8 = UTF16BE_to_UTF8; - } else { - ToUTF8 = UTF16LE_to_UTF8; - } - - UTF8Unit buffer [1000]; - size_t inCount, outCount; - - outStr->erase(); - outStr->reserve ( utf16Len * 2 ); // As good a guess as any. - - while ( utf16Len > 0 ) { - ToUTF8 ( utf16Ptr, utf16Len, buffer, sizeof(buffer), &inCount, &outCount ); - outStr->append ( (XMP_StringPtr)buffer, outCount ); - utf16Ptr += inCount; - utf16Len -= inCount; - } - -} // UTF16_to_UTF8 - -// ================================================================================================= -// TIFF_Manager::DecodeString -// ========================== -// -// Convert an explicitly encoded string to UTF-8. The input must be encoded according to table 6 of -// the Exif 2.2 specification. The input pointer is to the start of the 8 byte header, the length is -// the full length. In other words, the pointer and length from the IFD entry. Returns true if the -// encoding is supported and the conversion succeeds. - -// *** JIS encoding is not supported yet. Need a static mapping table from JIS X 208-1990 to Unicode. - -bool TIFF_Manager::DecodeString ( const void * encodedPtr, size_t encodedLen, std::string* utf8Str ) const -{ - XMP_StringPtr typePtr = (XMP_StringPtr)encodedPtr; - XMP_StringPtr valuePtr = typePtr + 8; - size_t valueLen = encodedLen - 8; - - utf8Str->erase(); - - if ( *typePtr == 'A' ) { - - utf8Str->assign ( valuePtr, valueLen ); - return true; - - } else if ( *typePtr == 'U' ) { - - try { - - const UTF16Unit * utf16Ptr = (const UTF16Unit *) valuePtr; - size_t utf16Len = valueLen >> 1; // The number of UTF-16 storage units, not bytes. - if ( utf16Len == 0 ) return false; - bool isBigEndian = this->bigEndian; // Default to stream endian, unless there is a BOM ... - if ( (*utf16Ptr == 0xFEFF) || (*utf16Ptr == 0xFFFE) ) { // Check for an explicit BOM - isBigEndian = (*((XMP_Uns8*)utf16Ptr) == 0xFE); - utf16Ptr += 1; // Don't translate the BOM. - utf16Len -= 1; - if ( utf16Len == 0 ) return false; - } - UTF16_to_UTF8 ( utf16Ptr, utf16Len, isBigEndian, utf8Str ); - return true; - - } catch ( ... ) { - return false; // Ignore the tag if there are conversion errors. - } - - } else if ( *typePtr == 'J' ) { - - return false; // ! Ignore JIS for now. - - } - - return false; // ! Ignore all other encodings. - -} // TIFF_Manager::DecodeString - -// ================================================================================================= -// UTF8_to_UTF16 -// ============= - -static void UTF8_to_UTF16 ( const UTF8Unit * utf8Ptr, size_t utf8Len, bool bigEndian, std::string * outStr ) -{ - UTF8_to_UTF16_Proc ToUTF16 = 0; - if ( bigEndian ) { - ToUTF16 = UTF8_to_UTF16BE; - } else { - ToUTF16 = UTF8_to_UTF16LE; - } - - enum { kUTF16Len = 1000 }; - UTF16Unit buffer [kUTF16Len]; - size_t inCount, outCount; - - outStr->erase(); - outStr->reserve ( utf8Len * 2 ); // As good a guess as any. - - while ( utf8Len > 0 ) { - ToUTF16 ( utf8Ptr, utf8Len, buffer, kUTF16Len, &inCount, &outCount ); - outStr->append ( (XMP_StringPtr)buffer, outCount*2 ); - utf8Ptr += inCount; - utf8Len -= inCount; - } - -} // UTF8_to_UTF16 - -// ================================================================================================= -// TIFF_Manager::EncodeString -// ========================== -// -// Convert a UTF-8 string to an explicitly encoded form according to table 6 of the Exif 2.2 -// specification. Returns true if the encoding is supported and the conversion succeeds. - -// *** JIS encoding is not supported yet. Need a static mapping table to JIS X 208-1990 from Unicode. - -bool TIFF_Manager::EncodeString ( const std::string& utf8Str, XMP_Uns8 encoding, std::string* encodedStr ) -{ - encodedStr -> erase(); - - if ( encoding == kTIFF_EncodeASCII ) { - - encodedStr->assign ( "ASCII\0\0\0", 8 ); - XMP_Assert (encodedStr->size() == 8 ); - - encodedStr->append ( utf8Str ); // ! Let the caller filter the value. (?) - - return true; - - } else if ( encoding == kTIFF_EncodeUnicode ) { - - encodedStr->assign ( "UNICODE\0", 8 ); - XMP_Assert (encodedStr->size() == 8 ); - - try { - std::string temp; - UTF8_to_UTF16 ( (const UTF8Unit*)utf8Str.c_str(), utf8Str.size(), this->bigEndian, &temp ); - encodedStr->append ( temp ); - return true; - } catch ( ... ) { - return false; // Ignore the tag if there are conversion errors. - } - - } else if ( encoding == kTIFF_EncodeJIS ) { - - XMP_Throw ( "Encoding to JIS is not implemented", kXMPErr_Unimplemented ); - - // encodedStr->assign ( "JIS\0\0\0\0\0", 8 ); - // XMP_Assert (encodedStr->size() == 8 ); - - // ... - - // return true; - - } else { - - XMP_Throw ( "Invalid TIFF string encoding", kXMPErr_BadParam ); - - } - - return false; // ! Should never get here. - -} // TIFF_Manager::EncodeString - -// ================================================================================================= diff --git a/source/XMPFiles/FormatSupport/TIFF_Support.hpp b/source/XMPFiles/FormatSupport/TIFF_Support.hpp deleted file mode 100644 index 94f6198..0000000 --- a/source/XMPFiles/FormatSupport/TIFF_Support.hpp +++ /dev/null @@ -1,915 +0,0 @@ -#ifndef __TIFF_Support_hpp__ -#define __TIFF_Support_hpp__ 1 - -// ================================================================================================= -// ADOBE SYSTEMS INCORPORATED -// Copyright 2006 Adobe Systems Incorporated -// 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 "XMP_Environment.h" // ! This must be the first include. - -#include <map> -#include <stdlib.h> -#include <string.h> - -#include "XMP_Const.h" -#include "XMPFiles_Impl.hpp" -#include "EndianUtils.hpp" - -// ================================================================================================= -/// \file TIFF_Support.hpp -/// \brief XMPFiles support for TIFF streams. -/// -/// This header provides TIFF stream support specific to the needs of XMPFiles. This is not intended -/// for general purpose TIFF processing. TIFF_Manager is an abstract base class with 2 concrete -/// derived classes, TIFF_MemoryReader and TIFF_FileWriter. -/// -/// TIFF_MemoryReader provides read-only support for TIFF streams that are small enough to be kept -/// entirely in memory. This allows optimizations to reduce heap usage and processing code. It is -/// sufficient for browsing access to the Exif metadata in JPEG and Photoshop files. Think of -/// TIFF_MemoryReader as "memory-based AND read-only". Since the entire TIFF stream is available, -/// GetTag will return information about any tag in the stream. -/// -/// TIFF_FileWriter is for cases where updates are needed or the TIFF stream is too large to be kept -/// entirely in memory. Think of TIFF_FileWriter as "file-based OR read-write". TIFF_FileWriter only -/// maintains information for tags of interest as metadata. -/// -/// The needs of XMPFiles are well defined metadata access. Only 4 IFDs are processed: -/// \li The 0th IFD, for the primary image, the first one in the outer list of IFDs. -/// \li The Exif general metadata IFD, from tag 34665 in the primary image IFD. -/// \li The Exif GPS Info metadata IFD, from tag 34853 in the primary image IFD. -/// \li The Exif Interoperability IFD, from tag 40965 in the Exif general metadata IFD. -/// -/// \note These classes are for use only when directly compiled and linked. They should not be -/// packaged in a DLL by themselves. They do not provide any form of C++ ABI protection. -// ================================================================================================= - - -// ================================================================================================= -// TIFF IFD and type constants -// =========================== -// -// These aren't inside TIFF_Manager because static data members can't be initialized there. - -enum { // Constants for the recognized IFDs. - kTIFF_PrimaryIFD = 0, // The primary image IFD, also called the 0th IFD. - kTIFF_TNailIFD = 1, // The thumbnail image IFD also called the 1st IFD. (not used) - kTIFF_ExifIFD = 2, // The Exif general metadata IFD. - kTIFF_GPSInfoIFD = 3, // The Exif GPS Info IFD. - kTIFF_InteropIFD = 4, // The Exif Interoperability IFD. - kTIFF_LastRealIFD = 4, - kTIFF_KnownIFDCount = 5, - kTIFF_KnownIFD = 9 // The IFD that a tag is known to belong in. -}; - -enum { // Constants for the type field of a tag, as defined by TIFF. - kTIFF_ShortOrLongType = 0, // ! Not part of the TIFF spec, never in a tag! - kTIFF_ByteType = 1, - kTIFF_ASCIIType = 2, - kTIFF_ShortType = 3, - kTIFF_LongType = 4, - kTIFF_RationalType = 5, - kTIFF_SByteType = 6, - kTIFF_UndefinedType = 7, - kTIFF_SShortType = 8, - kTIFF_SLongType = 9, - kTIFF_SRationalType = 10, - kTIFF_FloatType = 11, - kTIFF_DoubleType = 12, - kTIFF_LastType = 12 -}; - -static const size_t kTIFF_TypeSizes[] = { 0, 1, 1, 2, 4, 8, 1, 1, 2, 4, 8, 4, 8 }; - -static const char * kTIFF_TypeNames[] = { "ShortOrLong", "BYTE", "ASCII", "SHORT", "LONG", "RATIONAL", - "SBYTE", "UNDEFINED", "SSHORT", "SLONG", "SRATIONAL", - "FLOAT", "DOUBLE" }; - -enum { // Encodings for SetTag_EncodedString. - kTIFF_EncodeUndefined = 0, - kTIFF_EncodeASCII = 1, - kTIFF_EncodeUnicode = 2, // UTF-16 in the endianness of the TIFF stream. - kTIFF_EncodeJIS = 3, // Exif 2.2 uses JIS X 208-1990. - kTIFF_EncodeUnknown = 9 -}; - -// ================================================================================================= -// Recognized TIFF tags -// ==================== - -// --------------------------------------------------------------------------- -// An enum of IDs for all of the tags as potential interest as metadata. - -enum { - - // General 0th IFD tags. Some of these can also be in the thumbnail IFD. - - kTIFF_ImageWidth = 256, - kTIFF_ImageLength = 257, - kTIFF_BitsPerSample = 258, - kTIFF_Compression = 259, - kTIFF_PhotometricInterpretation = 262, - kTIFF_ImageDescription = 270, - kTIFF_Make = 271, - kTIFF_Model = 272, - kTIFF_Orientation = 274, - kTIFF_SamplesPerPixel = 277, - kTIFF_XResolution = 282, - kTIFF_YResolution = 283, - kTIFF_PlanarConfiguration = 284, - kTIFF_ResolutionUnit = 296, - kTIFF_TransferFunction = 301, - kTIFF_Software = 305, - kTIFF_DateTime = 306, - kTIFF_Artist = 315, - kTIFF_WhitePoint = 318, - kTIFF_PrimaryChromaticities = 319, - kTIFF_YCbCrCoefficients = 529, - kTIFF_YCbCrSubSampling = 530, - kTIFF_YCbCrPositioning = 531, - kTIFF_ReferenceBlackWhite = 532, - kTIFF_XMP = 700, - kTIFF_Copyright = 33432, - kTIFF_IPTC = 33723, - kTIFF_PSIR = 34377, - kTIFF_ExifIFDPointer = 34665, - kTIFF_GPSInfoIFDPointer = 34853, - kTIFF_DNGVersion = 50706, - kTIFF_DNGBackwardVersion = 50707, - - // Additional thumbnail IFD tags. We also care about 256, 257, and 259 in thumbnails. - kTIFF_JPEGInterchangeFormat = 513, - kTIFF_JPEGInterchangeFormatLength = 514, - - // Additional tags that need special handling when rewriting memory-based TIFF. - kTIFF_StripOffsets = 273, - kTIFF_StripByteCounts = 279, - kTIFF_FreeOffsets = 288, - kTIFF_FreeByteCounts = 289, - kTIFF_TileOffsets = 324, - kTIFF_TileByteCounts = 325, - kTIFF_SubIFDs = 330, - kTIFF_JPEGQTables = 519, - kTIFF_JPEGDCTables = 520, - kTIFF_JPEGACTables = 521, - - // Exif IFD tags. - - kTIFF_ExposureTime = 33434, - kTIFF_FNumber = 33437, - kTIFF_ExposureProgram = 34850, - kTIFF_SpectralSensitivity = 34852, - kTIFF_ISOSpeedRatings = 34855, - kTIFF_OECF = 34856, - kTIFF_ExifVersion = 36864, - kTIFF_DateTimeOriginal = 36867, - kTIFF_DateTimeDigitized = 36868, - kTIFF_ComponentsConfiguration = 37121, - kTIFF_CompressedBitsPerPixel = 37122, - kTIFF_ShutterSpeedValue = 37377, - kTIFF_ApertureValue = 37378, - kTIFF_BrightnessValue = 37379, - kTIFF_ExposureBiasValue = 37380, - kTIFF_MaxApertureValue = 37381, - kTIFF_SubjectDistance = 37382, - kTIFF_MeteringMode = 37383, - kTIFF_LightSource = 37384, - kTIFF_Flash = 37385, - kTIFF_FocalLength = 37386, - kTIFF_SubjectArea = 37396, - kTIFF_UserComment = 37510, - kTIFF_SubSecTime = 37520, - kTIFF_SubSecTimeOriginal = 37521, - kTIFF_SubSecTimeDigitized = 37522, - kTIFF_FlashpixVersion = 40960, - kTIFF_ColorSpace = 40961, - kTIFF_PixelXDimension = 40962, - kTIFF_PixelYDimension = 40963, - kTIFF_RelatedSoundFile = 40964, - kTIFF_InteroperabilityIFDPointer = 40965, - kTIFF_FlashEnergy = 41483, - kTIFF_SpatialFrequencyResponse = 41484, - kTIFF_FocalPlaneXResolution = 41486, - kTIFF_FocalPlaneYResolution = 41487, - kTIFF_FocalPlaneResolutionUnit = 41488, - kTIFF_SubjectLocation = 41492, - kTIFF_ExposureIndex = 41493, - kTIFF_SensingMethod = 41495, - kTIFF_FileSource = 41728, - kTIFF_SceneType = 41729, - kTIFF_CFAPattern = 41730, - kTIFF_CustomRendered = 41985, - kTIFF_ExposureMode = 41986, - kTIFF_WhiteBalance = 41987, - kTIFF_DigitalZoomRatio = 41988, - kTIFF_FocalLengthIn35mmFilm = 41989, - kTIFF_SceneCaptureType = 41990, - kTIFF_GainControl = 41991, - kTIFF_Contrast = 41992, - kTIFF_Saturation = 41993, - kTIFF_Sharpness = 41994, - kTIFF_DeviceSettingDescription = 41995, - kTIFF_SubjectDistanceRange = 41996, - kTIFF_ImageUniqueID = 42016, - - kTIFF_MakerNote = 37500, // Gets deleted when rewriting memory-based TIFF. - - // GPS IFD tags. - - kTIFF_GPSVersionID = 0, - kTIFF_GPSLatitudeRef = 1, - kTIFF_GPSLatitude = 2, - kTIFF_GPSLongitudeRef = 3, - kTIFF_GPSLongitude = 4, - kTIFF_GPSAltitudeRef = 5, - kTIFF_GPSAltitude = 6, - kTIFF_GPSTimeStamp = 7, - kTIFF_GPSSatellites = 8, - kTIFF_GPSStatus = 9, - kTIFF_GPSMeasureMode = 10, - kTIFF_GPSDOP = 11, - kTIFF_GPSSpeedRef = 12, - kTIFF_GPSSpeed = 13, - kTIFF_GPSTrackRef = 14, - kTIFF_GPSTrack = 15, - kTIFF_GPSImgDirectionRef = 16, - kTIFF_GPSImgDirection = 17, - kTIFF_GPSMapDatum = 18, - kTIFF_GPSDestLatitudeRef = 19, - kTIFF_GPSDestLatitude = 20, - kTIFF_GPSDestLongitudeRef = 21, - kTIFF_GPSDestLongitude = 22, - kTIFF_GPSDestBearingRef = 23, - kTIFF_GPSDestBearing = 24, - kTIFF_GPSDestDistanceRef = 25, - kTIFF_GPSDestDistance = 26, - kTIFF_GPSProcessingMethod = 27, - kTIFF_GPSAreaInformation = 28, - kTIFF_GPSDateStamp = 29, - kTIFF_GPSDifferential = 30 - -}; - -// ------------------------------------------------------------------ -// Sorted arrays of the tags that are recognized in the various IFDs. - -static const XMP_Uns16 sKnownPrimaryIFDTags[] = -{ - kTIFF_ImageWidth, // 256 - kTIFF_ImageLength, // 257 - kTIFF_BitsPerSample, // 258 - kTIFF_Compression, // 259 - kTIFF_PhotometricInterpretation, // 262 - kTIFF_ImageDescription, // 270 - kTIFF_Make, // 271 - kTIFF_Model, // 272 - kTIFF_Orientation, // 274 - kTIFF_SamplesPerPixel, // 277 - kTIFF_XResolution, // 282 - kTIFF_YResolution, // 283 - kTIFF_PlanarConfiguration, // 284 - kTIFF_ResolutionUnit, // 296 - kTIFF_TransferFunction, // 301 - kTIFF_Software, // 305 - kTIFF_DateTime, // 306 - kTIFF_Artist, // 315 - kTIFF_WhitePoint, // 318 - kTIFF_PrimaryChromaticities, // 319 - kTIFF_YCbCrCoefficients, // 529 - kTIFF_YCbCrSubSampling, // 530 - kTIFF_YCbCrPositioning, // 531 - kTIFF_ReferenceBlackWhite, // 532 - kTIFF_XMP, // 700 - kTIFF_Copyright, // 33432 - kTIFF_IPTC, // 33723 - kTIFF_PSIR, // 34377 - kTIFF_ExifIFDPointer, // 34665 - kTIFF_GPSInfoIFDPointer, // 34853 - kTIFF_DNGVersion, // 50706 - kTIFF_DNGBackwardVersion, // 50707 - 0xFFFF // Must be last as a sentinel. -}; - -static const XMP_Uns16 sKnownThumbnailIFDTags[] = -{ - kTIFF_ImageWidth, // 256 - kTIFF_ImageLength, // 257 - kTIFF_Compression, // 259 - kTIFF_JPEGInterchangeFormat, // 513 - kTIFF_JPEGInterchangeFormatLength, // 514 - 0xFFFF // Must be last as a sentinel. -}; - -static const XMP_Uns16 sKnownExifIFDTags[] = -{ - kTIFF_ExposureTime, // 33434 - kTIFF_FNumber, // 33437 - kTIFF_ExposureProgram, // 34850 - kTIFF_SpectralSensitivity, // 34852 - kTIFF_ISOSpeedRatings, // 34855 - kTIFF_OECF, // 34856 - kTIFF_ExifVersion, // 36864 - kTIFF_DateTimeOriginal, // 36867 - kTIFF_DateTimeDigitized, // 36868 - kTIFF_ComponentsConfiguration, // 37121 - kTIFF_CompressedBitsPerPixel, // 37122 - kTIFF_ShutterSpeedValue, // 37377 - kTIFF_ApertureValue, // 37378 - kTIFF_BrightnessValue, // 37379 - kTIFF_ExposureBiasValue, // 37380 - kTIFF_MaxApertureValue, // 37381 - kTIFF_SubjectDistance, // 37382 - kTIFF_MeteringMode, // 37383 - kTIFF_LightSource, // 37384 - kTIFF_Flash, // 37385 - kTIFF_FocalLength, // 37386 - kTIFF_SubjectArea, // 37396 - kTIFF_UserComment, // 37510 - kTIFF_SubSecTime, // 37520 - kTIFF_SubSecTimeOriginal, // 37521 - kTIFF_SubSecTimeDigitized, // 37522 - kTIFF_FlashpixVersion, // 40960 - kTIFF_ColorSpace, // 40961 - kTIFF_PixelXDimension, // 40962 - kTIFF_PixelYDimension, // 40963 - kTIFF_RelatedSoundFile, // 40964 - kTIFF_FlashEnergy, // 41483 - kTIFF_SpatialFrequencyResponse, // 41484 - kTIFF_FocalPlaneXResolution, // 41486 - kTIFF_FocalPlaneYResolution, // 41487 - kTIFF_FocalPlaneResolutionUnit, // 41488 - kTIFF_SubjectLocation, // 41492 - kTIFF_ExposureIndex, // 41493 - kTIFF_SensingMethod, // 41495 - kTIFF_FileSource, // 41728 - kTIFF_SceneType, // 41729 - kTIFF_CFAPattern, // 41730 - kTIFF_CustomRendered, // 41985 - kTIFF_ExposureMode, // 41986 - kTIFF_WhiteBalance, // 41987 - kTIFF_DigitalZoomRatio, // 41988 - kTIFF_FocalLengthIn35mmFilm, // 41989 - kTIFF_SceneCaptureType, // 41990 - kTIFF_GainControl, // 41991 - kTIFF_Contrast, // 41992 - kTIFF_Saturation, // 41993 - kTIFF_Sharpness, // 41994 - kTIFF_DeviceSettingDescription, // 41995 - kTIFF_SubjectDistanceRange, // 41996 - kTIFF_ImageUniqueID, // 42016 - 0xFFFF // Must be last as a sentinel. -}; - -static const XMP_Uns16 sKnownGPSInfoIFDTags[] = -{ - kTIFF_GPSVersionID, // 0 - kTIFF_GPSLatitudeRef, // 1 - kTIFF_GPSLatitude, // 2 - kTIFF_GPSLongitudeRef, // 3 - kTIFF_GPSLongitude, // 4 - kTIFF_GPSAltitudeRef, // 5 - kTIFF_GPSAltitude, // 6 - kTIFF_GPSTimeStamp, // 7 - kTIFF_GPSSatellites, // 8 - kTIFF_GPSStatus, // 9 - kTIFF_GPSMeasureMode, // 10 - kTIFF_GPSDOP, // 11 - kTIFF_GPSSpeedRef, // 12 - kTIFF_GPSSpeed, // 13 - kTIFF_GPSTrackRef, // 14 - kTIFF_GPSTrack, // 15 - kTIFF_GPSImgDirectionRef, // 16 - kTIFF_GPSImgDirection, // 17 - kTIFF_GPSMapDatum, // 18 - kTIFF_GPSDestLatitudeRef, // 19 - kTIFF_GPSDestLatitude, // 20 - kTIFF_GPSDestLongitudeRef, // 21 - kTIFF_GPSDestLongitude, // 22 - kTIFF_GPSDestBearingRef, // 23 - kTIFF_GPSDestBearing, // 24 - kTIFF_GPSDestDistanceRef, // 25 - kTIFF_GPSDestDistance, // 26 - kTIFF_GPSProcessingMethod, // 27 - kTIFF_GPSAreaInformation, // 28 - kTIFF_GPSDateStamp, // 29 - kTIFF_GPSDifferential, // 30 - 0xFFFF // Must be last as a sentinel. -}; - -static const XMP_Uns16 sKnownInteroperabilityIFDTags[] = -{ - // ! Yes, there are none at present. - 0xFFFF // Must be last as a sentinel. -}; - -// ! Make sure these are in the same order as the IFD enum! -static const XMP_Uns16* sKnownTags[kTIFF_KnownIFDCount] = { sKnownPrimaryIFDTags, - sKnownThumbnailIFDTags, - sKnownExifIFDTags, - sKnownGPSInfoIFDTags, - sKnownInteroperabilityIFDTags }; - - -// ================================================================================================= -// ================================================================================================= - - -// ================================================================================================= -// TIFF_Manager -// ============ - -class TIFF_Manager { // The abstract base class. -public: - - // --------------------------------------------------------------------------------------------- - // Types and constants - - static const XMP_Uns32 kBigEndianPrefix = 0x4D4D002AUL; - static const XMP_Uns32 kLittleEndianPrefix = 0x49492A00UL; - - static const size_t kEmptyTIFFLength = 8; // Just the header. - static const size_t kEmptyIFDLength = 2 + 4; // Entry count and next-IFD offset. - static const size_t kIFDEntryLength = 12; - - struct TagInfo { - XMP_Uns16 id; - XMP_Uns16 type; - XMP_Uns32 count; - const void* dataPtr; // ! The data must not be changed! Stream endian format! - XMP_Uns32 dataLen; // The length in bytes. - TagInfo() : id(0), type(0), count(0), dataPtr(0), dataLen(0) {}; - TagInfo ( XMP_Uns16 _id, XMP_Uns16 _type, XMP_Uns32 _count, const void* _dataPtr, XMP_Uns32 _dataLen ) - : id(_id), type(_type), count(_count), dataPtr(_dataPtr), dataLen(_dataLen) {}; - }; - - typedef std::map<XMP_Uns16,TagInfo> TagInfoMap; - - struct Rational { XMP_Uns32 num, denom; }; - struct SRational { XMP_Int32 num, denom; }; - - // --------------------------------------------------------------------------------------------- - // The IsXyzEndian methods return the external endianness of the original parsed TIFF stream. - // The \c GetTag methods return native endian values, the \c SetTag methods take native values. - // The original endianness is preserved in output. - - bool IsBigEndian() const { return this->bigEndian; }; - bool IsLittleEndian() const { return (! this->bigEndian); }; - bool IsNativeEndian() const { return this->nativeEndian; }; - - // --------------------------------------------------------------------------------------------- - // The TIFF_Manager only keeps explicit knowledge of up to 4 IFDs: - // - The primary image IFD, also known as the 0th IFD. This must be present. - // - A possible Exif general metadata IFD, found from tag 34665 in the primary image IFD. - // - A possible Exif GPS metadata IFD, found from tag 34853 in the primary image IFD. - // - A possible Exif Interoperability IFD, found from tag 40965 in the Exif general metadata IFD. - // - // Parsing will silently forget about certain aspects of ill-formed streams. If any tags are - // repeated in an IFD, only the last is kept. Any known tags that are in the wrong IFD are - // removed. Parsing will sort the tags into ascending order, AppendTIFF and ComposeTIFF will - // preserve the sorted order. These fixes do not cause IsChanged to return true, that only - // happens if the client makes explicit changes using SetTag or DeleteTag. - - virtual bool HasExifIFD() const = 0; - virtual bool HasGPSInfoIFD() const = 0; - - // --------------------------------------------------------------------------------------------- - // These are the basic methods to get a map of all of the tags in an IFD, to get or set a tag, - // or to delete a tag. The dataPtr returned by \c GetTag is consided read-only, the client must - // not change it. The ifd parameter to \c GetIFD must be for one of the recognized actual IFDs. - // The ifd parameter for the GetTag or SetTag methods can be a specific IFD of kTIFF_KnownIFD. - // Using the specific IFD will be slightly faster, saving a lookup in the known tag map. An - // exception is thrown if kTIFF_KnownIFD is passed to GetTag or SetTag and the tag is not known. - // \c SetTag replaces an existing tag regardless of type or count. \c DeleteTag deletes a tag, - // it is a no-op if the tag does not exist. \c GetValueOffset returns the offset within the - // parsed stream of the tag's value. It returns 0 if the tag was not in the parsed input. - - virtual bool GetIFD ( XMP_Uns8 ifd, TagInfoMap* ifdMap ) const = 0; - - virtual bool GetTag ( XMP_Uns8 ifd, XMP_Uns16 id, TagInfo* info ) const = 0; - - virtual void SetTag ( XMP_Uns8 ifd, XMP_Uns16 id, XMP_Uns16 type, XMP_Uns32 count, const void* dataPtr ) = 0; - - virtual void DeleteTag ( XMP_Uns8 ifd, XMP_Uns16 id ) = 0; - - virtual XMP_Uns32 GetValueOffset ( XMP_Uns8 ifd, XMP_Uns16 id ) const = 0; - - // --------------------------------------------------------------------------------------------- - // These methods are for tags whose type can be short or long, depending on the actual value. - // \c GetTag_Integer returns false if an existing tag's type is not short, or long, or if the - // count is not 1. \c SetTag_Integer replaces an existing tag regardless of type or count, the - // new tag will have type short or long. - - virtual bool GetTag_Integer ( XMP_Uns8 ifd, XMP_Uns16 id, XMP_Uns32* data ) const = 0; - - void SetTag_Integer ( XMP_Uns8 ifd, XMP_Uns16 id, XMP_Uns32 data ); - - // --------------------------------------------------------------------------------------------- - // These are customized forms of GetTag that verify the type and return a typed value. False is - // returned if the type does not match or if the count is not 1. - - // *** Should also add support for ASCII multi-strings? - - virtual bool GetTag_Byte ( XMP_Uns8 ifd, XMP_Uns16 id, XMP_Uns8* data ) const = 0; - virtual bool GetTag_SByte ( XMP_Uns8 ifd, XMP_Uns16 id, XMP_Int8* data ) const = 0; - virtual bool GetTag_Short ( XMP_Uns8 ifd, XMP_Uns16 id, XMP_Uns16* data ) const = 0; - virtual bool GetTag_SShort ( XMP_Uns8 ifd, XMP_Uns16 id, XMP_Int16* data ) const = 0; - virtual bool GetTag_Long ( XMP_Uns8 ifd, XMP_Uns16 id, XMP_Uns32* data ) const = 0; - virtual bool GetTag_SLong ( XMP_Uns8 ifd, XMP_Uns16 id, XMP_Int32* data ) const = 0; - - virtual bool GetTag_Rational ( XMP_Uns8 ifd, XMP_Uns16 id, Rational* data ) const = 0; - virtual bool GetTag_SRational ( XMP_Uns8 ifd, XMP_Uns16 id, SRational* data ) const = 0; - - virtual bool GetTag_Float ( XMP_Uns8 ifd, XMP_Uns16 id, float* data ) const = 0; - virtual bool GetTag_Double ( XMP_Uns8 ifd, XMP_Uns16 id, double* data ) const = 0; - - virtual bool GetTag_ASCII ( XMP_Uns8 ifd, XMP_Uns16 id, XMP_StringPtr* dataPtr, XMP_StringLen* dataLen ) const = 0; - - // --------------------------------------------------------------------------------------------- - - void SetTag_Byte ( XMP_Uns8 ifd, XMP_Uns16 id, XMP_Uns8 data ); - void SetTag_SByte ( XMP_Uns8 ifd, XMP_Uns16 id, XMP_Int8 data ); - void SetTag_Short ( XMP_Uns8 ifd, XMP_Uns16 id, XMP_Uns16 data ); - void SetTag_SShort ( XMP_Uns8 ifd, XMP_Uns16 id, XMP_Int16 data ); - void SetTag_Long ( XMP_Uns8 ifd, XMP_Uns16 id, XMP_Uns32 data ); - void SetTag_SLong ( XMP_Uns8 ifd, XMP_Uns16 id, XMP_Int32 data ); - - void SetTag_Rational ( XMP_Uns8 ifd, XMP_Uns16 id, XMP_Uns32 num, XMP_Uns32 denom ); - void SetTag_SRational ( XMP_Uns8 ifd, XMP_Uns16 id, XMP_Int32 num, XMP_Int32 denom ); - - void SetTag_Float ( XMP_Uns8 ifd, XMP_Uns16 id, float data ); - void SetTag_Double ( XMP_Uns8 ifd, XMP_Uns16 id, double data ); - - void SetTag_ASCII ( XMP_Uns8 ifd, XMP_Uns16 id, XMP_StringPtr dataPtr ); - - // --------------------------------------------------------------------------------------------- - - virtual bool GetTag_EncodedString ( XMP_Uns8 ifd, XMP_Uns16 id, std::string* utf8Str ) const = 0; - virtual void SetTag_EncodedString ( XMP_Uns8 ifd, XMP_Uns16 id, const std::string& utf8Str, XMP_Uns8 encoding ) = 0; - - bool DecodeString ( const void * encodedPtr, size_t encodedLen, std::string* utf8Str ) const; - bool EncodeString ( const std::string& utf8Str, XMP_Uns8 encoding, std::string* encodedStr ); - - // --------------------------------------------------------------------------------------------- - // \c IsChanged returns true if a read-write stream has changes that need to be saved. This is - // only the case when a \c SetTag method has been called. It is not true for changes related to - // parsing normalization such as sorting of tags. \c IsChanged returns false for read-only streams. - - virtual bool IsChanged() = 0; - - // --------------------------------------------------------------------------------------------- - // \c IsLegacyChanged returns true if a read-write stream has changes that need to be saved to - // tags other than the XMP (tag 700). This only the case when a \c SetTag method has been - // called. It is not true for changes related to parsing normalization such as sorting of tags. - // \c IsLegacyChanged returns false for read-only streams. - - virtual bool IsLegacyChanged() = 0; - - // --------------------------------------------------------------------------------------------- - // \c UpdateMemoryStream is mainly applicable to memory-based read-write streams. It recomposes - // the memory stream to incorporate all changes. The new length and data pointer are returned. - // \c UpdateMemoryStream can be used with a read-only memory stream to get the raw stream info. - // - // \c UpdateFileStream updates file-based TIFF. The client must guarantee that the TIFF portion - // of the file matches that from the parse in the file-based constructor. Offsets saved from that - // parse must still be valid. The open file reference need not be the same, e.g. the client can - // be doing a crash-safe update into a temporary copy. - // - // Both \c UpdateMemoryStream and \c UpdateFileStream use an update-by-append model. Changes are - // written in-place where they fit, anything requiring growth is appended to the end and the old - // space is abandoned. The end for memory-based TIFF is the end of the data block, the end for - // file-based TIFF is the end of the file. This update-by-append model has the advantage of not - // perturbing any hidden offsets, a common feature of proprietary MakerNotes. - // - // The condenseStream parameter to UpdateMemoryStream can be used to rewrite the full stream - // instead of appending. This will discard any MakerNote tags and risks breaking offsets that - // are hidden. This can be necessary though to try to make the TIFF fit in a JPEG file. - - virtual void ParseMemoryStream ( const void* data, XMP_Uns32 length, bool copyData = true ) = 0; - virtual void ParseFileStream ( LFA_FileRef fileRef ) = 0; - - virtual void IntegrateFromPShop6 ( const void * buriedPtr, size_t buriedLen ) = 0; - - virtual XMP_Uns32 UpdateMemoryStream ( void** dataPtr, bool condenseStream = false ) = 0; - virtual void UpdateFileStream ( LFA_FileRef fileRef ) = 0; - - // --------------------------------------------------------------------------------------------- - - GetUns16_Proc GetUns16; // Get values from the TIFF stream. - GetUns32_Proc GetUns32; // Always native endian on the outside, stream endian in the stream. - GetFloat_Proc GetFloat; - GetDouble_Proc GetDouble; - - PutUns16_Proc PutUns16; // Put values into the TIFF stream. - PutUns32_Proc PutUns32; // Always native endian on the outside, stream endian in the stream. - PutFloat_Proc PutFloat; - PutDouble_Proc PutDouble; - - virtual ~TIFF_Manager() {}; - -protected: - - bool bigEndian, nativeEndian; - - XMP_Uns32 CheckTIFFHeader ( const XMP_Uns8* tiffPtr, XMP_Uns32 length ); - - TIFF_Manager(); // Force clients to use the reader or writer derived classes. - - struct RawIFDEntry { - XMP_Uns16 id; - XMP_Uns16 type; - XMP_Uns32 count; - XMP_Uns32 dataOrOffset; - }; - -}; // TIFF_Manager - - -// ================================================================================================= -// ================================================================================================= - - -// ================================================================================================= -// TIFF_MemoryReader -// ================= - -class TIFF_MemoryReader : public TIFF_Manager { // The derived class for memory-based read-only access. -public: - - bool HasExifIFD() const { return (containedIFDs[kTIFF_ExifIFD].count != 0); }; - bool HasGPSInfoIFD() const { return (containedIFDs[kTIFF_GPSInfoIFD].count != 0); }; - - bool GetIFD ( XMP_Uns8 ifd, TagInfoMap* ifdMap ) const; - - bool GetTag ( XMP_Uns8 ifd, XMP_Uns16 id, TagInfo* info ) const; - - void SetTag ( XMP_Uns8 ifd, XMP_Uns16 id, XMP_Uns16 type, XMP_Uns32 count, const void* dataPtr ) { NotAppropriate(); }; - - void DeleteTag ( XMP_Uns8 ifd, XMP_Uns16 id ) { NotAppropriate(); }; - - XMP_Uns32 GetValueOffset ( XMP_Uns8 ifd, XMP_Uns16 id ) const; - - bool GetTag_Integer ( XMP_Uns8 ifd, XMP_Uns16 id, XMP_Uns32* data ) const; - - bool GetTag_Byte ( XMP_Uns8 ifd, XMP_Uns16 id, XMP_Uns8* data ) const; - bool GetTag_SByte ( XMP_Uns8 ifd, XMP_Uns16 id, XMP_Int8* data ) const; - bool GetTag_Short ( XMP_Uns8 ifd, XMP_Uns16 id, XMP_Uns16* data ) const; - bool GetTag_SShort ( XMP_Uns8 ifd, XMP_Uns16 id, XMP_Int16* data ) const; - bool GetTag_Long ( XMP_Uns8 ifd, XMP_Uns16 id, XMP_Uns32* data ) const; - bool GetTag_SLong ( XMP_Uns8 ifd, XMP_Uns16 id, XMP_Int32* data ) const; - - bool GetTag_Rational ( XMP_Uns8 ifd, XMP_Uns16 id, Rational* data ) const; - bool GetTag_SRational ( XMP_Uns8 ifd, XMP_Uns16 id, SRational* data ) const; - - bool GetTag_Float ( XMP_Uns8 ifd, XMP_Uns16 id, float* data ) const; - bool GetTag_Double ( XMP_Uns8 ifd, XMP_Uns16 id, double* data ) const; - - bool GetTag_ASCII ( XMP_Uns8 ifd, XMP_Uns16 id, XMP_StringPtr* dataPtr, XMP_StringLen* dataLen ) const; - - bool GetTag_EncodedString ( XMP_Uns8 ifd, XMP_Uns16 id, std::string* utf8Str ) const; - - void SetTag_EncodedString ( XMP_Uns8 ifd, XMP_Uns16 id, const std::string& utf8Str, XMP_Uns8 encoding ) { NotAppropriate(); }; - - bool IsChanged() { return false; }; - bool IsLegacyChanged() { return false; }; - - void ParseMemoryStream ( const void* data, XMP_Uns32 length, bool copyData = true ); - void ParseFileStream ( LFA_FileRef fileRef ) { NotAppropriate(); }; - - void IntegrateFromPShop6 ( const void * buriedPtr, size_t buriedLen ) { NotAppropriate(); }; - - XMP_Uns32 UpdateMemoryStream ( void** dataPtr, bool condenseStream = false ) { if ( dataPtr != 0 ) *dataPtr = tiffStream; return tiffLength; }; - void UpdateFileStream ( LFA_FileRef fileRef ) { NotAppropriate(); }; - - TIFF_MemoryReader() : ownedStream(false), tiffStream(0), tiffLength(0) {}; - - virtual ~TIFF_MemoryReader() { if ( this->ownedStream ) free ( this->tiffStream ); }; - -private: - - bool ownedStream; - - XMP_Uns8* tiffStream; - XMP_Uns32 tiffLength; - - // Memory usage notes: TIFF_MemoryReader is for memory-based read-only usage (both apply). There - // is no need to ever allocate separate blocks of memory, everything is used directly from the - // TIFF stream. Data pointers are computed on the fly, the offset field is 4 bytes and pointers - // will be 8 bytes for 64-bit platforms. - - struct TweakedIFDEntry { // ! Most fields are in native byte order, dataOrPos is for offsets only. - XMP_Uns16 id; - XMP_Uns16 type; - XMP_Uns32 bytes; - XMP_Uns32 dataOrPos; - TweakedIFDEntry() : id(0), type(0), bytes(0), dataOrPos(0) {}; - }; - - struct TweakedIFDInfo { - XMP_Uns16 count; - TweakedIFDEntry* entries; - TweakedIFDInfo() : count(0), entries(0) {}; - }; - - TweakedIFDInfo containedIFDs[kTIFF_KnownIFDCount]; - - static void SortIFD ( TweakedIFDInfo* thisIFD ); - - XMP_Uns32 ProcessOneIFD ( XMP_Uns32 ifdOffset, XMP_Uns8 ifd ); - - const TweakedIFDEntry* FindTagInIFD ( XMP_Uns8 ifd, XMP_Uns16 id ) const; - - const inline void* GetDataPtr ( const TweakedIFDEntry* tifdEntry ) const - { if ( tifdEntry->bytes <= 4 ) return &tifdEntry->dataOrPos; else return (this->tiffStream + tifdEntry->dataOrPos); }; - - static inline void NotAppropriate() { XMP_Throw ( "Not appropriate for TIFF_Reader", kXMPErr_InternalFailure ); }; - -}; // TIFF_MemoryReader - - -// ================================================================================================= -// ================================================================================================= - - -// ================================================================================================= -// TIFF_FileWriter -// =============== - -class TIFF_FileWriter : public TIFF_Manager { // The derived class for file-based or read-write access. -public: - - bool HasExifIFD() const { return this->containedIFDs[kTIFF_ExifIFD].tagMap.size() != 0; }; - bool HasGPSInfoIFD() const { return this->containedIFDs[kTIFF_GPSInfoIFD].tagMap.size() != 0; }; - - bool GetIFD ( XMP_Uns8 ifd, TagInfoMap* ifdMap ) const; - - bool GetTag ( XMP_Uns8 ifd, XMP_Uns16 id, TagInfo* info ) const; - - void SetTag ( XMP_Uns8 ifd, XMP_Uns16 id, XMP_Uns16 type, XMP_Uns32 count, const void* dataPtr ); - - void DeleteTag ( XMP_Uns8 ifd, XMP_Uns16 id ); - - XMP_Uns32 GetValueOffset ( XMP_Uns8 ifd, XMP_Uns16 id ) const; - - bool GetTag_Integer ( XMP_Uns8 ifd, XMP_Uns16 id, XMP_Uns32* data ) const; - - bool GetTag_Byte ( XMP_Uns8 ifd, XMP_Uns16 id, XMP_Uns8* data ) const; - bool GetTag_SByte ( XMP_Uns8 ifd, XMP_Uns16 id, XMP_Int8* data ) const; - bool GetTag_Short ( XMP_Uns8 ifd, XMP_Uns16 id, XMP_Uns16* data ) const; - bool GetTag_SShort ( XMP_Uns8 ifd, XMP_Uns16 id, XMP_Int16* data ) const; - bool GetTag_Long ( XMP_Uns8 ifd, XMP_Uns16 id, XMP_Uns32* data ) const; - bool GetTag_SLong ( XMP_Uns8 ifd, XMP_Uns16 id, XMP_Int32* data ) const; - - bool GetTag_Rational ( XMP_Uns8 ifd, XMP_Uns16 id, Rational* data ) const; - bool GetTag_SRational ( XMP_Uns8 ifd, XMP_Uns16 id, SRational* data ) const; - - bool GetTag_Float ( XMP_Uns8 ifd, XMP_Uns16 id, float* data ) const; - bool GetTag_Double ( XMP_Uns8 ifd, XMP_Uns16 id, double* data ) const; - - bool GetTag_ASCII ( XMP_Uns8 ifd, XMP_Uns16 id, XMP_StringPtr* dataPtr, XMP_StringLen* dataLen ) const; - - bool GetTag_EncodedString ( XMP_Uns8 ifd, XMP_Uns16 id, std::string* utf8Str ) const; - - void SetTag_EncodedString ( XMP_Uns8 ifd, XMP_Uns16 id, const std::string& utf8Str, XMP_Uns8 encoding ); - - bool IsChanged() { return this->changed; }; - - bool IsLegacyChanged(); - - enum { kDoNotCopyData = false }; - - void ParseMemoryStream ( const void* data, XMP_Uns32 length, bool copyData = true ); - void ParseFileStream ( LFA_FileRef fileRef ); - - void IntegrateFromPShop6 ( const void * buriedPtr, size_t buriedLen ); - - XMP_Uns32 UpdateMemoryStream ( void** dataPtr, bool condenseStream = false ); - void UpdateFileStream ( LFA_FileRef fileRef ); - - TIFF_FileWriter(); - - virtual ~TIFF_FileWriter(); - -private: - - bool changed, legacyDeleted; - bool memParsed, fileParsed; - bool ownedStream; - - XMP_Uns8* memStream; - XMP_Uns32 tiffLength; - - // Memory usage notes: TIFF_FileWriter is for file-based OR read/write usage. For memory-based - // streams the dataPtr is initially into the stream, regardless of size. For file-based streams - // the dataPtr is initially a separate allocation for large values (over 4 bytes), and points to - // the smallValue field for small values. This is also the usage when a tag is changed (for both - // memory and file cases), the dataPtr is a separate allocation for large values (over 4 bytes), - // and points to the smallValue field for small values. - - // ! The working data values are always stream endian, no matter where stored. They are flipped - // ! as necessary by GetTag and SetTag. - - static const bool kIsFileBased = true; // For use in the InternalTagInfo constructor. - static const bool kIsMemoryBased = false; - - class InternalTagInfo { - public: - - XMP_Uns16 id; - XMP_Uns16 type; - XMP_Uns32 count; - XMP_Uns32 dataLen; - XMP_Uns32 smallValue; // Small value in stream endianness, but "left" justified. - XMP_Uns8* dataPtr; // Parsing captures all small values, only large ones that we care about. - XMP_Uns32 origDataLen; // The original (parse time) data length in bytes. - XMP_Uns32 origDataOffset; // The original data offset, regardless of length. - bool changed; - bool fileBased; - - inline void FreeData() { - if ( this->fileBased || this->changed ) { - if ( (this->dataLen > 4) && (this->dataPtr != 0) ) { free ( this->dataPtr ); this->dataPtr = 0; } - } - } - - InternalTagInfo ( XMP_Uns16 _id, XMP_Uns16 _type, XMP_Uns32 _count, bool _fileBased ) - : id(_id), type(_type), count(_count), dataLen(0), smallValue(0), dataPtr(0), - origDataLen(0), origDataOffset(0), changed(false), fileBased(_fileBased) {}; - ~InternalTagInfo() { this->FreeData(); }; - - void operator= ( const InternalTagInfo & in ) - { - // ! Gag! Transfer ownership of the dataPtr! - this->FreeData(); - memcpy ( this, &in, sizeof(*this) ); // AUDIT: Use of sizeof(InternalTagInfo) is safe. - if ( this->dataLen <= 4 ) { - this->dataPtr = (XMP_Uns8*) &this->smallValue; // Don't use the copied pointer. - } else { - *((XMP_Uns8**)&in.dataPtr) = 0; // The pointer is now owned by "this". - } - }; - - private: - - InternalTagInfo() // Hidden on purpose, fileBased must be properly set. - : id(0), type(0), count(0), dataLen(0), smallValue(0), dataPtr(0), - origDataLen(0), origDataOffset(0), changed(false), fileBased(false) {}; - - }; - - typedef std::map<XMP_Uns16,InternalTagInfo> InternalTagMap; - - struct InternalIFDInfo { - bool changed; - XMP_Uns16 origCount; // Original number of IFD entries. - XMP_Uns32 origIFDOffset; // Original stream offset of the IFD. - XMP_Uns32 origNextIFD; // Original stream offset of the following IFD. - InternalTagMap tagMap; - InternalIFDInfo() : changed(false), origCount(0), origIFDOffset(0), origNextIFD(0) {}; - inline void clear() - { - this->changed = false; - this->origCount = 0; - this->origIFDOffset = this->origNextIFD = 0; - this->tagMap.clear(); - }; - }; - - InternalIFDInfo containedIFDs[kTIFF_KnownIFDCount]; - - static XMP_Uns8 PickIFD ( XMP_Uns8 ifd, XMP_Uns16 id ); - const InternalTagInfo* FindTagInIFD ( XMP_Uns8 ifd, XMP_Uns16 id ) const; - - void DeleteExistingInfo(); - - XMP_Uns32 ProcessMemoryIFD ( XMP_Uns32 ifdOffset, XMP_Uns8 ifd ); - XMP_Uns32 ProcessFileIFD ( XMP_Uns8 ifd, XMP_Uns32 ifdOffset, LFA_FileRef fileRef, IOBuffer* ioBuf ); - - void ProcessPShop6IFD ( const TIFF_MemoryReader& buriedExif, XMP_Uns8 ifd ); - - void* CopyTagToMasterIFD ( const TagInfo& ps6Tag, InternalIFDInfo* masterIFD ); - - void PreflightIFDLinkage(); - - XMP_Uns32 DetermineVisibleLength(); - - XMP_Uns32 DetermineAppendInfo ( XMP_Uns32 appendedOrigin, - bool appendedIFDs[kTIFF_KnownIFDCount], - XMP_Uns32 newIFDOffsets[kTIFF_KnownIFDCount], - bool appendAll = false ); - - void UpdateMemByAppend ( XMP_Uns8** newStream_out, XMP_Uns32* newLength_out, - bool appendAll = false, XMP_Uns32 extraSpace = 0 ); - void UpdateMemByRewrite ( XMP_Uns8** newStream_out, XMP_Uns32* newLength_out ); - - void WriteFileIFD ( LFA_FileRef fileRef, InternalIFDInfo & thisIFD ); - -}; // TIFF_FileWriter - - -// ================================================================================================= - -#endif // __TIFF_Support_hpp__ diff --git a/source/XMPFiles/FormatSupport/XDCAM_Support.cpp b/source/XMPFiles/FormatSupport/XDCAM_Support.cpp deleted file mode 100644 index b86e69e..0000000 --- a/source/XMPFiles/FormatSupport/XDCAM_Support.cpp +++ /dev/null @@ -1,350 +0,0 @@ -// ================================================================================================= -// ADOBE SYSTEMS INCORPORATED -// Copyright 2008 Adobe Systems Incorporated -// 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 "XDCAM_Support.hpp" - -// ================================================================================================= -/// \file XDCAM_Support.cpp -/// -// ================================================================================================= - -namespace XDCAM_Support -{ - -// ================================================================================================= -// CreateChildElement -// ================== - -namespace -{ -XML_Node * CreateChildElement ( XML_Node * parent, XMP_StringPtr localName, XMP_StringPtr legacyNS, int indent /* = 0 */ ) -{ - XML_Node * wsNode; - XML_Node * childNode = parent->GetNamedElement ( legacyNS, localName ); - - if ( childNode == 0 ) { - - // The indenting is a hack, assuming existing 2 spaces per level. - - wsNode = new XML_Node ( parent, "", kCDataNode ); - wsNode->value = " "; // Add 2 spaces to the existing WS before the parent's close tag. - parent->content.push_back ( wsNode ); - - childNode = new XML_Node ( parent, localName, kElemNode ); - childNode->ns = parent->ns; - childNode->nsPrefixLen = parent->nsPrefixLen; - childNode->name.insert ( 0, parent->name, 0, parent->nsPrefixLen ); - parent->content.push_back ( childNode ); - - wsNode = new XML_Node ( parent, "", kCDataNode ); - wsNode->value = '\n'; - for ( ; indent > 1; --indent ) wsNode->value += " "; // Indent less 1, to "outdent" the parent's close. - parent->content.push_back ( wsNode ); - - } - - return childNode; - -} -} - -// ================================================================================================= -// XDCAM_Support::GetLegacyMetaData -// ================================= - -bool GetLegacyMetaData ( SXMPMeta * xmpObjPtr, - XML_NodePtr rootElem, - XMP_StringPtr legacyNS, - bool digestFound, - std::string& umid ) -{ - bool containsXMP = false; - - XML_NodePtr legacyContext = 0, legacyProp = 0; - - // UMID - if ( digestFound || (! xmpObjPtr->DoesPropertyExist ( kXMP_NS_DC, "identifier" )) ) { - legacyProp = rootElem->GetNamedElement ( legacyNS, "TargetMaterial" ); - if ( (legacyProp != 0) && legacyProp->IsEmptyLeafNode() ) { - XMP_StringPtr legacyValue = legacyProp->GetAttrValue ( "umidRef" ); - if ( legacyValue != 0 ) { - umid = legacyValue; - xmpObjPtr->SetProperty ( kXMP_NS_DC, "identifier", legacyValue, kXMP_DeleteExisting ); - containsXMP = true; - } - } - } - - // Creation date - if ( digestFound || (! xmpObjPtr->DoesPropertyExist ( kXMP_NS_XMP, "CreateDate" )) ) { - legacyProp = rootElem->GetNamedElement ( legacyNS, "CreationDate" ); - if ( (legacyProp != 0) && legacyProp->IsEmptyLeafNode() ) { - XMP_StringPtr legacyValue = legacyProp->GetAttrValue ( "value" ); - if ( legacyValue != 0 ) { - xmpObjPtr->SetProperty ( kXMP_NS_XMP, "CreateDate", legacyValue, kXMP_DeleteExisting ); - containsXMP = true; - } - } - } - - // Modify Date - if ( digestFound || (! xmpObjPtr->DoesPropertyExist ( kXMP_NS_XMP, "ModifyDate" )) ) { - legacyProp = rootElem->GetNamedElement ( legacyNS, "LastUpdate" ); - if ( (legacyProp != 0) && legacyProp->IsEmptyLeafNode() ) { - XMP_StringPtr legacyValue = legacyProp->GetAttrValue ( "value" ); - if ( legacyValue != 0 ) { - xmpObjPtr->SetProperty ( kXMP_NS_XMP, "ModifyDate", legacyValue, kXMP_DeleteExisting ); - containsXMP = true; - } - } - } - - // Metadata Modify Date - if ( digestFound || (! xmpObjPtr->DoesPropertyExist ( kXMP_NS_XMP, "MetadataDate" )) ) { - legacyProp = rootElem->GetNamedElement ( legacyNS, "lastUpdate" ); - if ( (legacyProp != 0) && legacyProp->IsEmptyLeafNode() ) { - XMP_StringPtr legacyValue = legacyProp->GetAttrValue ( "value" ); - if ( legacyValue != 0 ) { - xmpObjPtr->SetProperty ( kXMP_NS_XMP, "MetadataDate", legacyValue, kXMP_DeleteExisting ); - containsXMP = true; - } - } - } - - // Description - if ( digestFound || (! xmpObjPtr->DoesPropertyExist ( kXMP_NS_DC, "description" )) ) { - legacyProp = rootElem->GetNamedElement ( legacyNS, "Description" ); - if ( (legacyProp != 0) && legacyProp->IsLeafContentNode() ) { - XMP_StringPtr legacyValue = legacyProp->GetLeafContentValue(); - if ( legacyValue != 0 ) { - xmpObjPtr->SetLocalizedText ( kXMP_NS_DC, "description", "", "x-default", legacyValue, kXMP_DeleteExisting ); - containsXMP = true; - } - } - } - - legacyContext = rootElem->GetNamedElement ( legacyNS, "VideoFormat" ); - - if ( legacyContext != 0 ) { - - // frame size - if ( digestFound || (! xmpObjPtr->DoesPropertyExist ( kXMP_NS_DM, "videoFrameSize" )) ) { - legacyProp = legacyContext->GetNamedElement ( legacyNS, "VideoLayout" ); - if ( (legacyProp != 0) && legacyProp->IsEmptyLeafNode() ) { - - XMP_StringPtr widthValue = legacyProp->GetAttrValue ( "pixel" ); - XMP_StringPtr heightValue = legacyProp->GetAttrValue ( "numOfVerticalLine" ); - - if ( (widthValue != 0) && (heightValue != 0) ) { - - xmpObjPtr->DeleteProperty ( kXMP_NS_DM, "videoFrameSize" ); - xmpObjPtr->SetStructField ( kXMP_NS_DM, "videoFrameSize", kXMP_NS_XMP_Dimensions, "w", widthValue ); - xmpObjPtr->SetStructField ( kXMP_NS_DM, "videoFrameSize", kXMP_NS_XMP_Dimensions, "h", heightValue ); - xmpObjPtr->SetStructField ( kXMP_NS_DM, "videoFrameSize", kXMP_NS_XMP_Dimensions, "unit", "pixels" ); - - containsXMP = true; - - } - - } - } - - // Aspect ratio - if ( digestFound || (! xmpObjPtr->DoesPropertyExist ( kXMP_NS_DM, "videoPixelAspectRatio" )) ) { - legacyProp = legacyContext->GetNamedElement ( legacyNS, "VideoLayout" ); - if ( (legacyProp != 0) && legacyProp->IsEmptyLeafNode() ) { - XMP_StringPtr aspectRatio = legacyProp->GetAttrValue ( "aspectRatio" ); - if ( aspectRatio != 0 ) { - xmpObjPtr->SetProperty ( kXMP_NS_DM, "videoPixelAspectRatio", aspectRatio, kXMP_DeleteExisting ); - containsXMP = true; - } - } - } - - // Frame rate - if ( digestFound || (! xmpObjPtr->DoesPropertyExist ( kXMP_NS_DM, "videoFrameRate" )) ) { - legacyProp = legacyContext->GetNamedElement ( legacyNS, "VideoFrame" ); - if ( (legacyProp != 0) && legacyProp->IsEmptyLeafNode() ) { - XMP_StringPtr prop = legacyProp->GetAttrValue ( "formatFps" ); - if ( prop != 0 ) { - xmpObjPtr->SetProperty ( kXMP_NS_DM, "videoFrameRate", prop, kXMP_DeleteExisting ); - containsXMP = true; - } - } - } - - // Video codec - if ( digestFound || (! xmpObjPtr->DoesPropertyExist ( kXMP_NS_DM, "videoCompressor" )) ) { - legacyProp = legacyContext->GetNamedElement ( legacyNS, "VideoFrame" ); - if ( (legacyProp != 0) && legacyProp->IsEmptyLeafNode() ) { - XMP_StringPtr prop = legacyProp->GetAttrValue ( "videoCodec" ); - if ( prop != 0 ) { - xmpObjPtr->SetProperty ( kXMP_NS_DM, "videoCompressor", prop, kXMP_DeleteExisting ); - containsXMP = true; - } - } - } - - } // VideoFormat - - legacyContext = rootElem->GetNamedElement ( legacyNS, "AudioFormat" ); - - if ( legacyContext != 0 ) { - - // Audio codec - if ( digestFound || (! xmpObjPtr->DoesPropertyExist ( kXMP_NS_DM, "audioCompressor" )) ) { - legacyProp = legacyContext->GetNamedElement ( legacyNS, "AudioRecPort" ); - if ( (legacyProp != 0) && legacyProp->IsEmptyLeafNode() ) { - XMP_StringPtr prop = legacyProp->GetAttrValue ( "audioCodec" ); - if ( prop != 0 ) { - xmpObjPtr->SetProperty ( kXMP_NS_DM, "audioCompressor", prop, kXMP_DeleteExisting ); - containsXMP = true; - } - } - } - - } // AudioFormat - - // Duration - if ( digestFound || (! xmpObjPtr->DoesPropertyExist ( kXMP_NS_DM, "duration" )) ) { - - std::string durationFrames, timecodeFPS; - legacyProp = rootElem->GetNamedElement ( legacyNS, "Duration" ); - if ( legacyProp != 0 ) { - XMP_StringPtr durationValue = legacyProp->GetAttrValue ( "value" ); - if ( durationValue != 0 ) durationFrames = durationValue; - } - - legacyProp = rootElem->GetNamedElement ( legacyNS, "LtcChangeTable" ); - if ( legacyProp != 0 ) { - // [TODO] Verify that this is the correct framerate to use from the legacy metadata. gemiller - XMP_StringPtr fps = legacyProp->GetAttrValue ( "tcFps" ); - if ( fps != 0 ) { - timecodeFPS = "1/"; - timecodeFPS += fps; - } - } - - if ( (!timecodeFPS.empty()) && (!durationFrames.empty())) { - xmpObjPtr->DeleteProperty ( kXMP_NS_DM, "duration" ); - xmpObjPtr->SetStructField ( kXMP_NS_DM, "duration", kXMP_NS_DM, "value", durationFrames ); - xmpObjPtr->SetStructField ( kXMP_NS_DM, "duration", kXMP_NS_DM, "scale", timecodeFPS ); - containsXMP = true; - } - - } - - legacyContext = rootElem->GetNamedElement ( legacyNS, "Device" ); - if ( legacyContext != 0 ) { - - std::string model; - - // manufacturer string - XMP_StringPtr manufacturer = legacyContext->GetAttrValue ( "manufacturer" ); - if ( manufacturer != 0 ) { - model += manufacturer; - } - - // model string - XMP_StringPtr modelName = legacyContext->GetAttrValue ( "modelName" ); - if ( modelName != 0 ) { - if ( model.size() > 0 ) { - model += " "; - } - model += modelName; - } - - - // For the dm::cameraModel property, concat the make and model. - if ( digestFound || (! xmpObjPtr->DoesPropertyExist ( kXMP_NS_DM, "cameraModel" )) ) { - if ( model.size() != 0 ) { - xmpObjPtr->SetProperty ( kXMP_NS_DM, "cameraModel", model, kXMP_DeleteExisting ); - containsXMP = true; - } - } - - // EXIF Model - if ( digestFound || (! xmpObjPtr->DoesPropertyExist ( kXMP_NS_TIFF, "Model" )) ) { - xmpObjPtr->SetProperty ( kXMP_NS_TIFF, "Model", modelName, kXMP_DeleteExisting ); - } - - // EXIF Make - if ( digestFound || (! xmpObjPtr->DoesPropertyExist ( kXMP_NS_TIFF, "Make" )) ) { - xmpObjPtr->SetProperty ( kXMP_NS_TIFF, "Make", manufacturer, kXMP_DeleteExisting ); - } - - // EXIF-AUX Serial number - XMP_StringPtr serialNumber = legacyContext->GetAttrValue ( "serialNo" ); - if ( serialNumber != 0 && (digestFound || (! xmpObjPtr->DoesPropertyExist ( kXMP_NS_EXIF_Aux, "SerialNumber" ))) ) { - xmpObjPtr->SetProperty ( kXMP_NS_EXIF_Aux, "SerialNumber", serialNumber, kXMP_DeleteExisting ); - } - - } - - - return containsXMP; - -} - -// ================================================================================================= -// XDCAM_Support::SetLegacyMetaData -// ================================ - -bool SetLegacyMetaData ( XML_Node * clipMetadata, - SXMPMeta * xmpObj, - XMP_StringPtr legacyNS ) -{ - bool updateLegacyXML = false; - bool xmpFound = false; - std::string xmpValue; - XML_Node * xmlNode = 0; - - xmpFound = xmpObj->GetProperty ( kXMP_NS_DC, "title", &xmpValue, 0 ); - - if ( xmpFound ) { - - xmlNode = CreateChildElement ( clipMetadata, "Title", legacyNS, 3 ); - if ( xmpValue != xmlNode->GetLeafContentValue() ) { - xmlNode->SetLeafContentValue ( xmpValue.c_str() ); - updateLegacyXML = true; - } - - } - - xmpFound = xmpObj->GetArrayItem ( kXMP_NS_DC, "creator", 1, &xmpValue, 0 ); - - if ( xmpFound ) { - xmlNode = CreateChildElement ( clipMetadata, "Creator", legacyNS, 3 ); - XMP_StringPtr creatorName = xmlNode->GetAttrValue ( "name" ); - if ( creatorName == 0 ) creatorName = ""; - if ( xmpValue != creatorName ) { - xmlNode->SetAttrValue ( "name", xmpValue.c_str() ); - updateLegacyXML = true; - } - } - - xmpFound = xmpObj->GetProperty ( kXMP_NS_DC, "description", &xmpValue, 0 ); - - if ( xmpFound ) { - xmlNode = CreateChildElement ( clipMetadata, "Description", legacyNS, 3 ); - if ( xmpValue != xmlNode->GetLeafContentValue() ) { - // description in non real time metadata is limited to 2047 bytes - if ( xmpValue.size() > 2047 ) xmpValue.resize ( 2047 ); - xmlNode->SetLeafContentValue ( xmpValue.c_str() ); - updateLegacyXML = true; - } - } - - return updateLegacyXML; - -} - -// ================================================================================================= - -} // namespace XDCAM_Support diff --git a/source/XMPFiles/FormatSupport/XDCAM_Support.hpp b/source/XMPFiles/FormatSupport/XDCAM_Support.hpp deleted file mode 100644 index 51811c7..0000000 --- a/source/XMPFiles/FormatSupport/XDCAM_Support.hpp +++ /dev/null @@ -1,43 +0,0 @@ -#ifndef __XDCAM_Support_hpp__ -#define __XDCAM_Support_hpp__ 1 - -// ================================================================================================= -// ADOBE SYSTEMS INCORPORATED -// Copyright 2008 Adobe Systems Incorporated -// 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 "XMP_Environment.h" // ! This must be the first include. -#include "XMP_Const.h" -#include "XMPFiles_Impl.hpp" -#include "ExpatAdapter.hpp" - -// ================================================================================================= -/// \file XDCAM_Support.hpp -/// \brief XMPFiles support for XDCAM streams. -/// -// ================================================================================================= - -namespace XDCAM_Support -{ - // Read XDCAM legacy XML metadata and translate to appropriate XMP. - bool GetLegacyMetaData ( SXMPMeta * xmpObjPtr, - XML_NodePtr rootElem, - XMP_StringPtr legacyNS, - bool digestFound, - std::string& umid ); - - // Write XMP metadata back to legacy XDCAM XML. - bool SetLegacyMetaData ( XML_Node * clipMetadata, - SXMPMeta * xmpObj, - XMP_StringPtr legacyNS ); - - -} // namespace XDCAM_Support - -// ================================================================================================= - -#endif // __XDCAM_Support_hpp__ diff --git a/source/XMPFiles/FormatSupport/XMPScanner.cpp b/source/XMPFiles/FormatSupport/XMPScanner.cpp deleted file mode 100644 index a67c2c6..0000000 --- a/source/XMPFiles/FormatSupport/XMPScanner.cpp +++ /dev/null @@ -1,1480 +0,0 @@ -// ================================================================================================= -// Copyright 2004 Adobe Systems Incorporated -// 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. -// -// Adobe patent application tracking #P435, entitled 'Unique markers to simplify embedding data of -// one format in a file with a different format', inventors: Sean Parent, Greg Gilley. -// ================================================================================================= - -#if WIN32 - #pragma warning ( disable : 4127 ) // conditional expression is constant - #pragma warning ( disable : 4510 ) // default constructor could not be generated - #pragma warning ( disable : 4610 ) // user defined constructor required - #pragma warning ( disable : 4786 ) // debugger can't handle long symbol names -#endif - - -#include "XMPScanner.hpp" - -#include <cassert> -#include <string> -#include <cstdlib> - -#if DEBUG - #include <iostream> - #include <iomanip> - #include <fstream> -#endif - - -#ifndef UseStringPushBack // VC++ 6.x does not provide push_back for strings! - #define UseStringPushBack 0 -#endif - - -using namespace std; - - -// *** Consider Boyer-Moore style search for "<?xpacket begin=". It isn't an obvious win, the -// *** additional code might be slower than scanning every character. Especially if we will -// *** read every cache line anyway. - - -// ================================================================================================= -// ================================================================================================= -// class PacketMachine -// =================== -// -// This is the packet recognizer state machine. The top of the machine is FindNextPacket, this -// calls the specific state components and handles transitions. The states are described by an -// array of RecognizerInfo records, indexed by the RecognizerKind enumeration. Each RecognizerInfo -// record has a function that does that state's work, the success and failure transition states, -// and a string literal that is passed to the state function. The literal lets a common MatchChar -// or MatchString function be used in several places. -// -// The state functions are responsible for consuming input to recognize their particular state. -// This includes intervening nulls for 16 and 32 bit character forms. For the simplicity, things -// are treated as essentially little endian and the nulls are not actually checked. The opening -// '<' is found with a byte-by-byte search, then the number of bytes per character is determined -// by counting the following nulls. From then on, consuming a character means incrementing the -// buffer pointer by the number of bytes per character. Thus the buffer pointer only points to -// the "real" bytes. This also means that the pointer can go off the end of the buffer by a -// variable amount. The amount of overrun is saved so that the pointer can be positioned at the -// right byte to start the next buffer. -// -// The state functions return a TriState value, eTriYes means the pattern was found, eTriNo means -// the pattern was definitely not found, eTriMaybe means that the end of the buffer was reached -// while working through the pattern. -// -// When eTriYes is returned, the fBufferPtr data member is left pointing to the "real" byte -// following the last actual byte. Which might not be addressable memory! This also means that -// a state function can be entered with nothing available in the buffer. When eTriNo is returned, -// the fBufferPtr data member is left pointing to the byte that caused the failure. The state -// machine starts over from the failure byte. -// -// The state functions must preserve their internal micro-state before returning eTriMaybe, and -// resume processing when called with the next buffer. The fPosition data member is used to denote -// how many actual characters have been consumed. The fNullCount data member is used to denote how -// many nulls are left before the next actual character. - - -// ================================================================================================= -// PacketMachine -// ============= - -XMPScanner::PacketMachine::PacketMachine ( XMP_Int64 bufferOffset, const void * bufferOrigin, XMP_Int64 bufferLength ) : - - // Public members - fPacketStart ( 0 ), - fPacketLength ( 0 ), - fBytesAttr ( -1 ), - fCharForm ( eChar8Bit ), - fAccess ( ' ' ), - fBogusPacket ( false ), - - // Private members - fBufferOffset ( bufferOffset ), - fBufferOrigin ( (const char *) bufferOrigin ), - fBufferPtr ( fBufferOrigin ), - fBufferLimit ( fBufferOrigin + bufferLength ), - fRecognizer ( eLeadInRecognizer ), - fPosition ( 0 ), - fBytesPerChar ( 1 ), - fBufferOverrun ( 0 ), - fQuoteChar ( ' ' ) - -{ - /* - REVIEW NOTES : Should the buffer stuff be in a class? - */ - - assert ( bufferOrigin != NULL ); - assert ( bufferLength != 0 ); - -} // PacketMachine - - -// ================================================================================================= -// ~PacketMachine -// ============== - -XMPScanner::PacketMachine::~PacketMachine () -{ - - // An empty placeholder. - -} // ~PacketMachine - - -// ================================================================================================= -// AssociateBuffer -// =============== - -void -XMPScanner::PacketMachine::AssociateBuffer ( XMP_Int64 bufferOffset, const void * bufferOrigin, XMP_Int64 bufferLength ) -{ - - fBufferOffset = bufferOffset; - fBufferOrigin = (const char *) bufferOrigin; - fBufferPtr = fBufferOrigin + fBufferOverrun; - fBufferLimit = fBufferOrigin + bufferLength; - -} // AssociateBuffer - - -// ================================================================================================= -// ResetMachine -// ============ - -void -XMPScanner::PacketMachine::ResetMachine () -{ - - fRecognizer = eLeadInRecognizer; - fPosition = 0; - fBufferOverrun = 0; - fCharForm = eChar8Bit; - fBytesPerChar = 1; - fAccess = ' '; - fBytesAttr = -1; - fBogusPacket = false; - - fAttrName.erase ( fAttrName.begin(), fAttrName.end() ); - fAttrValue.erase ( fAttrValue.begin(), fAttrValue.end() ); - fEncodingAttr.erase ( fEncodingAttr.begin(), fEncodingAttr.end() ); - -} // ResetMachine - - -// ================================================================================================= -// FindLessThan -// ============ - -XMPScanner::PacketMachine::TriState -XMPScanner::PacketMachine::FindLessThan ( PacketMachine * ths, const char * which ) -{ - - if ( *which == 'H' ) { - - // -------------------------------------------------------------------------------- - // We're looking for the '<' of the header. If we fail there is no packet in this - // part of the input, so return eTriNo. - - ths->fCharForm = eChar8Bit; // We might have just failed from a bogus 16 or 32 bit case. - ths->fBytesPerChar = 1; - - while ( ths->fBufferPtr < ths->fBufferLimit ) { // Don't skip nulls for the header's '<'! - if ( *ths->fBufferPtr == '<' ) break; - ths->fBufferPtr++; - } - - if ( ths->fBufferPtr >= ths->fBufferLimit ) return eTriNo; - ths->fBufferPtr++; - return eTriYes; - - } else { - - // -------------------------------------------------------------------------------- - // We're looking for the '<' of the trailer. We're already inside the packet body, - // looking for the trailer. So here if we fail we must return eTriMaybe so that we - // keep looking for the trailer in the next buffer. - - const int bytesPerChar = ths->fBytesPerChar; - - while ( ths->fBufferPtr < ths->fBufferLimit ) { - if ( *ths->fBufferPtr == '<' ) break; - ths->fBufferPtr += bytesPerChar; - } - - if ( ths->fBufferPtr >= ths->fBufferLimit ) return eTriMaybe; - ths->fBufferPtr += bytesPerChar; - return eTriYes; - - } - -} // FindLessThan - - -// ================================================================================================= -// MatchString -// =========== - -XMPScanner::PacketMachine::TriState -XMPScanner::PacketMachine::MatchString ( PacketMachine * ths, const char * literal ) -{ - const int bytesPerChar = ths->fBytesPerChar; - const char * litPtr = literal + ths->fPosition; - const XMP_Int32 charsToGo = (XMP_Int32) strlen ( literal ) - ths->fPosition; - int charsDone = 0; - - while ( (charsDone < charsToGo) && (ths->fBufferPtr < ths->fBufferLimit) ) { - if ( *litPtr != *ths->fBufferPtr ) return eTriNo; - charsDone++; - litPtr++; - ths->fBufferPtr += bytesPerChar; - } - - if ( charsDone == charsToGo ) return eTriYes; - ths->fPosition += charsDone; - return eTriMaybe; - -} // MatchString - - -// ================================================================================================= -// MatchChar -// ========= - -XMPScanner::PacketMachine::TriState -XMPScanner::PacketMachine::MatchChar ( PacketMachine * ths, const char * literal ) -{ - const int bytesPerChar = ths->fBytesPerChar; - - if ( ths->fBufferPtr >= ths->fBufferLimit ) return eTriMaybe; - - const char currChar = *ths->fBufferPtr; - if ( currChar != *literal ) return eTriNo; - ths->fBufferPtr += bytesPerChar; - return eTriYes; - -} // MatchChar - - -// ================================================================================================= -// MatchOpenQuote -// ============== - -XMPScanner::PacketMachine::TriState -XMPScanner::PacketMachine::MatchOpenQuote ( PacketMachine * ths, const char * /* unused */ ) -{ - const int bytesPerChar = ths->fBytesPerChar; - - if ( ths->fBufferPtr >= ths->fBufferLimit ) return eTriMaybe; - - const char currChar = *ths->fBufferPtr; - if ( (currChar != '\'') && (currChar != '"') ) return eTriNo; - ths->fQuoteChar = currChar; - ths->fBufferPtr += bytesPerChar; - return eTriYes; - -} // MatchOpenQuote - - -// ================================================================================================= -// MatchCloseQuote -// =============== - -XMPScanner::PacketMachine::TriState -XMPScanner::PacketMachine::MatchCloseQuote ( PacketMachine * ths, const char * /* unused */ ) -{ - - return MatchChar ( ths, &ths->fQuoteChar ); - -} // MatchCloseQuote - - -// ================================================================================================= -// CaptureAttrName -// =============== - -XMPScanner::PacketMachine::TriState -XMPScanner::PacketMachine::CaptureAttrName ( PacketMachine * ths, const char * /* unused */ ) -{ - const int bytesPerChar = ths->fBytesPerChar; - char currChar; - - if ( ths->fPosition == 0 ) { // Get the first character in the name. - - if ( ths->fBufferPtr >= ths->fBufferLimit ) return eTriMaybe; - - currChar = *ths->fBufferPtr; - if ( ths->fAttrName.size() == 0 ) { - if ( ! ( ( ('a' <= currChar) && (currChar <= 'z') ) || - ( ('A' <= currChar) && (currChar <= 'Z') ) || - (currChar == '_') || (currChar == ':') ) ) { - return eTriNo; - } - } - - ths->fAttrName.erase ( ths->fAttrName.begin(), ths->fAttrName.end() ); - #if UseStringPushBack - ths->fAttrName.push_back ( currChar ); - #else - ths->fAttrName.insert ( ths->fAttrName.end(), currChar ); - #endif - ths->fBufferPtr += bytesPerChar; - - } - - while ( ths->fBufferPtr < ths->fBufferLimit ) { // Get the remainder of the name. - - currChar = *ths->fBufferPtr; - if ( ! ( ( ('a' <= currChar) && (currChar <= 'z') ) || - ( ('A' <= currChar) && (currChar <= 'Z') ) || - ( ('0' <= currChar) && (currChar <= '9') ) || - (currChar == '-') || (currChar == '.') || (currChar == '_') || (currChar == ':') ) ) { - break; - } - - #if UseStringPushBack - ths->fAttrName.push_back ( currChar ); - #else - ths->fAttrName.insert ( ths->fAttrName.end(), currChar ); - #endif - ths->fBufferPtr += bytesPerChar; - - } - - if ( ths->fBufferPtr < ths->fBufferLimit ) return eTriYes; - ths->fPosition = (long) ths->fAttrName.size(); // The name might span into the next buffer. - return eTriMaybe; - -} // CaptureAttrName - - -// ================================================================================================= -// CaptureAttrValue -// ================ -// -// Recognize the equal sign and the quoted string value, capture the value along the way. - -XMPScanner::PacketMachine::TriState -XMPScanner::PacketMachine::CaptureAttrValue ( PacketMachine * ths, const char * /* unused */ ) -{ - const int bytesPerChar = ths->fBytesPerChar; - char currChar = 0; - TriState result = eTriMaybe; - - if ( ths->fBufferPtr >= ths->fBufferLimit ) return eTriMaybe; - - switch ( ths->fPosition ) { - - case 0 : // The name should haved ended at the '=', nulls already skipped. - - if ( ths->fBufferPtr >= ths->fBufferLimit ) return eTriMaybe; - if ( *ths->fBufferPtr != '=' ) return eTriNo; - ths->fBufferPtr += bytesPerChar; - ths->fPosition = 1; - // fall through OK because MatchOpenQuote will check the buffer limit and nulls ... - - case 1 : // Look for the open quote. - - result = MatchOpenQuote ( ths, NULL ); - if ( result != eTriYes ) return result; - ths->fPosition = 2; - // fall through OK because the buffer limit and nulls are checked below ... - - default : // Look for the close quote, capturing the value along the way. - - assert ( ths->fPosition == 2 ); - - const char quoteChar = ths->fQuoteChar; - - while ( ths->fBufferPtr < ths->fBufferLimit ) { - currChar = *ths->fBufferPtr; - if ( currChar == quoteChar ) break; - #if UseStringPushBack - ths->fAttrValue.push_back ( currChar ); - #else - ths->fAttrValue.insert ( ths->fAttrValue.end(), currChar ); - #endif - ths->fBufferPtr += bytesPerChar; - } - - if ( ths->fBufferPtr >= ths->fBufferLimit ) return eTriMaybe; - assert ( currChar == quoteChar ); - ths->fBufferPtr += bytesPerChar; // Advance past the closing quote. - return eTriYes; - - } - -} // CaptureAttrValue - - -// ================================================================================================= -// RecordStart -// =========== -// -// Note that this routine looks at bytes, not logical characters. It has to figure out how many -// bytes per character there are so that the other recognizers can skip intervening nulls. - -XMPScanner::PacketMachine::TriState -XMPScanner::PacketMachine::RecordStart ( PacketMachine * ths, const char * /* unused */ ) -{ - - while ( true ) { - - if ( ths->fBufferPtr >= ths->fBufferLimit ) return eTriMaybe; - - const char currByte = *ths->fBufferPtr; - - switch ( ths->fPosition ) { - - case 0 : // Record the length. - assert ( ths->fCharForm == eChar8Bit ); - assert ( ths->fBytesPerChar == 1 ); - ths->fPacketStart = ths->fBufferOffset + ((ths->fBufferPtr - 1) - ths->fBufferOrigin); - ths->fPacketLength = 0; - ths->fPosition = 1; - // ! OK to fall through here, we didn't consume a byte in this step. - - case 1 : // Look for the first null byte. - if ( currByte != 0 ) return eTriYes; // No nulls found. - ths->fCharForm = eChar16BitBig; // Assume 16 bit big endian for now. - ths->fBytesPerChar = 2; - ths->fBufferPtr++; - ths->fPosition = 2; - break; // ! Don't fall through, have to check for the end of the buffer between each byte. - - case 2 : // One null was found, look for a second. - if ( currByte != 0 ) return eTriYes; // Just one null found. - ths->fBufferPtr++; - ths->fPosition = 3; - break; - - case 3 : // Two nulls were found, look for a third. - if ( currByte != 0 ) return eTriNo; // Just two nulls is not valid. - ths->fCharForm = eChar32BitBig; // Assume 32 bit big endian for now. - ths->fBytesPerChar = 4; - ths->fBufferPtr++; - return eTriYes; - break; - - } - - } - -} // RecordStart - - -// ================================================================================================= -// RecognizeBOM -// ============ -// -// Recognizing the byte order marker is a surprisingly messy thing to do. It can't be done by the -// normal string matcher, there are no intervening nulls. There are 4 transitions after the opening -// quote, the closing quote or one of the three encodings. For the actual BOM there are then 1 or 2 -// following bytes that depend on which of the encodings we're in. Not to mention that the buffer -// might end at any point. -// -// The intervening null count done earlier determined 8, 16, or 32 bits per character, but not the -// big or little endian nature for the 16/32 bit cases. The BOM must be present for the 16 and 32 -// bit cases in order to determine the endian mode. There are six possible byte sequences for the -// quoted BOM string, ignoring the differences for quoting with ''' versus '"'. -// -// Keep in mind that for the 16 and 32 bit cases there will be nulls for the quote. In the table -// below the symbol <quote> means just the one byte containing the ''' or '"'. The nulls for the -// quote character are explicitly shown. -// -// <quote> <quote> - 1: No BOM, this must be an 8 bit case. -// <quote> \xEF \xBB \xBF <quote> - 1.12-13: The 8 bit form. -// -// <quote> \xFE \xFF \x00 <quote> - 1.22-23: The 16 bit, big endian form -// <quote> \x00 \xFF \xFE <quote> - 1.32-33: The 16 bit, little endian form. -// -// <quote> \x00 \x00 \xFE \xFF \x00 \x00 \x00 <quote> - 1.32.43-45.56-57: The 32 bit, big endian form. -// <quote> \x00 \x00 \x00 \xFF \xFE \x00 \x00 <quote> - 1.32.43.54-57: The 32 bit, little endian form. - -enum { - eBOM_8_1 = 0xEF, - eBOM_8_2 = 0xBB, - eBOM_8_3 = 0xBF, - eBOM_Big_1 = 0xFE, - eBOM_Big_2 = 0xFF, - eBOM_Little_1 = eBOM_Big_2, - eBOM_Little_2 = eBOM_Big_1 -}; - -XMPScanner::PacketMachine::TriState -XMPScanner::PacketMachine::RecognizeBOM ( PacketMachine * ths, const char * /* unused */ ) -{ - const int bytesPerChar = ths->fBytesPerChar; - - while ( true ) { // Handle one character at a time, the micro-state (fPosition) changes for each. - - if ( ths->fBufferPtr >= ths->fBufferLimit ) return eTriMaybe; - - const unsigned char currChar = *ths->fBufferPtr; // ! The BOM bytes look like integers bigger than 127. - - switch ( ths->fPosition ) { - - case 0 : // Look for the opening quote. - if ( (currChar != '\'') && (currChar != '"') ) return eTriNo; - ths->fQuoteChar = currChar; - ths->fBufferPtr++; - ths->fPosition = 1; - break; // ! Don't fall through, have to check for the end of the buffer between each byte. - - case 1 : // Look at the byte immediately following the opening quote. - if ( currChar == ths->fQuoteChar ) { // Closing quote, no BOM character, must be 8 bit. - if ( ths->fCharForm != eChar8Bit ) return eTriNo; - ths->fBufferPtr += bytesPerChar; // Skip the nulls after the closing quote. - return eTriYes; - } else if ( currChar == eBOM_8_1 ) { // Start of the 8 bit form. - if ( ths->fCharForm != eChar8Bit ) return eTriNo; - ths->fBufferPtr++; - ths->fPosition = 12; - } else if ( currChar == eBOM_Big_1 ) { // Start of the 16 bit big endian form. - if ( ths->fCharForm != eChar16BitBig ) return eTriNo; - ths->fBufferPtr++; - ths->fPosition = 22; - } else if ( currChar == 0 ) { // Start of the 16 bit little endian or either 32 bit form. - if ( ths->fCharForm == eChar8Bit ) return eTriNo; - ths->fBufferPtr++; - ths->fPosition = 32; - } else { - return eTriNo; - } - break; - - case 12 : // Look for the second byte of the 8 bit form. - if ( currChar != eBOM_8_2 ) return eTriNo; - ths->fPosition = 13; - ths->fBufferPtr++; - break; - - case 13 : // Look for the third byte of the 8 bit form. - if ( currChar != eBOM_8_3 ) return eTriNo; - ths->fPosition = 99; - ths->fBufferPtr++; - break; - - case 22 : // Look for the second byte of the 16 bit big endian form. - if ( currChar != eBOM_Big_2 ) return eTriNo; - ths->fPosition = 23; - ths->fBufferPtr++; - break; - - case 23 : // Look for the null before the closing quote of the 16 bit big endian form. - if ( currChar != 0 ) return eTriNo; - ths->fBufferPtr++; - ths->fPosition = 99; - break; - - case 32 : // Look at the second byte of the 16 bit little endian or either 32 bit form. - if ( currChar == eBOM_Little_1 ) { - ths->fPosition = 33; - } else if ( currChar == 0 ) { - ths->fPosition = 43; - } else { - return eTriNo; - } - ths->fBufferPtr++; - break; - - case 33 : // Look for the third byte of the 16 bit little endian form. - if ( ths->fCharForm != eChar16BitBig ) return eTriNo; // Null count before assumed big endian. - if ( currChar != eBOM_Little_2 ) return eTriNo; - ths->fCharForm = eChar16BitLittle; - ths->fPosition = 99; - ths->fBufferPtr++; - break; - - case 43 : // Look at the third byte of either 32 bit form. - if ( ths->fCharForm != eChar32BitBig ) return eTriNo; // Null count before assumed big endian. - if ( currChar == eBOM_Big_1 ) { - ths->fPosition = 44; - } else if ( currChar == 0 ) { - ths->fPosition = 54; - } else { - return eTriNo; - } - ths->fBufferPtr++; - break; - - case 44 : // Look for the fourth byte of the 32 bit big endian form. - if ( currChar != eBOM_Big_2 ) return eTriNo; - ths->fPosition = 45; - ths->fBufferPtr++; - break; - - case 45 : // Look for the first null before the closing quote of the 32 bit big endian form. - if ( currChar != 0 ) return eTriNo; - ths->fPosition = 56; - ths->fBufferPtr++; - break; - - case 54 : // Look for the fourth byte of the 32 bit little endian form. - ths->fCharForm = eChar32BitLittle; - if ( currChar != eBOM_Little_1 ) return eTriNo; - ths->fPosition = 55; - ths->fBufferPtr++; - break; - - case 55 : // Look for the fifth byte of the 32 bit little endian form. - if ( currChar != eBOM_Little_2 ) return eTriNo; - ths->fPosition = 56; - ths->fBufferPtr++; - break; - - case 56 : // Look for the next to last null before the closing quote of the 32 bit forms. - if ( currChar != 0 ) return eTriNo; - ths->fPosition = 57; - ths->fBufferPtr++; - break; - - case 57 : // Look for the last null before the closing quote of the 32 bit forms. - if ( currChar != 0 ) return eTriNo; - ths->fPosition = 99; - ths->fBufferPtr++; - break; - - default : // Look for the closing quote. - assert ( ths->fPosition == 99 ); - if ( currChar != ths->fQuoteChar ) return eTriNo; - ths->fBufferPtr += bytesPerChar; // Skip the nulls after the closing quote. - return eTriYes; - break; - - } - - } - -} // RecognizeBOM - - -// ================================================================================================= -// RecordHeadAttr -// ============== - -XMPScanner::PacketMachine::TriState -XMPScanner::PacketMachine::RecordHeadAttr ( PacketMachine * ths, const char * /* unused */ ) -{ - - if ( ths->fAttrName == "encoding" ) { - - assert ( ths->fEncodingAttr.empty() ); - ths->fEncodingAttr = ths->fAttrValue; - - } else if ( ths->fAttrName == "bytes" ) { - - long value = 0; - int count = (int) ths->fAttrValue.size(); - int i; - - assert ( ths->fBytesAttr == -1 ); - - if ( count > 0 ) { // Allow bytes='' to be the same as no bytes attribute. - - for ( i = 0; i < count; i++ ) { - const char currChar = ths->fAttrValue[i]; - if ( ('0' <= currChar) && (currChar <= '9') ) { - value = (value * 10) + (currChar - '0'); - } else { - ths->fBogusPacket = true; - value = -1; - break; - } - } - ths->fBytesAttr = value; - - if ( CharFormIs16Bit ( ths->fCharForm ) ) { - if ( (ths->fBytesAttr & 1) != 0 ) ths->fBogusPacket = true; - } else if ( CharFormIs32Bit ( ths->fCharForm ) ) { - if ( (ths->fBytesAttr & 3) != 0 ) ths->fBogusPacket = true; - } - - } - - } - - ths->fAttrName.erase ( ths->fAttrName.begin(), ths->fAttrName.end() ); - ths->fAttrValue.erase ( ths->fAttrValue.begin(), ths->fAttrValue.end() ); - - return eTriYes; - -} // RecordHeadAttr - - -// ================================================================================================= -// CaptureAccess -// ============= - -XMPScanner::PacketMachine::TriState -XMPScanner::PacketMachine::CaptureAccess ( PacketMachine * ths, const char * /* unused */ ) -{ - const int bytesPerChar = ths->fBytesPerChar; - - while ( true ) { - - if ( ths->fBufferPtr >= ths->fBufferLimit ) return eTriMaybe; - - const char currChar = *ths->fBufferPtr; - - switch ( ths->fPosition ) { - - case 0 : // Look for the opening quote. - if ( (currChar != '\'') && (currChar != '"') ) return eTriNo; - ths->fQuoteChar = currChar; - ths->fBufferPtr += bytesPerChar; - ths->fPosition = 1; - break; // ! Don't fall through, have to check for the end of the buffer between each byte. - - case 1 : // Look for the 'r' or 'w'. - if ( (currChar != 'r') && (currChar != 'w') ) return eTriNo; - ths->fAccess = currChar; - ths->fBufferPtr += bytesPerChar; - ths->fPosition = 2; - break; - - default : // Look for the closing quote. - assert ( ths->fPosition == 2 ); - if ( currChar != ths->fQuoteChar ) return eTriNo; - ths->fBufferPtr += bytesPerChar; - return eTriYes; - break; - - } - - } - -} // CaptureAccess - - -// ================================================================================================= -// RecordTailAttr -// ============== - -XMPScanner::PacketMachine::TriState -XMPScanner::PacketMachine::RecordTailAttr ( PacketMachine * ths, const char * /* unused */ ) -{ - - // There are no known "general" attributes for the packet trailer. - - ths->fAttrName.erase ( ths->fAttrName.begin(), ths->fAttrName.end() ); - ths->fAttrValue.erase ( ths->fAttrValue.begin(), ths->fAttrValue.end() ); - - return eTriYes; - - -} // RecordTailAttr - - -// ================================================================================================= -// CheckPacketEnd -// ============== -// -// Check for trailing padding and record the packet length. We have trailing padding if the bytes -// attribute is present and has a value greater than the current length. - -XMPScanner::PacketMachine::TriState -XMPScanner::PacketMachine::CheckPacketEnd ( PacketMachine * ths, const char * /* unused */ ) -{ - const int bytesPerChar = ths->fBytesPerChar; - - if ( ths->fPosition == 0 ) { // First call, decide if there is trailing padding. - - const XMP_Int64 currLen64 = (ths->fBufferOffset + (ths->fBufferPtr - ths->fBufferOrigin)) - ths->fPacketStart; - if ( currLen64 > 0x7FFFFFFF ) throw std::runtime_error ( "Packet length exceeds 2GB-1" ); - const XMP_Int32 currLength = (XMP_Int32)currLen64; - - if ( (ths->fBytesAttr != -1) && (ths->fBytesAttr != currLength) ) { - if ( ths->fBytesAttr < currLength ) { - ths->fBogusPacket = true; // The bytes attribute value is too small. - } else { - ths->fPosition = ths->fBytesAttr - currLength; - if ( (ths->fPosition % ths->fBytesPerChar) != 0 ) { - ths->fBogusPacket = true; // The padding is not a multiple of the character size. - ths->fPosition = (ths->fPosition / ths->fBytesPerChar) * ths->fBytesPerChar; - } - } - } - - } - - while ( ths->fPosition > 0 ) { - - if ( ths->fBufferPtr >= ths->fBufferLimit ) return eTriMaybe; - - const char currChar = *ths->fBufferPtr; - - if ( (currChar != ' ') && (currChar != '\t') && (currChar != '\n') && (currChar != '\r') ) { - ths->fBogusPacket = true; // The padding is not whitespace. - break; // Stop the packet here. - } - - ths->fPosition -= bytesPerChar; - ths->fBufferPtr += bytesPerChar; - - } - - const XMP_Int64 currLen64 = (ths->fBufferOffset + (ths->fBufferPtr - ths->fBufferOrigin)) - ths->fPacketStart; - if ( currLen64 > 0x7FFFFFFF ) throw std::runtime_error ( "Packet length exceeds 2GB-1" ); - ths->fPacketLength = (XMP_Int32)currLen64; - return eTriYes; - -} // CheckPacketEnd - - -// ================================================================================================= -// CheckFinalNulls -// =============== -// -// Do some special case processing for little endian characters. We have to make sure the presumed -// nulls after the last character actually exist, i.e. that the stream does not end too soon. Note -// that the prior character scanning has moved the buffer pointer to the address following the last -// byte of the last character. I.e. we're already past the presumed nulls, so we can't check their -// content. All we can do is verify that the stream does not end too soon. -// -// Doing this check is simple yet subtle. If we're still in the current buffer then the trailing -// bytes obviously exist. If we're exactly at the end of the buffer then the bytes also exist. -// The only question is when we're actually past this buffer, partly into the next buffer. This is -// when "ths->fBufferPtr > ths->fBufferLimit" on entry. For that case we have to wait until we've -// actually seen enough extra bytes of input. -// -// Since the normal buffer processing is already adjusting for this partial character overrun, all -// that needs to be done here is wait until "ths->fBufferPtr <= ths->fBufferLimit" on entry. In -// other words, if we're presently too far, ths->fBufferPtr will be adjusted by the amount of the -// overflow the next time XMPScanner::Scan is called. This might still be too far, so just keep -// waiting for enough data to pass by. -// -// Note that there is a corresponding special case for big endian characters, we must decrement the -// starting offset by the number of leading nulls. But we don't do that here, we leave it to the -// outer code. This is because the leading nulls might have been at the exact end of a previous -// buffer, in which case we have to also decrement the length of that raw data snip. - -XMPScanner::PacketMachine::TriState -XMPScanner::PacketMachine::CheckFinalNulls ( PacketMachine * ths, const char * /* unused */ ) -{ - - if ( (ths->fCharForm != eChar8Bit) && CharFormIsLittleEndian ( ths->fCharForm ) ) { - if ( ths->fBufferPtr > ths->fBufferLimit ) return eTriMaybe; - } - - return eTriYes; - -} // CheckFinalNulls - - -// ================================================================================================= -// SetNextRecognizer -// ================= - -void -XMPScanner::PacketMachine::SetNextRecognizer ( RecognizerKind nextRecognizer ) -{ - - fRecognizer = nextRecognizer; - fPosition = 0; - -} // SetNextRecognizer - - -// ================================================================================================= -// FindNextPacket -// ============== - -// *** When we start validating intervening nulls for 2 and 4 bytes characters, throw an exception -// *** for errors. Don't return eTriNo, that might skip at an optional point. - -XMPScanner::PacketMachine::TriState -XMPScanner::PacketMachine::FindNextPacket () -{ - - TriState status; - - #define kPacketHead "?xpacket begin=" - #define kPacketID "W5M0MpCehiHzreSzNTczkc9d" - #define kPacketTail "?xpacket end=" - - static const RecognizerInfo recognizerTable [eRecognizerCount] = { // ! Would be safer to assign these explicitly. - - // proc successNext failureNext literal - - { NULL, eFailureRecognizer, eFailureRecognizer, NULL}, // eFailureRecognizer - { NULL, eSuccessRecognizer, eSuccessRecognizer, NULL}, // eSuccessRecognizer - - { FindLessThan, eHeadStartRecorder, eFailureRecognizer, "H" }, // eLeadInRecognizer - { RecordStart, eHeadStartRecognizer, eLeadInRecognizer, NULL }, // eHeadStartRecorder - { MatchString, eBOMRecognizer, eLeadInRecognizer, kPacketHead }, // eHeadStartRecognizer - - { RecognizeBOM, eIDTagRecognizer, eLeadInRecognizer, NULL }, // eBOMRecognizer - - { MatchString, eIDOpenRecognizer, eLeadInRecognizer, " id=" }, // eIDTagRecognizer - { MatchOpenQuote, eIDValueRecognizer, eLeadInRecognizer, NULL }, // eIDOpenRecognizer - { MatchString, eIDCloseRecognizer, eLeadInRecognizer, kPacketID }, // eIDValueRecognizer - { MatchCloseQuote, eAttrSpaceRecognizer_1, eLeadInRecognizer, NULL }, // eIDCloseRecognizer - - { MatchChar, eAttrNameRecognizer_1, eHeadEndRecognizer, " " }, // eAttrSpaceRecognizer_1 - { CaptureAttrName, eAttrValueRecognizer_1, eLeadInRecognizer, NULL }, // eAttrNameRecognizer_1 - { CaptureAttrValue, eAttrValueRecorder_1, eLeadInRecognizer, NULL }, // eAttrValueRecognizer_1 - { RecordHeadAttr, eAttrSpaceRecognizer_1, eLeadInRecognizer, NULL }, // eAttrValueRecorder_1 - - { MatchString, eBodyRecognizer, eLeadInRecognizer, "?>" }, // eHeadEndRecognizer - - { FindLessThan, eTailStartRecognizer, eBodyRecognizer, "T"}, // eBodyRecognizer - - { MatchString, eAccessValueRecognizer, eBodyRecognizer, kPacketTail }, // eTailStartRecognizer - { CaptureAccess, eAttrSpaceRecognizer_2, eBodyRecognizer, NULL }, // eAccessValueRecognizer - - { MatchChar, eAttrNameRecognizer_2, eTailEndRecognizer, " " }, // eAttrSpaceRecognizer_2 - { CaptureAttrName, eAttrValueRecognizer_2, eBodyRecognizer, NULL }, // eAttrNameRecognizer_2 - { CaptureAttrValue, eAttrValueRecorder_2, eBodyRecognizer, NULL }, // eAttrValueRecognizer_2 - { RecordTailAttr, eAttrSpaceRecognizer_2, eBodyRecognizer, NULL }, // eAttrValueRecorder_2 - - { MatchString, ePacketEndRecognizer, eBodyRecognizer, "?>" }, // eTailEndRecognizer - { CheckPacketEnd, eCloseOutRecognizer, eBodyRecognizer, "" }, // ePacketEndRecognizer - { CheckFinalNulls, eSuccessRecognizer, eBodyRecognizer, "" } // eCloseOutRecognizer - - }; - - while ( true ) { - - switch ( fRecognizer ) { - - case eFailureRecognizer : - return eTriNo; - - case eSuccessRecognizer : - return eTriYes; - - default : - - // ------------------------------------------------------------------- - // For everything else, the normal cases, use the state machine table. - - const RecognizerInfo * thisState = &recognizerTable [fRecognizer]; - - status = thisState->proc ( this, thisState->literal ); - - switch ( status ) { - - case eTriNo : - SetNextRecognizer ( thisState->failureNext ); - continue; - - case eTriYes : - SetNextRecognizer ( thisState->successNext ); - continue; - - case eTriMaybe : - fBufferOverrun = (unsigned char)(fBufferPtr - fBufferLimit); - return eTriMaybe; // Keep this recognizer intact, to be resumed later. - - } - - } // switch ( fRecognizer ) { ... - - } // while ( true ) { ... - -} // FindNextPacket - - -// ================================================================================================= -// ================================================================================================= -// class InternalSnip -// ================== - - -// ================================================================================================= -// InternalSnip -// ============ - -XMPScanner::InternalSnip::InternalSnip ( XMP_Int64 offset, XMP_Int64 length ) -{ - - fInfo.fOffset = offset; - fInfo.fLength = length; - -} // InternalSnip - - -// ================================================================================================= -// InternalSnip -// ============ - -XMPScanner::InternalSnip::InternalSnip ( const InternalSnip & rhs ) : - fInfo ( rhs.fInfo ), - fMachine ( NULL ) -{ - - assert ( rhs.fMachine.get() == NULL ); // Don't copy a snip with a machine. - assert ( (rhs.fInfo.fEncodingAttr == 0) || (*rhs.fInfo.fEncodingAttr == 0) ); // Don't copy a snip with an encoding. - -} // InternalSnip - - -// ================================================================================================= -// ~InternalSnip -// ============= - -XMPScanner::InternalSnip::~InternalSnip () -{ -} // ~InternalSnip - - - -// ================================================================================================= -// ================================================================================================= -// class XMPScanner -// ================ - - -// ================================================================================================= -// DumpSnipList -// ============ - -#if DEBUG - -static const char * snipStateName [6] = { "not-seen", "pending", "raw-data", "good-packet", "partial", "bad-packet" }; - -void -XMPScanner::DumpSnipList ( const char * title ) -{ - InternalSnipIterator currPos = fInternalSnips.begin(); - InternalSnipIterator endPos = fInternalSnips.end(); - - cout << endl << title << " snip list: " << fInternalSnips.size() << endl; - - for ( ; currPos != endPos; ++currPos ) { - SnipInfo * currSnip = &currPos->fInfo; - cout << '\t' << currSnip << ' ' << snipStateName[currSnip->fState] << ' ' - << currSnip->fOffset << ".." << (currSnip->fOffset + currSnip->fLength - 1) - << ' ' << currSnip->fLength << ' ' << endl; - } -} // DumpSnipList - -#endif - - -// ================================================================================================= -// PrevSnip and NextSnip -// ===================== - -XMPScanner::InternalSnipIterator -XMPScanner::PrevSnip ( InternalSnipIterator snipPos ) -{ - - InternalSnipIterator prev = snipPos; - return --prev; - -} // PrevSnip - -XMPScanner::InternalSnipIterator -XMPScanner::NextSnip ( InternalSnipIterator snipPos ) -{ - - InternalSnipIterator next = snipPos; - return ++next; - -} // NextSnip - - -// ================================================================================================= -// XMPScanner -// ========== -// -// Initialize the scanner object with one "not seen" snip covering the whole stream. - -XMPScanner::XMPScanner ( XMP_Int64 streamLength ) : - - fStreamLength ( streamLength ) - -{ - InternalSnip rootSnip ( 0, streamLength ); - - if ( streamLength > 0 ) fInternalSnips.push_front ( rootSnip ); // Be nice for empty files. - // DumpSnipList ( "New XMPScanner" ); - -} // XMPScanner - - -// ================================================================================================= -// ~XMPScanner -// =========== - -XMPScanner::~XMPScanner() -{ - -} // ~XMPScanner - - -// ================================================================================================= -// GetSnipCount -// ============ - -long -XMPScanner::GetSnipCount () -{ - - return (long)fInternalSnips.size(); - -} // GetSnipCount - - -// ================================================================================================= -// StreamAllScanned -// ================ - -bool -XMPScanner::StreamAllScanned () -{ - InternalSnipIterator currPos = fInternalSnips.begin(); - InternalSnipIterator endPos = fInternalSnips.end(); - - for ( ; currPos != endPos; ++currPos ) { - if ( currPos->fInfo.fState == eNotSeenSnip ) return false; - } - return true; - -} // StreamAllScanned - - -// ================================================================================================= -// SplitInternalSnip -// ================= -// -// Split the given snip into up to 3 pieces. The new pieces are inserted before and after this one -// in the snip list. The relOffset is the first byte to be kept, it is relative to this snip. If -// the preceeding or following snips have the same state as this one, just shift the boundaries. -// I.e. move the contents from one snip to the other, don't create a new snip. - -// *** To be thread safe we ought to lock the entire list during manipulation. Let data scanning -// *** happen in parallel, serialize all mucking with the list. - -void -XMPScanner::SplitInternalSnip ( InternalSnipIterator snipPos, XMP_Int64 relOffset, XMP_Int64 newLength ) -{ - - assert ( (relOffset + newLength) > relOffset ); // Check for overflow. - assert ( (relOffset + newLength) <= snipPos->fInfo.fLength ); - - // ----------------------------------- - // First deal with the low offset end. - - if ( relOffset > 0 ) { - - InternalSnipIterator prevPos; - if ( snipPos != fInternalSnips.begin() ) prevPos = PrevSnip ( snipPos ); - - if ( (snipPos != fInternalSnips.begin()) && (snipPos->fInfo.fState == prevPos->fInfo.fState) ) { - prevPos->fInfo.fLength += relOffset; // Adjust the preceeding snip. - } else { - InternalSnip headExcess ( snipPos->fInfo.fOffset, relOffset ); - headExcess.fInfo.fState = snipPos->fInfo.fState; - headExcess.fInfo.fOutOfOrder = snipPos->fInfo.fOutOfOrder; - fInternalSnips.insert ( snipPos, headExcess ); // Insert the head piece before the middle piece. - } - - snipPos->fInfo.fOffset += relOffset; // Adjust the remainder of this snip. - snipPos->fInfo.fLength -= relOffset; - - } - - // ---------------------------------- - // Now deal with the high offset end. - - if ( newLength < snipPos->fInfo.fLength ) { - - InternalSnipIterator nextPos = NextSnip ( snipPos ); - const XMP_Int64 tailLength = snipPos->fInfo.fLength - newLength; - - if ( (nextPos != fInternalSnips.end()) && (snipPos->fInfo.fState == nextPos->fInfo.fState) ) { - nextPos->fInfo.fOffset -= tailLength; // Adjust the following snip. - nextPos->fInfo.fLength += tailLength; - } else { - InternalSnip tailExcess ( (snipPos->fInfo.fOffset + newLength), tailLength ); - tailExcess.fInfo.fState = snipPos->fInfo.fState; - tailExcess.fInfo.fOutOfOrder = snipPos->fInfo.fOutOfOrder; - fInternalSnips.insert ( nextPos, tailExcess ); // Insert the tail piece after the middle piece. - } - - snipPos->fInfo.fLength = newLength; - - } - -} // SplitInternalSnip - - -// ================================================================================================= -// MergeInternalSnips -// ================== - -XMPScanner::InternalSnipIterator -XMPScanner::MergeInternalSnips ( InternalSnipIterator firstPos, InternalSnipIterator secondPos ) -{ - - firstPos->fInfo.fLength += secondPos->fInfo.fLength; - fInternalSnips.erase ( secondPos ); - return firstPos; - -} // MergeInternalSnips - - -// ================================================================================================= -// Scan -// ==== - -void -XMPScanner::Scan ( const void * bufferOrigin, XMP_Int64 bufferOffset, XMP_Int64 bufferLength ) -{ - XMP_Int64 relOffset; - - #if 0 - cout << "Scan: @ " << bufferOrigin << ", " << bufferOffset << ", " << bufferLength << endl; - #endif - - if ( bufferLength == 0 ) return; - - // ---------------------------------------------------------------- - // These comparisons are carefully done to avoid overflow problems. - - if ( (bufferOffset >= fStreamLength) || - (bufferLength > (fStreamLength - bufferOffset)) || - (bufferOrigin == 0) ) { - throw ScanError ( "Bad origin, offset, or length" ); - } - - // ---------------------------------------------------------------------------------------------- - // This buffer must be within a not-seen snip. Find it and split it. The first snip whose whose - // end is beyond the buffer must be the enclosing one. - - // *** It would be friendly for rescans for out of order problems to accept any buffer postion. - - const XMP_Int64 endOffset = bufferOffset + bufferLength - 1; - InternalSnipIterator snipPos = fInternalSnips.begin(); - - while ( endOffset > (snipPos->fInfo.fOffset + snipPos->fInfo.fLength - 1) ) ++ snipPos; - if ( snipPos->fInfo.fState != eNotSeenSnip ) throw ScanError ( "Already seen" ); - - relOffset = bufferOffset - snipPos->fInfo.fOffset; - if ( (relOffset + bufferLength) > snipPos->fInfo.fLength ) throw ScanError ( "Not within existing snip" ); - - SplitInternalSnip ( snipPos, relOffset, bufferLength ); // *** If sequential & prev is partial, just tack on, - - // -------------------------------------------------------- - // Merge this snip with the preceeding snip if appropriate. - - // *** When out of order I/O is supported we have to do something about buffers who's predecessor is not seen. - - if ( snipPos->fInfo.fOffset > 0 ) { - InternalSnipIterator prevPos = PrevSnip ( snipPos ); - if ( prevPos->fInfo.fState == ePartialPacketSnip ) snipPos = MergeInternalSnips ( prevPos, snipPos ); - } - - // ---------------------------------- - // Look for packets within this snip. - - snipPos->fInfo.fState = ePendingSnip; - PacketMachine* thisMachine = snipPos->fMachine.get(); - // DumpSnipList ( "Before scan" ); - - if ( thisMachine != 0 ) { - thisMachine->AssociateBuffer ( bufferOffset, bufferOrigin, bufferLength ); - } else { - // *** snipPos->fMachine.reset ( new PacketMachine ( bufferOffset, bufferOrigin, bufferLength ) ); VC++ lacks reset - #if 0 - snipPos->fMachine = auto_ptr<PacketMachine> ( new PacketMachine ( bufferOffset, bufferOrigin, bufferLength ) ); - #else - { - // Some versions of gcc complain about the assignment operator above. This avoids the gcc bug. - PacketMachine * pm = new PacketMachine ( bufferOffset, bufferOrigin, bufferLength ); - auto_ptr<PacketMachine> ap ( pm ); - snipPos->fMachine = ap; - } - #endif - thisMachine = snipPos->fMachine.get(); - } - - bool bufferDone = false; - while ( ! bufferDone ) { - - PacketMachine::TriState foundPacket = thisMachine->FindNextPacket(); - - if ( foundPacket == PacketMachine::eTriNo ) { - - // ----------------------------------------------------------------------- - // No packet, mark the snip as raw data and get rid of the packet machine. - // We're done with this buffer. - - snipPos->fInfo.fState = eRawInputSnip; - #if 0 - snipPos->fMachine = auto_ptr<PacketMachine>(); // *** snipPos->fMachine.reset(); VC++ lacks reset - #else - { - // Some versions of gcc complain about the assignment operator above. This avoids the gcc bug. - auto_ptr<PacketMachine> ap ( 0 ); - snipPos->fMachine = ap; - } - #endif - bufferDone = true; - - } else { - - // --------------------------------------------------------------------------------------------- - // Either a full or partial packet. First trim any excess off of the front as a raw input snip. - // If this is a partial packet mark the snip and keep the packet machine to be resumed later. - // We're done with this buffer, the partial packet by definition extends to the end. If this is - // a complete packet first extract the additional information from the packet machine. If there - // is leftover data split the snip and transfer the packet machine to the new trailing snip. - - if ( thisMachine->fPacketStart > snipPos->fInfo.fOffset ) { - - // There is data at the front of the current snip that must be trimmed. - SnipState savedState = snipPos->fInfo.fState; - snipPos->fInfo.fState = eRawInputSnip; // ! So it gets propagated to the trimmed front part. - relOffset = thisMachine->fPacketStart - snipPos->fInfo.fOffset; - SplitInternalSnip ( snipPos, relOffset, (snipPos->fInfo.fLength - relOffset) ); - snipPos->fInfo.fState = savedState; - - } - - if ( foundPacket == PacketMachine::eTriMaybe ) { - - // We have only found a partial packet. - snipPos->fInfo.fState = ePartialPacketSnip; - bufferDone = true; - - } else { - - // We have found a complete packet. Extract all the info for it and split any trailing data. - - InternalSnipIterator packetSnip = snipPos; - SnipState packetState = eValidPacketSnip; - - if ( thisMachine->fBogusPacket ) packetState = eBadPacketSnip; - - packetSnip->fInfo.fAccess = thisMachine->fAccess; - packetSnip->fInfo.fCharForm = thisMachine->fCharForm; - packetSnip->fInfo.fBytesAttr = thisMachine->fBytesAttr; - packetSnip->fInfo.fEncodingAttr = thisMachine->fEncodingAttr.c_str(); - thisMachine->fEncodingAttr.erase ( thisMachine->fEncodingAttr.begin(), thisMachine->fEncodingAttr.end() ); - - if ( (thisMachine->fCharForm != eChar8Bit) && CharFormIsBigEndian ( thisMachine->fCharForm ) ) { - - // ------------------------------------------------------------------------------ - // Handle a special case for big endian characters. The packet machine works as - // though things were little endian. The packet starting offset points to the - // byte containing the opening '<', and the length includes presumed nulls that - // follow the last "real" byte. If the characters are big endian we now have to - // decrement the starting offset of the packet, and also decrement the length of - // the previous snip. - // - // Note that we can't do this before the head trimming above in general. The - // nulls might have been exactly at the end of a buffer and already in the - // previous snip. We are doing this before trimming the tail from the raw snip - // containing the packet. We adjust the raw snip's size because it ends with - // the input buffer. We don't adjust the packet's size, it is already correct. - // - // The raw snip (the one before the packet) might entirely disappear. A simple - // example of this is when the packet is at the start of the file. - - assert ( packetSnip != fInternalSnips.begin() ); // Leading nulls were trimmed! - - if ( packetSnip != fInternalSnips.begin() ) { // ... but let's program defensibly. - - InternalSnipIterator prevSnip = PrevSnip ( packetSnip ); - const unsigned int nullsToAdd = ( CharFormIs16Bit ( thisMachine->fCharForm ) ? 1 : 3 ); - - assert ( nullsToAdd <= prevSnip->fInfo.fLength ); - prevSnip->fInfo.fLength -= nullsToAdd; - if ( prevSnip->fInfo.fLength == 0 ) (void) fInternalSnips.erase ( prevSnip ); - - packetSnip->fInfo.fOffset -= nullsToAdd; - packetSnip->fInfo.fLength += nullsToAdd; - thisMachine->fPacketStart -= nullsToAdd; - - } - - } - - if ( thisMachine->fPacketLength == snipPos->fInfo.fLength ) { - - // This packet ends exactly at the end of the current snip. - #if 0 - snipPos->fMachine = auto_ptr<PacketMachine>(); // *** snipPos->fMachine.reset(); VC++ lacks reset - #else - { - // Some versions of gcc complain about the assignment operator above. This avoids the gcc bug. - auto_ptr<PacketMachine> ap ( 0 ); - snipPos->fMachine = ap; - } - #endif - bufferDone = true; - - } else { - - // There is trailing data to split from the just found packet. - SplitInternalSnip ( snipPos, 0, thisMachine->fPacketLength ); - - InternalSnipIterator tailPos = NextSnip ( snipPos ); - - tailPos->fMachine = snipPos->fMachine; // auto_ptr assignment - taking ownership - thisMachine->ResetMachine (); - - snipPos = tailPos; - - } - - packetSnip->fInfo.fState = packetState; // Do this last to avoid messing up the tail split. - // DumpSnipList ( "Found a packet" ); - - - } - - } - - } - - // -------------------------------------------------------- - // Merge this snip with the preceeding snip if appropriate. - - // *** When out of order I/O is supported we have to check the following snip too. - - if ( (snipPos->fInfo.fOffset > 0) && (snipPos->fInfo.fState == eRawInputSnip) ) { - InternalSnipIterator prevPos = PrevSnip ( snipPos ); - if ( prevPos->fInfo.fState == eRawInputSnip ) snipPos = MergeInternalSnips ( prevPos, snipPos ); - } - - // DumpSnipList ( "After scan" ); - -} // Scan - - -// ================================================================================================= -// Report -// ====== - -void -XMPScanner::Report ( SnipInfoVector& snips ) -{ - const int count = (int)fInternalSnips.size(); - InternalSnipIterator snipPos = fInternalSnips.begin(); - - int s; - - // DumpSnipList ( "Report" ); - - snips.erase ( snips.begin(), snips.end() ); // ! Should use snips.clear, but VC++ doesn't have it. - snips.reserve ( count ); - - for ( s = 0; s < count; s += 1 ) { - snips.push_back ( SnipInfo ( snipPos->fInfo.fState, snipPos->fInfo.fOffset, snipPos->fInfo.fLength ) ); - snips[s] = snipPos->fInfo; // Pick up all of the fields. - ++ snipPos; - } - -} // Report diff --git a/source/XMPFiles/FormatSupport/XMPScanner.hpp b/source/XMPFiles/FormatSupport/XMPScanner.hpp deleted file mode 100644 index 472a43e..0000000 --- a/source/XMPFiles/FormatSupport/XMPScanner.hpp +++ /dev/null @@ -1,330 +0,0 @@ -#ifndef __XMPScanner_hpp__ -#define __XMPScanner_hpp__ - -// ================================================================================================= -// Copyright 2004 Adobe Systems Incorporated -// 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. -// -// Adobe patent application tracking #P435, entitled 'Unique markers to simplify embedding data of -// one format in a file with a different format', inventors: Sean Parent, Greg Gilley. -// ================================================================================================= - -#include "XMP_Environment.h" // ! This must be the first include. - -#include <list> -#include <vector> -#include <string> -#include <memory> -#include <stdexcept> - -#include "XMP_Const.h" - -// ================================================================================================= -// The XMPScanner class is used to scan a stream of input for XMP packets. A scanner object is -// constructed then fed the input through a series of calls to Scan. Report may be called at any -// time to get the current knowledge of the input. -// -// A packet starts when a valid header is found and ends when a valid trailer is found. If the -// header contains a "bytes" attribute, additional whitespace must follow. -// -// *** RESTRICTIONS: The current implementation of the scanner has the the following restrictions: -// - The input must be presented in order. -// - Not fully thread safe, don't make concurrent calls to the same XMPScanner object. -// ================================================================================================= - -class XMPScanner { -public: - - // ============================================================================================= - // The entire input stream is represented as a series of snips. Each snip defines one portion - // of the input stream that either has not been seen, has been seen and contains no packets, is - // exactly one packet, or contains the start of an unfinished packet. Adjacent snips with the - // same state are merged, so the number of snips is always minimal. - // - // A newly constructed XMPScanner object has one snip covering the whole input with a state - // of "not seen". A block of input that contains a full XMP packet is split into 3 parts: a - // (possibly empty) raw input snip, the packet, and another (possibly empty) raw input snip. A - // block of input that contains the start of an XMP packet is split into two snips, a (possibly - // empty) raw input snip and the packet start; the following snip must be a "not seen" snip. - // - // It is possible to have ill-formed packets. These have a syntactically valid header and - // trailer, but some semantic error. For example, if the "bytes" attribute length does not span - // to the end of the trailer, or if the following packet begins within trailing padding. - - enum { - eNotSeenSnip, // This snip has not been seen yet. - ePendingSnip, // This snip is an input buffer being processed. - eRawInputSnip, // This snip is raw input, it doesn't contain any part of an XMP packet. - eValidPacketSnip, // This snip is a complete, valid XMP packet. - ePartialPacketSnip, // This snip contains the start of a possible XMP packet. - eBadPacketSnip // This snip contains a complete, but semantically incorrect XMP packet. - }; - typedef XMP_Uns8 SnipState; - - enum { // The values allow easy testing for 16/32 bit and big/little endian. - eChar8Bit = 0, - eChar16BitBig = 2, - eChar16BitLittle = 3, - eChar32BitBig = 4, - eChar32BitLittle = 5 - }; - typedef XMP_Uns8 CharacterForm; - - enum { - eChar16BitMask = 2, // These constant shouldn't be used directly, they are mainly - eChar32BitMask = 4, // for the CharFormIsXyz macros below. - eCharLittleEndianMask = 1 - }; - - #define CharFormIs16Bit(f) ( ((int)(f) & XMPScanner::eChar16BitMask) != 0 ) - #define CharFormIs32Bit(f) ( ((int)(f) & XMPScanner::eChar32BitMask) != 0 ) - - #define CharFormIsBigEndian(f) ( ((int)(f) & XMPScanner::eCharLittleEndianMask) == 0 ) - #define CharFormIsLittleEndian(f) ( ((int)(f) & XMPScanner::eCharLittleEndianMask) != 0 ) - - struct SnipInfo { - - XMP_Int64 fOffset; // The byte offset of this snip within the input stream. - XMP_Int64 fLength; // The length in bytes of this snip. - SnipState fState; // The state of this snip. - bool fOutOfOrder; // If true, this snip was seen before the one in front of it. - char fAccess; // The read-only/read-write access from the end attribute. - CharacterForm fCharForm; // How the packet is divided into characters. - const char * fEncodingAttr; // The value of the encoding attribute, if any, with nulls removed. - XMP_Int64 fBytesAttr; // The value of the bytes attribute, -1 if not present. - - SnipInfo() : - fOffset ( 0 ), - fLength ( 0 ), - fState ( eNotSeenSnip ), - fOutOfOrder ( false ), - fAccess ( ' ' ), - fCharForm ( eChar8Bit ), - fEncodingAttr ( "" ), - fBytesAttr( -1 ) - { } - - SnipInfo ( SnipState state, XMP_Int64 offset, XMP_Int64 length ) : - fOffset ( offset ), - fLength ( length ), - fState ( state ), - fOutOfOrder ( false ), - fAccess ( ' ' ), - fCharForm ( eChar8Bit ), - fEncodingAttr ( "" ), - fBytesAttr( -1 ) - { } - - }; - - typedef std::vector<SnipInfo> SnipInfoVector; - - XMPScanner ( XMP_Int64 streamLength ); - // Constructs a new XMPScanner object for a stream with the given length. - - ~XMPScanner(); - - long GetSnipCount(); - // Returns the number of snips that the stream has been divided into. - - bool StreamAllScanned(); - // Returns true if all of the stream has been seen. - - void Scan ( const void * bufferOrigin, XMP_Int64 bufferOffset, XMP_Int64 bufferLength ); - // Scans the given part of the input, incorporating it in to the known snips. - // The bufferOffset is the offset of this block of input relative to the entire stream. - // The bufferLength is the length in bytes of this block of input. - - void Report ( SnipInfoVector & snips ); - // Produces a report of what is known about the input stream. - - class ScanError : public std::logic_error { - public: - ScanError() throw() : std::logic_error ( "" ) {} - explicit ScanError ( const char * message ) throw() : std::logic_error ( message ) {} - virtual ~ScanError() throw() {} - }; - -private: // XMPScanner - - class PacketMachine; - - class InternalSnip { - public: - - SnipInfo fInfo; // The public info about this snip. - std::auto_ptr<PacketMachine> fMachine; // The state machine for "active" snips. - - InternalSnip ( XMP_Int64 offset, XMP_Int64 length ); - InternalSnip ( const InternalSnip & ); - ~InternalSnip (); - - }; // InternalSnip - - typedef std::list<InternalSnip> InternalSnipList; - typedef InternalSnipList::iterator InternalSnipIterator; - - class PacketMachine { - public: - - XMP_Int64 fPacketStart; // Byte offset relative to the entire stream. - XMP_Int32 fPacketLength; // Length in bytes to the end of the trailer processing instruction. - XMP_Int32 fBytesAttr; // The value of the bytes attribute, -1 if not present. - std::string fEncodingAttr; // The value of the encoding attribute, if any, with nulls removed. - CharacterForm fCharForm; // How the packet is divided into characters. - char fAccess; // The read-only/read-write access from the end attribute. - bool fBogusPacket; // True if the packet has an error such as a bad "bytes" attribute value. - - void ResetMachine(); - - enum TriState { - eTriNo, - eTriMaybe, - eTriYes - }; - - TriState FindNextPacket(); - - void AssociateBuffer ( XMP_Int64 bufferOffset, const void * bufferOrigin, XMP_Int64 bufferLength ); - - PacketMachine ( XMP_Int64 bufferOffset, const void * bufferOrigin, XMP_Int64 bufferLength ); - ~PacketMachine(); - - private: // PacketMachine - - PacketMachine() {}; // ! Hide the default constructor. - - enum RecognizerKind { - - eFailureRecognizer, // Not really recognizers, special states to end one buffer's processing. - eSuccessRecognizer, - - eLeadInRecognizer, // Anything up to the next '<'. - eHeadStartRecorder, // Save the starting offset, count intervening nulls. - eHeadStartRecognizer, // The literal string "?xpacket begin=". - - eBOMRecognizer, // Recognize and record the quoted byte order marker. - - eIDTagRecognizer, // The literal string " id=". - eIDOpenRecognizer, // The opening quote for the ID. - eIDValueRecognizer, // The literal string "W5M0MpCehiHzreSzNTczkc9d". - eIDCloseRecognizer, // The closing quote for the ID. - - eAttrSpaceRecognizer_1, // The space before an attribute. - eAttrNameRecognizer_1, // The name of an attribute. - eAttrValueRecognizer_1, // The equal sign and quoted string value for an attribute. - eAttrValueRecorder_1, // Record the value of an attribute. - - eHeadEndRecognizer, // The string literal "?>". - - eBodyRecognizer, // The packet body, anything up to the next '<'. - - eTailStartRecognizer, // The string literal "?xpacket end=". - eAccessValueRecognizer, // Recognize and record the quoted r/w access mode. - - eAttrSpaceRecognizer_2, // The space before an attribute. - eAttrNameRecognizer_2, // The name of an attribute. - eAttrValueRecognizer_2, // The equal sign and quoted string value for an attribute. - eAttrValueRecorder_2, // Record the value of an attribute. - - eTailEndRecognizer, // The string literal "?>". - ePacketEndRecognizer, // Look for trailing padding, check and record the packet size. - eCloseOutRecognizer, // Look for final nulls for little endian multibyte characters. - - eRecognizerCount - - }; - - XMP_Int64 fBufferOffset; // The offset of the data buffer within the input stream. - const char * fBufferOrigin; // The starting address of the data buffer for this snip. - const char * fBufferPtr; // The current postion in the data buffer. - const char * fBufferLimit; // The address one past the last byte in the data buffer. - - RecognizerKind fRecognizer; // Which recognizer is currently active. - signed long fPosition; // The internal position within a string literal, etc. - unsigned char fBytesPerChar; // The number of bytes per logical character, 1, 2, or 4. - unsigned char fBufferOverrun; // Non-zero if suspended while skipping intervening nulls. - char fQuoteChar; // The kind of quote seen at the start of a quoted value. - std::string fAttrName; // The name for an arbitrary attribute (other than "begin" and "id"). - std::string fAttrValue; // The value for an arbitrary attribute (other than "begin" and "id"). - - void SetNextRecognizer ( RecognizerKind nextRecognizer ); - - typedef TriState (* RecognizerProc) ( PacketMachine *, const char * ); - - static TriState - FindLessThan ( PacketMachine * ths, const char * which ); - - static TriState - MatchString ( PacketMachine * ths, const char * literal ); - - static TriState - MatchChar ( PacketMachine * ths, const char * literal ); - - static TriState - MatchOpenQuote ( PacketMachine * ths, const char * /* unused */ ); - - static TriState - MatchCloseQuote ( PacketMachine * ths, const char * /* unused */ ); - - static TriState - CaptureAttrName ( PacketMachine * ths, const char * /* unused */ ); - - static TriState - CaptureAttrValue ( PacketMachine * ths, const char * /* unused */ ); - - static TriState - RecordStart ( PacketMachine * ths, const char * /* unused */ ); - - static TriState - RecognizeBOM ( PacketMachine * ths, const char * /* unused */ ); - - static TriState - RecordHeadAttr ( PacketMachine * ths, const char * /* unused */ ); - - static TriState - CaptureAccess ( PacketMachine * ths, const char * /* unused */ ); - - static TriState - RecordTailAttr ( PacketMachine * ths, const char * /* unused */ ); - - static TriState - CheckPacketEnd ( PacketMachine * ths, const char * /* unused */ ); - - static TriState - CheckFinalNulls ( PacketMachine * ths, const char * /* unused */ ); - - struct RecognizerInfo { - RecognizerProc proc; - RecognizerKind successNext; - RecognizerKind failureNext; - const char * literal; - }; - - }; // PacketMachine - - XMP_Int64 fStreamLength; - InternalSnipList fInternalSnips; - - void - SplitInternalSnip ( InternalSnipIterator snipPos, XMP_Int64 relOffset, XMP_Int64 newLength ); - - InternalSnipIterator - MergeInternalSnips ( InternalSnipIterator firstPos, InternalSnipIterator secondPos ); - - InternalSnipIterator - PrevSnip ( InternalSnipIterator snipPos ); - - InternalSnipIterator - NextSnip ( InternalSnipIterator snipPos ); - - #if DEBUG - void DumpSnipList ( const char * title ); - #endif - -}; // XMPScanner - -#endif // __XMPScanner_hpp__ diff --git a/source/XMPFiles/WXMPFiles.cpp b/source/XMPFiles/WXMPFiles.cpp deleted file mode 100644 index 4e32607..0000000 --- a/source/XMPFiles/WXMPFiles.cpp +++ /dev/null @@ -1,294 +0,0 @@ -// ================================================================================================= -// ADOBE SYSTEMS INCORPORATED -// Copyright 2004 Adobe Systems Incorporated -// 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 "XMP_Environment.h" -#include "XMP_Const.h" - -#include "client-glue/WXMPFiles.hpp" - -#include "XMPFiles_Impl.hpp" -#include "XMPFiles.hpp" - -#if XMP_WinBuild - #if XMP_DebugBuild - #pragma warning ( disable : 4297 ) // function assumed not to throw an exception but does - #endif -#endif - -#if __cplusplus -extern "C" { -#endif - -// ================================================================================================= - -static WXMP_Result voidResult; // Used for functions that don't use the normal result mechanism. - -// ================================================================================================= - -void WXMPFiles_GetVersionInfo_1 ( XMP_VersionInfo * versionInfo ) -{ - WXMP_Result * wResult = &voidResult; // ! Needed to "fool" the EnterWrapper macro. - XMP_ENTER_NoLock ( "WXMPFiles_GetVersionInfo_1" ) - - XMPFiles::GetVersionInfo ( versionInfo ); - - XMP_EXIT_NoThrow -} - -// ------------------------------------------------------------------------------------------------- - -void WXMPFiles_Initialize_1 ( XMP_OptionBits options, - WXMP_Result * wResult ) -{ - XMP_ENTER_NoLock ( "WXMPFiles_Initialize_1" ) - - wResult->int32Result = XMPFiles::Initialize ( options ); - - XMP_EXIT -} - -// ------------------------------------------------------------------------------------------------- - -void WXMPFiles_Terminate_1() -{ - WXMP_Result * wResult = &voidResult; // ! Needed to "fool" the EnterWrapper macro. - XMP_ENTER_NoLock ( "WXMPFiles_Terminate_1" ) - - XMPFiles::Terminate(); - - XMP_EXIT_NoThrow -} - -// ================================================================================================= - -void WXMPFiles_CTor_1 ( WXMP_Result * wResult ) -{ - XMP_ENTER_Static ( "WXMPFiles_CTor_1" ) // No lib object yet, use the static entry. - - XMPFiles * newObj = new XMPFiles(); - ++newObj->clientRefs; - XMP_Assert ( newObj->clientRefs == 1 ); - wResult->ptrResult = newObj; - - XMP_EXIT -} - -// ------------------------------------------------------------------------------------------------- - -void WXMPFiles_IncrementRefCount_1 ( XMPFilesRef xmpObjRef ) -{ - WXMP_Result * wResult = &voidResult; // ! Needed to "fool" the EnterWrapper macro. - XMP_ENTER_ObjWrite ( XMPFiles, "WXMPFiles_IncrementRefCount_1" ) - - ++thiz->clientRefs; - XMP_Assert ( thiz->clientRefs > 0 ); - - XMP_EXIT_NoThrow -} - -// ------------------------------------------------------------------------------------------------- - -void WXMPFiles_DecrementRefCount_1 ( XMPFilesRef xmpObjRef ) -{ - WXMP_Result * wResult = &voidResult; // ! Needed to "fool" the EnterWrapper macro. - XMP_ENTER_ObjWrite ( XMPFiles, "WXMPFiles_DecrementRefCount_1" ) - - XMP_Assert ( thiz->clientRefs > 0 ); - --thiz->clientRefs; - if ( thiz->clientRefs <= 0 ) { - objLock.Release(); - delete ( thiz ); - } - - XMP_EXIT_NoThrow -} - -// ================================================================================================= - -void WXMPFiles_GetFormatInfo_1 ( XMP_FileFormat format, - XMP_OptionBits * flags, - WXMP_Result * wResult ) -{ - XMP_ENTER_Static ( "WXMPFiles_GetFormatInfo_1" ) - - wResult->int32Result = XMPFiles::GetFormatInfo ( format, flags ); - - XMP_EXIT -} - -// ================================================================================================= - -void WXMPFiles_CheckFileFormat_1 ( XMP_StringPtr filePath, - WXMP_Result * wResult ) -{ - XMP_ENTER_Static ( "WXMPFiles_CheckFileFormat_1" ) - - wResult->int32Result = XMPFiles::CheckFileFormat ( filePath ); - - XMP_EXIT -} - -// ================================================================================================= - -void WXMPFiles_CheckPackageFormat_1 ( XMP_StringPtr folderPath, - WXMP_Result * wResult ) -{ - XMP_ENTER_Static ( "WXMPFiles_CheckPackageFormat_1" ) - - wResult->int32Result = XMPFiles::CheckPackageFormat ( folderPath ); - - XMP_EXIT -} - -// ================================================================================================= - -void WXMPFiles_OpenFile_1 ( XMPFilesRef xmpObjRef, - XMP_StringPtr filePath, - XMP_FileFormat format, - XMP_OptionBits openFlags, - WXMP_Result * wResult ) -{ - XMP_ENTER_ObjWrite ( XMPFiles, "WXMPFiles_OpenFile_1" ) - StartPerfCheck ( kAPIPerf_OpenFile, filePath ); - - bool ok = thiz->OpenFile ( filePath, format, openFlags ); - wResult->int32Result = ok; - - EndPerfCheck ( kAPIPerf_OpenFile ); - XMP_EXIT -} - -// ------------------------------------------------------------------------------------------------- - -void WXMPFiles_CloseFile_1 ( XMPFilesRef xmpObjRef, - XMP_OptionBits closeFlags, - WXMP_Result * wResult ) -{ - XMP_ENTER_ObjWrite ( XMPFiles, "WXMPFiles_CloseFile_1" ) - StartPerfCheck ( kAPIPerf_CloseFile, "" ); - - thiz->CloseFile ( closeFlags ); - - EndPerfCheck ( kAPIPerf_CloseFile ); - XMP_EXIT -} - -// ------------------------------------------------------------------------------------------------- - -void WXMPFiles_GetFileInfo_1 ( XMPFilesRef xmpObjRef, - void * clientPath, - XMP_OptionBits * openFlags, - XMP_FileFormat * format, - XMP_OptionBits * handlerFlags, - SetClientStringProc SetClientString, - WXMP_Result * wResult ) -{ - XMP_ENTER_ObjRead ( XMPFiles, "WXMPFiles_GetFileInfo_1" ) - - XMP_StringPtr pathStr; - XMP_StringLen pathLen; - - bool isOpen = thiz.GetFileInfo ( &pathStr, &pathLen, openFlags, format, handlerFlags ); - if ( isOpen && (clientPath != 0) ) (*SetClientString) ( clientPath, pathStr, pathLen ); - wResult->int32Result = isOpen; - - XMP_EXIT -} - -// ------------------------------------------------------------------------------------------------- - -void WXMPFiles_SetAbortProc_1 ( XMPFilesRef xmpObjRef, - XMP_AbortProc abortProc, - void * abortArg, - WXMP_Result * wResult ) -{ - XMP_ENTER_ObjWrite ( XMPFiles, "WXMPFiles_SetAbortProc_1" ) - - thiz->SetAbortProc ( abortProc, abortArg ); - - XMP_EXIT -} - -// ------------------------------------------------------------------------------------------------- - -void WXMPFiles_GetXMP_1 ( XMPFilesRef xmpObjRef, - XMPMetaRef xmpRef, - void * clientPacket, - XMP_PacketInfo * packetInfo, - SetClientStringProc SetClientString, - WXMP_Result * wResult ) -{ - XMP_ENTER_ObjWrite ( XMPFiles, "WXMPFiles_GetXMP_1" ) - StartPerfCheck ( kAPIPerf_GetXMP, "" ); - - bool hasXMP = false; - XMP_StringPtr packetStr; - XMP_StringLen packetLen; - - if ( xmpRef == 0 ) { - hasXMP = thiz->GetXMP ( 0, &packetStr, &packetLen, packetInfo ); - } else { - SXMPMeta xmpObj ( xmpRef ); - hasXMP = thiz->GetXMP ( &xmpObj, &packetStr, &packetLen, packetInfo ); - } - - if ( hasXMP && (clientPacket != 0) ) (*SetClientString) ( clientPacket, packetStr, packetLen ); - wResult->int32Result = hasXMP; - - EndPerfCheck ( kAPIPerf_GetXMP ); - XMP_EXIT -} - -// ------------------------------------------------------------------------------------------------- - -void WXMPFiles_PutXMP_1 ( XMPFilesRef xmpObjRef, - XMPMetaRef xmpRef, // ! Only one of the XMP object or packet are passed. - XMP_StringPtr xmpPacket, - XMP_StringLen xmpPacketLen, - WXMP_Result * wResult ) -{ - XMP_ENTER_ObjWrite ( XMPFiles, "WXMPFiles_PutXMP_1" ) - StartPerfCheck ( kAPIPerf_PutXMP, "" ); - - if ( xmpRef != 0 ) { - thiz->PutXMP ( xmpRef ); - } else { - thiz->PutXMP ( xmpPacket, xmpPacketLen ); - } - - EndPerfCheck ( kAPIPerf_PutXMP ); - XMP_EXIT -} - -// ------------------------------------------------------------------------------------------------- - -void WXMPFiles_CanPutXMP_1 ( XMPFilesRef xmpObjRef, - XMPMetaRef xmpRef, // ! Only one of the XMP object or packet are passed. - XMP_StringPtr xmpPacket, - XMP_StringLen xmpPacketLen, - WXMP_Result * wResult ) -{ - XMP_ENTER_ObjWrite ( XMPFiles, "WXMPFiles_CanPutXMP_1" ) - StartPerfCheck ( kAPIPerf_CanPutXMP, "" ); - - if ( xmpRef != 0 ) { - wResult->int32Result = thiz->CanPutXMP ( xmpRef ); - } else { - wResult->int32Result = thiz->CanPutXMP ( xmpPacket, xmpPacketLen ); - } - - EndPerfCheck ( kAPIPerf_CanPutXMP ); - XMP_EXIT -} - -// ================================================================================================= - -#if __cplusplus -} -#endif diff --git a/source/XMPFiles/XMPFiles.cpp b/source/XMPFiles/XMPFiles.cpp deleted file mode 100644 index ba16e3f..0000000 --- a/source/XMPFiles/XMPFiles.cpp +++ /dev/null @@ -1,1536 +0,0 @@ -// ================================================================================================= -// ADOBE SYSTEMS INCORPORATED -// Copyright 2004 Adobe Systems Incorporated -// 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 <vector> -#include <string.h> - -#include "XMPFiles_Impl.hpp" -#include "UnicodeConversions.hpp" - -// These are the official, fully supported handlers. -#include "FileHandlers/JPEG_Handler.hpp" -#include "FileHandlers/TIFF_Handler.hpp" -#include "FileHandlers/PSD_Handler.hpp" -#include "FileHandlers/InDesign_Handler.hpp" -#include "FileHandlers/PostScript_Handler.hpp" -#include "FileHandlers/Scanner_Handler.hpp" -#include "FileHandlers/MPEG2_Handler.hpp" -#include "FileHandlers/PNG_Handler.hpp" -#include "FileHandlers/RIFF_Handler.hpp" -#include "FileHandlers/MP3_Handler.hpp" -#include "FileHandlers/SWF_Handler.hpp" -#include "FileHandlers/UCF_Handler.hpp" -#include "FileHandlers/MPEG4_Handler.hpp" -#include "FileHandlers/FLV_Handler.hpp" -#include "FileHandlers/P2_Handler.hpp" -#include "FileHandlers/SonyHDV_Handler.hpp" -#include "FileHandlers/XDCAM_Handler.hpp" -#include "FileHandlers/XDCAMEX_Handler.hpp" -#include "FileHandlers/AVCHD_Handler.hpp" -#include "FileHandlers/ASF_Handler.hpp" - -// ================================================================================================= -/// \file XMPFiles.cpp -/// \brief High level support to access metadata in files of interest to Adobe applications. -/// -/// This header ... -/// -// ================================================================================================= - -// ================================================================================================= - -XMP_Int32 sXMPFilesInitCount = 0; - -#if GatherPerformanceData - APIPerfCollection* sAPIPerf = 0; -#endif - -// These are embedded version strings. - -#if XMP_DebugBuild - #define kXMPFiles_DebugFlag 1 -#else - #define kXMPFiles_DebugFlag 0 -#endif - -#define kXMPFiles_VersionNumber ( (kXMPFiles_DebugFlag << 31) | \ - (XMP_API_VERSION_MAJOR << 24) | \ - (XMP_API_VERSION_MINOR << 16) | \ - (XMP_API_VERSION_MICRO << 8) ) - - #define kXMPFilesName "XMP Files" - #define kXMPFiles_VersionMessage kXMPFilesName " " XMP_API_VERSION_STRING -const char * kXMPFiles_EmbeddedVersion = kXMPFiles_VersionMessage; -const char * kXMPFiles_EmbeddedCopyright = kXMPFilesName " " kXMP_CopyrightStr; - -// ================================================================================================= - -struct XMPFileHandlerInfo { - XMP_FileFormat format; - XMP_OptionBits flags; - void * checkProc; - XMPFileHandlerCTor handlerCTor; - XMPFileHandlerInfo() : format(0), flags(0), checkProc(0), handlerCTor(0) {}; - XMPFileHandlerInfo ( XMP_FileFormat _format, XMP_OptionBits _flags, - CheckFileFormatProc _checkProc, XMPFileHandlerCTor _handlerCTor ) - : format(_format), flags(_flags), checkProc((void*)_checkProc), handlerCTor(_handlerCTor) {}; - XMPFileHandlerInfo ( XMP_FileFormat _format, XMP_OptionBits _flags, - CheckFolderFormatProc _checkProc, XMPFileHandlerCTor _handlerCTor ) - : format(_format), flags(_flags), checkProc((void*)_checkProc), handlerCTor(_handlerCTor) {}; -}; - -// Don't use a map for the handler tables, -typedef std::map <XMP_FileFormat, XMPFileHandlerInfo> XMPFileHandlerTable; -typedef XMPFileHandlerTable::iterator XMPFileHandlerTablePos; -typedef std::pair <XMP_FileFormat, XMPFileHandlerInfo> XMPFileHandlerTablePair; - -static XMPFileHandlerTable * sFolderHandlers = 0; // The directory-oriented handlers. -static XMPFileHandlerTable * sNormalHandlers = 0; // The normal file-oriented handlers. -static XMPFileHandlerTable * sOwningHandlers = 0; // The file-oriented handlers that "own" the file. - -static XMPFileHandlerInfo kScannerHandlerInfo ( kXMP_UnknownFile, kScanner_HandlerFlags, (CheckFileFormatProc)0, Scanner_MetaHandlerCTor ); - -// ================================================================================================= - -static void -RegisterFolderHandler ( XMP_FileFormat format, - XMP_OptionBits flags, - CheckFolderFormatProc checkProc, - XMPFileHandlerCTor handlerCTor ) -{ - XMP_Assert ( format != kXMP_UnknownFile ); - std::string noExt; - - XMP_Assert ( flags & kXMPFiles_HandlerOwnsFile ); - XMP_Assert ( flags & kXMPFiles_FolderBasedFormat ); - XMP_Assert ( (flags & kXMPFiles_CanInjectXMP) ? (flags & kXMPFiles_CanExpand) : 1 ); - - XMP_Assert ( sFolderHandlers->find ( format ) == sFolderHandlers->end() ); - XMP_Assert ( sNormalHandlers->find ( format ) == sNormalHandlers->end() ); - XMP_Assert ( sOwningHandlers->find ( format ) == sOwningHandlers->end() ); - - XMPFileHandlerInfo handlerInfo ( format, flags, checkProc, handlerCTor ); - sFolderHandlers->insert ( sFolderHandlers->end(), XMPFileHandlerTablePair ( format, handlerInfo ) ); - -} // RegisterFolderHandler - -// ================================================================================================= - -static void -RegisterNormalHandler ( XMP_FileFormat format, - XMP_OptionBits flags, - CheckFileFormatProc checkProc, - XMPFileHandlerCTor handlerCTor ) -{ - XMP_Assert ( format != kXMP_UnknownFile ); - std::string noExt; - - XMP_Assert ( ! (flags & kXMPFiles_HandlerOwnsFile) ); - XMP_Assert ( ! (flags & kXMPFiles_FolderBasedFormat) ); - XMP_Assert ( (flags & kXMPFiles_CanInjectXMP) ? (flags & kXMPFiles_CanExpand) : 1 ); - - XMP_Assert ( sFolderHandlers->find ( format ) == sFolderHandlers->end() ); - XMP_Assert ( sNormalHandlers->find ( format ) == sNormalHandlers->end() ); - XMP_Assert ( sOwningHandlers->find ( format ) == sOwningHandlers->end() ); - - XMPFileHandlerInfo handlerInfo ( format, flags, checkProc, handlerCTor ); - sNormalHandlers->insert ( sNormalHandlers->end(), XMPFileHandlerTablePair ( format, handlerInfo ) ); - -} // RegisterNormalHandler - -// ================================================================================================= - -static void -RegisterOwningHandler ( XMP_FileFormat format, - XMP_OptionBits flags, - CheckFileFormatProc checkProc, - XMPFileHandlerCTor handlerCTor ) -{ - XMP_Assert ( format != kXMP_UnknownFile ); - std::string noExt; - - XMP_Assert ( flags & kXMPFiles_HandlerOwnsFile ); - XMP_Assert ( ! (flags & kXMPFiles_FolderBasedFormat) ); - XMP_Assert ( (flags & kXMPFiles_CanInjectXMP) ? (flags & kXMPFiles_CanExpand) : 1 ); - - XMP_Assert ( sFolderHandlers->find ( format ) == sFolderHandlers->end() ); - XMP_Assert ( sNormalHandlers->find ( format ) == sNormalHandlers->end() ); - XMP_Assert ( sOwningHandlers->find ( format ) == sOwningHandlers->end() ); - - XMPFileHandlerInfo handlerInfo ( format, flags, checkProc, handlerCTor ); - sOwningHandlers->insert ( sOwningHandlers->end(), XMPFileHandlerTablePair ( format, handlerInfo ) ); - -} // RegisterOwningHandler - -// ================================================================================================= - -static XMPFileHandlerInfo * -PickDefaultHandler ( XMP_FileFormat format, const std::string & fileExt ) -{ - if ( (format == kXMP_UnknownFile) && (! fileExt.empty()) ) { - for ( int i = 0; kFileExtMap[i].format != 0; ++i ) { - if ( fileExt == kFileExtMap[i].ext ) { - format = kFileExtMap[i].format; - break; - } - } - } - - if ( format == kXMP_UnknownFile ) return 0; - - XMPFileHandlerTablePos handlerPos; - - handlerPos = sNormalHandlers->find ( format ); - if ( handlerPos != sNormalHandlers->end() ) return &handlerPos->second; - - handlerPos = sOwningHandlers->find ( format ); - if ( handlerPos != sOwningHandlers->end() ) return &handlerPos->second; - - handlerPos = sFolderHandlers->find ( format ); - if ( handlerPos != sFolderHandlers->end() ) return &handlerPos->second; - - return 0; - -} // PickDefaultHandler - -// ================================================================================================= - -static const char * kP2ContentChildren[] = { "CLIP", "VIDEO", "AUDIO", "ICON", "VOICE", "PROXY", 0 }; - -static inline bool CheckP2ContentChild ( const std::string & folderName ) -{ - for ( int i = 0; kP2ContentChildren[i] != 0; ++i ) { - if ( folderName == kP2ContentChildren[i] ) return true; - } - return false; -} - -// ------------------------------------------------------------------------------------------------- - -static XMP_FileFormat -CheckParentFolderNames ( const std::string & rootPath, const std::string & gpName, - const std::string & parentName, const std::string & leafName ) -{ - IgnoreParam ( parentName ); - - // This is called when the input path to XMPFiles::OpenFile names an existing file. We need to - // quickly decide if this might be inside a folder-handler's structure. See if the containing - // folders might match any of the registered folder handlers. This check does not have to be - // precise, the handler will do that. This does have to be fast. - // - // Since we don't have many folder handlers, this is simple hardwired code. Note that the caller - // has already shifted the names to upper case. - - // P2 .../MyMovie/CONTENTS/<group>/<file>.<ext> - check CONTENTS and <group> - if ( (gpName == "CONTENTS") && CheckP2ContentChild ( parentName ) ) return kXMP_P2File; - - // XDCAM-EX .../MyMovie/BPAV/CLPR/<clip>/<file>.<ext> - check for BPAV/CLPR - // ! This must be checked before XDCAM-SAM because both have a "CLPR" grandparent. - if ( gpName == "CLPR" ) { - std::string tempPath, greatGP; - tempPath = rootPath; - SplitLeafName ( &tempPath, &greatGP ); - MakeUpperCase ( &greatGP ); - if ( greatGP == "BPAV" ) return kXMP_XDCAM_EXFile; - } - - // XDCAM-FAM .../MyMovie/<group>/<file>.<ext> - check that <group> is CLIP, or EDIT, or SUB - // ! The standard says Clip/Edit/Sub, but the caller has already shifted to upper case. - if ( (parentName == "CLIP") || (parentName == "EDIT") || (parentName == "SUB") ) return kXMP_XDCAM_FAMFile; - - // XDCAM-SAM .../MyMovie/PROAV/<group>/<clip>/<file>.<ext> - check for PROAV and CLPR or EDTR - if ( (gpName == "CLPR") || (gpName == "EDTR") ) { - std::string tempPath, greatGP; - tempPath = rootPath; - SplitLeafName ( &tempPath, &greatGP ); - MakeUpperCase ( &greatGP ); - if ( greatGP == "PROAV" ) return kXMP_XDCAM_SAMFile; - } - - // Sony HDV .../MyMovie/VIDEO/HVR/<file>.<ext> - check for VIDEO and HVR - if ( (gpName == "VIDEO") && (parentName == "HVR") ) return kXMP_SonyHDVFile; - - // AVCHD .../MyMovie/BDMV/<group>/<file>.<ext> - check for BDMV and CLIPINF or STREAM - if ( (gpName == "BDMV") && ((parentName == "CLIPINF") || (parentName == "STREAM")) ) return kXMP_AVCHDFile; - - return kXMP_UnknownFile; - -} // CheckParentFolderNames - -// ================================================================================================= - -static XMP_FileFormat -CheckTopFolderName ( const std::string & rootPath ) -{ - // This is called when the input path to XMPFiles::OpenFile does not name an existing file (or - // existing anything). We need to quickly decide if this might be a logical path for a folder - // handler. See if the root contains the top content folder for any of the registered folder - // handlers. This check does not have to be precise, the handler will do that. This does have to - // be fast. - // - // Since we don't have many folder handlers, this is simple hardwired code. - - std::string childPath = rootPath; - childPath += kDirChar; - size_t baseLen = childPath.size(); - - // P2 .../MyMovie/CONTENTS/<group>/... - only check for CONTENTS/CLIP - childPath += "CONTENTS"; - childPath += kDirChar; - childPath += "CLIP"; - if ( GetFileMode ( childPath.c_str() ) == kFMode_IsFolder ) return kXMP_P2File; - childPath.erase ( baseLen ); - - // XDCAM-FAM .../MyMovie/<group>/... - only check for Clip and MEDIAPRO.XML - childPath += "Clip"; // ! Yes, mixed case. - if ( GetFileMode ( childPath.c_str() ) == kFMode_IsFolder ) { - childPath.erase ( baseLen ); - childPath += "MEDIAPRO.XML"; - if ( GetFileMode ( childPath.c_str() ) == kFMode_IsFile ) return kXMP_XDCAM_FAMFile; - } - childPath.erase ( baseLen ); - - // XDCAM-SAM .../MyMovie/PROAV/<group>/... - only check for PROAV/CLPR - childPath += "PROAV"; - childPath += kDirChar; - childPath += "CLPR"; - if ( GetFileMode ( childPath.c_str() ) == kFMode_IsFolder ) return kXMP_XDCAM_SAMFile; - childPath.erase ( baseLen ); - - // XDCAM-EX .../MyMovie/BPAV/<group>/... - check for BPAV/CLPR - childPath += "BPAV"; - childPath += kDirChar; - childPath += "CLPR"; - if ( GetFileMode ( childPath.c_str() ) == kFMode_IsFolder ) return kXMP_XDCAM_EXFile; - childPath.erase ( baseLen ); - - // Sony HDV .../MyMovie/VIDEO/HVR/<file>.<ext> - check for VIDEO/HVR - childPath += "VIDEO"; - childPath += kDirChar; - childPath += "HVR"; - if ( GetFileMode ( childPath.c_str() ) == kFMode_IsFolder ) return kXMP_SonyHDVFile; - childPath.erase ( baseLen ); - - // AVCHD .../MyMovie/BDMV/CLIPINF/<file>.<ext> - check for BDMV/CLIPINF - childPath += "BDMV"; - childPath += kDirChar; - childPath += "CLIPINF"; - if ( GetFileMode ( childPath.c_str() ) == kFMode_IsFolder ) return kXMP_AVCHDFile; - childPath.erase ( baseLen ); - - return kXMP_UnknownFile; - -} // CheckTopFolderName - -// ================================================================================================= - -static XMPFileHandlerInfo * -TryFolderHandlers ( XMP_FileFormat format, - const std::string & rootPath, - const std::string & gpName, - const std::string & parentName, - const std::string & leafName, - XMPFiles * parentObj ) -{ - bool foundHandler = false; - XMPFileHandlerInfo * handlerInfo = 0; - XMPFileHandlerTablePos handlerPos; - - // We know we're in a possible context for a folder-oriented handler, so try them. - - if ( format != kXMP_UnknownFile ) { - - // Have an explicit format, pick that or nothing. - handlerPos = sFolderHandlers->find ( format ); - if ( handlerPos != sFolderHandlers->end() ) { - handlerInfo = &handlerPos->second; - CheckFolderFormatProc CheckProc = (CheckFolderFormatProc) (handlerInfo->checkProc); - foundHandler = CheckProc ( handlerInfo->format, rootPath, gpName, parentName, leafName, parentObj ); - XMP_Assert ( foundHandler || (parentObj->tempPtr == 0) ); - } - - } else { - - // Try all of the folder handlers. - for ( handlerPos = sFolderHandlers->begin(); handlerPos != sFolderHandlers->end(); ++handlerPos ) { - handlerInfo = &handlerPos->second; - CheckFolderFormatProc CheckProc = (CheckFolderFormatProc) (handlerInfo->checkProc); - foundHandler = CheckProc ( handlerInfo->format, rootPath, gpName, parentName, leafName, parentObj ); - XMP_Assert ( foundHandler || (parentObj->tempPtr == 0) ); - if ( foundHandler ) break; // ! Exit before incrementing handlerPos. - } - - } - - if ( ! foundHandler ) handlerInfo = 0; - return handlerInfo; - -} // TryFolderHandlers - -// ================================================================================================= - -static XMPFileHandlerInfo * -SelectSmartHandler ( XMPFiles * thiz, XMP_StringPtr clientPath, XMP_FileFormat format, XMP_OptionBits openFlags ) -{ - - // There are 4 stages in finding a handler, ending at the first success: - // 1. If the client passes in a format, try that handler. - // 2. Try all of the folder-oriented handlers. - // 3. Try a file-oriented handler based on the file extension. - // 4. Try all of the file-oriented handlers. - // - // The most common case is almost certainly #3, so we want to get there quickly. Most of the - // time the client won't pass in a format, so #1 takes no time. The folder-oriented handler - // checks are preceded by minimal folder checks. These checks are meant to be fast in the - // failure case. The folder-oriented checks have to go before the general file-oriented checks - // because the client path might be to one of the inner files, and we might have a file-oriented - // handler for that kind of file, but we want to recognize the clip. More details are below. - // - // In brief, the folder-oriented formats use shallow trees with specific folder names and - // highly stylized file names. The user thinks of the tree as a collection of clips, each clip - // is stored as multiple files for video, audio, metadata, etc. The folder-oriented stage has - // to be first because there can be files in the structure that are also covered by a - // file-oriented handler. - // - // In the file-oriented case, the CheckProc should do as little as possible to determine the - // format, based on the actual file content. If that is not possible, use the format hint. The - // initial CheckProc calls (steps 1 and 3) has the presumed format in this->format, the later - // calls (step 4) have kXMP_UnknownFile there. - // - // The folder-oriented checks need to be well optimized since the formats are relatively rare, - // but have to go first and could require multiple file system calls to identify. We want to - // get to the first file-oriented guess as quickly as possible, that is the real handler most of - // the time. - // - // The folder-oriented handlers are for things like P2 and XDCAM that use files distributed in a - // well defined folder structure. Using a portion of P2 as an example: - // .../MyMovie - // CONTENTS - // CLIP - // 0001AB.XML - // 0002CD.XML - // VIDEO - // 0001AB.MXF - // 0002CD.MXF - // VOICE - // 0001AB.WAV - // 0002CD.WAV - // - // The user thinks of .../MyMovie as the container of P2 stuff, in this case containing 2 clips - // called 0001AB and 0002CD. The exact folder structure and file layout differs, but the basic - // concepts carry across all of the folder-oriented handlers. - // - // The client path can be a conceptual clip path like .../MyMovie/0001AB, or a full path to any - // of the contained files. For file paths we have to behave the same as the implied conceptual - // path, e.g. we don't want .../MyMovie/CONTENTS/VOICE/0001AB.WAV to invoke the WAV handler. - // There might also be a mapping from user friendly names to clip names (e.g. Intro to 0001AB). - // If so that is private to the handler and does not affect this code. - // - // In order to properly handle the file path input we have to look for the folder-oriented case - // before any of the file-oriented cases. And since these are relatively rare, hence fail most of - // the time, we have to get in and out fast in the not handled case. That is what we do here. - // - // The folder-oriented processing done here is roughly: - // - // 1. Get the state of the client path: does-not-exist, is-file, is-folder, is-other. - // 2. Reject is-folder and is-other, they can't possibly be a valid case. - // 3. For does-not-exist: - // 3a. Split the client path into a leaf component and root path. - // 3b. Make sure the root path names an existing folder. - // 3c. Make sure the root folder has a viable top level child folder (e.g. CONTENTS). - // 4. For is-file: - // 4a. Split the client path into a root path, grandparent folder, parent folder, and leaf name. - // 4b. Make sure the parent or grandparent has a viable name (e.g. CONTENTS). - // 5. Try the registered folder handlers. - // - // For the common case of "regular" files, we should only get as far as 3b. This is just 1 file - // system call to get the client path state and some string processing. - - char openMode = 'r'; - if ( openFlags & kXMPFiles_OpenForUpdate ) openMode = 'w'; - - XMPFileHandlerInfo * handlerInfo = 0; - bool foundHandler = false; - - FileMode clientMode = GetFileMode ( clientPath ); - if ( (clientMode == kFMode_IsFolder) || (clientMode == kFMode_IsOther) ) return 0; - - // Extract some info from the clientPath, needed for various checks. - - std::string rootPath, leafName, fileExt; - - rootPath = clientPath; - SplitLeafName ( &rootPath, &leafName ); - if ( leafName.empty() ) return 0; - - size_t extPos = leafName.size(); - for ( --extPos; extPos > 0; --extPos ) if ( leafName[extPos] == '.' ) break; - if ( leafName[extPos] == '.' ) { - fileExt.assign ( &leafName[extPos+1] ); - MakeLowerCase ( &fileExt ); - leafName.erase ( extPos ); - } - - thiz->format = kXMP_UnknownFile; // Make sure it is preset for later checks. - thiz->openFlags = openFlags; - - // If the client passed in a format, try that first. - - if ( format != kXMP_UnknownFile ) { - - std::string emptyStr; - handlerInfo = PickDefaultHandler ( format, emptyStr ); // Picks based on just the format. - - if ( handlerInfo != 0 ) { - - if ( (thiz->fileRef == 0) && (! (handlerInfo->flags & kXMPFiles_HandlerOwnsFile)) ) { - thiz->fileRef = LFA_Open ( clientPath, openMode ); - XMP_Assert ( thiz->fileRef != 0 ); // LFA_Open must either succeed or throw. - } - thiz->format = format; // ! Hack to tell the CheckProc thiz is an initial call. - - if ( ! (handlerInfo->flags & kXMPFiles_FolderBasedFormat) ) { - CheckFileFormatProc CheckProc = (CheckFileFormatProc) (handlerInfo->checkProc); - foundHandler = CheckProc ( format, clientPath, thiz->fileRef, thiz ); - } else { - // *** Don't try here yet. These are messy, needing existence checking and path processing. - // *** CheckFolderFormatProc CheckProc = (CheckFolderFormatProc) (handlerInfo->checkProc); - // *** foundHandler = CheckProc ( handlerInfo->format, rootPath, gpName, parentName, leafName, thiz ); - // *** Don't let OpenStrictly cause an early exit: - if ( openFlags & kXMPFiles_OpenStrictly ) openFlags ^= kXMPFiles_OpenStrictly; - } - - XMP_Assert ( foundHandler || (thiz->tempPtr == 0) ); - if ( foundHandler ) return handlerInfo; - handlerInfo = 0; // ! Clear again for later use. - - } - - if ( openFlags & kXMPFiles_OpenStrictly ) return 0; - - } - - // Try the folder handlers if appropriate. - - XMP_Assert ( handlerInfo == 0 ); - XMP_Assert ( (clientMode == kFMode_IsFile) || (clientMode == kFMode_DoesNotExist) ); - - std::string gpName, parentName; - - if ( clientMode == kFMode_DoesNotExist ) { - - // 3. For does-not-exist: - // 3a. Split the client path into a leaf component and root path. - // 3b. Make sure the root path names an existing folder. - // 3c. Make sure the root folder has a viable top level child folder. - - // ! This does "return 0" on failure, the file does not exist so a normal file handler can't apply. - - if ( GetFileMode ( rootPath.c_str() ) != kFMode_IsFolder ) return 0; - thiz->format = CheckTopFolderName ( rootPath ); - if ( thiz->format == kXMP_UnknownFile ) return 0; - - handlerInfo = TryFolderHandlers ( thiz->format, rootPath, gpName, parentName, leafName, thiz ); // ! Parent and GP are empty. - return handlerInfo; // ! Return found handler or 0. - - } - - XMP_Assert ( clientMode == kFMode_IsFile ); - - // 4. For is-file: - // 4a. Split the client path into root, grandparent, parent, and leaf. - // 4b. Make sure the parent or grandparent has a viable name. - - // ! Don't "return 0" on failure, this has to fall through to the normal file handlers. - - SplitLeafName ( &rootPath, &parentName ); - SplitLeafName ( &rootPath, &gpName ); - std::string origGPName ( gpName ); // ! Save the original case for XDCAM-FAM. - MakeUpperCase ( &parentName ); - MakeUpperCase ( &gpName ); - - thiz->format = CheckParentFolderNames ( rootPath, gpName, parentName, leafName ); - - if ( thiz->format != kXMP_UnknownFile ) { - - if ( (thiz->format == kXMP_XDCAM_FAMFile) && - ((parentName == "CLIP") || (parentName == "EDIT") || (parentName == "SUB")) ) { - // ! The standard says Clip/Edit/Sub, but we just shifted to upper case. - gpName = origGPName; // ! XDCAM-FAM has just 1 level of inner folder, preserve the "MyMovie" case. - } - - handlerInfo = TryFolderHandlers ( thiz->format, rootPath, gpName, parentName, leafName, thiz ); - if ( handlerInfo != 0 ) return handlerInfo; - - } - - // Try an initial file-oriented handler based on the extension. - - handlerInfo = PickDefaultHandler ( kXMP_UnknownFile, fileExt ); // Picks based on just the extension. - - if ( handlerInfo != 0 ) { - if ( (thiz->fileRef == 0) && (! (handlerInfo->flags & kXMPFiles_HandlerOwnsFile)) ) { - thiz->fileRef = LFA_Open ( clientPath, openMode ); - XMP_Assert ( thiz->fileRef != 0 ); // LFA_Open must either succeed or throw. - } else if ( (thiz->fileRef != 0) && (handlerInfo->flags & kXMPFiles_HandlerOwnsFile) ) { - LFA_Close ( thiz->fileRef ); - thiz->fileRef = 0; - } - thiz->format = handlerInfo->format; // ! Hack to tell the CheckProc thiz is an initial call. - CheckFileFormatProc CheckProc = (CheckFileFormatProc) (handlerInfo->checkProc); - foundHandler = CheckProc ( handlerInfo->format, clientPath, thiz->fileRef, thiz ); - XMP_Assert ( foundHandler || (thiz->tempPtr == 0) ); - if ( foundHandler ) return handlerInfo; - } - - // Search the handlers that don't want to open the file themselves. - - if ( thiz->fileRef == 0 ) thiz->fileRef = LFA_Open ( clientPath, openMode ); - XMP_Assert ( thiz->fileRef != 0 ); // LFA_Open must either succeed or throw. - XMPFileHandlerTablePos handlerPos = sNormalHandlers->begin(); - - for ( ; handlerPos != sNormalHandlers->end(); ++handlerPos ) { - thiz->format = kXMP_UnknownFile; // ! Hack to tell the CheckProc this is not an initial call. - handlerInfo = &handlerPos->second; - CheckFileFormatProc CheckProc = (CheckFileFormatProc) (handlerInfo->checkProc); - foundHandler = CheckProc ( handlerInfo->format, clientPath, thiz->fileRef, thiz ); - XMP_Assert ( foundHandler || (thiz->tempPtr == 0) ); - if ( foundHandler ) return handlerInfo; - } - - // Search the handlers that do want to open the file themselves. - - LFA_Close ( thiz->fileRef ); - thiz->fileRef = 0; - handlerPos = sOwningHandlers->begin(); - - for ( ; handlerPos != sOwningHandlers->end(); ++handlerPos ) { - thiz->format = kXMP_UnknownFile; // ! Hack to tell the CheckProc this is not an initial call. - handlerInfo = &handlerPos->second; - CheckFileFormatProc CheckProc = (CheckFileFormatProc) (handlerInfo->checkProc); - foundHandler = CheckProc ( handlerInfo->format, clientPath, thiz->fileRef, thiz ); - XMP_Assert ( foundHandler || (thiz->tempPtr == 0) ); - if ( foundHandler ) return handlerInfo; - } - - // Failed to find a smart handler. - - return 0; - -} // SelectSmartHandler - -// ================================================================================================= - -/* class-static */ -void -XMPFiles::GetVersionInfo ( XMP_VersionInfo * info ) -{ - - memset ( info, 0, sizeof(XMP_VersionInfo) ); - - info->major = XMP_API_VERSION_MAJOR; - info->minor = XMP_API_VERSION_MINOR; - info->micro = XMP_API_VERSION_MICRO; - info->isDebug = kXMPFiles_DebugFlag; - info->flags = 0; // ! None defined yet. - info->message = kXMPFiles_VersionMessage; - -} // XMPFiles::GetVersionInfo - -// ================================================================================================= - -#if XMP_TraceFilesCalls - FILE * xmpFilesLog = stderr; -#endif - -#if UseGlobalLibraryLock & (! XMP_StaticBuild ) - XMP_BasicMutex sLibraryLock; // ! Handled in XMPMeta for static builds. -#endif - -/* class static */ -bool -XMPFiles::Initialize ( XMP_OptionBits options /* = 0 */ ) -{ - ++sXMPFilesInitCount; - if ( sXMPFilesInitCount > 1 ) return true; - - #if XMP_TraceFilesCallsToFile - xmpFilesLog = fopen ( "XMPFilesLog.txt", "w" ); - if ( xmpFilesLog == 0 ) xmpFilesLog = stderr; - #endif - - #if UseGlobalLibraryLock & (! XMP_StaticBuild ) - InitializeBasicMutex ( sLibraryLock ); // ! Handled in XMPMeta for static builds. - #endif - - SXMPMeta::Initialize(); // Just in case the client does not. - - if ( ! Initialize_LibUtils() ) return false; - - #if GatherPerformanceData - sAPIPerf = new APIPerfCollection; - #endif - - XMP_Uns16 endianInt = 0x00FF; - XMP_Uns8 endianByte = *((XMP_Uns8*)&endianInt); - if ( kBigEndianHost ) { - if ( endianByte != 0 ) XMP_Throw ( "Big endian flag mismatch", kXMPErr_InternalFailure ); - } else { - if ( endianByte != 0xFF ) XMP_Throw ( "Little endian flag mismatch", kXMPErr_InternalFailure ); - } - - XMP_Assert ( kUTF8_PacketHeaderLen == strlen ( "<?xpacket begin='xxx' id='W5M0MpCehiHzreSzNTczkc9d'" ) ); - XMP_Assert ( kUTF8_PacketTrailerLen == strlen ( (const char *) kUTF8_PacketTrailer ) ); - - sFolderHandlers = new XMPFileHandlerTable; - sNormalHandlers = new XMPFileHandlerTable; - sOwningHandlers = new XMPFileHandlerTable; - - InitializeUnicodeConversions(); - - ignoreLocalText = XMP_OptionIsSet ( options, kXMPFiles_IgnoreLocalText ); - #if XMP_UNIXBuild - if ( ! ignoreLocalText ) XMP_Throw ( "Generic UNIX clients must pass kXMPFiles_IgnoreLocalText", kXMPErr_EnforceFailure ); - #endif - - // ----------------------------------------- - // Register the directory-oriented handlers. - - RegisterFolderHandler ( kXMP_P2File, kP2_HandlerFlags, P2_CheckFormat, P2_MetaHandlerCTor ); - RegisterFolderHandler ( kXMP_SonyHDVFile, kSonyHDV_HandlerFlags, SonyHDV_CheckFormat, SonyHDV_MetaHandlerCTor ); - RegisterFolderHandler ( kXMP_XDCAM_FAMFile, kXDCAM_HandlerFlags, XDCAM_CheckFormat, XDCAM_MetaHandlerCTor ); - RegisterFolderHandler ( kXMP_XDCAM_SAMFile, kXDCAM_HandlerFlags, XDCAM_CheckFormat, XDCAM_MetaHandlerCTor ); - RegisterFolderHandler ( kXMP_XDCAM_EXFile, kXDCAMEX_HandlerFlags, XDCAMEX_CheckFormat, XDCAMEX_MetaHandlerCTor ); - RegisterFolderHandler ( kXMP_AVCHDFile, kAVCHD_HandlerFlags, AVCHD_CheckFormat, AVCHD_MetaHandlerCTor ); - - // ------------------------------------------------------------------------------------------ - // Register the file-oriented handlers that don't want to open and close the file themselves. - - RegisterNormalHandler ( kXMP_JPEGFile, kJPEG_HandlerFlags, JPEG_CheckFormat, JPEG_MetaHandlerCTor ); - RegisterNormalHandler ( kXMP_TIFFFile, kTIFF_HandlerFlags, TIFF_CheckFormat, TIFF_MetaHandlerCTor ); - RegisterNormalHandler ( kXMP_PhotoshopFile, kPSD_HandlerFlags, PSD_CheckFormat, PSD_MetaHandlerCTor ); - RegisterNormalHandler ( kXMP_InDesignFile, kInDesign_HandlerFlags, InDesign_CheckFormat, InDesign_MetaHandlerCTor ); - RegisterNormalHandler ( kXMP_PNGFile, kPNG_HandlerFlags, PNG_CheckFormat, PNG_MetaHandlerCTor ); - - // ! EPS and PostScript have the same handler, EPS is a proper subset of PostScript. - RegisterNormalHandler ( kXMP_EPSFile, kPostScript_HandlerFlags, PostScript_CheckFormat, PostScript_MetaHandlerCTor ); - RegisterNormalHandler ( kXMP_PostScriptFile, kPostScript_HandlerFlags, PostScript_CheckFormat, PostScript_MetaHandlerCTor ); - RegisterNormalHandler ( kXMP_WMAVFile, kASF_HandlerFlags, ASF_CheckFormat, ASF_MetaHandlerCTor ); - RegisterNormalHandler ( kXMP_MP3File, kMP3_HandlerFlags, MP3_CheckFormat, MP3_MetaHandlerCTor ); - RegisterNormalHandler ( kXMP_WAVFile, kRIFF_HandlerFlags, RIFF_CheckFormat, RIFF_MetaHandlerCTor ); - RegisterNormalHandler ( kXMP_AVIFile, kRIFF_HandlerFlags, RIFF_CheckFormat, RIFF_MetaHandlerCTor ); - - RegisterNormalHandler ( kXMP_SWFFile, kSWF_HandlerFlags, SWF_CheckFormat, SWF_MetaHandlerCTor ); - RegisterNormalHandler ( kXMP_UCFFile, kUCF_HandlerFlags, UCF_CheckFormat, UCF_MetaHandlerCTor ); - RegisterNormalHandler ( kXMP_MPEG4File, kMPEG4_HandlerFlags, MPEG4_CheckFormat, MPEG4_MetaHandlerCTor ); - RegisterNormalHandler ( kXMP_MOVFile, kMPEG4_HandlerFlags, MPEG4_CheckFormat, MPEG4_MetaHandlerCTor ); // ! Yes, MPEG-4 includes MOV. - RegisterNormalHandler ( kXMP_FLVFile, kFLV_HandlerFlags, FLV_CheckFormat, FLV_MetaHandlerCTor ); - - // --------------------------------------------------------------------------------------- - // Register the file-oriented handlers that do want to open and close the file themselves. - - RegisterOwningHandler ( kXMP_MPEGFile, kMPEG2_HandlerFlags, MPEG2_CheckFormat, MPEG2_MetaHandlerCTor ); - RegisterOwningHandler ( kXMP_MPEG2File, kMPEG2_HandlerFlags, MPEG2_CheckFormat, MPEG2_MetaHandlerCTor ); - - // Make sure the embedded info strings are referenced and kept. - if ( (kXMPFiles_EmbeddedVersion[0] == 0) || (kXMPFiles_EmbeddedCopyright[0] == 0) ) return false; - // Verify critical type sizes. - XMP_Assert ( sizeof(XMP_Int8) == 1 ); - XMP_Assert ( sizeof(XMP_Int16) == 2 ); - XMP_Assert ( sizeof(XMP_Int32) == 4 ); - XMP_Assert ( sizeof(XMP_Int64) == 8 ); - XMP_Assert ( sizeof(XMP_Uns8) == 1 ); - XMP_Assert ( sizeof(XMP_Uns16) == 2 ); - XMP_Assert ( sizeof(XMP_Uns32) == 4 ); - XMP_Assert ( sizeof(XMP_Uns64) == 8 ); - - return true; - -} // XMPFiles::Initialize - -// ================================================================================================= - -#if GatherPerformanceData - -#if XMP_WinBuild - #pragma warning ( disable : 4996 ) // '...' was declared deprecated -#endif - -#include "PerfUtils.cpp" - -static void ReportPerformanceData() -{ - struct SummaryInfo { - size_t callCount; - double totalTime; - SummaryInfo() : callCount(0), totalTime(0.0) {}; - }; - - SummaryInfo perfSummary [kAPIPerfProcCount]; - - XMP_DateTime now; - SXMPUtils::CurrentDateTime ( &now ); - std::string nowStr; - SXMPUtils::ConvertFromDate ( now, &nowStr ); - - #if XMP_WinBuild - #define kPerfLogPath "C:\\XMPFilesPerformanceLog.txt" - #else - #define kPerfLogPath "/XMPFilesPerformanceLog.txt" - #endif - FILE * perfLog = fopen ( kPerfLogPath, "ab" ); - if ( perfLog == 0 ) return; - - fprintf ( perfLog, "\n\n// =================================================================================================\n\n" ); - fprintf ( perfLog, "XMPFiles performance data\n" ); - fprintf ( perfLog, " %s\n", kXMPFiles_VersionMessage ); - fprintf ( perfLog, " Reported at %s\n", nowStr.c_str() ); - fprintf ( perfLog, " %s\n", PerfUtils::GetTimerInfo() ); - - // Gather and report the summary info. - - for ( size_t i = 0; i < sAPIPerf->size(); ++i ) { - SummaryInfo& summaryItem = perfSummary [(*sAPIPerf)[i].whichProc]; - ++summaryItem.callCount; - summaryItem.totalTime += (*sAPIPerf)[i].elapsedTime; - } - - fprintf ( perfLog, "\nSummary data:\n" ); - - for ( size_t i = 0; i < kAPIPerfProcCount; ++i ) { - long averageTime = 0; - if ( perfSummary[i].callCount != 0 ) { - averageTime = (long) (((perfSummary[i].totalTime/perfSummary[i].callCount) * 1000.0*1000.0) + 0.5); - } - fprintf ( perfLog, " %s, %d total calls, %d average microseconds per call\n", - kAPIPerfNames[i], perfSummary[i].callCount, averageTime ); - } - - fprintf ( perfLog, "\nPer-call data:\n" ); - - // Report the info for each call. - - for ( size_t i = 0; i < sAPIPerf->size(); ++i ) { - long time = (long) (((*sAPIPerf)[i].elapsedTime * 1000.0*1000.0) + 0.5); - fprintf ( perfLog, " %s, %d microSec, ref %.8X, \"%s\"\n", - kAPIPerfNames[(*sAPIPerf)[i].whichProc], time, - (*sAPIPerf)[i].xmpFilesRef, (*sAPIPerf)[i].extraInfo.c_str() ); - } - - fclose ( perfLog ); - -} // ReportAReportPerformanceDataPIPerformance - -#endif - -// ================================================================================================= - -/* class static */ -void -XMPFiles::Terminate() -{ - --sXMPFilesInitCount; - if ( sXMPFilesInitCount != 0 ) return; // Not ready to terminate, or already terminated. - - #if GatherPerformanceData - ReportPerformanceData(); - EliminateGlobal ( sAPIPerf ); - #endif - - EliminateGlobal ( sFolderHandlers ); - EliminateGlobal ( sNormalHandlers ); - EliminateGlobal ( sOwningHandlers ); - - SXMPMeta::Terminate(); // Just in case the client does not. - - Terminate_LibUtils(); - - #if UseGlobalLibraryLock & (! XMP_StaticBuild ) - TerminateBasicMutex ( sLibraryLock ); // ! Handled in XMPMeta for static builds. - #endif - - #if XMP_TraceFilesCallsToFile - if ( xmpFilesLog != stderr ) fclose ( xmpFilesLog ); - xmpFilesLog = stderr; - #endif - -} // XMPFiles::Terminate - -// ================================================================================================= - -XMPFiles::XMPFiles() : - clientRefs(0), - format(kXMP_UnknownFile), - fileRef(0), - openFlags(0), - abortProc(0), - abortArg(0), - handler(0), - tempPtr(0), - tempUI32(0) -{ - // Nothing more to do, clientRefs is incremented in wrapper. - -} // XMPFiles::XMPFiles - -// ================================================================================================= - -XMPFiles::~XMPFiles() -{ - XMP_Assert ( this->clientRefs <= 0 ); - - if ( this->handler != 0 ) { - delete this->handler; - this->handler = 0; - } - - if ( this->fileRef != 0 ) { - LFA_Close ( this->fileRef ); - this->fileRef = 0; - } - - if ( this->tempPtr != 0 ) free ( this->tempPtr ); // ! Must have been malloc-ed! - -} // XMPFiles::~XMPFiles - -// ================================================================================================= - -/* class static */ -bool -XMPFiles::GetFormatInfo ( XMP_FileFormat format, - XMP_OptionBits * flags /* = 0 */ ) -{ - if ( flags == 0 ) flags = &voidOptionBits; - - XMPFileHandlerTablePos handlerPos; - - handlerPos = sFolderHandlers->find ( format ); - if ( handlerPos != sFolderHandlers->end() ) { - *flags = handlerPos->second.flags; - return true; - } - - handlerPos = sNormalHandlers->find ( format ); - if ( handlerPos != sNormalHandlers->end() ) { - *flags = handlerPos->second.flags; - return true; - } - - handlerPos = sOwningHandlers->find ( format ); - if ( handlerPos != sOwningHandlers->end() ) { - *flags = handlerPos->second.flags; - return true; - } - - return false; - -} // XMPFiles::GetFormatInfo - -// ================================================================================================= - -/* class static */ -XMP_FileFormat -XMPFiles::CheckFileFormat ( XMP_StringPtr filePath ) -{ - if ( (filePath == 0) || (*filePath == 0) ) return kXMP_UnknownFile; - - XMPFiles bogus; - XMPFileHandlerInfo * handlerInfo = SelectSmartHandler ( &bogus, filePath, kXMP_UnknownFile, kXMPFiles_OpenForRead ); - if ( handlerInfo == 0 ) return kXMP_UnknownFile; - return handlerInfo->format; - -} // XMPFiles::CheckFileFormat - -// ================================================================================================= - -/* class static */ -XMP_FileFormat -XMPFiles::CheckPackageFormat ( XMP_StringPtr folderPath ) -{ - // This is called with a path to a folder, and checks to see if that folder is the top level of - // a "package" that should be recognized by one of the folder-oriented handlers. The checks here - // are not overly extensive, but hopefully enough to weed out false positives. - // - // Since we don't have many folder handlers, this is simple hardwired code. - - FileMode folderMode = GetFileMode ( folderPath ); - if ( folderMode != kFMode_IsFolder ) return kXMP_UnknownFile; - - return CheckTopFolderName ( std::string ( folderPath ) ); - -} // XMPFiles::CheckPackageFormat - -// ================================================================================================= - -bool -XMPFiles::OpenFile ( XMP_StringPtr clientPath, - XMP_FileFormat format /* = kXMP_UnknownFile */, - XMP_OptionBits openFlags /* = 0 */ ) -{ - if ( this->handler != 0 ) XMP_Throw ( "File already open", kXMPErr_BadParam ); - if ( this->fileRef != 0 ) { // ! Sanity check to prevent open file leaks. - LFA_Close ( this->fileRef ); - this->fileRef = 0; - } - - this->format = kXMP_UnknownFile; // Make sure it is preset for later check. - this->openFlags = openFlags; - - char openMode = 'r'; - if ( openFlags & kXMPFiles_OpenForUpdate ) openMode = 'w'; - - FileMode clientMode = GetFileMode ( clientPath ); - if ( (clientMode == kFMode_IsFolder) || (clientMode == kFMode_IsOther) ) return false; - XMP_Assert ( (clientMode == kFMode_IsFile) || (clientMode == kFMode_DoesNotExist) ); - - std::string fileExt; // Used to check for camera raw files and OK to scan files. - - if ( clientMode == kFMode_IsFile ) { - - // Find the file extension. OK to be "wrong" for something like "C:\My.dir\file". Any - // filtering looks for matches with real extensions, "dir\file" won't match any of these. - XMP_StringPtr extPos = clientPath + strlen ( clientPath ); - for ( ; (extPos != clientPath) && (*extPos != '.'); --extPos ) {} - if ( *extPos == '.' ) { - fileExt.assign ( extPos+1 ); - MakeLowerCase ( &fileExt ); - } - - // See if this file is one that XMPFiles should never process. - for ( size_t i = 0; kKnownRejectedFiles[i] != 0; ++i ) { - if ( fileExt == kKnownRejectedFiles[i] ) return false; - } - - } - - // Find the handler, fill in the XMPFiles member variables, cache the desired file data. - - XMPFileHandlerInfo * handlerInfo = 0; - XMPFileHandlerCTor handlerCTor = 0; - XMP_OptionBits handlerFlags = 0; - - if ( ! (openFlags & kXMPFiles_OpenUsePacketScanning) ) { - handlerInfo = SelectSmartHandler ( this, clientPath, format, openFlags ); - } - - if ( handlerInfo == 0 ) { - - // No smart handler, packet scan if appropriate. - - if ( clientMode != kFMode_IsFile ) return false; - if ( openFlags & kXMPFiles_OpenUseSmartHandler ) return false; - - if ( openFlags & kXMPFiles_OpenLimitedScanning ) { - bool scanningOK = false; - for ( size_t i = 0; kKnownScannedFiles[i] != 0; ++i ) { - if ( fileExt == kKnownScannedFiles[i] ) { scanningOK = true; break; } - } - if ( ! scanningOK ) return false; - } - - handlerInfo = &kScannerHandlerInfo; - if ( fileRef == 0 ) fileRef = LFA_Open ( clientPath, openMode ); - - } - - XMP_Assert ( handlerInfo != 0 ); - handlerCTor = handlerInfo->handlerCTor; - handlerFlags = handlerInfo->flags; - - this->filePath = clientPath; - - XMPFileHandler* handler = (*handlerCTor) ( this ); - XMP_Assert ( handlerFlags == handler->handlerFlags ); - - this->handler = handler; - if ( this->format == kXMP_UnknownFile ) this->format = handlerInfo->format; // ! The CheckProc might have set it. - - try { - handler->CacheFileData(); - } catch ( ... ) { - delete this->handler; - this->handler = 0; - if ( ! (handlerFlags & kXMPFiles_HandlerOwnsFile) ) { - LFA_Close ( this->fileRef ); - this->fileRef = 0; - } - throw; - } - - if ( handler->containsXMP ) FillPacketInfo ( handler->xmpPacket, &handler->packetInfo ); - - if ( (! (openFlags & kXMPFiles_OpenForUpdate)) && (! (handlerFlags & kXMPFiles_HandlerOwnsFile)) ) { - // Close the disk file now if opened for read-only access. - LFA_Close ( this->fileRef ); - this->fileRef = 0; - } - - return true; - -} // XMPFiles::OpenFile - -// ================================================================================================= - -void -XMPFiles::CloseFile ( XMP_OptionBits closeFlags /* = 0 */ ) -{ - if ( this->handler == 0 ) return; // Return if there is no open file (not an error). - - bool needsUpdate = this->handler->needsUpdate; - XMP_OptionBits handlerFlags = this->handler->handlerFlags; - - // Decide if we're doing a safe update. If so, make sure the handler supports it. All handlers - // that don't own the file tolerate safe update using common code below. - - bool doSafeUpdate = XMP_OptionIsSet ( closeFlags, kXMPFiles_UpdateSafely ); - #if GatherPerformanceData - if ( doSafeUpdate ) sAPIPerf->back().extraInfo += ", safe update"; // Client wants safe update. - #endif - - if ( ! (this->openFlags & kXMPFiles_OpenForUpdate) ) doSafeUpdate = false; - if ( ! needsUpdate ) doSafeUpdate = false; - - bool safeUpdateOK = ( (handlerFlags & kXMPFiles_AllowsSafeUpdate) || - (! (handlerFlags & kXMPFiles_HandlerOwnsFile)) ); - if ( doSafeUpdate && (! safeUpdateOK) ) { - XMP_Throw ( "XMPFiles::CloseFile - Safe update not supported", kXMPErr_Unavailable ); - } - - // Try really hard to make sure the file is closed and the handler is deleted. - - LFA_FileRef origFileRef = this->fileRef; // Used during crash-safe saves. - std::string origFilePath ( this->filePath ); - - LFA_FileRef tempFileRef = 0; - std::string tempFilePath; - - LFA_FileRef copyFileRef = 0; - std::string copyFilePath; - - try { - - if ( (! doSafeUpdate) || (handlerFlags & kXMPFiles_HandlerOwnsFile) ) { // ! Includes no update case. - - // Close the file without doing common crash-safe writing. The handler might do it. - - if ( needsUpdate ) { - #if GatherPerformanceData - sAPIPerf->back().extraInfo += ", direct update"; - #endif - this->handler->UpdateFile ( doSafeUpdate ); - } - - delete this->handler; - this->handler = 0; - if ( this->fileRef != 0 ) LFA_Close ( this->fileRef ); - this->fileRef = 0; - - } else { - - // Update the file in a crash-safe manner. This somewhat convoluted approach preserves - // the ownership, permissions, and Mac resources of the final file - at a slightly - // higher risk of confusion in the event of a midstream crash. - - if ( handlerFlags & kXMPFiles_CanRewrite ) { - - // The handler can rewrite an entire file based on the original. Do this into a temp - // file next to the original, with the same ownership and permissions if possible. - - #if GatherPerformanceData - sAPIPerf->back().extraInfo += ", file rewrite"; - #endif - - CreateTempFile ( origFilePath, &tempFilePath, kCopyMacRsrc ); - XMP_Assert ( tempFileRef == 0 ); - tempFileRef = LFA_Open ( tempFilePath.c_str(), 'w' ); - this->fileRef = tempFileRef; - tempFileRef = 0; - this->filePath = tempFilePath; - this->handler->WriteFile ( origFileRef, origFilePath ); - - } else { - - // The handler can only update an existing file. Do a little dance so that the final - // file is the updated original, thus preserving ownership, permissions, etc. This - // does have the risk that the interim copy under the original name has "current" - // ownership and permissions. The dance steps: - // - Copy the original file to a temp name, the copyFile. - // - Rename the original file to a different temp name, the tempFile. - // - Rename the copyFile back to the original name. - // - Call the handler's UpdateFile method for the tempFile. - // A failure inside the handler's UpdateFile method will leave the copied file under - // the original name. - - // *** A user abort might leave the copy file under the original name! Need better - // *** duplicate code that handles all parts of a file, and for CreateTempFile to - // *** preserve ownership and permissions. Then the original can stay put until - // *** the final delete/rename. - - #if GatherPerformanceData - sAPIPerf->back().extraInfo += ", copy update"; - #endif - - CreateTempFile ( origFilePath, ©FilePath, kCopyMacRsrc ); - XMP_Assert ( copyFileRef == 0 ); - copyFileRef = LFA_Open ( copyFilePath.c_str(), 'w' ); - XMP_Int64 fileSize = LFA_Measure ( origFileRef ); - LFA_Seek ( origFileRef, 0, SEEK_SET ); - LFA_Copy ( origFileRef, copyFileRef, fileSize, this->abortProc, this->abortArg ); - - LFA_Close ( origFileRef ); - LFA_Close ( copyFileRef ); - copyFileRef = origFileRef = this->fileRef = 0; - - CreateTempFile ( origFilePath, &tempFilePath ); - LFA_Delete ( tempFilePath.c_str() ); // ! Slight risk of name being grabbed before rename. - LFA_Rename ( origFilePath.c_str(), tempFilePath.c_str() ); - - LFA_Rename ( copyFilePath.c_str(), origFilePath.c_str() ); - copyFilePath.clear(); - - XMP_Assert ( tempFileRef == 0 ); - tempFileRef = LFA_Open ( tempFilePath.c_str(), 'w' ); - this->fileRef = tempFileRef; - tempFileRef = 0; - this->filePath = tempFilePath; - - try { - this->handler->UpdateFile ( false ); // We're doing the safe update, not the handler. - } catch ( ... ) { - this->fileRef = 0; - this->filePath = origFilePath; // This is really the copied file. - LFA_Close ( tempFileRef ); - LFA_Delete ( tempFilePath.c_str() ); - tempFileRef = 0; - tempFilePath.clear(); - throw; - } - - } - - delete this->handler; - this->handler = 0; - - if ( this->fileRef != 0 ) LFA_Close ( this->fileRef ); - if ( origFileRef != 0 ) LFA_Close ( origFileRef ); - - this->fileRef = 0; - origFileRef = 0; - tempFileRef = 0; - - LFA_Delete ( origFilePath.c_str() ); - LFA_Rename ( tempFilePath.c_str(), origFilePath.c_str() ); - tempFilePath.clear(); - - } - - } catch ( ... ) { - - // *** Don't delete the temp or copy files, not sure which is best. - - try { - if ( this->fileRef != 0 ) LFA_Close ( this->fileRef ); - } catch ( ... ) { /* Do nothing, throw the outer exception later. */ } - try { - if ( origFileRef != 0 ) LFA_Close ( origFileRef ); - } catch ( ... ) { /* Do nothing, throw the outer exception later. */ } - try { - if ( tempFileRef != 0 ) LFA_Close ( tempFileRef ); - } catch ( ... ) { /* Do nothing, throw the outer exception later. */ } - try { - if ( ! tempFilePath.empty() ) LFA_Delete ( tempFilePath.c_str() ); - } catch ( ... ) { /* Do nothing, throw the outer exception later. */ } - try { - if ( copyFileRef != 0 ) LFA_Close ( copyFileRef ); - } catch ( ... ) { /* Do nothing, throw the outer exception later. */ } - try { - if ( ! copyFilePath.empty() ) LFA_Delete ( copyFilePath.c_str() ); - } catch ( ... ) { /* Do nothing, throw the outer exception later. */ } - try { - if ( this->handler != 0 ) delete this->handler; - } catch ( ... ) { /* Do nothing, throw the outer exception later. */ } - - this->handler = 0; - this->format = kXMP_UnknownFile; - this->fileRef = 0; - this->filePath.clear(); - this->openFlags = 0; - - if ( this->tempPtr != 0 ) free ( this->tempPtr ); // ! Must have been malloc-ed! - this->tempPtr = 0; - this->tempUI32 = 0; - - throw; - - } - - // Clear the XMPFiles member variables. - - this->handler = 0; - this->format = kXMP_UnknownFile; - this->fileRef = 0; - this->filePath.clear(); - this->openFlags = 0; - - if ( this->tempPtr != 0 ) free ( this->tempPtr ); // ! Must have been malloc-ed! - this->tempPtr = 0; - this->tempUI32 = 0; - -} // XMPFiles::CloseFile - -// ================================================================================================= - -bool -XMPFiles::GetFileInfo ( XMP_StringPtr * filePath /* = 0 */, - XMP_StringLen * pathLen /* = 0 */, - XMP_OptionBits * openFlags /* = 0 */, - XMP_FileFormat * format /* = 0 */, - XMP_OptionBits * handlerFlags /* = 0 */ ) const -{ - if ( this->handler == 0 ) return false; - XMPFileHandler * handler = this->handler; - - if ( filePath == 0 ) filePath = &voidStringPtr; - if ( pathLen == 0 ) pathLen = &voidStringLen; - if ( openFlags == 0 ) openFlags = &voidOptionBits; - if ( format == 0 ) format = &voidFileFormat; - if ( handlerFlags == 0 ) handlerFlags = &voidOptionBits; - - *filePath = this->filePath.c_str(); - *pathLen = (XMP_StringLen) this->filePath.size(); - *openFlags = this->openFlags; - *format = this->format; - *handlerFlags = this->handler->handlerFlags; - - return true; - -} // XMPFiles::GetFileInfo - -// ================================================================================================= - -void -XMPFiles::SetAbortProc ( XMP_AbortProc abortProc, - void * abortArg ) -{ - this->abortProc = abortProc; - this->abortArg = abortArg; - - XMP_Assert ( (abortProc != (XMP_AbortProc)0) || (abortArg != (void*)(unsigned long long)0xDEADBEEFULL) ); // Hack to test the assert callback. -} // XMPFiles::SetAbortProc - -// ================================================================================================= -// SetClientPacketInfo -// =================== -// -// Set the packet info returned to the client. This is the internal packet info at first, which -// tells what is in the file. But once the file needs update (PutXMP has been called), we return -// info about the latest XMP. The internal packet info is left unchanged since it is needed when -// the file is updated to locate the old packet in the file. - -static void -SetClientPacketInfo ( XMP_PacketInfo * clientInfo, const XMP_PacketInfo & handlerInfo, - const std::string & xmpPacket, bool needsUpdate ) -{ - - if ( clientInfo == 0 ) return; - - if ( ! needsUpdate ) { - *clientInfo = handlerInfo; - } else { - clientInfo->offset = kXMPFiles_UnknownOffset; - clientInfo->length = (XMP_Int32) xmpPacket.size(); - FillPacketInfo ( xmpPacket, clientInfo ); - } - -} // SetClientPacketInfo - -// ================================================================================================= - -bool -XMPFiles::GetXMP ( SXMPMeta * xmpObj /* = 0 */, - XMP_StringPtr * xmpPacket /* = 0 */, - XMP_StringLen * xmpPacketLen /* = 0 */, - XMP_PacketInfo * packetInfo /* = 0 */ ) -{ - if ( this->handler == 0 ) XMP_Throw ( "XMPFiles::GetXMP - No open file", kXMPErr_BadObject ); - - XMP_OptionBits applyTemplateFlags = kXMPTemplate_AddNewProperties | kXMPTemplate_IncludeInternalProperties; - - if ( ! this->handler->processedXMP ) { - try { - this->handler->ProcessXMP(); - } catch ( ... ) { - // Return the outputs then rethrow the exception. - if ( xmpObj != 0 ) { - // ! Don't use Clone, that replaces the internal ref in the local xmpObj, leaving the client unchanged! - xmpObj->Erase(); - SXMPUtils::ApplyTemplate ( xmpObj, this->handler->xmpObj, applyTemplateFlags ); - } - if ( xmpPacket != 0 ) *xmpPacket = this->handler->xmpPacket.c_str(); - if ( xmpPacketLen != 0 ) *xmpPacketLen = (XMP_StringLen) this->handler->xmpPacket.size(); - SetClientPacketInfo ( packetInfo, this->handler->packetInfo, - this->handler->xmpPacket, this->handler->needsUpdate ); - throw; - } - } - - if ( ! this->handler->containsXMP ) return false; - - #if 0 // *** See bug 1131815. A better way might be to pass the ref up from here. - if ( xmpObj != 0 ) *xmpObj = this->handler->xmpObj.Clone(); - #else - if ( xmpObj != 0 ) { - // ! Don't use Clone, that replaces the internal ref in the local xmpObj, leaving the client unchanged! - xmpObj->Erase(); - SXMPUtils::ApplyTemplate ( xmpObj, this->handler->xmpObj, applyTemplateFlags ); - } - #endif - - if ( xmpPacket != 0 ) *xmpPacket = this->handler->xmpPacket.c_str(); - if ( xmpPacketLen != 0 ) *xmpPacketLen = (XMP_StringLen) this->handler->xmpPacket.size(); - SetClientPacketInfo ( packetInfo, this->handler->packetInfo, - this->handler->xmpPacket, this->handler->needsUpdate ); - - return true; - -} // XMPFiles::GetXMP - -// ================================================================================================= - -static bool -DoPutXMP ( XMPFiles * thiz, const SXMPMeta & xmpObj, const bool doIt ) -{ - // Check some basic conditions to see if the Put should be attempted. - - if ( thiz->handler == 0 ) XMP_Throw ( "XMPFiles::PutXMP - No open file", kXMPErr_BadObject ); - if ( ! (thiz->openFlags & kXMPFiles_OpenForUpdate) ) { - XMP_Throw ( "XMPFiles::PutXMP - Not open for update", kXMPErr_BadObject ); - } - - XMPFileHandler * handler = thiz->handler; - XMP_OptionBits handlerFlags = handler->handlerFlags; - XMP_PacketInfo & packetInfo = handler->packetInfo; - std::string & xmpPacket = handler->xmpPacket; - - if ( ! handler->processedXMP ) handler->ProcessXMP(); // Might have Open/Put with no GetXMP. - - size_t oldPacketOffset = (size_t)packetInfo.offset; - size_t oldPacketLength = packetInfo.length; - - if ( oldPacketOffset == (size_t)kXMPFiles_UnknownOffset ) oldPacketOffset = 0; // ! Simplify checks. - if ( oldPacketLength == (size_t)kXMPFiles_UnknownLength ) oldPacketLength = 0; - - bool fileHasPacket = (oldPacketOffset != 0) && (oldPacketLength != 0); - - if ( ! fileHasPacket ) { - if ( ! (handlerFlags & kXMPFiles_CanInjectXMP) ) { - XMP_Throw ( "XMPFiles::PutXMP - Can't inject XMP", kXMPErr_Unavailable ); - } - if ( handler->stdCharForm == kXMP_CharUnknown ) { - XMP_Throw ( "XMPFiles::PutXMP - No standard character form", kXMPErr_InternalFailure ); - } - } - - // Serialize the XMP and update the handler's info. - - XMP_Uns8 charForm = handler->stdCharForm; - if ( charForm == kXMP_CharUnknown ) charForm = packetInfo.charForm; - - XMP_OptionBits options = handler->GetSerializeOptions() | XMP_CharToSerializeForm ( charForm ); - if ( handlerFlags & kXMPFiles_NeedsReadOnlyPacket ) options |= kXMP_ReadOnlyPacket; - if ( fileHasPacket && (thiz->format == kXMP_UnknownFile) && (! packetInfo.writeable) ) options |= kXMP_ReadOnlyPacket; - - bool preferInPlace = ((handlerFlags & kXMPFiles_PrefersInPlace) != 0); - bool tryInPlace = (fileHasPacket & preferInPlace) || (! (handlerFlags & kXMPFiles_CanExpand)); - - if ( handlerFlags & kXMPFiles_UsesSidecarXMP ) tryInPlace = false; - - if ( tryInPlace ) { - try { - xmpObj.SerializeToBuffer ( &xmpPacket, (options | kXMP_ExactPacketLength), (XMP_StringLen) oldPacketLength ); - XMP_Assert ( xmpPacket.size() == oldPacketLength ); - } catch ( ... ) { - if ( preferInPlace ) { - tryInPlace = false; // ! Try again, out of place this time. - } else { - if ( ! doIt ) return false; - throw; - } - } - } - - if ( ! tryInPlace ) { - try { - xmpObj.SerializeToBuffer ( &xmpPacket, options ); - } catch ( ... ) { - if ( ! doIt ) return false; - throw; - } - } - - if ( doIt ) { - handler->xmpObj = xmpObj.Clone(); - handler->containsXMP = true; - handler->processedXMP = true; - handler->needsUpdate = true; - } - - return true; - -} // DoPutXMP - -// ================================================================================================= - -void -XMPFiles::PutXMP ( const SXMPMeta & xmpObj ) -{ - (void) DoPutXMP ( this, xmpObj, true ); - -} // XMPFiles::PutXMP - -// ================================================================================================= - -void -XMPFiles::PutXMP ( XMP_StringPtr xmpPacket, - XMP_StringLen xmpPacketLen /* = kXMP_UseNullTermination */ ) -{ - SXMPMeta xmpObj ( xmpPacket, xmpPacketLen ); - this->PutXMP ( xmpObj ); - -} // XMPFiles::PutXMP - -// ================================================================================================= - -bool -XMPFiles::CanPutXMP ( const SXMPMeta & xmpObj ) -{ - if ( this->handler == 0 ) XMP_Throw ( "XMPFiles::CanPutXMP - No open file", kXMPErr_BadObject ); - - if ( ! (this->openFlags & kXMPFiles_OpenForUpdate) ) return false; - - if ( this->handler->handlerFlags & kXMPFiles_CanInjectXMP ) return true; - if ( ! this->handler->containsXMP ) return false; - if ( this->handler->handlerFlags & kXMPFiles_CanExpand ) return true; - - return DoPutXMP ( this, xmpObj, false ); - -} // XMPFiles::CanPutXMP - -// ================================================================================================= - -bool -XMPFiles::CanPutXMP ( XMP_StringPtr xmpPacket, - XMP_StringLen xmpPacketLen /* = kXMP_UseNullTermination */ ) -{ - SXMPMeta xmpObj ( xmpPacket, xmpPacketLen ); - return this->CanPutXMP ( xmpObj ); - -} // XMPFiles::CanPutXMP - -// ================================================================================================= diff --git a/source/XMPFiles/XMPFiles.hpp b/source/XMPFiles/XMPFiles.hpp deleted file mode 100644 index 8744a31..0000000 --- a/source/XMPFiles/XMPFiles.hpp +++ /dev/null @@ -1,230 +0,0 @@ -#ifndef __XMPFiles_hpp__ -#define __XMPFiles_hpp__ 1 - -// ================================================================================================= -// ADOBE SYSTEMS INCORPORATED -// Copyright 2004 Adobe Systems Incorporated -// 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> - -#define TXMP_STRING_TYPE std::string -#include "XMP.hpp" - -#if ! UNIX_ENV - typedef void * LFA_FileRef; -#else - typedef XMP_Int32 LFA_FileRef; -#endif - -class XMPFileHandler; - -// ================================================================================================= -/// \file XMPFiles.hpp -/// \brief High level support to access metadata in files of interest to Adobe applications. -/// -/// This header ... -/// -// ================================================================================================= - -// ================================================================================================= -// *** Usage Notes (eventually to become Doxygen comments) *** -// =========================================================== -// -// This is the main part of the internal (DLL side) implementation of XMPFiles. Other parts are -// the entry point wrappers and the file format handlers. The XMPFiles class distills the client -// API from TXMPFiles.hpp, removing convenience overloads and substituting a pointer/length pair -// for output strings. -// -// The wrapper functions provide a stable binary interface and perform minor impedance correction -// between the client template API from TDocMeta.hpp and the DLL's XMPFiles class. The details of -// the wrappers should be considered private. -// -// File handlers are registered during DLL initialization with hard coded calls in Init_XMPFiles. -// Each file handler provides 2 standalone functions, CheckFormatProc and DocMetaHandlerCTor, plus a -// class derived from DocMetaHandler. The format and capability flags are passed when registering. -// This allows the same physical handler to be registered for multiple formats. -// -// ------------------------------------------------------------------------------------------------- -// -// Basic outlines of the processing by the XMPFiles methods: -// -// Constructor: -// - Minimal work to create an empty XMPFiles object, set the ref count to 1. -// -// Destructor: -// - Decrement the ref count, return if greater than zero. -// - Call LFA_Close if necessary. -// -// UnlockLib & UnlockObj: -// - Release the thread lock. Same for now, no per-object lock. -// -// GetFormatInfo: -// - Return the flags for the registered handler. -// -// OpenFile: -// - The physical file is opened via LFA_OpenFile. -// - A handler is selected by calling the registered format checkers. -// - The handler object is created by calling the registered constructor proc. -// -// CloseFile: -// - Return if there is no open file (not an error). -// - If not a crash-safe update (includes read-only or no update), or the handler owns the file: -// - Throw an exception if the handler owns the file but does not support safe update. -// - If the file needs updating, call the handler's UpdateFile method. -// - else: -// - If the handler supports file rewrite: -// - *** This might not preserve ownership and permissions. -// - Create an empty temp file. -// - Call the handler's WriteFile method, writing to the temp file. -// - else -// - *** This preserves ownership, permissions, and Mac resources. -// - Copy the original file to a temp name (Mac data fork only). -// - Rename the original file to a different temp name. -// - Rename the copy file back to the original name. -// - Call the handler's UpdateFile method for the "original as temp" file. -// - Close both the original and temp files. -// - Delete the file with the original name. -// - Rename the temp file to the original name. -// - Delete the handler object. -// - Call LFA_Close if necessary. -// -// GetFileInfo: -// - Return the file info from the XMPFiles member variables. -// -// GetXMP: -// - Throw an exception if there is no open file. -// - Call the handler's GetXMP method. -// -// PutXMP: -// - Throw an exception if there is no open file. -// - Call the handler's PutXMP method. -// -// CanPutXMP: -// - Implement roughly as shown in TXMPFiles.hpp, there is no handler CanPutXMP method. -// -// ------------------------------------------------------------------------------------------------- -// -// The format checker should do nothing but the minimal work to identify the overall file format. -// In particular it should not look for XMP or other metadata. Note that the format checker has no -// means to carry state forward, it just returns a yes/no answer about a particular file format. -// -// The format checker and file handler should use the LFA_* functions for all I/O. They should not -// open or close the file themselves unless the handler sets the "handler-owns-file" flag. -// -// The format checker is passed the format being checked, allowing one checker to handle multiple -// formats. It is passed the LFA file ref so that it can do additional reads if necessary. The -// buffer is from the start of the file, the file will be positioned to the byte following the -// buffer. The buffer length will be at least 4K, unless the file is smaller in which case it will -// be the length of the file. This buffer may be reused for additional reads. -// -// Identifying some file formats can require checking variable length strings. Doing seeks and reads -// for each is suboptimal. There are utilities to maintain a rolling buffer and ensure that a given -// amount of data is available. See the template file handler code for details. -// -// ------------------------------------------------------------------------------------------------- -// -// The file handler has no explicit open and close methods. These are implicit in the handler's -// constructor and destructor. The file handler should use the XMPFiles member variables for the -// active file ref (and path if necessary), unless it owns the file. Note that these might change -// between the open and close in the case of crash-safe updates. Don't copy the XMPFiles member -// variables in the handler's constructor, save the pointer to the XMPFiles object and access -// directly as needed. -// -// The handler should have an UpdateFile method. This is called from XMPFiles::CloseFile if the -// file needs to be updated. The handler's destructor must only close the file, not update it. -// The handler can optionally have a WriteFile method, if it can rewrite the entire file. -// -// The handler is free to use its best judgement about caching parts of the file in memory. Overall -// speed of a single open/get/put/close cycle is probably the best goal, assuming a modern processor -// with a reasonable (significant but not enormous) amount of RAM. -// -// The handler methods will be called in a per-object thread safe manner. Concurrent access might -// occur for different objects, but not for the same object. The handler's constructor and destructor -// will always be globally serialized, so they can safely modify global data structures. -// -// (Testing issue: What about separate XMPFiles objects accessing the same file?) -// -// Handler's must not have any global objects that are heap allocated. Use pointers to objects that -// are allocated and deleted during the XMPFiles initialization and termination process. Some -// client apps are very picky about what they detect as memory leaks. -// -// static char gSomeBuffer [10*1000]; // OK, not from the heap. -// static std::string gSomeString; // Not OK, content from the heap. -// static std::vector<int> gSomeVector; // Not OK, content from the heap. -// static std::string * gSomeString = 0; // OK, alloc at init, delete at term. -// static std::vector<int> * gSomeVector = 0; // OK, alloc at init, delete at term. -// -// ================================================================================================= - -class XMPFiles { -public: - - static void GetVersionInfo ( XMP_VersionInfo * info ); - - static bool Initialize ( XMP_OptionBits options = 0 ); - static void Terminate(); - - XMPFiles(); - virtual ~XMPFiles(); - - static bool GetFormatInfo ( XMP_FileFormat format, - XMP_OptionBits * flags = 0 ); - - static XMP_FileFormat CheckFileFormat ( XMP_StringPtr filePath ); - static XMP_FileFormat CheckPackageFormat ( XMP_StringPtr folderPath ); - - bool OpenFile ( XMP_StringPtr filePath, - XMP_FileFormat format = kXMP_UnknownFile, - XMP_OptionBits openFlags = 0 ); - - void CloseFile ( XMP_OptionBits closeFlags = 0 ); - - bool GetFileInfo ( XMP_StringPtr * filePath = 0, - XMP_StringLen * filePathLen = 0, - XMP_OptionBits * openFlags = 0, - XMP_FileFormat * format = 0, - XMP_OptionBits * handlerFlags = 0 ) const; - - void SetAbortProc ( XMP_AbortProc abortProc, - void * abortArg ); - - bool GetXMP ( SXMPMeta * xmpObj = 0, - XMP_StringPtr * xmpPacket = 0, - XMP_StringLen * xmpPacketLen = 0, - XMP_PacketInfo * packetInfo = 0 ); - - void PutXMP ( const SXMPMeta & xmpObj ); - - void PutXMP ( XMP_StringPtr xmpPacket, - XMP_StringLen xmpPacketLen = kXMP_UseNullTermination ); - - bool CanPutXMP ( const SXMPMeta & xmpObj ); - - bool CanPutXMP ( XMP_StringPtr xmpPacket, - XMP_StringLen xmpPacketLen = kXMP_UseNullTermination ); - - // Leave this data public so file handlers can see it. - - XMP_Int32 clientRefs; // ! Must be signed to allow decrement from zero. - XMP_ReadWriteLock lock; - - XMP_FileFormat format; - LFA_FileRef fileRef; // Non-zero if a file is open. - std::string filePath; - XMP_OptionBits openFlags; - XMPFileHandler * handler; // Non-null if a file is open. - - void * tempPtr; // For use between the CheckProc and handler creation. - XMP_Uns32 tempUI32; - - XMP_AbortProc abortProc; - void * abortArg; - -}; // XMPFiles - -#endif /* __XMPFiles_hpp__ */ diff --git a/source/XMPFiles/XMPFiles_Impl.cpp b/source/XMPFiles/XMPFiles_Impl.cpp deleted file mode 100644 index 1677141..0000000 --- a/source/XMPFiles/XMPFiles_Impl.cpp +++ /dev/null @@ -1,780 +0,0 @@ -// ================================================================================================= -// ADOBE SYSTEMS INCORPORATED -// Copyright 2004 Adobe Systems Incorporated -// 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 "XMPFiles_Impl.hpp" - -#include "UnicodeConversions.hpp" - -using namespace std; - -// Internal code should be using #if with XMP_MacBuild, XMP_WinBuild, or XMP_UNIXBuild. -// This is a sanity check in case of accidental use of *_ENV. Some clients use the poor -// practice of defining the *_ENV macro with an empty value. -#if defined ( MAC_ENV ) - #if ! MAC_ENV - #error "MAC_ENV must be defined so that \"#if MAC_ENV\" is true" - #endif -#elif defined ( WIN_ENV ) - #if ! WIN_ENV - #error "WIN_ENV must be defined so that \"#if WIN_ENV\" is true" - #endif -#elif defined ( UNIX_ENV ) - #if ! UNIX_ENV - #error "UNIX_ENV must be defined so that \"#if UNIX_ENV\" is true" - #endif -#endif - -// ================================================================================================= -/// \file XMPFiles_Impl.cpp -/// \brief ... -/// -/// This file ... -/// -// ================================================================================================= - -#if XMP_WinBuild - #pragma warning ( disable : 4290 ) // C++ exception specification ignored except to indicate a function is not __declspec(nothrow) - #pragma warning ( disable : 4800 ) // forcing value to bool 'true' or 'false' (performance warning) -#endif - -bool ignoreLocalText = false; - -XMP_FileFormat voidFileFormat = 0; // Used as sink for unwanted output parameters. -// ================================================================================================= - -// Add all known mappings, multiple mappings (tif, tiff) are OK. -const FileExtMapping kFileExtMap[] = - { { "pdf", kXMP_PDFFile }, - { "ps", kXMP_PostScriptFile }, - { "eps", kXMP_EPSFile }, - - { "jpg", kXMP_JPEGFile }, - { "jpeg", kXMP_JPEGFile }, - { "jpx", kXMP_JPEG2KFile }, - { "tif", kXMP_TIFFFile }, - { "tiff", kXMP_TIFFFile }, - { "dng", kXMP_TIFFFile }, // DNG files are well behaved TIFF. - { "gif", kXMP_GIFFile }, - { "giff", kXMP_GIFFile }, - { "png", kXMP_PNGFile }, - - { "swf", kXMP_SWFFile }, - { "flv", kXMP_FLVFile }, - - { "aif", kXMP_AIFFFile }, - - { "mov", kXMP_MOVFile }, - { "avi", kXMP_AVIFile }, - { "cin", kXMP_CINFile }, - { "wav", kXMP_WAVFile }, - { "mp3", kXMP_MP3File }, - { "mp4", kXMP_MPEG4File }, - { "m4v", kXMP_MPEG4File }, - { "m4a", kXMP_MPEG4File }, - { "f4v", kXMP_MPEG4File }, - { "ses", kXMP_SESFile }, - { "cel", kXMP_CELFile }, - { "wma", kXMP_WMAVFile }, - { "wmv", kXMP_WMAVFile }, - - { "mpg", kXMP_MPEGFile }, - { "mpeg", kXMP_MPEGFile }, - { "mp2", kXMP_MPEGFile }, - { "mod", kXMP_MPEGFile }, - { "m2v", kXMP_MPEGFile }, - { "mpa", kXMP_MPEGFile }, - { "mpv", kXMP_MPEGFile }, - { "m2p", kXMP_MPEGFile }, - { "m2a", kXMP_MPEGFile }, - { "m2t", kXMP_MPEGFile }, - { "mpe", kXMP_MPEGFile }, - { "vob", kXMP_MPEGFile }, - { "ms-pvr", kXMP_MPEGFile }, - { "dvr-ms", kXMP_MPEGFile }, - - { "html", kXMP_HTMLFile }, - { "xml", kXMP_XMLFile }, - { "txt", kXMP_TextFile }, - { "text", kXMP_TextFile }, - - { "psd", kXMP_PhotoshopFile }, - { "ai", kXMP_IllustratorFile }, - { "indd", kXMP_InDesignFile }, - { "indt", kXMP_InDesignFile }, - { "aep", kXMP_AEProjectFile }, - { "aepx", kXMP_AEProjectFile }, - { "aet", kXMP_AEProjTemplateFile }, - { "ffx", kXMP_AEFilterPresetFile }, - { "ncor", kXMP_EncoreProjectFile }, - { "prproj", kXMP_PremiereProjectFile }, - { "prtl", kXMP_PremiereTitleFile }, - { "ucf", kXMP_UCFFile }, - { "xfl", kXMP_UCFFile }, - { "pdfxml", kXMP_UCFFile }, - { "mars", kXMP_UCFFile }, - { "idml", kXMP_UCFFile }, - { "idap", kXMP_UCFFile }, - { "icap", kXMP_UCFFile }, - { "", 0 } }; // ! Must be last as a sentinel. - -// Files known to contain XMP but have no smart handling, here or elsewhere. -const char * kKnownScannedFiles[] = - { "gif", // GIF, public format but no smart handler. - "ai", // Illustrator, actually a PDF file. - "ait", // Illustrator template, actually a PDF file. - "svg", // SVG, an XML file. - "aet", // After Effects template project file. - "ffx", // After Effects filter preset file. - "aep", // After Effects project file in proprietary format - "aepx", // After Effects project file in XML format - "inx", // InDesign interchange, an XML file. - "inds", // InDesign snippet, an XML file. - "inpk", // InDesign package for GoLive, a text file (not XML). - "incd", // InCopy story, an XML file. - "inct", // InCopy template, an XML file. - "incx", // InCopy interchange, an XML file. - "fm", // FrameMaker file, proprietary format. - "book", // FrameMaker book, proprietary format. - "icml", // an inCopy (inDesign) format - "icmt", // an inCopy (inDesign) format - "idms", // an inCopy (inDesign) format - 0 }; // ! Keep a 0 sentinel at the end. - - -// Extensions that XMPFiles never handles. -const char * kKnownRejectedFiles[] = - { - // RAW files - "cr2", "erf", "fff", "dcr", "kdc", "mos", "mfw", "mef", - "raw", "nef", "orf", "pef", "arw", "sr2", "srf", "sti", - "3fr", "rwl", "crw", "sraw", "mos", "mrw", "nrw", "rw2", - "c3f", - // UCF subformats - "air", - // Others - "r3d", - 0 }; // ! Keep a 0 sentinel at the end. - -// ================================================================================================= - -// ================================================================================================= - -void LFA_Throw ( const char* msg, int id ) -{ - switch ( id ) { - case kLFAErr_InternalFailure: - XMP_Throw ( msg, kXMPErr_InternalFailure ); - case kLFAErr_ExternalFailure: - XMP_Throw ( msg, kXMPErr_ExternalFailure ); - case kLFAErr_UserAbort: - XMP_Throw ( msg, kXMPErr_UserAbort ); - default: - XMP_Throw ( msg, kXMPErr_UnknownException ); - } -} - -// ================================================================================================= - -#if XMP_MacBuild | XMP_UNIXBuild - //copy from LargeFileAccess.cpp - static bool FileExists ( const char * filePath ) - { - struct stat info; - int err = stat ( filePath, &info ); - return (err == 0); - } -#endif - -// ================================================================================================= - -static bool CreateNewFile ( const char * newPath, const char * origPath, size_t filePos, bool copyMacRsrc ) -{ - // Try to create a new file with the same ownership and permissions as some other file. - // *** The ownership and permissions are not handled on all platforms. - - #if XMP_MacBuild | XMP_UNIXBuild - - if ( FileExists ( newPath ) ) return false; - - #elif XMP_WinBuild - - { - std::string wideName; - const size_t utf8Len = strlen(newPath); - const size_t maxLen = 2 * (utf8Len+1); - wideName.reserve ( maxLen ); - wideName.assign ( maxLen, ' ' ); - int wideLen = MultiByteToWideChar ( CP_UTF8, 0, newPath, -1, (LPWSTR)wideName.data(), (int)maxLen ); - if ( wideLen == 0 ) return false; - HANDLE temp = CreateFileW ( (LPCWSTR)wideName.data(), GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, - (FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS), 0 ); - if ( temp != INVALID_HANDLE_VALUE ) { - CloseHandle ( temp ); - return false; - } - } - - #endif - - try { - LFA_FileRef newFile = LFA_Create ( newPath ); - LFA_Close ( newFile ); - } catch ( ... ) { - // *** Unfortunate that LFA_Create throws for an existing file. - return false; - } - - #if XMP_WinBuild - - IgnoreParam(origPath); IgnoreParam(filePos); IgnoreParam(copyMacRsrc); - - // *** Don't handle Windows specific info yet. - - #elif XMP_MacBuild - - IgnoreParam(filePos); - - OSStatus err; - FSRef newFSRef, origFSRef; // Copy the "copyable" catalog info, includes the Finder info. - - err = FSPathMakeRef ( (XMP_Uns8*)origPath, &origFSRef, 0 ); - if ( err != noErr ) XMP_Throw ( "CreateNewFile: FSPathMakeRef failure", kXMPErr_ExternalFailure ); - err = FSPathMakeRef ( (XMP_Uns8*)newPath, &newFSRef, 0 ); - if ( err != noErr ) XMP_Throw ( "CreateNewFile: FSPathMakeRef failure", kXMPErr_ExternalFailure ); - - FSCatalogInfo catInfo; // *** What about the GetInfo comment? The Finder label? - memset ( &catInfo, 0, sizeof(FSCatalogInfo) ); - - err = FSGetCatalogInfo ( &origFSRef, kFSCatInfoGettableInfo, &catInfo, 0, 0, 0 ); - if ( err != noErr ) XMP_Throw ( "CreateNewFile: FSGetCatalogInfo failure", kXMPErr_ExternalFailure ); - err = FSSetCatalogInfo ( &newFSRef, kFSCatInfoSettableInfo, &catInfo ); - - // *** [1841019] tolerate non mac filesystems, i.e. SMB mounts - // this measure helps under 10.5, albeit not reliably under 10.4 - if ( err == afpAccessDenied ) - copyMacRsrc = false; - else if ( err != noErr ) // all other errors are still an error - XMP_Throw ( "CreateNewFile: FSSetCatalogInfo failure", kXMPErr_ExternalFailure ); - - // *** [1841019] tolerate non mac filesystems, i.e. SMB mounts - // this measure helps under 10.4 (and besides might be an optimization) - if ( catInfo.rsrcLogicalSize == 0 ) - copyMacRsrc = false; - - if ( copyMacRsrc ) { // Copy the resource fork as a byte stream. - LFA_FileRef origRsrcRef = 0; - LFA_FileRef copyRsrcRef = 0; - try { - origRsrcRef = LFA_OpenRsrc ( origPath, 'r' ); - XMP_Int64 rsrcSize = LFA_Measure ( origRsrcRef ); - if ( rsrcSize > 0 ) { - copyRsrcRef = LFA_OpenRsrc ( newPath, 'w' ); - LFA_Copy ( origRsrcRef, copyRsrcRef, rsrcSize, 0, 0 ); // ! Resource fork small enough to not need abort checking. - LFA_Close ( copyRsrcRef ); - } - LFA_Close ( origRsrcRef ); - } catch ( ... ) { - if ( origRsrcRef != 0 ) LFA_Close ( origRsrcRef ); - if ( copyRsrcRef != 0 ) LFA_Close ( copyRsrcRef ); - throw; - } - } - - - #elif XMP_UNIXBuild - - IgnoreParam(filePos); IgnoreParam(copyMacRsrc); - // *** Can't use on Mac because of frigging CW POSIX header problems! - - // *** Don't handle UNIX specific info yet. - - int err, newRef; - struct stat origInfo; - err = stat ( origPath, &origInfo ); - if ( err != 0 ) XMP_Throw ( "CreateNewFile: stat failure", kXMPErr_ExternalFailure ); - - (void) chmod ( newPath, origInfo.st_mode ); // Ignore errors. - - #endif - - return true; - -} // CreateNewFile - -// ================================================================================================= - -void CreateTempFile ( const std::string & origPath, std::string * tempPath, bool copyMacRsrc ) -{ - // Create an empty temp file next to the source file with the same ownership and permissions. - // The temp file has "._nn_" added as a prefix to the file name, where "nn" is a unique - // sequence number. The "._" start is important for Bridge, telling it to ignore the file. - - // *** The ownership and permissions are not yet handled. - - #if XMP_WinBuild - #define kUseBS true - #else - #define kUseBS false - #endif - - // Break the full path into folder path and file name portions. - - size_t namePos; // The origPath index of the first byte of the file name part. - - for ( namePos = origPath.size(); namePos > 0; --namePos ) { - if ( (origPath[namePos] == '/') || (kUseBS && (origPath[namePos] == '\\')) ) { - ++namePos; - break; - } - } - if ( (origPath[namePos] == '/') || (kUseBS && (origPath[namePos] == '\\')) ) ++namePos; - if ( namePos == origPath.size() ) XMP_Throw ( "CreateTempFile: Empty file name part", kXMPErr_InternalFailure ); - - std::string folderPath ( origPath, 0, namePos ); - std::string origName ( origPath, namePos ); - - // First try to create a file with "._nn_" added as a file name prefix. - - char tempPrefix[6] = "._nn_"; - - tempPath->reserve ( origPath.size() + 5 ); - tempPath->assign ( origPath, 0, namePos ); - tempPath->append ( tempPrefix, 5 ); - tempPath->append ( origName ); - - for ( char n1 = '0'; n1 <= '9'; ++n1 ) { - (*tempPath)[namePos+2] = n1; - for ( char n2 = '0'; n2 <= '9'; ++n2 ) { - (*tempPath)[namePos+3] = n2; - if ( CreateNewFile ( tempPath->c_str(), origPath.c_str(), namePos, copyMacRsrc ) ) return; - } - } - - // Now try to create a file with the name "._nn_XMPFilesTemp" - - tempPath->assign ( origPath, 0, namePos ); - tempPath->append ( tempPrefix, 5 ); - tempPath->append ( "XMPFilesTemp" ); - - for ( char n1 = '0'; n1 <= '9'; ++n1 ) { - (*tempPath)[namePos+2] = n1; - for ( char n2 = '0'; n2 <= '9'; ++n2 ) { - (*tempPath)[namePos+3] = n2; - if ( CreateNewFile ( tempPath->c_str(), origPath.c_str(), namePos, copyMacRsrc ) ) return; - } - } - - XMP_Throw ( "CreateTempFile: Can't find unique name", kXMPErr_InternalFailure ); - -} // CreateTempFile - -// ================================================================================================= -// File mode and folder info utilities -// ----------------------------------- - -#if XMP_WinBuild - - // --------------------------------------------------------------------------------------------- - - static DWORD kOtherAttrs = (FILE_ATTRIBUTE_DEVICE | FILE_ATTRIBUTE_OFFLINE); - - FileMode GetFileMode ( const char * path ) - { - std::string utf16; // GetFileAttributes wants native UTF-16. - ToUTF16Native ( (UTF8Unit*)path, strlen(path), &utf16 ); - utf16.append ( 2, '\0' ); // Make sure there are at least 2 final zero bytes. - - // ! A shortcut is seen as a file, we would need extra code to recognize it and find the target. - - DWORD fileAttrs = GetFileAttributesW ( (LPCWSTR) utf16.c_str() ); - if ( fileAttrs == INVALID_FILE_ATTRIBUTES ) return kFMode_DoesNotExist; // ! Any failure turns into does-not-exist. - - if ( fileAttrs & FILE_ATTRIBUTE_DIRECTORY ) return kFMode_IsFolder; - if ( fileAttrs & kOtherAttrs ) return kFMode_IsOther; - return kFMode_IsFile; - - } // GetFileMode - - // --------------------------------------------------------------------------------------------- - - void XMP_FolderInfo::Open ( const char * folderPath ) - { - - if ( this->dirRef != 0 ) this->Close(); - - this->folderPath = folderPath; - - } // XMP_FolderInfo::Open - - // --------------------------------------------------------------------------------------------- - - void XMP_FolderInfo::Close() - { - - if ( this->dirRef != 0 ) (void) FindClose ( this->dirRef ); - this->dirRef = 0; - this->folderPath.erase(); - - } // XMP_FolderInfo::Close - - // --------------------------------------------------------------------------------------------- - - bool XMP_FolderInfo::GetFolderPath ( std::string * folderPath ) - { - - if ( this->folderPath.empty() ) return false; - - *folderPath = this->folderPath; - return true; - - } // XMP_FolderInfo::GetFolderPath - - // --------------------------------------------------------------------------------------------- - - bool XMP_FolderInfo::GetNextChild ( std::string * childName ) - { - bool found = false; - WIN32_FIND_DATAW childInfo; - - if ( this->dirRef != 0 ) { - - found = FindNextFile ( this->dirRef, &childInfo ); - if ( ! found ) return false; - - } else { - - if ( this->folderPath.empty() ) { - XMP_Throw ( "XMP_FolderInfo::GetNextChild - not open", kXMPErr_InternalFailure ); - } - - std::string findPath = this->folderPath; - findPath += "\\*"; - std::string utf16; // FindFirstFile wants native UTF-16. - ToUTF16Native ( (UTF8Unit*)findPath.c_str(), findPath.size(), &utf16 ); - utf16.append ( 2, '\0' ); // Make sure there are at least 2 final zero bytes. - - this->dirRef = FindFirstFileW ( (LPCWSTR) utf16.c_str(), &childInfo ); - if ( this->dirRef == 0 ) return false; - found = true; - - } - - // Ignore all children with names starting in '.'. This covers ., .., .DS_Store, etc. - while ( found && (childInfo.cFileName[0] == '.') ) { - found = FindNextFile ( this->dirRef, &childInfo ); - } - if ( ! found ) return false; - - size_t len16 = 0; - while ( childInfo.cFileName[len16] != 0 ) ++len16; - FromUTF16Native ( (UTF16Unit*)childInfo.cFileName, len16, childName ); // The cFileName field is native UTF-16. - - return true; - - } // XMP_FolderInfo::GetNextChild - - // --------------------------------------------------------------------------------------------- - -#else // Mac and UNIX both use POSIX functions. - - // --------------------------------------------------------------------------------------------- - - FileMode GetFileMode ( const char * path ) - { - struct stat fileInfo; - - int err = stat ( path, &fileInfo ); - if ( err != 0 ) return kFMode_DoesNotExist; // ! Any failure turns into does-not-exist. - - // ! The target of a symlink is properly recognized, not the symlink itself. A Mac alias is - // ! seen as a file, we would need extra code to recognize it and find the target. - - if ( S_ISREG ( fileInfo.st_mode ) ) return kFMode_IsFile; - if ( S_ISDIR ( fileInfo.st_mode ) ) return kFMode_IsFolder; - return kFMode_IsOther; - - } // GetFileMode - - // --------------------------------------------------------------------------------------------- - - void XMP_FolderInfo::Open ( const char * folderPath ) - { - - if ( this->dirRef != 0 ) this->Close(); - - this->dirRef = opendir ( folderPath ); - if ( this->dirRef == 0 ) XMP_Throw ( "XMP_FolderInfo::Open - opendir failed", kXMPErr_ExternalFailure ); - this->folderPath = folderPath; - - } // XMP_FolderInfo::Open - - // --------------------------------------------------------------------------------------------- - - void XMP_FolderInfo::Close() - { - - if ( this->dirRef != 0 ) (void) closedir ( this->dirRef ); - this->dirRef = 0; - this->folderPath.erase(); - - } // XMP_FolderInfo::Close - - // --------------------------------------------------------------------------------------------- - - bool XMP_FolderInfo::GetFolderPath ( std::string * folderPath ) - { - - if ( this->folderPath.empty() ) return false; - - *folderPath = this->folderPath; - return true; - - } // XMP_FolderInfo::GetFolderPath - - // --------------------------------------------------------------------------------------------- - - bool XMP_FolderInfo::GetNextChild ( std::string * childName ) - { - struct dirent * childInfo = 0; - - if ( this->dirRef == 0 ) XMP_Throw ( "XMP_FolderInfo::GetNextChild - not open", kXMPErr_InternalFailure ); - - while ( true ) { - // Ignore all children with names starting in '.'. This covers ., .., .DS_Store, etc. - childInfo = readdir ( this->dirRef ); // ! Depends on global lock, readdir is not thread safe. - if ( childInfo == 0 ) return false; - if ( *childInfo->d_name != '.' ) break; - } - - *childName = childInfo->d_name; - return true; - - } // XMP_FolderInfo::GetNextChild - - // --------------------------------------------------------------------------------------------- - -#endif - -// ================================================================================================= -// GetPacketCharForm -// ================= -// -// The first character must be U+FEFF or ASCII, typically '<' for an outermost element, initial -// processing instruction, or XML declaration. The second character can't be U+0000. -// The possible input sequences are: -// Cases with U+FEFF -// EF BB BF -- - UTF-8 -// FE FF -- -- - Big endian UTF-16 -// 00 00 FE FF - Big endian UTF 32 -// FF FE 00 00 - Little endian UTF-32 -// FF FE -- -- - Little endian UTF-16 -// Cases with ASCII -// nn mm -- -- - UTF-8 - -// 00 00 00 nn - Big endian UTF-32 -// 00 nn -- -- - Big endian UTF-16 -// nn 00 00 00 - Little endian UTF-32 -// nn 00 -- -- - Little endian UTF-16 - -static XMP_Uns8 GetPacketCharForm ( XMP_StringPtr packetStr, XMP_StringLen packetLen ) -{ - XMP_Uns8 charForm = kXMP_CharUnknown; - XMP_Uns8 * unsBytes = (XMP_Uns8*)packetStr; // ! Make sure comparisons are unsigned. - - if ( packetLen < 2 ) return kXMP_Char8Bit; - - if ( packetLen < 4 ) { - - // These cases are based on the first 2 bytes: - // 00 nn Big endian UTF-16 - // nn 00 Little endian UTF-16 - // FE FF Big endian UTF-16 - // FF FE Little endian UTF-16 - // Otherwise UTF-8 - - if ( packetStr[0] == 0 ) return kXMP_Char16BitBig; - if ( packetStr[1] == 0 ) return kXMP_Char16BitLittle; - if ( CheckBytes ( packetStr, "\xFE\xFF", 2 ) ) return kXMP_Char16BitBig; - if ( CheckBytes ( packetStr, "\xFF\xFE", 2 ) ) return kXMP_Char16BitLittle; - return kXMP_Char8Bit; - - } - - // If we get here the packet is at least 4 bytes, could be any form. - - if ( unsBytes[0] == 0 ) { - - // These cases are: - // 00 nn -- -- - Big endian UTF-16 - // 00 00 00 nn - Big endian UTF-32 - // 00 00 FE FF - Big endian UTF 32 - - if ( unsBytes[1] != 0 ) { - charForm = kXMP_Char16BitBig; // 00 nn - } else { - if ( (unsBytes[2] == 0) && (unsBytes[3] != 0) ) { - charForm = kXMP_Char32BitBig; // 00 00 00 nn - } else if ( (unsBytes[2] == 0xFE) && (unsBytes[3] == 0xFF) ) { - charForm = kXMP_Char32BitBig; // 00 00 FE FF - } - } - - } else { - - // These cases are: - // FE FF -- -- - Big endian UTF-16, FE isn't valid UTF-8 - // FF FE 00 00 - Little endian UTF-32, FF isn't valid UTF-8 - // FF FE -- -- - Little endian UTF-16 - // nn mm -- -- - UTF-8, includes EF BB BF case - // nn 00 00 00 - Little endian UTF-32 - // nn 00 -- -- - Little endian UTF-16 - - if ( unsBytes[0] == 0xFE ) { - if ( unsBytes[1] == 0xFF ) charForm = kXMP_Char16BitBig; // FE FF - } else if ( unsBytes[0] == 0xFF ) { - if ( unsBytes[1] == 0xFE ) { - if ( (unsBytes[2] == 0) && (unsBytes[3] == 0) ) { - charForm = kXMP_Char32BitLittle; // FF FE 00 00 - } else { - charForm = kXMP_Char16BitLittle; // FF FE - } - } - } else if ( unsBytes[1] != 0 ) { - charForm = kXMP_Char8Bit; // nn mm - } else { - if ( (unsBytes[2] == 0) && (unsBytes[3] == 0) ) { - charForm = kXMP_Char32BitLittle; // nn 00 00 00 - } else { - charForm = kXMP_Char16BitLittle; // nn 00 - } - } - - } - - // XMP_Assert ( charForm != kXMP_CharUnknown ); - return charForm; - -} // GetPacketCharForm - -// ================================================================================================= -// FillPacketInfo -// ============== -// -// If a packet wrapper is present, the the packet string is roughly: -// <?xpacket begin= ...?> -// <outer-XML-element> -// ... more XML ... -// </outer-XML-element> -// ... whitespace padding ... -// <?xpacket end='.'?> - -// The 8-bit form is 14 bytes, the 16-bit form is 28 bytes, the 32-bit form is 56 bytes. -#define k8BitTrailer "<?xpacket end=" -#define k16BitTrailer "<\0?\0x\0p\0a\0c\0k\0e\0t\0 \0e\0n\0d\0=\0" -#define k32BitTrailer "<\0\0\0?\0\0\0x\0\0\0p\0\0\0a\0\0\0c\0\0\0k\0\0\0e\0\0\0t\0\0\0 \0\0\0e\0\0\0n\0\0\0d\0\0\0=\0\0\0" -static XMP_StringPtr kPacketTrailiers[3] = { k8BitTrailer, k16BitTrailer, k32BitTrailer }; - -void FillPacketInfo ( const std::string & packet, XMP_PacketInfo * info ) -{ - XMP_StringPtr packetStr = packet.c_str(); - XMP_StringLen packetLen = (XMP_StringLen) packet.size(); - if ( packetLen == 0 ) return; - - info->charForm = GetPacketCharForm ( packetStr, packetLen ); - XMP_StringLen charSize = XMP_GetCharSize ( info->charForm ); - - // Look for a packet wrapper. For our purposes, we can be lazy and just look for the trailer PI. - // If that is present we'll assume that a recognizable header is present. First do a bytewise - // search for '<', then a char sized comparison for the start of the trailer. We don't really - // care about big or little endian here. We're looking for ASCII bytes with zeroes between. - // Shorten the range comparisons (n*charSize) by 1 to easily tolerate both big and little endian. - - XMP_StringLen padStart, padEnd; - XMP_StringPtr packetTrailer = kPacketTrailiers [ charSize>>1 ]; - - padEnd = packetLen - 1; - for ( ; padEnd > 0; --padEnd ) if ( packetStr[padEnd] == '<' ) break; - if ( (packetStr[padEnd] != '<') || ((packetLen - padEnd) < (18*charSize)) ) return; - if ( ! CheckBytes ( &packetStr[padEnd], packetTrailer, (13*charSize) ) ) return; - - info->hasWrapper = true; - - char rwFlag = packetStr [padEnd + 15*charSize]; - if ( rwFlag == 'w' ) info->writeable = true; - - // Look for the start of the padding, right after the last XML end tag. - - padStart = padEnd; // Don't do the -charSize here, might wrap below zero. - for ( ; padStart >= charSize; padStart -= charSize ) if ( packetStr[padStart] == '>' ) break; - if ( padStart < charSize ) return; - padStart += charSize; // The padding starts after the '>'. - - info->padSize = padEnd - padStart; // We want bytes of padding, not character units. - -} // FillPacketInfo - -// ================================================================================================= -// ReadXMPPacket -// ============= - -void ReadXMPPacket ( XMPFileHandler * handler ) -{ - LFA_FileRef fileRef = handler->parent->fileRef; - std::string & xmpPacket = handler->xmpPacket; - XMP_StringLen packetLen = handler->packetInfo.length; - - if ( packetLen == 0 ) XMP_Throw ( "ReadXMPPacket - No XMP packet", kXMPErr_BadXMP ); - - xmpPacket.erase(); - xmpPacket.reserve ( packetLen ); - xmpPacket.append ( packetLen, ' ' ); - - XMP_StringPtr packetStr = XMP_StringPtr ( xmpPacket.c_str() ); // Don't set until after reserving the space! - - LFA_Seek ( fileRef, handler->packetInfo.offset, SEEK_SET ); - LFA_Read ( fileRef, (char*)packetStr, packetLen, kLFA_RequireAll ); - -} // ReadXMPPacket - -// ================================================================================================= -// XMPFileHandler::ProcessXMP -// ========================== -// -// This base implementation just parses the XMP. If the derived handler does reconciliation then it -// must have its own implementation of ProcessXMP. - -void XMPFileHandler::ProcessXMP() -{ - - if ( (!this->containsXMP) || this->processedXMP ) return; - - if ( this->handlerFlags & kXMPFiles_CanReconcile ) { - XMP_Throw ( "Reconciling file handlers must implement ProcessXMP", kXMPErr_InternalFailure ); - } - - SXMPUtils::RemoveProperties ( &this->xmpObj, 0, 0, kXMPUtil_DoAllProperties ); - this->xmpObj.ParseFromBuffer ( this->xmpPacket.c_str(), (XMP_StringLen)this->xmpPacket.size() ); - this->processedXMP = true; - -} // XMPFileHandler::ProcessXMP - -// ================================================================================================= -// XMPFileHandler::GetSerializeOptions -// =================================== -// -// This base implementation just selects compact serialization. The character form and padding/in-place -// settings are added in the common code before calling SerializeToBuffer. - -XMP_OptionBits XMPFileHandler::GetSerializeOptions() -{ - - return kXMP_UseCompactFormat; - -} // XMPFileHandler::GetSerializeOptions - -// ================================================================================================= diff --git a/source/XMPFiles/XMPFiles_Impl.hpp b/source/XMPFiles/XMPFiles_Impl.hpp deleted file mode 100644 index 294ca5f..0000000 --- a/source/XMPFiles/XMPFiles_Impl.hpp +++ /dev/null @@ -1,446 +0,0 @@ -#ifndef __XMPFiles_Impl_hpp__ -#define __XMPFiles_Impl_hpp__ 1 - -// ================================================================================================= -// ADOBE SYSTEMS INCORPORATED -// Copyright 2004 Adobe Systems Incorporated -// 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 "XMP_Environment.h" // ! Must be the first #include! -#include "XMP_Const.h" -#include "XMP_BuildInfo.h" -#include "XMP_LibUtils.hpp" -#include "EndianUtils.hpp" - -#include <string> -#define TXMP_STRING_TYPE std::string -#define XMP_INCLUDE_XMPFILES 1 -#include "XMP.hpp" - -#include "XMPFiles.hpp" -#include "LargeFileAccess.hpp" - -#include <vector> -#include <string> -#include <map> - -#include <cassert> - -#if XMP_WinBuild - #define snprintf _snprintf -#else - #if XMP_MacBuild - #include <Files.h> - #endif - // POSIX headers for both Mac and generic UNIX. - #include <fcntl.h> - #include <unistd.h> - #include <dirent.h> - #include <sys/stat.h> - #include <sys/types.h> -#endif - -// ================================================================================================= -// General global variables and macros - -extern bool ignoreLocalText; - -extern XMP_Int32 sXMPFilesInitCount; - -#ifndef GatherPerformanceData - #define GatherPerformanceData 0 -#endif - -#if ! GatherPerformanceData - - #define StartPerfCheck(proc,info) /* do nothing */ - #define EndPerfCheck(proc) /* do nothing */ - -#else - - #include "PerfUtils.hpp" - - enum { - kAPIPerf_OpenFile, - kAPIPerf_CloseFile, - kAPIPerf_GetXMP, - kAPIPerf_PutXMP, - kAPIPerf_CanPutXMP, - kAPIPerfProcCount // Last, count of the procs. - }; - - static const char* kAPIPerfNames[] = - { "OpenFile", "CloseFile", "GetXMP", "PutXMP", "CanPutXMP", 0 }; - - struct APIPerfItem { - XMP_Uns8 whichProc; - double elapsedTime; - XMPFilesRef xmpFilesRef; - std::string extraInfo; - APIPerfItem ( XMP_Uns8 proc, double time, XMPFilesRef ref, const char * info ) - : whichProc(proc), elapsedTime(time), xmpFilesRef(ref), extraInfo(info) {}; - }; - - typedef std::vector<APIPerfItem> APIPerfCollection; - - extern APIPerfCollection* sAPIPerf; - - #define StartPerfCheck(proc,info) \ - sAPIPerf->push_back ( APIPerfItem ( proc, 0.0, xmpFilesRef, info ) ); \ - APIPerfItem & thisPerf = sAPIPerf->back(); \ - PerfUtils::MomentValue startTime, endTime; \ - try { \ - startTime = PerfUtils::NoteThisMoment(); - - #define EndPerfCheck(proc) \ - endTime = PerfUtils::NoteThisMoment(); \ - thisPerf.elapsedTime = PerfUtils::GetElapsedSeconds ( startTime, endTime ); \ - } catch ( ... ) { \ - endTime = PerfUtils::NoteThisMoment(); \ - thisPerf.elapsedTime = PerfUtils::GetElapsedSeconds ( startTime, endTime ); \ - thisPerf.extraInfo += " ** THROW **"; \ - throw; \ - } - -#endif - -extern XMP_FileFormat voidFileFormat; // Used as sink for unwanted output parameters. -extern XMP_PacketInfo voidPacketInfo; -extern void * voidVoidPtr; -extern XMP_StringPtr voidStringPtr; -extern XMP_StringLen voidStringLen; -extern XMP_OptionBits voidOptionBits; - -static const XMP_Uns8 * kUTF8_PacketStart = (const XMP_Uns8 *) "<?xpacket begin="; -static const XMP_Uns8 * kUTF8_PacketID = (const XMP_Uns8 *) "W5M0MpCehiHzreSzNTczkc9d"; -static const size_t kUTF8_PacketHeaderLen = 51; // ! strlen ( "<?xpacket begin='xxx' id='W5M0MpCehiHzreSzNTczkc9d'" ) - -static const XMP_Uns8 * kUTF8_PacketTrailer = (const XMP_Uns8 *) "<?xpacket end=\"w\"?>"; -static const size_t kUTF8_PacketTrailerLen = 19; // ! strlen ( kUTF8_PacketTrailer ) - -struct FileExtMapping { - XMP_StringPtr ext; - XMP_FileFormat format; -}; - -extern const FileExtMapping kFileExtMap[]; -extern const char * kKnownScannedFiles[]; -extern const char * kKnownRejectedFiles[]; - -#define Uns8Ptr(p) ((XMP_Uns8 *) (p)) - -#define IsNewline( ch ) ( ((ch) == kLF) || ((ch) == kCR) ) -#define IsSpaceOrTab( ch ) ( ((ch) == ' ') || ((ch) == kTab) ) -#define IsWhitespace( ch ) ( IsSpaceOrTab ( ch ) || IsNewline ( ch ) ) - -static inline void MakeLowerCase ( std::string * str ) -{ - for ( size_t i = 0, limit = str->size(); i < limit; ++i ) { - char ch = (*str)[i]; - if ( ('A' <= ch) && (ch <= 'Z') ) (*str)[i] += 0x20; - } -} - -static inline void MakeUpperCase ( std::string * str ) -{ - for ( size_t i = 0, limit = str->size(); i < limit; ++i ) { - char ch = (*str)[i]; - if ( ('a' <= ch) && (ch <= 'z') ) (*str)[i] -= 0x20; - } -} - -#define XMP_LitMatch(s,l) (std::strcmp((s),(l)) == 0) -#define XMP_LitNMatch(s,l,n) (std::strncmp((s),(l),(n)) == 0) - -// ================================================================================================= -// Support for call tracing - -#ifndef XMP_TraceFilesCalls - #define XMP_TraceFilesCalls 0 - #define XMP_TraceFilesCallsToFile 0 -#endif - -#if XMP_TraceFilesCalls - - #undef AnnounceThrow - #undef AnnounceCatch - - #undef AnnounceEntry - #undef AnnounceNoLock - #undef AnnounceExit - - extern FILE * xmpFilesLog; - - #define AnnounceThrow(msg) \ - fprintf ( xmpFilesLog, "XMP_Throw: %s\n", msg ); fflush ( xmpFilesLog ) - #define AnnounceCatch(msg) \ - fprintf ( xmpFilesLog, "Catch in %s: %s\n", procName, msg ); fflush ( xmpFilesLog ) - - #define AnnounceEntry(proc) \ - const char * procName = proc; \ - fprintf ( xmpFilesLog, "Entering %s\n", procName ); fflush ( xmpFilesLog ) - #define AnnounceNoLock(proc) \ - const char * procName = proc; \ - fprintf ( xmpFilesLog, "Entering %s (no lock)\n", procName ); fflush ( xmpFilesLog ) - #define AnnounceExit() \ - fprintf ( xmpFilesLog, "Exiting %s\n", procName ); fflush ( xmpFilesLog ) - -#endif - -// ================================================================================================= -// Support for memory leak tracking - -#ifndef TrackMallocAndFree - #define TrackMallocAndFree 0 -#endif - -#if TrackMallocAndFree - - static void* ChattyMalloc ( size_t size ) - { - void* ptr = malloc ( size ); - fprintf ( stderr, "Malloc %d bytes @ %.8X\n", size, ptr ); - return ptr; - } - - static void ChattyFree ( void* ptr ) - { - fprintf ( stderr, "Free @ %.8X\n", ptr ); - free ( ptr ); - } - - #define malloc(s) ChattyMalloc ( s ) - #define free(p) ChattyFree ( p ) - -#endif - -// ================================================================================================= -// FileHandler declarations - -extern void ReadXMPPacket ( XMPFileHandler * handler ); - -extern void FillPacketInfo ( const XMP_VarString & packet, XMP_PacketInfo * info ); - -class XMPFileHandler { // See XMPFiles.hpp for usage notes. -public: - - #define DefaultCTorPresets \ - handlerFlags(0), stdCharForm(kXMP_CharUnknown), \ - containsXMP(false), processedXMP(false), needsUpdate(false) - - XMPFileHandler() : parent(0), DefaultCTorPresets {}; - XMPFileHandler (XMPFiles * _parent) : parent(_parent), DefaultCTorPresets {}; - - virtual ~XMPFileHandler() {}; // ! The specific handler is responsible for tnailInfo.tnailImage. - - virtual void CacheFileData() = 0; - virtual void ProcessXMP(); // The default implementation just parses the XMP. - - virtual XMP_OptionBits GetSerializeOptions(); // The default is compact. - - virtual void UpdateFile ( bool doSafeUpdate ) = 0; - virtual void WriteFile ( LFA_FileRef sourceRef, const std::string & sourcePath ) = 0; - - // ! Leave the data members public so common code can see them. - - XMPFiles * parent; // Let's the handler see the file info. - XMP_OptionBits handlerFlags; // Capabilities of this handler. - XMP_Uns8 stdCharForm; // The standard character form for output. - - bool containsXMP; // True if the file has XMP or PutXMP has been called. - bool processedXMP; // True if the XMP is parsed and reconciled. - bool needsUpdate; // True if the file needs to be updated. - - XMP_PacketInfo packetInfo; // ! This is always info about the packet in the file, if any! - std::string xmpPacket; // ! This is the current XMP, updated by XMPFiles::PutXMP. - SXMPMeta xmpObj; - -}; // XMPFileHandler - -typedef XMPFileHandler * (* XMPFileHandlerCTor) ( XMPFiles * parent ); - -typedef bool (* CheckFileFormatProc ) ( XMP_FileFormat format, - XMP_StringPtr filePath, - LFA_FileRef fileRef, - XMPFiles * parent ); - -typedef bool (*CheckFolderFormatProc ) ( XMP_FileFormat format, - const std::string & rootPath, - const std::string & gpName, - const std::string & parentName, - const std::string & leafName, - XMPFiles * parent ); - -// ================================================================================================= - -#if XMP_MacBuild - extern LFA_FileRef LFA_OpenRsrc ( const char * fileName, char openMode ); // Open the Mac resource fork. -#endif - -extern void CreateTempFile ( const std::string & origPath, std::string * tempPath, bool copyMacRsrc = false ); -enum { kCopyMacRsrc = true }; - -struct AutoFile { // Provides auto close of files on exit or exception. - LFA_FileRef fileRef; - AutoFile() : fileRef(0) {}; - ~AutoFile() { if ( fileRef != 0 ) LFA_Close ( fileRef ); }; -}; - -enum { kFMode_DoesNotExist, kFMode_IsFile, kFMode_IsFolder, kFMode_IsOther }; -typedef XMP_Uns8 FileMode; - -#if XMP_WinBuild - #define kDirChar '\\' -#else - #define kDirChar '/' -#endif - -class XMP_FolderInfo { -public: - - XMP_FolderInfo() : dirRef(0) {}; - ~XMP_FolderInfo() { if ( this->dirRef != 0 ) this->Close(); }; - - void Open ( const char * folderPath ); - void Close(); - - bool GetFolderPath ( XMP_VarString * folderPath ); - bool GetNextChild ( XMP_VarString * childName ); - -private: - - std::string folderPath; - - #if XMP_WinBuild - HANDLE dirRef; // Windows uses FindFirstFile and FindNextFile. - #else - DIR * dirRef; // Mac and UNIX use the POSIX opendir/readdir/closedir functions. - #endif - -}; - -extern FileMode GetFileMode ( const char * path ); - -static inline FileMode GetChildMode ( std::string & path, XMP_StringPtr childName ) -{ - size_t pathLen = path.size(); - path += kDirChar; - path += childName; - FileMode mode = GetFileMode ( path.c_str() ); - path.erase ( pathLen ); - return mode; -} - -static inline void SplitLeafName ( std::string * path, std::string * leafName ) -{ - size_t dirPos = path->size(); - if ( dirPos == 0 ) { - leafName->erase(); - return; - } - - for ( --dirPos; dirPos > 0; --dirPos ) { - #if XMP_WinBuild - if ( (*path)[dirPos] == '/' ) (*path)[dirPos] = kDirChar; // Tolerate both '\' and '/'. - #endif - if ( (*path)[dirPos] == kDirChar ) break; - } - if ( (*path)[dirPos] == kDirChar ) { - leafName->assign ( &(*path)[dirPos+1] ); - path->erase ( dirPos ); - } else if ( dirPos == 0 ) { - leafName->erase(); - leafName->swap ( *path ); - } - -} - -// ------------------------------------------------------------------------------------------------- - -static inline bool CheckBytes ( const void * left, const void * right, size_t length ) -{ - return (std::memcmp ( left, right, length ) == 0); -} - -// ------------------------------------------------------------------------------------------------- - -static inline bool CheckCString ( const void * left, const void * right ) -{ - return (std::strcmp ( (char*)left, (char*)right ) == 0); -} - -// ------------------------------------------------------------------------------------------------- -// CheckFileSpace and RefillBuffer -// ------------------------------- -// -// There is always a problem in file scanning of managing what you want to check against what is -// available in a buffer, trying to keep the logic understandable and minimize data movement. The -// CheckFileSpace and RefillBuffer functions are used here for a standard scanning model. -// -// The format scanning routines have an outer, "infinite" loop that looks for file markers. There -// is a local (on stack) buffer, a pointer to the current position in the buffer, and a pointer for -// the end of the buffer. The end pointer is just past the end of the buffer, "bufPtr == bufLimit" -// means you are out of data. The outer loop ends when the necessary markers are found or we reach -// the end of the file. -// -// The filePos is the file offset of the start of the current buffer. This is maintained so that -// we can tell where the packet is in the file, part of the info returned to the client. -// -// At each check CheckFileSpace is used to make sure there is enough data in the buffer for the -// check to be made. It refills the buffer if necessary, preserving the unprocessed data, setting -// bufPtr and bufLimit appropriately. If we are too close to the end of the file to make the check -// a failure status is returned. - -enum { kIOBufferSize = 128*1024 }; - -struct IOBuffer { - XMP_Int64 filePos; - XMP_Uns8* ptr; - XMP_Uns8* limit; - size_t len; - XMP_Uns8 data [kIOBufferSize]; - IOBuffer() : filePos(0), ptr(&data[0]), limit(ptr), len(0) {}; -}; - -static inline void FillBuffer ( LFA_FileRef fileRef, XMP_Int64 fileOffset, IOBuffer* ioBuf ) -{ - ioBuf->filePos = LFA_Seek ( fileRef, fileOffset, SEEK_SET ); - if ( ioBuf->filePos != fileOffset ) XMP_Throw ( "Seek failure in FillBuffer", kXMPErr_ExternalFailure ); - ioBuf->len = LFA_Read ( fileRef, &ioBuf->data[0], kIOBufferSize ); - ioBuf->ptr = &ioBuf->data[0]; - ioBuf->limit = ioBuf->ptr + ioBuf->len; -} - -static inline void MoveToOffset ( LFA_FileRef fileRef, XMP_Int64 fileOffset, IOBuffer* ioBuf ) -{ - if ( (ioBuf->filePos <= fileOffset) && (fileOffset < (XMP_Int64)(ioBuf->filePos + ioBuf->len)) ) { - size_t bufOffset = (size_t)(fileOffset - ioBuf->filePos); - ioBuf->ptr = &ioBuf->data[bufOffset]; - } else { - FillBuffer ( fileRef, fileOffset, ioBuf ); - } -} - -static inline void RefillBuffer ( LFA_FileRef fileRef, IOBuffer* ioBuf ) -{ - ioBuf->filePos += (ioBuf->ptr - &ioBuf->data[0]); // ! Increment before the read. - size_t bufTail = ioBuf->limit - ioBuf->ptr; // We'll re-read the tail portion of the buffer. - if ( bufTail > 0 ) ioBuf->filePos = LFA_Seek ( fileRef, -((XMP_Int64)bufTail), SEEK_CUR ); - ioBuf->len = LFA_Read ( fileRef, &ioBuf->data[0], kIOBufferSize ); - ioBuf->ptr = &ioBuf->data[0]; - ioBuf->limit = ioBuf->ptr + ioBuf->len; -} - -static inline bool CheckFileSpace ( LFA_FileRef fileRef, IOBuffer* ioBuf, size_t neededLen ) -{ - if ( size_t(ioBuf->limit - ioBuf->ptr) < size_t(neededLen) ) { // ! Avoid VS.Net compare warnings. - RefillBuffer ( fileRef, ioBuf ); - } - return (size_t(ioBuf->limit - ioBuf->ptr) >= size_t(neededLen)); -} - -#endif /* __XMPFiles_Impl_hpp__ */ |