summaryrefslogtreecommitdiff
path: root/source/XMPFiles/XMPFiles.cpp
diff options
context:
space:
mode:
authorHubert Figuiere <hub@figuiere.net>2008-11-17 23:42:00 -0500
committerHubert Figuiere <hub@figuiere.net>2008-11-17 23:42:00 -0500
commit88af812fde414aca8f9add90bc800ea3d8e9a281 (patch)
tree0403dd1897c0b287d4d710dd422827683c59dfcb /source/XMPFiles/XMPFiles.cpp
parent9d7d7c3caac05db240692ad7e9196fcb7f5a1ce5 (diff)
upgrade to XMP-SDK 4.4.2
Diffstat (limited to 'source/XMPFiles/XMPFiles.cpp')
-rw-r--r--source/XMPFiles/XMPFiles.cpp962
1 files changed, 760 insertions, 202 deletions
diff --git a/source/XMPFiles/XMPFiles.cpp b/source/XMPFiles/XMPFiles.cpp
index 136feb3..8471675 100644
--- a/source/XMPFiles/XMPFiles.cpp
+++ b/source/XMPFiles/XMPFiles.cpp
@@ -1,6 +1,6 @@
// =================================================================================================
// ADOBE SYSTEMS INCORPORATED
-// Copyright 2002-2007 Adobe Systems Incorporated
+// Copyright 2002-2008 Adobe Systems Incorporated
// All Rights Reserved
//
// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms
@@ -12,7 +12,6 @@
#include "XMPFiles_Impl.hpp"
#include "UnicodeConversions.hpp"
-#include "QuickTime_Support.hpp"
// These are the official, fully supported handlers.
#include "FileHandlers/JPEG_Handler.hpp"
@@ -21,12 +20,26 @@
#include "FileHandlers/InDesign_Handler.hpp"
#include "FileHandlers/PostScript_Handler.hpp"
#include "FileHandlers/Scanner_Handler.hpp"
-#include "FileHandlers/MOV_Handler.hpp"
-#include "FileHandlers/MPEG_Handler.hpp"
-#include "FileHandlers/MP3_Handler.hpp"
+#include "FileHandlers/MPEG2_Handler.hpp"
#include "FileHandlers/PNG_Handler.hpp"
#include "FileHandlers/AVI_Handler.hpp"
#include "FileHandlers/WAV_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"
+
+#if ! (XMP_64 || XMP_UNIXBuild)
+ #include "QuickTime_Support.hpp"
+ #include "FileHandlers/MOV_Handler.hpp" //old MOV handler
+#endif
// =================================================================================================
/// \file XMPFiles.cpp
@@ -65,26 +78,106 @@ const char * kXMPFiles_EmbeddedCopyright = kXMPFilesName " " kXMP_CopyrightStr;
// =================================================================================================
struct XMPFileHandlerInfo {
- XMP_FileFormat format;
- XMP_OptionBits flags;
- CheckFormatProc checkProc;
+ 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,
- CheckFormatProc _checkProc, XMPFileHandlerCTor _handlerCTor )
- : format(_format), flags(_flags), checkProc(_checkProc), handlerCTor(_handlerCTor) {};
+ 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) {};
};
-typedef std::vector <XMPFileHandlerInfo> XMPFileHandlerTable;
+// 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;
-static XMPFileHandlerTable * sRegisteredHandlers = 0; // ! Only smart handlers are registered!
+ 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 XMPFileHandlerTablePos
-FindHandler ( XMP_FileFormat format,
- std::string & fileExt )
+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 ) {
@@ -95,39 +188,446 @@ FindHandler ( XMP_FileFormat format,
}
}
- if ( format == kXMP_UnknownFile ) return sRegisteredHandlers->end();
+ 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 };
- XMPFileHandlerTablePos handlerPos = sRegisteredHandlers->begin();
- for ( ; handlerPos != sRegisteredHandlers->end(); ++handlerPos ) {
- if ( handlerPos->format == format ) return handlerPos;
+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;
+
+ // XDCAMEX .../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
+ 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;
- return sRegisteredHandlers->end();
+ // AVCHD .../MyMovie/BDMV/<group>/<file>.<ext> - check for BDMV and CLIPINF or STREAM
+ if ( (gpName == "BDMV") && ((parentName == "CLIPINF") || (parentName == "STREAM")) ) return kXMP_AVCHDFile;
-} // FindHandler
+ return kXMP_UnknownFile;
+
+} // CheckParentFolderNames
// =================================================================================================
-static void
-RegisterXMPFileHandler ( XMP_FileFormat format,
- XMP_OptionBits flags,
- CheckFormatProc checkProc,
- XMPFileHandlerCTor handlerCTor )
+static XMP_FileFormat
+CheckTopFolderName ( const std::string & rootPath )
{
- XMP_Assert ( format != kXMP_UnknownFile );
- std::string noExt;
+ // 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";
+ 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;
+
+ std::string leafName ( _leafName );
+ MakeUpperCase ( &leafName );
- if ( FindHandler ( format, noExt ) != sRegisteredHandlers->end() ) {
- XMP_Throw ( "Duplicate handler registration", kXMPErr_InternalFailure );
+ // 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->handlerTemp == 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->handlerTemp == 0) );
+ if ( foundHandler ) break; // ! Exit before incrementing handlerPos.
+ }
+
}
- if ( (flags & kXMPFiles_CanInjectXMP) && (! (flags & kXMPFiles_CanExpand)) ) {
- XMP_Throw ( "Inconsistent handler flags", kXMPErr_InternalFailure );
+ 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 = handlerInfo->format; // ! Hack to tell the CheckProc thiz is an initial call.
+
+ if ( ! (handlerInfo->flags & kXMPFiles_FolderBasedFormat) ) {
+ CheckFileFormatProc CheckProc = (CheckFileFormatProc) (handlerInfo->checkProc);
+ foundHandler = CheckProc ( handlerInfo->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->handlerTemp == 0) );
+ if ( foundHandler ) return handlerInfo;
+ handlerInfo = 0; // ! Clear again for later use.
+
+ }
+
+ if ( openFlags & kXMPFiles_OpenStrictly ) return 0;
+
}
- sRegisteredHandlers->push_back ( XMPFileHandlerInfo ( format, flags, checkProc, handlerCTor ) );
+ // Try the folder handlers if appropriate.
+
+ XMP_Assert ( handlerInfo == 0 );
+ XMP_Assert ( (clientMode == kFMode_IsFile) || (clientMode == kFMode_DoesNotExist) );
+
+ std::string gpName, parentName;
-} // RegisterXMPFileHandler
+ 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")) ) {
+ 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.
+ }
+ 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->handlerTemp == 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->handlerTemp == 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->handlerTemp == 0) );
+ if ( foundHandler ) return handlerInfo;
+ }
+
+ // Failed to find a smart handler.
+
+ return 0;
+
+} // SelectSmartHandler
// =================================================================================================
@@ -149,7 +649,9 @@ XMPFiles::GetVersionInfo ( XMP_VersionInfo * info )
// =================================================================================================
-static bool sIgnoreQuickTime = false;
+#if ! (XMP_64 || XMP_UNIXBuild)
+ static bool sIgnoreQuickTime = false; // Not vital, but helps catching missing excludes elsewhere.
+#endif
/* class static */
bool
@@ -177,62 +679,68 @@ XMPFiles::Initialize ( XMP_OptionBits options /* = 0 */ )
XMP_Assert ( kUTF8_PacketHeaderLen == strlen ( "<?xpacket begin='xxx' id='W5M0MpCehiHzreSzNTczkc9d'" ) );
XMP_Assert ( kUTF8_PacketTrailerLen == strlen ( (const char *) kUTF8_PacketTrailer ) );
- sRegisteredHandlers = new XMPFileHandlerTable;
+ sFolderHandlers = new XMPFileHandlerTable;
+ sNormalHandlers = new XMPFileHandlerTable;
+ sOwningHandlers = new XMPFileHandlerTable;
+
sXMPFilesExceptionMessage = new XMP_VarString;
InitializeUnicodeConversions();
- sIgnoreQuickTime = XMP_OptionIsSet ( options, kXMPFiles_NoQuickTimeInit );
- if ( ! sIgnoreQuickTime ) {
- (void) QuickTime_Support::MainInitialize(); // Don't worry about failure, the MOV handler checks that.
- }
+ #if ! (XMP_64 || XMP_UNIXBuild)
+ sIgnoreQuickTime = XMP_OptionIsSet ( options, kXMPFiles_NoQuickTimeInit );
+ (void) QuickTime_Support::MainInitialize ( sIgnoreQuickTime ); // Don't worry about failure, the MOV handler checks that.
+ #endif
- // ----------------------------------------------------------------------------------
- // First register the handlers that don't want to open and close the file themselves.
-
- XMP_Assert ( ! (kJPEG_HandlerFlags & kXMPFiles_HandlerOwnsFile) );
- RegisterXMPFileHandler ( kXMP_JPEGFile, kJPEG_HandlerFlags, JPEG_CheckFormat, JPEG_MetaHandlerCTor );
-
- XMP_Assert ( ! (kTIFF_HandlerFlags & kXMPFiles_HandlerOwnsFile) );
- RegisterXMPFileHandler ( kXMP_TIFFFile, kTIFF_HandlerFlags, TIFF_CheckFormat, TIFF_MetaHandlerCTor );
+#if XMP_UNIXBuild
- XMP_Assert ( ! (kPSD_HandlerFlags & kXMPFiles_HandlerOwnsFile) );
- RegisterXMPFileHandler ( kXMP_PhotoshopFile, kPSD_HandlerFlags, PSD_CheckFormat, PSD_MetaHandlerCTor );
+ // *** For the time being only allow the JPEG smart handler for generic UNIX, not even packet scanning.
+ RegisterNormalHandler ( kXMP_JPEGFile, kJPEG_HandlerFlags, JPEG_CheckFormat, JPEG_MetaHandlerCTor );
- XMP_Assert ( ! (kInDesign_HandlerFlags & kXMPFiles_HandlerOwnsFile) );
- RegisterXMPFileHandler ( kXMP_InDesignFile, kInDesign_HandlerFlags, InDesign_CheckFormat, InDesign_MetaHandlerCTor );
-
- // ! EPS and PostScript have the same handler, EPS is a proper subset of PostScript.
- XMP_Assert ( ! (kPostScript_HandlerFlags & kXMPFiles_HandlerOwnsFile) );
- RegisterXMPFileHandler ( kXMP_EPSFile, kPostScript_HandlerFlags, PostScript_CheckFormat, PostScript_MetaHandlerCTor );
- RegisterXMPFileHandler ( kXMP_PostScriptFile, kPostScript_HandlerFlags, PostScript_CheckFormat, PostScript_MetaHandlerCTor );
-
- XMP_Assert ( ! (kPNG_HandlerFlags & kXMPFiles_HandlerOwnsFile) );
- RegisterXMPFileHandler ( kXMP_PNGFile, kPNG_HandlerFlags, PNG_CheckFormat, PNG_MetaHandlerCTor );
+#else
+
+ // -----------------------------------------
+ // Register the directory-oriented handlers.
- XMP_Assert ( ! (kAVI_HandlerFlags & kXMPFiles_HandlerOwnsFile) );
- RegisterXMPFileHandler ( kXMP_AVIFile, kAVI_HandlerFlags, AVI_CheckFormat, AVI_MetaHandlerCTor );
+ 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 );
- XMP_Assert ( ! (kWAV_HandlerFlags & kXMPFiles_HandlerOwnsFile) );
- RegisterXMPFileHandler ( kXMP_WAVFile, kWAV_HandlerFlags, WAV_CheckFormat, WAV_MetaHandlerCTor );
+ // ------------------------------------------------------------------------------------------
+ // Register the file-oriented handlers that don't want to open and close the file themselves.
- XMP_Assert ( ! (kMP3_HandlerFlags & kXMPFiles_HandlerOwnsFile) );
- RegisterXMPFileHandler ( kXMP_MP3File, kMP3_HandlerFlags, MP3_CheckFormat, MP3_MetaHandlerCTor );
-
- #if XMP_WinBuild
-
- // Windows-only handlers.
+ 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, kWAV_HandlerFlags, WAV_CheckFormat, WAV_MetaHandlerCTor );
+ RegisterNormalHandler ( kXMP_AVIFile, kAVI_HandlerFlags, AVI_CheckFormat, AVI_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_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 );
+
+ #if ! (XMP_64 || XMP_UNIXBuild)
+ RegisterOwningHandler ( kXMP_MOVFile, kMOV_HandlerFlags, MOV_CheckFormat, MOV_MetaHandlerCTor );
#endif
- // -----------------------------------------------------------------------------
- // Now register the handlers that do want to open and close the file themselves.
-
- XMP_Assert ( kMOV_HandlerFlags & kXMPFiles_HandlerOwnsFile );
- RegisterXMPFileHandler ( kXMP_MOVFile, kMOV_HandlerFlags, MOV_CheckFormat, MOV_MetaHandlerCTor );
-
- XMP_Assert ( kMPEG_HandlerFlags & kXMPFiles_HandlerOwnsFile );
- RegisterXMPFileHandler ( kXMP_MPEGFile, kMPEG_HandlerFlags, MPEG_CheckFormat, MPEG_MetaHandlerCTor );
+#endif // XMP_UNIXBuild, temporary exclusions
// Make sure the embedded info strings are referenced and kept.
if ( (kXMPFiles_EmbeddedVersion[0] == 0) || (kXMPFiles_EmbeddedCopyright[0] == 0) ) return false;
@@ -326,14 +834,19 @@ XMPFiles::Terminate()
--sXMPFilesInitCount;
if ( sXMPFilesInitCount != 0 ) return;
- if ( ! sIgnoreQuickTime ) QuickTime_Support::MainTerminate();
-
+ #if ! (XMP_64 || XMP_UNIXBuild)
+ QuickTime_Support::MainTerminate ( sIgnoreQuickTime );
+ #endif
+
#if GatherPerformanceData
ReportPerformanceData();
EliminateGlobal ( sAPIPerf );
#endif
- EliminateGlobal ( sRegisteredHandlers );
+ EliminateGlobal ( sFolderHandlers );
+ EliminateGlobal ( sNormalHandlers );
+ EliminateGlobal ( sOwningHandlers );
+
EliminateGlobal ( sXMPFilesExceptionMessage );
XMP_TermMutex ( sXMPFilesLock );
@@ -368,10 +881,13 @@ XMPFiles::~XMPFiles()
delete this->handler;
this->handler = 0;
}
+
if ( this->fileRef != 0 ) {
LFA_Close ( this->fileRef );
this->fileRef = 0;
}
+
+ if ( this->handlerTemp != 0 ) free ( this->handlerTemp ); // ! Must have been malloc-ed!
} // XMPFiles::~XMPFiles
@@ -417,12 +933,24 @@ XMPFiles::GetFormatInfo ( XMP_FileFormat format,
{
if ( flags == 0 ) flags = &voidOptionBits;
- XMPFileHandlerTablePos handlerPos = sRegisteredHandlers->begin();
- for ( ; handlerPos != sRegisteredHandlers->end(); ++handlerPos ) {
- if ( handlerPos->format == format ) {
- *flags = handlerPos->flags;
- return true;
- }
+ 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;
@@ -431,145 +959,148 @@ XMPFiles::GetFormatInfo ( XMP_FileFormat format,
// =================================================================================================
+/* 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 filePath,
+XMPFiles::OpenFile ( XMP_StringPtr clientPath,
XMP_FileFormat format /* = kXMP_UnknownFile */,
XMP_OptionBits openFlags /* = 0 */ )
{
- // *** Check consistency of the option bits, use of kXMPFiles_OpenUseSmartHandler.
-
+#if XMP_UNIXBuild
+ // *** For the time being only allow the JPEG smart handler for generic UNIX, not even packet scanning.
+ format = kXMP_JPEGFile;
+ openFlags |= (kXMPFiles_OpenUseSmartHandler | kXMPFiles_OpenStrictly);
+#endif
+
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;
+ }
- // Select the file handler. Don't set the XMPFiles member variables until the end, leave them
- // clean in case something fails along the way. Use separate handler search loops, filtering on
- // whether the handler wants to open the file itself.
-
- // An initial handler is tried based on the format parameter, or the file extension if the
- // parameter is kXMP_UnknownFile. If that fails, all of the handlers are tried in registration
- // order. 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 call
- // has the presumed format in this->format, the later calls have kXMP_UnknownFile there.
-
- bool foundHandler = false;
- LFA_FileRef 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';
- XMPFileHandlerTablePos handlerPos = sRegisteredHandlers->end();
-
- std::string fileExt;
- size_t extPos = strlen ( filePath );
- for ( --extPos; extPos > 0; --extPos ) if ( filePath[extPos] == '.' ) break;
- if ( filePath[extPos] == '.' ) {
- ++extPos;
- fileExt.assign ( &filePath[extPos] );
- for ( size_t i = 0; i < fileExt.size(); ++i ) {
- if ( ('A' <= fileExt[i]) && (fileExt[i] <= 'Z') ) fileExt[i] += 0x20;
- }
- }
+ FileMode clientMode = GetFileMode ( clientPath );
+ if ( (clientMode == kFMode_IsFolder) || (clientMode == kFMode_IsOther) ) return false;
+ XMP_Assert ( (clientMode == kFMode_IsFile) || (clientMode == kFMode_DoesNotExist) );
- this->format = kXMP_UnknownFile; // Make sure it is preset for later check.
- this->openFlags = openFlags; // ! The QuickTime support needs the InBackground bit.
+ std::string fileExt; // Used to check for camera raw files and OK to scan files.
- if ( ! (openFlags & kXMPFiles_OpenUsePacketScanning) ) {
+ if ( clientMode == kFMode_IsFile ) {
- // Try an initial handler based on the format or file extension.
- handlerPos = FindHandler ( format, fileExt );
- if ( handlerPos != sRegisteredHandlers->end() ) {
- if ( ! (handlerPos->flags & kXMPFiles_HandlerOwnsFile) ) fileRef = LFA_Open ( filePath, openMode );
- this->format = handlerPos->format; // ! Hack to tell the CheckProc this is the first call.
- foundHandler = handlerPos->checkProc ( handlerPos->format, filePath, fileRef, this );
+ // 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 );
}
- if ( (openFlags & kXMPFiles_OpenStrictly) && (format != kXMP_UnknownFile) && (! foundHandler) ) {
- return false;
- }
-
- // Search the handlers that don't want to open the file themselves. They must be registered first.
- if ( ! foundHandler ) {
- if ( fileRef == 0 ) fileRef = LFA_Open ( filePath, openMode );
- XMP_Assert ( fileRef != 0 ); // LFA_Open must either succeed or throw.
- for ( handlerPos = sRegisteredHandlers->begin(); handlerPos != sRegisteredHandlers->end(); ++handlerPos ) {
- if ( handlerPos->flags & kXMPFiles_HandlerOwnsFile ) break;
- this->format = kXMP_UnknownFile; // ! Hack to tell the CheckProc this is not the first call.
- foundHandler = handlerPos->checkProc ( handlerPos->format, filePath, fileRef, this );
- if ( foundHandler ) break; // ! Exit before incrementing handlerPos.
- }
- }
-
- // Search the handlers that do want to open the file themselves. They must be registered last.
- if ( ! foundHandler ) {
- LFA_Close ( fileRef );
- fileRef = 0;
- for ( ; handlerPos != sRegisteredHandlers->end(); ++handlerPos ) {
- XMP_Assert ( handlerPos->flags & kXMPFiles_HandlerOwnsFile );
- this->format = kXMP_UnknownFile; // ! Hack to tell the CheckProc this is not the first call.
- foundHandler = handlerPos->checkProc ( handlerPos->format, filePath, 0, this );
- if ( foundHandler ) break; // ! Exit before incrementing handlerPos.
- }
- }
-
- if ( (! foundHandler) && (openFlags & kXMPFiles_OpenUseSmartHandler) ) {
- return false;
+ // 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;
}
}
- // Create the handler object, fill in the XMPFiles member variables, cache the desired file data.
-
- XMPFileHandlerCTor handlerCTor = 0;
- XMP_OptionBits handlerFlags = 0;
-
- if ( foundHandler ) {
+ // Find the handler, fill in the XMPFiles member variables, cache the desired file data.
+
+ XMPFileHandlerInfo * handlerInfo = 0;
+ XMPFileHandlerCTor handlerCTor = 0;
+ XMP_OptionBits handlerFlags = 0;
- // Found a smart handler.
- format = handlerPos->format;
- handlerCTor = handlerPos->handlerCTor;
- handlerFlags = handlerPos->flags;
+ if ( ! (openFlags & kXMPFiles_OpenUsePacketScanning) ) {
+ handlerInfo = SelectSmartHandler ( this, clientPath, format, openFlags );
+ }
- } else {
+ 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 ) {
- size_t i;
- for ( i = 0; kKnownScannedFiles[i] != 0; ++i ) {
- if ( fileExt == kKnownScannedFiles[i] ) break;
+ bool scanningOK = false;
+ for ( size_t i = 0; kKnownScannedFiles[i] != 0; ++i ) {
+ if ( fileExt == kKnownScannedFiles[i] ) { scanningOK = true; break; }
}
- if ( kKnownScannedFiles[i] == 0 ) return false;
+ if ( ! scanningOK ) return false;
}
- format = kXMP_UnknownFile;
- handlerCTor = Scanner_MetaHandlerCTor;
- handlerFlags = kScanner_HandlerFlags;
- if ( fileRef == 0 ) fileRef = LFA_Open ( filePath, openMode );
+
+ handlerInfo = &kScannerHandlerInfo;
+ if ( fileRef == 0 ) fileRef = LFA_Open ( clientPath, openMode );
}
+
+ XMP_Assert ( handlerInfo != 0 );
+ format = handlerInfo->format;
+ handlerCTor = handlerInfo->handlerCTor;
+ handlerFlags = handlerInfo->flags;
- this->fileRef = fileRef;
- this->filePath = filePath;
+ this->filePath = clientPath;
- XMPFileHandler * handler = (*handlerCTor) ( this );
+ XMPFileHandler* handler = (*handlerCTor) ( this );
XMP_Assert ( handlerFlags == handler->handlerFlags );
this->handler = handler;
if ( this->format == kXMP_UnknownFile ) this->format = format; // ! The CheckProc might have set it.
- handler->CacheFileData();
+ try {
+ handler->CacheFileData();
+ } catch ( ... ) {
+ delete this->handler;
+ this->handler = 0;
+ if ( ! (handlerFlags & kXMPFiles_HandlerOwnsFile) ) {
+ LFA_Close ( this->fileRef );
+ this->fileRef = 0;
+ }
+ throw;
+ }
if ( ! (openFlags & kXMPFiles_OpenCacheTNail) ) {
handler->containsTNail = false; // Make sure GetThumbnail will cleanly return false.
handler->processedTNail = true;
}
- if ( handler->containsXMP ) {
- XMP_StringPtr packetStr = handler->xmpPacket.c_str();
- XMP_StringLen packetLen = handler->xmpPacket.size();
- if ( packetLen > 0 ) {
- handler->packetInfo.charForm = GetPacketCharForm ( packetStr, packetLen );
- handler->packetInfo.padSize = GetPacketPadSize ( packetStr, packetLen );
- handler->packetInfo.writeable = GetPacketRWMode ( packetStr, packetLen, XMP_GetCharSize ( handler->packetInfo.charForm ) );
- }
- }
+ 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.
@@ -621,7 +1152,7 @@ XMPFiles::CloseFile ( XMP_OptionBits closeFlags /* = 0 */ )
try {
- if ( (! doSafeUpdate) || (handlerFlags & kXMPFiles_HandlerOwnsFile) ) {
+ if ( (! doSafeUpdate) || (handlerFlags & kXMPFiles_HandlerOwnsFile) ) { // ! Includes no update case.
// Close the file without doing common crash-safe writing. The handler might do it.
@@ -651,6 +1182,7 @@ XMPFiles::CloseFile ( XMP_OptionBits closeFlags /* = 0 */ )
#endif
CreateTempFile ( origFilePath, &tempFilePath, kCopyMacRsrc );
+ XMP_Assert ( tempFileRef == 0 );
tempFileRef = LFA_Open ( tempFilePath.c_str(), 'w' );
this->fileRef = tempFileRef;
this->filePath = tempFilePath;
@@ -677,6 +1209,7 @@ XMPFiles::CloseFile ( XMP_OptionBits closeFlags /* = 0 */ )
#endif
CreateTempFile ( origFilePath, &copyFilePath, kCopyMacRsrc );
+ XMP_Assert ( copyFileRef == 0 );
copyFileRef = LFA_Open ( copyFilePath.c_str(), 'w' );
XMP_Int64 fileSize = LFA_Measure ( origFileRef );
LFA_Seek ( origFileRef, 0, SEEK_SET );
@@ -691,6 +1224,7 @@ XMPFiles::CloseFile ( XMP_OptionBits closeFlags /* = 0 */ )
LFA_Delete ( tempFilePath.c_str() ); // ! Slight risk of name being grabbed before rename.
LFA_Rename ( origFilePath.c_str(), tempFilePath.c_str() );
+ XMP_Assert ( tempFileRef == 0 );
tempFileRef = LFA_Open ( tempFilePath.c_str(), 'w' );
this->fileRef = tempFileRef;
@@ -774,6 +1308,7 @@ XMPFiles::GetFileInfo ( XMP_StringPtr * filePath /* = 0 */,
XMP_OptionBits * handlerFlags /* = 0 */ )
{
if ( this->handler == 0 ) return false;
+ XMPFileHandler * handler = this->handler;
if ( filePath == 0 ) filePath = &voidStringPtr;
if ( pathLen == 0 ) pathLen = &voidStringLen;
@@ -782,7 +1317,7 @@ XMPFiles::GetFileInfo ( XMP_StringPtr * filePath /* = 0 */,
if ( handlerFlags == 0 ) handlerFlags = &voidOptionBits;
*filePath = this->filePath.c_str();
- *pathLen = this->filePath.size();
+ *pathLen = (XMP_StringLen) this->filePath.size();
*openFlags = this->openFlags;
*format = this->format;
*handlerFlags = this->handler->handlerFlags;
@@ -800,11 +1335,36 @@ XMPFiles::SetAbortProc ( XMP_AbortProc abortProc,
this->abortProc = abortProc;
this->abortArg = abortArg;
- XMP_Assert ( (abortProc != (XMP_AbortProc)0) || (abortArg != (void*)0xDEADBEEF) ); // Hack to test the assert callback.
-
+ 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 */,
@@ -813,7 +1373,7 @@ XMPFiles::GetXMP ( SXMPMeta * xmpObj /* = 0 */,
XMP_PacketInfo * packetInfo /* = 0 */ )
{
if ( this->handler == 0 ) XMP_Throw ( "XMPFiles::GetXMP - No open file", kXMPErr_BadObject );
-
+
if ( ! this->handler->processedXMP ) {
try {
this->handler->ProcessXMP();
@@ -824,8 +1384,9 @@ XMPFiles::GetXMP ( SXMPMeta * xmpObj /* = 0 */,
SXMPUtils::AppendProperties ( this->handler->xmpObj, xmpObj, kXMPUtil_DoAllProperties );
}
if ( xmpPacket != 0 ) *xmpPacket = this->handler->xmpPacket.c_str();
- if ( xmpPacketLen != 0 ) *xmpPacketLen = this->handler->xmpPacket.size();
- if ( packetInfo != 0 ) *packetInfo = this->handler->packetInfo;
+ if ( xmpPacketLen != 0 ) *xmpPacketLen = (XMP_StringLen) this->handler->xmpPacket.size();
+ SetClientPacketInfo ( packetInfo, this->handler->packetInfo,
+ this->handler->xmpPacket, this->handler->needsUpdate );
throw;
}
}
@@ -842,8 +1403,9 @@ XMPFiles::GetXMP ( SXMPMeta * xmpObj /* = 0 */,
#endif
if ( xmpPacket != 0 ) *xmpPacket = this->handler->xmpPacket.c_str();
- if ( xmpPacketLen != 0 ) *xmpPacketLen = this->handler->xmpPacket.size();
- if ( packetInfo != 0 ) *packetInfo = this->handler->packetInfo;
+ if ( xmpPacketLen != 0 ) *xmpPacketLen = (XMP_StringLen) this->handler->xmpPacket.size();
+ SetClientPacketInfo ( packetInfo, this->handler->packetInfo,
+ this->handler->xmpPacket, this->handler->needsUpdate );
return true;
@@ -907,7 +1469,7 @@ DoPutXMP ( XMPFiles * thiz, const SXMPMeta & xmpObj, const bool doIt )
XMP_Uns8 charForm = handler->stdCharForm;
if ( charForm == kXMP_CharUnknown ) charForm = packetInfo.charForm;
- XMP_OptionBits options = kXMP_UseCompactFormat | XMP_CharToSerializeForm ( 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;
@@ -917,9 +1479,8 @@ DoPutXMP ( XMPFiles * thiz, const SXMPMeta & xmpObj, const bool doIt )
if ( handlerFlags & kXMPFiles_UsesSidecarXMP ) tryInPlace = false;
if ( tryInPlace ) {
- XMP_Assert ( handler->containsXMP && (oldPacketLength == xmpPacket.size()) );
try {
- xmpObj.SerializeToBuffer ( &xmpPacket, (options | kXMP_ExactPacketLength), oldPacketLength );
+ xmpObj.SerializeToBuffer ( &xmpPacket, (options | kXMP_ExactPacketLength), (XMP_StringLen) oldPacketLength );
XMP_Assert ( xmpPacket.size() == oldPacketLength );
} catch ( ... ) {
if ( preferInPlace ) {
@@ -939,12 +1500,8 @@ DoPutXMP ( XMPFiles * thiz, const SXMPMeta & xmpObj, const bool doIt )
throw;
}
}
-
+
if ( doIt ) {
- if ( ! tryInPlace ) packetInfo.offset = kXMPFiles_UnknownOffset;
- packetInfo.length = xmpPacket.size();
- packetInfo.padSize = GetPacketPadSize ( xmpPacket.c_str(), xmpPacket.size() );
- packetInfo.charForm = charForm;
handler->xmpObj = xmpObj.Clone();
handler->containsXMP = true;
handler->processedXMP = true;
@@ -981,6 +1538,7 @@ 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;