summaryrefslogtreecommitdiff
path: root/samples/source
diff options
context:
space:
mode:
Diffstat (limited to 'samples/source')
-rw-r--r--samples/source/CustomSchema.cpp2
-rw-r--r--samples/source/DumpMainXMP.cpp22
-rw-r--r--samples/source/DumpScannedXMP.cpp6
-rw-r--r--samples/source/ModifyingXMP.cpp13
-rw-r--r--samples/source/ReadingXMP.cpp9
-rw-r--r--samples/source/XMPCoreCoverage.cpp190
-rw-r--r--samples/source/XMPFilesCoverage.cpp23
-rw-r--r--samples/source/XMPIterations.cpp321
-rw-r--r--samples/source/XMPScanner.cpp1480
-rw-r--r--samples/source/XMPScanner.hpp330
-rw-r--r--samples/source/XmpIterations.cpp318
-rw-r--r--samples/source/common/DumpFile.cpp1560
-rw-r--r--samples/source/common/DumpFile.h7
-rw-r--r--samples/source/common/Log.cpp46
-rw-r--r--samples/source/common/Log.h16
-rw-r--r--samples/source/common/OutputUtils.cpp227
-rw-r--r--samples/source/common/OutputUtils.h50
-rw-r--r--samples/source/common/QEScanner.cpp1469
-rw-r--r--samples/source/common/QEScanner.hpp331
-rw-r--r--samples/source/common/TagTree.cpp139
-rw-r--r--samples/source/common/TagTree.h28
-rw-r--r--samples/source/common/globals.h56
-rw-r--r--samples/source/dumpfile/main.cpp41
-rw-r--r--samples/source/xmpcommand/Actions.cpp204
-rw-r--r--samples/source/xmpcommand/Actions.h5
-rw-r--r--samples/source/xmpcommand/PrintUsage.cpp4
-rw-r--r--samples/source/xmpcommand/PrintUsage.h2
-rw-r--r--samples/source/xmpcommand/XMPCommand.cpp (renamed from samples/source/xmpcommand/main.cpp)13
28 files changed, 1757 insertions, 5155 deletions
diff --git a/samples/source/CustomSchema.cpp b/samples/source/CustomSchema.cpp
index 7e159b4..d29c10f 100644
--- a/samples/source/CustomSchema.cpp
+++ b/samples/source/CustomSchema.cpp
@@ -1,5 +1,5 @@
// =================================================================================================
-// Copyright 2002-2008 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
diff --git a/samples/source/DumpMainXMP.cpp b/samples/source/DumpMainXMP.cpp
index 06d367a..389e10a 100644
--- a/samples/source/DumpMainXMP.cpp
+++ b/samples/source/DumpMainXMP.cpp
@@ -1,5 +1,5 @@
// =================================================================================================
-// Copyright 2002-2008 Adobe Systems Incorporated
+// Copyright 2002 Adobe Systems Incorporated
// All Rights Reserved.
//
// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms
@@ -19,7 +19,7 @@
#include <stdexcept>
#include <errno.h>
-#if WIN_ENV
+#if XMP_WinBuild
#pragma warning ( disable : 4127 ) // conditional expression is constant
#pragma warning ( disable : 4996 ) // '...' was declared deprecated
#endif
@@ -108,19 +108,19 @@ main ( int argc, const char * argv [] )
printf ( "## SXMPMeta::Initialize failed!\n" );
return -1;
}
-
- if ( ! SXMPFiles::Initialize() ) {
+
+ XMP_OptionBits options = 0;
+ #if UNIX_ENV
+ options |= kXMPFiles_ServerMode;
+ #endif
+
+ if ( ! SXMPFiles::Initialize ( options ) ) {
+
printf ( "## SXMPFiles::Initialize failed!\n" );
return -1;
}
- if ( argc != 2 ) // 2 := command and 1 parameter
- {
- printf( "usage: DumpMainXMP (filename)\n");
- return 0;
- }
-
- ProcessFile ( argv[1] );
+ for ( int i = 1; i < argc; ++i ) ProcessFile ( argv[i] );
SXMPFiles::Terminate();
SXMPMeta::Terminate();
diff --git a/samples/source/DumpScannedXMP.cpp b/samples/source/DumpScannedXMP.cpp
index 37929c6..5ef137b 100644
--- a/samples/source/DumpScannedXMP.cpp
+++ b/samples/source/DumpScannedXMP.cpp
@@ -1,5 +1,5 @@
// =================================================================================================
-// Copyright 2002-2008 Adobe Systems Incorporated
+// Copyright 2002 Adobe Systems Incorporated
// All Rights Reserved.
//
// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms
@@ -19,7 +19,7 @@
#include <stdexcept>
#include <errno.h>
-#if WIN_ENV
+#if XMP_WinBuild
#pragma warning ( disable : 4127 ) // conditional expression is constant
#pragma warning ( disable : 4996 ) // '...' was declared deprecated
#endif
@@ -154,7 +154,7 @@ main ( int argc, const char * argv [] )
return 0;
}
- ProcessFile ( argv[1] );
+ for ( int i = 1; i < argc; ++i ) ProcessFile ( argv[i] );
SXMPMeta::Terminate();
return 0;
diff --git a/samples/source/ModifyingXMP.cpp b/samples/source/ModifyingXMP.cpp
index 7b73bc0..5ff0003 100644
--- a/samples/source/ModifyingXMP.cpp
+++ b/samples/source/ModifyingXMP.cpp
@@ -1,5 +1,5 @@
// =================================================================================================
-// Copyright 2002-2008 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
@@ -165,9 +165,14 @@ int main ( int argc, const char * argv[] )
cout << "Could not initialize toolkit!";
return -1;
}
-
+
+ XMP_OptionBits options = 0;
+ #if UNIX_ENV
+ options |= kXMPFiles_ServerMode;
+ #endif
+
// Must initialize SXMPFiles before we use it
- if(SXMPFiles::Initialize())
+ if(SXMPFiles::Initialize(options))
{
try
{
@@ -240,7 +245,7 @@ int main ( int argc, const char * argv[] )
// This will:
// a) Add ANY new TOP LEVEL properties in the source (rdfMeta) to the destination (meta)
// b) Replace any top level properties in the source with the matching properties from the destination
- SXMPUtils::AppendProperties(rdfMeta, &meta, (kXMPUtil_DoAllProperties | kXMPUtil_ReplaceOldValues ));
+ SXMPUtils::ApplyTemplate(&meta, rdfMeta, kXMPTemplate_AddNewProperties | kXMPTemplate_ReplaceExistingProperties | kXMPTemplate_IncludeInternalProperties);
// Display the properties again to show changes
cout << "After Appending Properties:" << endl;
diff --git a/samples/source/ReadingXMP.cpp b/samples/source/ReadingXMP.cpp
index b1ec3c4..f3f1333 100644
--- a/samples/source/ReadingXMP.cpp
+++ b/samples/source/ReadingXMP.cpp
@@ -1,5 +1,5 @@
// =================================================================================================
-// Copyright 2002-2008 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
@@ -76,9 +76,12 @@ int main ( int argc, const char * argv[] )
cout << "Could not initialize toolkit!";
return -1;
}
-
+ XMP_OptionBits options = 0;
+ #if UNIX_ENV
+ options |= kXMPFiles_ServerMode;
+ #endif
// Must initialize SXMPFiles before we use it
- if ( ! SXMPFiles::Initialize() )
+ if ( ! SXMPFiles::Initialize ( options ) )
{
cout << "Could not initialize SXMPFiles.";
return -1;
diff --git a/samples/source/XMPCoreCoverage.cpp b/samples/source/XMPCoreCoverage.cpp
index c0c2cef..0fb766c 100644
--- a/samples/source/XMPCoreCoverage.cpp
+++ b/samples/source/XMPCoreCoverage.cpp
@@ -1,5 +1,5 @@
// =================================================================================================
-// Copyright 2002-2008 Adobe Systems Incorporated
+// Copyright 2002 Adobe Systems Incorporated
// All Rights Reserved.
//
// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms
@@ -287,6 +287,30 @@ static const char * kDateTimeRDF =
// -------------------------------------------------------------------------------------------------
+static void FillDateTime ( XMP_DateTime * dateTime, XMP_Int32 year, XMP_Int32 month, XMP_Int32 day,
+ XMP_Int32 hour, XMP_Int32 minute, XMP_Int32 second,
+ XMP_Bool hasDate, XMP_Bool hasTime, XMP_Bool hasTimeZone,
+ XMP_Int8 tzSign, XMP_Int32 tzHour, XMP_Int32 tzMinute, XMP_Int32 nanoSecond )
+{
+
+ dateTime->year = year;
+ dateTime->month = month;
+ dateTime->day = day;
+ dateTime->hour = hour;
+ dateTime->minute = minute;
+ dateTime->second = second;
+ dateTime->hasDate = hasDate;
+ dateTime->hasTime = hasTime;
+ dateTime->hasTimeZone = hasTimeZone;
+ dateTime->tzSign = tzSign;
+ dateTime->tzHour = tzHour;
+ dateTime->tzMinute = tzMinute;
+ dateTime->nanoSecond = nanoSecond;
+
+} // FillDateTime
+
+// -------------------------------------------------------------------------------------------------
+
static void WriteMajorLabel ( FILE * log, const char * title )
{
@@ -397,11 +421,9 @@ static void DoXMPCoreCoverage ( FILE * log )
// --------------------------------------------------------------------------------------------
- WriteMajorLabel ( log, "Dump predefined namespaces and aliases" );
+ WriteMajorLabel ( log, "Dump predefined namespaces" );
SXMPMeta::DumpNamespaces ( DumpToFile, log );
- fprintf ( log, "\n" );
- SXMPMeta::DumpAliases ( DumpToFile, log );
// --------------------------------------------------------------------------------------------
@@ -480,118 +502,6 @@ static void DoXMPCoreCoverage ( FILE * log )
#endif
// --------------------------------------------------------------------------------------------
- // Static alias functions
- // ----------------------
-
- WriteMajorLabel ( log, "Initial default aliases" );
- fprintf ( log, "\n" );
-
- SXMPMeta::DumpAliases ( DumpToFile, log );
-
- WriteMajorLabel ( log, "Add ns2: to ns1: aliases" );
- fprintf ( log, "\n" );
-
- SXMPMeta::RegisterAlias ( kNS2, "SimpleAlias", kNS1, "SimpleActual" );
-
- SXMPMeta::RegisterAlias ( kNS2, "BagAlias", kNS1, "BagActual" );
- SXMPMeta::RegisterAlias ( kNS2, "ns2:SeqAlias", kNS1, "SeqActual" );
- SXMPMeta::RegisterAlias ( kNS2, "AltAlias", kNS1, "AltActual" );
- SXMPMeta::RegisterAlias ( kNS2, "AltTextAlias", kNS1, "AltTextActual" );
-
- SXMPMeta::RegisterAlias ( kNS2, "BagItemAlias", kNS1, "BagActual", kXMP_PropValueIsArray );
- SXMPMeta::RegisterAlias ( kNS2, "SeqItemAlias", kNS1, "ns1:SeqActual", kXMP_PropArrayIsOrdered );
- SXMPMeta::RegisterAlias ( kNS2, "AltItemAlias", kNS1, "ns1:AltActual", kXMP_PropArrayIsAlternate );
- SXMPMeta::RegisterAlias ( kNS2, "AltTextItemAlias", kNS1, "ns1:AltTextActual", kXMP_PropArrayIsAltText );
-
- SXMPMeta::DumpAliases ( DumpToFile, log );
-
- WriteMajorLabel ( log, "Resolve ns2: to ns1: aliases" );
- fprintf ( log, "\n" );
-
- tmpStr1.erase(); tmpStr2.erase();
- ok = SXMPMeta::ResolveAlias ( kNS1, "SimpleActual", &tmpStr1, &tmpStr2, &options );
- fprintf ( log, "ResolveAlias ns1:SimpleActual : %s\n", FoundOrNot ( ok ) );
-
- fprintf ( log, "\n" );
-
- tmpStr1.erase(); tmpStr2.erase();
- ok = SXMPMeta::ResolveAlias ( kNS2, "SimpleAlias", &tmpStr1, &tmpStr2, &options );
- fprintf ( log, "ResolveAlias ns2:SimpleAlias : %s, %s %s, 0x%X\n", FoundOrNot ( ok ), tmpStr1.c_str(), tmpStr2.c_str(), options );
-
- fprintf ( log, "\n" );
-
- tmpStr1.erase(); tmpStr2.erase();
- ok = SXMPMeta::ResolveAlias ( kNS2, "ns2:BagAlias", &tmpStr1, &tmpStr2, &options );
- fprintf ( log, "ResolveAlias ns2:BagAlias : %s, %s %s, 0x%X\n", FoundOrNot ( ok ), tmpStr1.c_str(), tmpStr2.c_str(), options );
-
- tmpStr1.erase(); tmpStr2.erase();
- ok = SXMPMeta::ResolveAlias ( kNS2, "SeqAlias", &tmpStr1, &tmpStr2, &options );
- fprintf ( log, "ResolveAlias ns2:SeqAlias : %s, %s %s, 0x%X\n", FoundOrNot ( ok ), tmpStr1.c_str(), tmpStr2.c_str(), options );
-
- tmpStr1.erase(); tmpStr2.erase();
- ok = SXMPMeta::ResolveAlias ( kNS2, "AltAlias", &tmpStr1, &tmpStr2, &options );
- fprintf ( log, "ResolveAlias ns2:AltAlias : %s, %s %s, 0x%X\n", FoundOrNot ( ok ), tmpStr1.c_str(), tmpStr2.c_str(), options );
-
- tmpStr1.erase(); tmpStr2.erase();
- ok = SXMPMeta::ResolveAlias ( kNS2, "AltTextAlias", &tmpStr1, &tmpStr2, &options );
- fprintf ( log, "ResolveAlias ns2:AltTextAlias : %s, %s %s, 0x%X\n", FoundOrNot ( ok ), tmpStr1.c_str(), tmpStr2.c_str(), options );
-
- fprintf ( log, "\n" );
-
- tmpStr1.erase(); tmpStr2.erase();
- ok = SXMPMeta::ResolveAlias ( kNS2, "BagItemAlias", &tmpStr1, &tmpStr2, &options );
- fprintf ( log, "ResolveAlias ns2:BagItemAlias : %s, %s %s, 0x%X\n", FoundOrNot ( ok ), tmpStr1.c_str(), tmpStr2.c_str(), options );
-
- tmpStr1.erase(); tmpStr2.erase();
- ok = SXMPMeta::ResolveAlias ( kNS2, "SeqItemAlias", &tmpStr1, &tmpStr2, &options );
- fprintf ( log, "ResolveAlias ns2:SeqItemAlias : %s, %s %s, 0x%X\n", FoundOrNot ( ok ), tmpStr1.c_str(), tmpStr2.c_str(), options );
-
- tmpStr1.erase(); tmpStr2.erase();
- ok = SXMPMeta::ResolveAlias ( kNS2, "AltItemAlias", &tmpStr1, &tmpStr2, &options );
- fprintf ( log, "ResolveAlias ns2:AltItemAlias : %s, %s %s, 0x%X\n", FoundOrNot ( ok ), tmpStr1.c_str(), tmpStr2.c_str(), options );
-
- tmpStr1.erase(); tmpStr2.erase();
- ok = SXMPMeta::ResolveAlias ( kNS2, "AltTextItemAlias", &tmpStr1, &tmpStr2, &options );
- fprintf ( log, "ResolveAlias ns2:AltTextItemAlias : %s, %s %s, 0x%X\n", FoundOrNot ( ok ), tmpStr1.c_str(), tmpStr2.c_str(), options );
-
- {
- SXMPMeta meta;
-
- WriteMajorLabel ( log, "Test SetProperty through ns2: simple aliases" );
-
- meta.SetProperty ( kNS2, "SimpleAlias", "Simple value" );
- meta.SetProperty ( kNS2, "ns2:BagItemAlias", "BagItem value" );
- meta.SetProperty ( kNS2, "SeqItemAlias", "SeqItem value" );
- meta.SetProperty ( kNS2, "AltItemAlias", "AltItem value" );
- meta.SetProperty ( kNS2, "AltTextItemAlias", "AltTextItem value" );
-
- DumpXMPObj ( log, meta, "Check for aliases and bases" );
-
- }
-
- #if 0
-
- WriteMajorLabel ( log, "Delete some ns2: to ns1: aliases" );
- fprintf ( log, "\n" );
-
- SXMPMeta::DeleteAlias ( kNS2, "ns2:SimpleAlias" );
- SXMPMeta::DeleteAlias ( kNS2, "SeqAlias" );
- SXMPMeta::DeleteAlias ( kNS2, "AltAlias" );
- SXMPMeta::DeleteAlias ( kNS2, "SeqItemAlias" );
- SXMPMeta::DeleteAlias ( kNS2, "AltItemAlias" );
-
- SXMPMeta::DumpAliases ( DumpToFile, log );
-
- #endif
-
- WriteMajorLabel ( log, "Register standard EXIF aliases" );
- fprintf ( log, "\n" );
-
- SXMPMeta::RegisterStandardAliases ( kXMP_NS_EXIF );
-
- SXMPMeta::DumpAliases ( DumpToFile, log );
-
- // --------------------------------------------------------------------------------------------
// Basic set/get methods
// ---------------------
@@ -943,13 +853,15 @@ static void DoXMPCoreCoverage ( FILE * log )
{
SXMPMeta meta ( kDateTimeRDF, strlen(kDateTimeRDF) );
- XMP_DateTime dateValue = { 2000, 1, 2, 3, 4, 5, 0, 0, 0, 0 };
- bool boolValue;
- long intValue;
- double floatValue;
- char dateName [8];
+ XMP_DateTime dateValue;
+ bool boolValue;
+ XMP_Int32 intValue;
+ double floatValue;
+ char dateName [8];
WriteMajorLabel ( log, "Test SetProperty... and GetProperty... methods (set/get with binary values)" );
+
+ FillDateTime ( &dateValue, 2000, 1, 2, 3, 4, 5, true, true, false, 0, 0, 0, 0 );
meta.SetProperty_Bool ( kNS1, "Bool0", false );
meta.SetProperty_Bool ( kNS1, "Bool1", true );
@@ -1129,7 +1041,7 @@ static void DoXMPCoreCoverage ( FILE * log )
meta2.SetProperty ( kXMP_NS_PDF, "Author", "PDF Author" );
tmpStr1.erase();
- meta2.SerializeToBuffer ( &tmpStr1, kXMP_ReadOnlyPacket | kXMP_WriteAliasComments );
+ meta2.SerializeToBuffer ( &tmpStr1, kXMP_ReadOnlyPacket );
WriteMinorLabel ( log, "Read-only serialize with alias comments" );
fprintf ( log, "%s\n", tmpStr1.c_str() );
@@ -1137,7 +1049,7 @@ static void DoXMPCoreCoverage ( FILE * log )
meta2.SetProperty ( kXMP_NS_XMP, "Actual", "XMP Actual" );
tmpStr1.erase();
- meta2.SerializeToBuffer ( &tmpStr1, kXMP_ReadOnlyPacket | kXMP_WriteAliasComments );
+ meta2.SerializeToBuffer ( &tmpStr1, kXMP_ReadOnlyPacket );
WriteMinorLabel ( log, "Read-only serialize with alias comments (more actuals)" );
fprintf ( log, "%s\n", tmpStr1.c_str() );
}
@@ -1475,22 +1387,6 @@ static void DoXMPCoreCoverage ( FILE * log )
}
}
}
-
- SXMPIterator iter2 ( meta, kXMP_IterIncludeAliases );
- WriteMinorLabel ( log, "Iterate showing aliases" );
- while ( true ) {
- tmpStr1.erase(); tmpStr2.erase(); tmpStr3.erase();
- if ( ! iter2.Next ( &tmpStr1, &tmpStr2, &tmpStr3, &options ) ) break;
- fprintf ( log, " %s %s = \"%s\", 0x%X\n", tmpStr1.c_str(), tmpStr2.c_str(), tmpStr3.c_str(), options );
- if ( ! (options & kXMP_SchemaNode) ) {
- tmpStr4.erase();
- options &= ~(kXMP_PropIsAlias | kXMP_PropHasAliases); // So the comparison below works.
- ok = meta.GetProperty ( tmpStr1.c_str(), tmpStr2.c_str(), &tmpStr4, &opt2 );
- if ( (! ok) || (tmpStr4 != tmpStr3) || (opt2 != options) ) {
- fprintf ( log, " ** GetProperty failed: %s, \"%s\", 0x%X\n", FoundOrNot(ok), tmpStr4.c_str(), opt2 );
- }
- }
- }
}
}
@@ -1688,8 +1584,9 @@ static void DoXMPCoreCoverage ( FILE * log )
fprintf ( log, "\n" );
- XMP_DateTime date1 = { 2000, 1, 31, 12, 34, 56, -1, 8, 0, 0 };
- XMP_DateTime date2 = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+ XMP_DateTime date1, date2;
+ FillDateTime ( &date1, 2000, 1, 31, 12, 34, 56, true, true, true, -1, 8, 0, 0 );
+
tmpStr1.erase();
SXMPUtils::ConvertFromDate ( date1, &tmpStr1 );
@@ -1708,8 +1605,7 @@ static void DoXMPCoreCoverage ( FILE * log )
WriteMajorLabel ( log, "Test date/time utilities and special values" );
fprintf ( log, "\n" );
- XMP_DateTime utcNow = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
- XMP_DateTime localNow = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+ XMP_DateTime utcNow, localNow;
SXMPUtils::SetTimeZone ( &utcNow );
fprintf ( log, "SetTimeZone : %d-%02d-%02d %02d:%02d:%02d %d*%02d:%02d %d\n",
@@ -1825,25 +1721,25 @@ static void DoXMPCoreCoverage ( FILE * log )
meta2.SetProperty ( kXMP_NS_XMP, "Format", "new Format" );
DumpXMPObj ( log, meta2, "Create 2nd XMP object with new values" );
- SXMPUtils::AppendProperties ( meta2, &meta1 );
+ SXMPUtils::ApplyTemplate ( &meta1, meta2, kXMPTemplate_AddNewProperties );
DumpXMPObj ( log, meta1, "Append 2nd to 1st, keeping old values, external only" );
meta2.SetProperty ( kXMP_NS_XMP, "CreatorTool", "newer CreatorTool" );
meta2.SetProperty ( kXMP_NS_XMP, "Nickname", "newer Nickname" );
meta2.SetProperty ( kXMP_NS_XMP, "Format", "newer Format" );
- SXMPUtils::AppendProperties ( meta2, &meta1, kXMPUtil_DoAllProperties );
+ SXMPUtils::ApplyTemplate ( &meta1, meta2, kXMPTemplate_AddNewProperties | kXMPTemplate_IncludeInternalProperties );
DumpXMPObj ( log, meta1, "Append 2nd to 1st, keeping old values, internal also" );
meta2.SetProperty ( kXMP_NS_XMP, "CreatorTool", "newest CreatorTool" );
meta2.SetProperty ( kXMP_NS_XMP, "Nickname", "newest Nickname" );
meta2.SetProperty ( kXMP_NS_XMP, "Format", "newest Format" );
- SXMPUtils::AppendProperties ( meta2, &meta1, kXMPUtil_ReplaceOldValues );
+ SXMPUtils::ApplyTemplate ( &meta1, meta2, kXMPTemplate_AddNewProperties | kXMPTemplate_ReplaceExistingProperties );
DumpXMPObj ( log, meta1, "Append 2nd to 1st, replacing old values, external only" );
meta2.SetProperty ( kXMP_NS_XMP, "CreatorTool", "final CreatorTool" );
meta2.SetProperty ( kXMP_NS_XMP, "Nickname", "final Nickname" );
meta2.SetProperty ( kXMP_NS_XMP, "Format", "final Format" );
- SXMPUtils::AppendProperties ( meta2, &meta1, (kXMPUtil_ReplaceOldValues | kXMPUtil_DoAllProperties) );
+ SXMPUtils::ApplyTemplate ( &meta1, meta2, kXMPTemplate_AddNewProperties | kXMPTemplate_ReplaceExistingProperties | kXMPTemplate_IncludeInternalProperties );
DumpXMPObj ( log, meta1, "Append 2nd to 1st, replacing old values, internal also" );
}
diff --git a/samples/source/XMPFilesCoverage.cpp b/samples/source/XMPFilesCoverage.cpp
index 84e5e27..759baef 100644
--- a/samples/source/XMPFilesCoverage.cpp
+++ b/samples/source/XMPFilesCoverage.cpp
@@ -1,5 +1,5 @@
// =================================================================================================
-// Copyright 2002-2008 Adobe Systems Incorporated
+// Copyright 2002 Adobe Systems Incorporated
// All Rights Reserved.
//
// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms
@@ -141,7 +141,7 @@ static void OpenTestFile ( const char * fileName, XMP_OptionBits rwMode, SXMPMet
static const char * charForms[] = { "UTF-8", "unknown char form", "UTF-16BE", "UTF-16LE", "UTF-32BE", "UTF-32LE" };
- XMP_OptionBits smartFlags = rwMode | kXMPFiles_OpenUseSmartHandler | kXMPFiles_OpenCacheTNail;
+ XMP_OptionBits smartFlags = rwMode | kXMPFiles_OpenUseSmartHandler;
XMP_OptionBits scanFlags = rwMode | kXMPFiles_OpenUsePacketScanning;
ok = xmpFile->OpenFile ( fileName, kXMP_UnknownFile, smartFlags );
@@ -156,19 +156,6 @@ static void OpenTestFile ( const char * fileName, XMP_OptionBits rwMode, SXMPMet
fprintf ( sLogFile, "File info : format = \"%.4s\", handler flags = 0x%X, open flags = 0x%X (%s)\n",
&format, handlerFlags, openFlags, (isUpdate ? "update" : "read-only") );
-
- XMP_ThumbnailInfo tnail;
- ok = xmpFile->GetThumbnail ( &tnail );
- if ( ! ok ) {
- fprintf ( sLogFile, "No thumbnail\n" );
- } else {
- fprintf ( sLogFile, "Thumbnail info : file format = \"%.4s\", tnail format = %d, tnail size = %d\n",
- &tnail.fileFormat, tnail.tnailFormat, tnail.tnailSize );
- fprintf ( sLogFile, " Image width x height, orientation : %d x %d, %d\n",
- tnail.fullWidth, tnail.fullHeight, tnail.fullOrientation );
- fprintf ( sLogFile, " TNail width x height, orientation : %d x %d, %d\n",
- tnail.tnailWidth, tnail.tnailHeight, tnail.tnailOrientation );
- }
ok = xmpFile->GetXMP ( xmpMeta, 0, &xmpPacket );
if ( ! ok ) {
@@ -299,7 +286,11 @@ extern "C" int main ( int argc, const char * argv[] )
fprintf ( sLogFile, "## XMPMeta::Initialize failed!\n" );
return -1;
}
- if ( ! SXMPFiles::Initialize() ) {
+ XMP_OptionBits options = 0;
+ #if UNIX_ENV
+ options |= kXMPFiles_ServerMode;
+ #endif
+ if ( ! SXMPFiles::Initialize ( options ) ) {
fprintf ( sLogFile, "## SXMPFiles::Initialize failed!\n" );
return -1;
}
diff --git a/samples/source/XMPIterations.cpp b/samples/source/XMPIterations.cpp
new file mode 100644
index 0000000..7a4273a
--- /dev/null
+++ b/samples/source/XMPIterations.cpp
@@ -0,0 +1,321 @@
+// =================================================================================================
+// 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.
+// =================================================================================================
+
+/**
+* Demonstrates how to use the iteration utility in the XMPCore component to walk through property trees.
+*/
+
+#include <string>
+
+// Must be defined to instantiate template classes
+#define TXMP_STRING_TYPE std::string
+
+// Must be defined to give access to XMPFiles
+#define XMP_INCLUDE_XMPFILES 1
+
+// Ensure XMP templates are instantiated
+#include "XMP.incl_cpp"
+
+// Provide access to the API
+#include "XMP.hpp"
+
+#include <iostream>
+
+using namespace std;
+
+// Provide some custom XMP
+static const char * rdf =
+ "<rdf:RDF xmlns:rdf='http://www.w3.org/1999/02/22-rdf-syntax-ns#'>"
+ " <rdf:Description rdf:about='' xmlns:xmpTest='http://ns.adobe.com/xmpTest/'>"
+ ""
+ " <xmpTest:MySimpleProp rdf:parseType='Resource'>"
+ " <rdf:value>A Value</rdf:value>"
+ " <xmpTest:MyQual>Qual Value</xmpTest:MyQual>"
+ " </xmpTest:MySimpleProp>"
+ ""
+ " <xmpTest:MyTopStruct rdf:parseType='Resource'>"
+ " <xmpTest:MySecondStruct rdf:parseType='Resource'>"
+ " <xmpTest:MyThirdStruct rdf:parseType='Resource'>"
+ " <xmpTest:MyThirdStructField>Field Value 3</xmpTest:MyThirdStructField>"
+ " </xmpTest:MyThirdStruct>"
+ " <xmpTest:MySecondStructField>Field Value 2</xmpTest:MySecondStructField>"
+ " </xmpTest:MySecondStruct>"
+ " <xmpTest:MyTopStructField>Field Value 1</xmpTest:MyTopStructField>"
+ " </xmpTest:MyTopStruct>"
+
+ " <xmpTest:MyArrayWithNestedArray>"
+ " <rdf:Bag>"
+ " <rdf:li>"
+ " <rdf:Seq>"
+ " <rdf:li>Item 1</rdf:li>"
+ " <rdf:li>Item 2</rdf:li>"
+ " </rdf:Seq>"
+ " </rdf:li>"
+ " </rdf:Bag>"
+ " </xmpTest:MyArrayWithNestedArray>"
+
+ " <xmpTest:MyArrayWithStructures>"
+ " <rdf:Seq>"
+ " <rdf:li rdf:parseType='Resource'>"
+ " <rdf:value>Field Value 1</rdf:value>"
+ " <xmpTest:FirstQual>Qual Value 1</xmpTest:FirstQual>"
+ " <xmpTest:SecondQual>Qual Value 2</xmpTest:SecondQual>"
+ " </rdf:li>"
+ " <rdf:li rdf:parseType='Resource'>"
+ " <rdf:value>Field Value 2</rdf:value>"
+ " <xmpTest:FirstQual>Qual Value 3</xmpTest:FirstQual>"
+ " <xmpTest:SecondQual>Qual Value 4</xmpTest:SecondQual>"
+ " </rdf:li>"
+ " </rdf:Seq>"
+ " </xmpTest:MyArrayWithStructures>"
+ ""
+ " <xmpTest:MyStructureWithArray rdf:parseType='Resource'>"
+ " <xmpTest:NestedArray>"
+ " <rdf:Bag>"
+ " <rdf:li>Item 3</rdf:li>"
+ " <rdf:li>Item 4</rdf:li>"
+ " <rdf:li>Item 5</rdf:li>"
+ " <rdf:li>Item 6</rdf:li>"
+ " </rdf:Bag>"
+ " </xmpTest:NestedArray>"
+ " <xmpTest:NestedArray2>"
+ " <rdf:Bag>"
+ " <rdf:li>Item 66</rdf:li>"
+ " <rdf:li>Item 46</rdf:li>"
+ " <rdf:li>Item 56</rdf:li>"
+ " <rdf:li>Item 66</rdf:li>"
+ " </rdf:Bag>"
+ " </xmpTest:NestedArray2>"
+ " </xmpTest:MyStructureWithArray>"
+ ""
+ " </rdf:Description>"
+ "</rdf:RDF>";
+
+// The namespace to be used. This will be automatically registered
+// when the RDF is parsed.
+const XMP_StringPtr kXMP_NS_SDK = "http://ns.adobe.com/xmpTest/";
+
+/**
+* Reads some metadata from a file and appends some custom XMP to it. Then does several
+* iterations, using various iterators. Each iteration is displayed in the console window.
+*/
+int main()
+{
+ if(SXMPMeta::Initialize())
+ {
+ XMP_OptionBits options = 0;
+ #if UNIX_ENV
+ options |= kXMPFiles_ServerMode;
+ #endif
+ if ( SXMPFiles::Initialize ( options ) ) {
+ bool ok;
+ SXMPFiles myFile;
+
+ XMP_OptionBits opts = kXMPFiles_OpenForRead | kXMPFiles_OpenUseSmartHandler;
+ ok = myFile.OpenFile("../../../testfiles/Image1.jpg", kXMP_UnknownFile, opts);
+ if(ok)
+ {
+ SXMPMeta xmp;
+ myFile.GetXMP(&xmp);
+
+ // Add some custom metadata to the XMP object
+ SXMPMeta custXMP(rdf, (XMP_StringLen) strlen(rdf));
+ SXMPUtils::ApplyTemplate(&xmp, custXMP, kXMPTemplate_AddNewProperties);
+
+ // Store any details from the iter.Next() call
+ string schemaNS, propPath, propVal;
+
+ // Only visit the immediate children that are leaf properties of the Dublin Core schema
+ SXMPIterator dcLeafIter(xmp, kXMP_NS_DC, (kXMP_IterJustChildren | kXMP_IterJustLeafNodes));
+ while(dcLeafIter.Next(&schemaNS, &propPath, &propVal))
+ {
+ cout << schemaNS << " " << propPath << " = " << propVal << endl;
+ }
+
+ cout << "----------------------------------" << endl;
+
+ // Visit one property from the XMP Basic schema
+ SXMPIterator xmpKeywordsIter(xmp, kXMP_NS_XMP, "Keywords", kXMP_IterJustLeafNodes);
+ while(xmpKeywordsIter.Next(&schemaNS, &propPath, &propVal))
+ {
+ cout << schemaNS << " " << propPath << " = " << propVal << endl;
+ }
+
+ cout << "----------------------------------" << endl;
+
+ // Visit the Dublin Core schema, omit any quailifiers and only
+ // show the leaf properties
+ SXMPIterator dcIter(xmp, kXMP_NS_DC, (kXMP_IterOmitQualifiers | kXMP_IterJustLeafNodes));
+ while(dcIter.Next(&schemaNS, &propPath, &propVal))
+ {
+ cout << schemaNS << " " << propPath << " = " << propVal << endl;
+ }
+
+ cout << "----------------------------------" << endl;
+
+ // Visit the Dublin Core schema, omit any quailifiers,
+ // show the leaf properties but only return the leaf name and not the full path
+ SXMPIterator dcIter2(xmp, kXMP_NS_DC, (kXMP_IterOmitQualifiers | kXMP_IterJustLeafNodes | kXMP_IterJustLeafName));
+ while(dcIter2.Next(&schemaNS, &propPath, &propVal))
+ {
+ cout << schemaNS << " " << propPath << " = " << propVal << endl;
+ }
+
+ cout << "----------------------------------" << endl;
+
+ // Iterate over a single namespace. Show all properties within
+ // the Photoshop schema
+ SXMPIterator exifIter(xmp, kXMP_NS_Photoshop);
+ while(exifIter.Next(&schemaNS, &propPath, &propVal))
+ {
+ cout << schemaNS << " " << propPath << " = " << propVal << endl;
+ }
+
+ cout << "----------------------------------" << endl;
+
+ // Just visit the leaf nodes of EXIF properties. That is just
+ // properties that may have values.
+ SXMPIterator exifLeafIter(xmp, kXMP_NS_EXIF, kXMP_IterJustLeafNodes);
+ while(exifLeafIter.Next(&schemaNS, &propPath, &propVal))
+ {
+ cout << schemaNS << " " << propPath << " = " << propVal << endl;
+ }
+
+ cout << "----------------------------------" << endl;
+
+ // Iterate over all properties but skip the EXIF schema and skip the custom schema
+ // and continue visiting nodes
+ SXMPIterator skipExifIter (xmp);
+ while(skipExifIter.Next(&schemaNS, &propPath, &propVal))
+ {
+ if(schemaNS == kXMP_NS_EXIF || schemaNS == kXMP_NS_SDK)
+ {
+ skipExifIter.Skip(kXMP_IterSkipSubtree);
+ }
+ else
+ {
+ cout << schemaNS << " " << propPath << " = " << propVal << endl;
+ }
+ }
+
+ cout << "----------------------------------" << endl;
+
+ // Iterate over all properties but skip the EXIF schema
+ // and any remaining siblings of the current node.
+ SXMPIterator stopAfterExifIter ( xmp );
+ while(stopAfterExifIter.Next(&schemaNS, &propPath, &propVal))
+ {
+ if(schemaNS == kXMP_NS_EXIF || schemaNS == kXMP_NS_SDK)
+ {
+ stopAfterExifIter.Skip(kXMP_IterSkipSiblings);
+ }
+ else
+ {
+ cout << schemaNS << " " << propPath << " = " << propVal << endl;
+ }
+ }
+
+ cout << "----------------------------------" << endl;
+
+ //////////////////////////////////////////////////////////////////////////////////////
+
+ // Iterate over the custom XMP
+
+ // Visit the immediate children of this node.
+ // No qualifiers are visisted as they are below the property being visisted.
+ SXMPIterator justChildrenIter(xmp, kXMP_NS_SDK, kXMP_IterJustChildren);
+ while(justChildrenIter.Next(&schemaNS, &propPath, &propVal))
+ {
+ cout << propPath << " = " << propVal << endl;
+ }
+
+ cout << "----------------------------------" << endl;
+
+ // Visit the immediate children of this node but only those that may have values.
+ // No qualifiers are visisted as they are below the property being visisted.
+ SXMPIterator justChildrenAndLeafIter(xmp, kXMP_NS_SDK, (kXMP_IterJustChildren | kXMP_IterJustLeafNodes));
+ while(justChildrenAndLeafIter.Next(&schemaNS, &propPath, &propVal))
+ {
+ cout << propPath << " = " << propVal << endl;
+ }
+
+ cout << "----------------------------------" << endl;
+
+ // Visit the leaf nodes of TopStructProperty
+ SXMPIterator myTopStructIter(xmp, kXMP_NS_SDK, "MyTopStruct", kXMP_IterJustLeafNodes);
+ while(myTopStructIter.Next(&schemaNS, &propPath, &propVal))
+ {
+ cout << propPath << " = " << propVal << endl;
+ }
+
+ cout << "----------------------------------" << endl;
+
+ // Visit the leaf nodes of the TopStructProperty but only return the names for
+ // the leaf components and not the full path
+ SXMPIterator xmyTopStructIterShortNames(xmp, kXMP_NS_SDK, "MyTopStruct", (kXMP_IterJustLeafNodes | kXMP_IterJustLeafName));
+ while(xmyTopStructIterShortNames.Next(&schemaNS, &propPath, &propVal))
+ {
+ cout << propPath << " = " << propVal << endl;
+ }
+
+ cout << "----------------------------------" << endl;
+
+ // Visit a property and all of the qualifiers
+ SXMPIterator iterArrayProp (xmp, kXMP_NS_SDK, "ArrayWithStructures", kXMP_IterJustLeafNodes );
+ while(iterArrayProp.Next(&schemaNS, &propPath, &propVal))
+ {
+ cout << propPath << " = " << propVal << endl;
+ }
+
+ cout << "----------------------------------" << endl;
+
+ // Visit a property and omit all of the qualifiers
+ SXMPIterator iterArrayPropNoQual (xmp, kXMP_NS_SDK, "ArrayWithStructures", (kXMP_IterJustLeafNodes | kXMP_IterOmitQualifiers));
+ while(iterArrayPropNoQual.Next(&schemaNS, &propPath, &propVal))
+ {
+ cout << propPath << " = " << propVal << endl;
+ }
+
+ cout << "----------------------------------" << endl;
+
+ // Skip a subtree and continue onwards. Once 'Item 4' is found then the we can skip all of the
+ // siblings of the current node. If the the current node were a top level node the iteration
+ // would be complete as all siblings would be skipped. However, when 'Item 4' is found the current
+ // node is not at the top level so there are other nodes further up the tree that still need to be
+ // visited.
+ SXMPIterator skipIter (xmp, kXMP_NS_SDK, (kXMP_IterJustLeafNodes | kXMP_IterOmitQualifiers | kXMP_IterJustLeafName));
+ while(skipIter.Next(&schemaNS, &propPath, &propVal))
+ {
+ if(propVal == "Item 4")
+ {
+ skipIter.Skip(kXMP_IterSkipSiblings);
+ }
+ else
+ {
+ cout << schemaNS << " " << propPath << " = " << propVal << endl;
+ }
+ }
+
+ /*
+ // Visit all properties and qualifiers
+ SXMPIterator allPropsIter(xmp);
+ while(allPropsIter.Next(&schemaNS, &propPath, &propVal))
+ {
+ cout << schemaNS << " " << propPath << " = " << propVal << endl;
+ }
+ */
+ }
+ }
+ }
+
+ SXMPFiles::Terminate();
+ SXMPMeta::Terminate();
+
+ return 0;
+}
+
diff --git a/samples/source/XMPScanner.cpp b/samples/source/XMPScanner.cpp
deleted file mode 100644
index 63f03d0..0000000
--- a/samples/source/XMPScanner.cpp
+++ /dev/null
@@ -1,1480 +0,0 @@
-// =================================================================================================
-// Copyright 2002-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.
-//
-// 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 int charsToGo = 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 = 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 = 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 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 = 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/samples/source/XMPScanner.hpp b/samples/source/XMPScanner.hpp
deleted file mode 100644
index 2c8b6fa..0000000
--- a/samples/source/XMPScanner.hpp
+++ /dev/null
@@ -1,330 +0,0 @@
-#ifndef __XMPScanner_hpp__
-#define __XMPScanner_hpp__
-
-// =================================================================================================
-// Copyright 2002-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.
-//
-// 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/samples/source/XmpIterations.cpp b/samples/source/XmpIterations.cpp
deleted file mode 100644
index 855bb71..0000000
--- a/samples/source/XmpIterations.cpp
+++ /dev/null
@@ -1,318 +0,0 @@
-// =================================================================================================
-// 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
-// of the Adobe license agreement accompanying it.
-// =================================================================================================
-
-/**
-* Demonstrates how to use the iteration utility in the XMPCore component to walk through property trees.
-*/
-
-#include <string>
-
-// Must be defined to instantiate template classes
-#define TXMP_STRING_TYPE std::string
-
-// Must be defined to give access to XMPFiles
-#define XMP_INCLUDE_XMPFILES 1
-
-// Ensure XMP templates are instantiated
-#include "XMP.incl_cpp"
-
-// Provide access to the API
-#include "XMP.hpp"
-
-#include <iostream>
-
-using namespace std;
-
-// Provide some custom XMP
-static const char * rdf =
- "<rdf:RDF xmlns:rdf='http://www.w3.org/1999/02/22-rdf-syntax-ns#'>"
- " <rdf:Description rdf:about='' xmlns:xmpTest='http://ns.adobe.com/xmpTest/'>"
- ""
- " <xmpTest:MySimpleProp rdf:parseType='Resource'>"
- " <rdf:value>A Value</rdf:value>"
- " <xmpTest:MyQual>Qual Value</xmpTest:MyQual>"
- " </xmpTest:MySimpleProp>"
- ""
- " <xmpTest:MyTopStruct rdf:parseType='Resource'>"
- " <xmpTest:MySecondStruct rdf:parseType='Resource'>"
- " <xmpTest:MyThirdStruct rdf:parseType='Resource'>"
- " <xmpTest:MyThirdStructField>Field Value 3</xmpTest:MyThirdStructField>"
- " </xmpTest:MyThirdStruct>"
- " <xmpTest:MySecondStructField>Field Value 2</xmpTest:MySecondStructField>"
- " </xmpTest:MySecondStruct>"
- " <xmpTest:MyTopStructField>Field Value 1</xmpTest:MyTopStructField>"
- " </xmpTest:MyTopStruct>"
-
- " <xmpTest:MyArrayWithNestedArray>"
- " <rdf:Bag>"
- " <rdf:li>"
- " <rdf:Seq>"
- " <rdf:li>Item 1</rdf:li>"
- " <rdf:li>Item 2</rdf:li>"
- " </rdf:Seq>"
- " </rdf:li>"
- " </rdf:Bag>"
- " </xmpTest:MyArrayWithNestedArray>"
-
- " <xmpTest:MyArrayWithStructures>"
- " <rdf:Seq>"
- " <rdf:li rdf:parseType='Resource'>"
- " <rdf:value>Field Value 1</rdf:value>"
- " <xmpTest:FirstQual>Qual Value 1</xmpTest:FirstQual>"
- " <xmpTest:SecondQual>Qual Value 2</xmpTest:SecondQual>"
- " </rdf:li>"
- " <rdf:li rdf:parseType='Resource'>"
- " <rdf:value>Field Value 2</rdf:value>"
- " <xmpTest:FirstQual>Qual Value 3</xmpTest:FirstQual>"
- " <xmpTest:SecondQual>Qual Value 4</xmpTest:SecondQual>"
- " </rdf:li>"
- " </rdf:Seq>"
- " </xmpTest:MyArrayWithStructures>"
- ""
- " <xmpTest:MyStructureWithArray rdf:parseType='Resource'>"
- " <xmpTest:NestedArray>"
- " <rdf:Bag>"
- " <rdf:li>Item 3</rdf:li>"
- " <rdf:li>Item 4</rdf:li>"
- " <rdf:li>Item 5</rdf:li>"
- " <rdf:li>Item 6</rdf:li>"
- " </rdf:Bag>"
- " </xmpTest:NestedArray>"
- " <xmpTest:NestedArray2>"
- " <rdf:Bag>"
- " <rdf:li>Item 66</rdf:li>"
- " <rdf:li>Item 46</rdf:li>"
- " <rdf:li>Item 56</rdf:li>"
- " <rdf:li>Item 66</rdf:li>"
- " </rdf:Bag>"
- " </xmpTest:NestedArray2>"
- " </xmpTest:MyStructureWithArray>"
- ""
- " </rdf:Description>"
- "</rdf:RDF>";
-
-// The namespace to be used. This will be automatically registered
-// when the RDF is parsed.
-const XMP_StringPtr kXMP_NS_SDK = "http://ns.adobe.com/xmpTest/";
-
-/**
-* Reads some metadata from a file and appends some custom XMP to it. Then does several
-* iterations, using various iterators. Each iteration is displayed in the console window.
-*/
-int main()
-{
- if(SXMPMeta::Initialize())
- {
- SXMPFiles::Initialize();
-
- bool ok;
- SXMPFiles myFile;
-
- XMP_OptionBits opts = kXMPFiles_OpenForRead | kXMPFiles_OpenUseSmartHandler;
- ok = myFile.OpenFile("../../../testfiles/Image1.jpg", kXMP_UnknownFile, opts);
- if(ok)
- {
- SXMPMeta xmp;
- myFile.GetXMP(&xmp);
-
- // Add some custom metadata to the XMP object
- SXMPMeta custXMP(rdf, (XMP_StringLen) strlen(rdf));
- SXMPUtils::AppendProperties(custXMP, &xmp);
-
- // Store any details from the iter.Next() call
- string schemaNS, propPath, propVal;
-
- // Only visit the immediate children that are leaf properties of the Dublin Core schema
- SXMPIterator dcLeafIter(xmp, kXMP_NS_DC, (kXMP_IterJustChildren | kXMP_IterJustLeafNodes));
- while(dcLeafIter.Next(&schemaNS, &propPath, &propVal))
- {
- cout << schemaNS << " " << propPath << " = " << propVal << endl;
- }
-
- cout << "----------------------------------" << endl;
-
- // Visit one property from the XMP Basic schema
- SXMPIterator xmpKeywordsIter(xmp, kXMP_NS_XMP, "Keywords", kXMP_IterJustLeafNodes);
- while(xmpKeywordsIter.Next(&schemaNS, &propPath, &propVal))
- {
- cout << schemaNS << " " << propPath << " = " << propVal << endl;
- }
-
- cout << "----------------------------------" << endl;
-
- // Visit the Dublin Core schema, omit any quailifiers and only
- // show the leaf properties
- SXMPIterator dcIter(xmp, kXMP_NS_DC, (kXMP_IterOmitQualifiers | kXMP_IterJustLeafNodes));
- while(dcIter.Next(&schemaNS, &propPath, &propVal))
- {
- cout << schemaNS << " " << propPath << " = " << propVal << endl;
- }
-
- cout << "----------------------------------" << endl;
-
- // Visit the Dublin Core schema, omit any quailifiers,
- // show the leaf properties but only return the leaf name and not the full path
- SXMPIterator dcIter2(xmp, kXMP_NS_DC, (kXMP_IterOmitQualifiers | kXMP_IterJustLeafNodes | kXMP_IterJustLeafName));
- while(dcIter2.Next(&schemaNS, &propPath, &propVal))
- {
- cout << schemaNS << " " << propPath << " = " << propVal << endl;
- }
-
- cout << "----------------------------------" << endl;
-
- // Iterate over a single namespace. Show all properties within
- // the Photoshop schema
- SXMPIterator exifIter(xmp, kXMP_NS_Photoshop);
- while(exifIter.Next(&schemaNS, &propPath, &propVal))
- {
- cout << schemaNS << " " << propPath << " = " << propVal << endl;
- }
-
- cout << "----------------------------------" << endl;
-
- // Just visit the leaf nodes of EXIF properties. That is just
- // properties that may have values.
- SXMPIterator exifLeafIter(xmp, kXMP_NS_EXIF, kXMP_IterJustLeafNodes);
- while(exifLeafIter.Next(&schemaNS, &propPath, &propVal))
- {
- cout << schemaNS << " " << propPath << " = " << propVal << endl;
- }
-
- cout << "----------------------------------" << endl;
-
- // Iterate over all properties but skip the EXIF schema and skip the custom schema
- // and continue visiting nodes
- SXMPIterator skipExifIter (xmp);
- while(skipExifIter.Next(&schemaNS, &propPath, &propVal))
- {
- if(schemaNS == kXMP_NS_EXIF || schemaNS == kXMP_NS_SDK)
- {
- skipExifIter.Skip(kXMP_IterSkipSubtree);
- }
- else
- {
- cout << schemaNS << " " << propPath << " = " << propVal << endl;
- }
- }
-
- cout << "----------------------------------" << endl;
-
- // Iterate over all properties but skip the EXIF schema
- // and any remaining siblings of the current node.
- SXMPIterator stopAfterExifIter ( xmp );
- while(stopAfterExifIter.Next(&schemaNS, &propPath, &propVal))
- {
- if(schemaNS == kXMP_NS_EXIF || schemaNS == kXMP_NS_SDK)
- {
- stopAfterExifIter.Skip(kXMP_IterSkipSiblings);
- }
- else
- {
- cout << schemaNS << " " << propPath << " = " << propVal << endl;
- }
- }
-
- cout << "----------------------------------" << endl;
-
- //////////////////////////////////////////////////////////////////////////////////////
-
- // Iterate over the custom XMP
-
- // Visit the immediate children of this node.
- // No qualifiers are visisted as they are below the property being visisted.
- SXMPIterator justChildrenIter(xmp, kXMP_NS_SDK, kXMP_IterJustChildren);
- while(justChildrenIter.Next(&schemaNS, &propPath, &propVal))
- {
- cout << propPath << " = " << propVal << endl;
- }
-
- cout << "----------------------------------" << endl;
-
- // Visit the immediate children of this node but only those that may have values.
- // No qualifiers are visisted as they are below the property being visisted.
- SXMPIterator justChildrenAndLeafIter(xmp, kXMP_NS_SDK, (kXMP_IterJustChildren | kXMP_IterJustLeafNodes));
- while(justChildrenAndLeafIter.Next(&schemaNS, &propPath, &propVal))
- {
- cout << propPath << " = " << propVal << endl;
- }
-
- cout << "----------------------------------" << endl;
-
- // Visit the leaf nodes of TopStructProperty
- SXMPIterator myTopStructIter(xmp, kXMP_NS_SDK, "MyTopStruct", kXMP_IterJustLeafNodes);
- while(myTopStructIter.Next(&schemaNS, &propPath, &propVal))
- {
- cout << propPath << " = " << propVal << endl;
- }
-
- cout << "----------------------------------" << endl;
-
- // Visit the leaf nodes of the TopStructProperty but only return the names for
- // the leaf components and not the full path
- SXMPIterator xmyTopStructIterShortNames(xmp, kXMP_NS_SDK, "MyTopStruct", (kXMP_IterJustLeafNodes | kXMP_IterJustLeafName));
- while(xmyTopStructIterShortNames.Next(&schemaNS, &propPath, &propVal))
- {
- cout << propPath << " = " << propVal << endl;
- }
-
- cout << "----------------------------------" << endl;
-
- // Visit a property and all of the qualifiers
- SXMPIterator iterArrayProp (xmp, kXMP_NS_SDK, "ArrayWithStructures", kXMP_IterJustLeafNodes );
- while(iterArrayProp.Next(&schemaNS, &propPath, &propVal))
- {
- cout << propPath << " = " << propVal << endl;
- }
-
- cout << "----------------------------------" << endl;
-
- // Visit a property and omit all of the qualifiers
- SXMPIterator iterArrayPropNoQual (xmp, kXMP_NS_SDK, "ArrayWithStructures", (kXMP_IterJustLeafNodes | kXMP_IterOmitQualifiers));
- while(iterArrayPropNoQual.Next(&schemaNS, &propPath, &propVal))
- {
- cout << propPath << " = " << propVal << endl;
- }
-
- cout << "----------------------------------" << endl;
-
- // Skip a subtree and continue onwards. Once 'Item 4' is found then the we can skip all of the
- // siblings of the current node. If the the current node were a top level node the iteration
- // would be complete as all siblings would be skipped. However, when 'Item 4' is found the current
- // node is not at the top level so there are other nodes further up the tree that still need to be
- // visited.
- SXMPIterator skipIter (xmp, kXMP_NS_SDK, (kXMP_IterJustLeafNodes | kXMP_IterOmitQualifiers | kXMP_IterJustLeafName));
- while(skipIter.Next(&schemaNS, &propPath, &propVal))
- {
- if(propVal == "Item 4")
- {
- skipIter.Skip(kXMP_IterSkipSiblings);
- }
- else
- {
- cout << schemaNS << " " << propPath << " = " << propVal << endl;
- }
- }
-
- /*
- // Visit all properties and qualifiers
- SXMPIterator allPropsIter(xmp);
- while(allPropsIter.Next(&schemaNS, &propPath, &propVal))
- {
- cout << schemaNS << " " << propPath << " = " << propVal << endl;
- }
- */
- }
-
- }
-
- SXMPFiles::Terminate();
- SXMPMeta::Terminate();
-
- return 0;
-}
-
diff --git a/samples/source/common/DumpFile.cpp b/samples/source/common/DumpFile.cpp
index 64aa548..2b8c834 100644
--- a/samples/source/common/DumpFile.cpp
+++ b/samples/source/common/DumpFile.cpp
@@ -1,6 +1,6 @@
// =================================================================================================
-// Copyright 2002-2008 Adobe Systems Incorporated
-// All Rights Reserved.
+// 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.
@@ -8,27 +8,25 @@
// =================================================================================================
//
// DumpFile is suitable for both dumping an entire file structure to screen _as well as_ access to
-// specific to specific legacy fields (as much required for auto-testing).
+// specific to specific legacy fields (as much required for auto-testing).
//
// Currently supports
// - JPEG
-// - TIFF
+// - TIFF
// - PHOTOSHOP
// - JPEG2K
// - WMAV (ASF/WMA/WMV)
-// - AVI
-// - WAV
+// - RIFF (AVI/WAV)
// - PNG
// - InDesign
// - MP3
// - MOV (Quicktime)
-// - UCF (done, including comment zips, zip64 (>4GB))
-// - SWF (in progress)
+// - UCF (done, including commented zips and zip64 (>4GB))
+// - SWF
// - FLV
// - MPEG-4
//
-// DumpFile does depend on XMPCore, it does not depend on XMPFiles. Thus not suitable for any form
-// of XMPCore testing. Suitable for XMPFiles in particular reconciliation issues.
+// DumpFile does depend on XMPCore and the packetscanner from XMPFiles.
#include <stdarg.h>
#include "globals.h"
@@ -236,9 +234,9 @@ using namespace std;
#include "XMP_Const.h"
//scanning routines - needed by PacketScan(...) routine for unknown files
-#include "QEScanner.hpp"
+#include "XMPScanner.hpp"
-// QE related
+// needed for logging
#include "Log.h"
//disabled warning (take-over)
@@ -255,6 +253,10 @@ using namespace std;
// resp. queried (testrunner)
static TagTree* tree;
+// specifc 'state machine' for QT/MPEG4 dumping
+// * false by default (set in DumpISO() stub)
+static bool TimeCodeTrack;
+
// =================================================================================================
long kOne = 1;
@@ -262,6 +264,7 @@ char firstByte = *((char*)&kOne);
const bool sBigEndianHost = (firstByte == 0);
const bool sLittleEndianHost = (firstByte == 1);
+static bool beTIFF;
typedef const char * ChPtr;
#define CheckBytes(left,right,len) (memcmp (((ChPtr)(left)), ((ChPtr)(right)), len) == 0)
@@ -293,10 +296,10 @@ static XMP_Uns64 GetUns64LE ( const void * addr );
// =================================================================================================
// ahead declarations
-static void DumpTIFF ( XMP_Uns8 * tiffContent, XMP_Uns32 tiffLen, XMP_Uns32 fileOffset, const char * label );
+static void DumpTIFF ( XMP_Uns8 * tiffContent, XMP_Uns32 tiffLen, XMP_Uns32 fileOffset, const char * label, std::string path );
static void DumpIPTC ( XMP_Uns8 * iptcOrigin, XMP_Uns32 iptcLen, XMP_Uns32 fileOffset, const char * label );
static void DumpImageResources ( XMP_Uns8 * psirOrigin, XMP_Uns32 psirLen, XMP_Uns32 fileOffset, const char * label );
-static void DumpIFDChain ( XMP_Uns8 * startPtr, XMP_Uns8 * endPtr, XMP_Uns8 * tiffContent, XMP_Uns32 fileOffset, const char * label );
+static void DumpIFDChain ( XMP_Uns8 * startPtr, XMP_Uns8 * endPtr, XMP_Uns8 * tiffContent, XMP_Uns32 fileOffset, const char * label, std::string path );
// =================================================================================================
@@ -345,6 +348,8 @@ enum { // IPTC DataSet IDs
kIPTC_Instructions = 40,
kIPTC_DateCreated = 55,
kIPTC_TimeCreated = 60,
+ kIPTC_DigitalCreationDate = 62 ,
+ kIPTC_DigitalCreationTime = 63,
kIPTC_Creator = 80,
kIPTC_CreatorJobtitle = 85,
kIPTC_City = 90,
@@ -377,6 +382,8 @@ static const DataSetInfo kDataSetNames[] =
{ kIPTC_Instructions, "Instructions" },
{ kIPTC_DateCreated, "Date Created" },
{ kIPTC_TimeCreated, "Time Created" },
+{ kIPTC_DigitalCreationDate, "Digital Creation Date" },
+{ kIPTC_DigitalCreationTime, "Digital Creation Time" },
{ kIPTC_Creator, "Creator" },
{ kIPTC_CreatorJobtitle, "Creator Jobtitle" },
{ kIPTC_City, "City" },
@@ -795,9 +802,9 @@ void static Rewind(LFA_FileRef file)
LFA_Seek (file, 0, SEEK_SET);
}
-XMP_Uns32 static Peek32u(LFA_FileRef file)
+XMP_Uns32 static Peek32u(LFA_FileRef file, bool bigEndian = false )
{
- XMP_Uns32 value = tree->digest32u(file);
+ XMP_Uns32 value = tree->digest32u(file, "", bigEndian );
Rewind(file, 4);
return value;
}
@@ -888,98 +895,29 @@ CaptureXMP ( const XMP_Uns8 * xmpPtr, const XMP_Uns32 xmpLen, XMP_Int64 fileOffs
// -------------------------------------------------------------------------------------------------
-static void PrintOnlyASCII_8 ( XMP_Uns8 * strPtr, XMP_Uns32 strLen, const char * /*label = 0*/, bool /*stopOnNUL*/ = true )
+static void PrintOnlyASCII_8 ( XMP_Uns8 * strPtr, XMP_Uns32 strLen, bool stopOnNUL = true )
{
//wrapping to QEBuginese
// - NB: remainder (zero termination earlier then length) is catered for...
- tree->addComment ( convert8Bit ( strPtr, true, strLen ) );
+ tree->addComment ( convert8Bit ( strPtr, stopOnNUL, strLen ) );
}
// -------------------------------------------------------------------------------------------------
-static void PrintOnlyASCII_16BE ( XMP_Uns16 * u16Ptr, XMP_Uns32 u16Bytes,
- const char * label = 0, bool stopOnNUL = true )
+// this wrap and the LE counterpart can only be inferior, since
+// its always added as a comment, even if value was more appropriate.
+// ==> callers should make use of convert16Bit directly.
+static void PrintOnlyASCII_16BE ( XMP_Uns16 * u16Ptr, XMP_Uns32 u16Bytes, bool stopOnNUL = true )
{
- //TODO: wrap to QEBuginese just like 8bit flavor above...
-
- bool prevBig = false;
- size_t remainder = 0;
- XMP_Uns32 u16Count = u16Bytes/2;
-
- if ( (label != 0) && (*label != 0) ) tree->addComment ( "%s \"", label );
-
- for ( XMP_Uns32 i = 0; i < u16Count; ++i ) {
-
- XMP_Uns16 u16 = u16Ptr[i];
- u16 = GetUns16BE (&u16);
-
- if ( (0x20 <= u16) && (u16 <= 0x7E) ) {
- if (prevBig) tree->addComment ( "\xA9" );
- tree->addComment ( "%c", u16 );
- prevBig = false;
- } else {
- if ( ! prevBig ) tree->addComment ( "\xA9" );
- tree->addComment ( "%.4X", u16 );
- prevBig = true;
- if ( (u16 == 0) && stopOnNUL ) {
- remainder = 2 * (u16Count - (i + 1));
- break;
- }
- }
-
- }
-
- if ( prevBig ) tree->addComment ( "\xA9" );
-
- if ( (label != 0) && (*label != 0) ) {
- tree->addComment ( "\"" );
- if (remainder != 0) tree->addComment ( "** remainder %d bytes **", remainder );
-
- }
-
+ tree->addComment ( convert16Bit( true, (XMP_Uns8*) u16Ptr, stopOnNUL, u16Bytes ) );
} // PrintOnlyASCII_16BE
// -------------------------------------------------------------------------------------------------
-static void PrintOnlyASCII_16LE ( XMP_Uns16 * u16Ptr, XMP_Uns32 u16Bytes,
- const char * label = 0, bool stopOnNUL = true )
+static void PrintOnlyASCII_16LE ( XMP_Uns16 * u16Ptr, XMP_Uns32 u16Bytes, bool stopOnNUL = true )
{
- bool prevBig = false;
- size_t remainder = 0;
- XMP_Uns32 u16Count = u16Bytes/2;
-
- if ( (label != 0) && (*label != 0) ) tree->addComment ( "%s \"", label );
-
- for ( XMP_Uns32 i = 0; i < u16Count; ++i ) {
-
- XMP_Uns16 u16 = u16Ptr[i];
- u16 = GetUns16LE (&u16);
-
- if ( (0x20 <= u16) && (u16 <= 0x7E) ) {
- if ( prevBig ) tree->addComment ( "\xA9" );
- tree->addComment ( "%c", u16 );
- prevBig = false;
- } else {
- if ( ! prevBig ) tree->addComment ( "\xA9" );
- tree->addComment ( "%.4X", u16 );
- prevBig = true;
- if ( (u16 == 0) && stopOnNUL ) {
- remainder = 2 * (u16Count - (i + 1));
- break;
- }
- }
-
- }
-
- if ( prevBig ) tree->addComment ( "\xA9" );
-
- if ( (label != 0) && (*label != 0) ) {
- tree->addComment ( "\"" );
- if ( remainder != 0 ) tree->addComment ( "** remainder %d bytes **", remainder );
-
- }
-
+ tree->addComment ( convert16Bit( false, (XMP_Uns8*) u16Ptr, stopOnNUL, u16Bytes ) );
} // PrintOnlyASCII_16LE
// =================================================================================================
@@ -1009,16 +947,7 @@ static const XMP_Int64 kInDesignMinSize = 2 * kINDD_PageSize; // Two master page
static const XMP_Int64 kISOMediaMinSize = 16; // At least a minimal file type box.
static const XMP_Uns8 kISOMediaFTyp[] = { 0x66, 0x74, 0x79, 0x70 }; // "ftyp"
-
static const XMP_Uns32 kISOTag_ftyp = 0x66747970UL;
-static const XMP_Uns32 kISOTag_uuid = 0x75756964UL;
-static const XMP_Uns32 kISOTag_moov = 0x6D6F6F76UL;
-static const XMP_Uns32 kISOTag_mvhd = 0x6D766864UL;
-static const XMP_Uns32 kISOTag_udta = 0x75647461UL;
-static const XMP_Uns32 kISOTag_trak = 0x7472616BUL;
-static const XMP_Uns32 kISOTag_meta = 0x6D657461UL;
-static const XMP_Uns32 kISOTag_ilst = 0x696C7374UL;
-
static const XMP_Uns32 kISOBrand_mp41 = 0x6D703431UL;
static const XMP_Uns32 kISOBrand_mp42 = 0x6D703432UL;
static const XMP_Uns32 kISOBrand_f4v = 0x66347620UL;
@@ -1120,7 +1049,7 @@ CheckFileFormat ( const char * filePath, XMP_Uns8 * fileContent, XMP_Int64 fileS
static void
DumpXMP (XMP_Uns8 * xmpPtr, XMP_Uns32 xmpLen, XMP_Int64 xmpOffset, const char * label)
{
- if (xmpOffset <= 0xFFFFFFFFUL) {
+ if (xmpOffset <= 0xFFFFFFFFL) {
tree->pushNode("XMP");
tree->addComment("from %s, offset %u (0x%X), size %d",
label, (XMP_Uns32)xmpOffset, (XMP_Uns32)xmpOffset, xmpLen);
@@ -1300,12 +1229,12 @@ DumpImageResources (XMP_Uns8 * psirOrigin, XMP_Uns32 psirLen, XMP_Uns32 fileOffs
tree->setKeyValue("PSIR:Exif-1","");
exif1Ptr = irPtr;
exif1Len = irLen;
- DumpTIFF (exif1Ptr, exif1Len, (fileOffset + (exif1Ptr - psirOrigin)), "PSIR #1058 (Exif 1)");
+ DumpTIFF (exif1Ptr, exif1Len, (fileOffset + (exif1Ptr - psirOrigin)), "PSIR #1058 (Exif 1)", "PSIR:Exif-1");
} else if (irID == kPSIR_Exif_3) { //****************
tree->setKeyValue("PSIR:Exif-3","");
exif3Ptr = irPtr;
exif3Len = irLen;
- if (exif3Ptr != 0) DumpTIFF (exif3Ptr, exif3Len, (fileOffset + (exif3Ptr - psirOrigin)), "PSIR #1059 (Exif 3)");
+ if (exif3Ptr != 0) DumpTIFF (exif3Ptr, exif3Len, (fileOffset + (exif3Ptr - psirOrigin)), "PSIR #1059 (Exif 3)", "PSIR:Exif-3");
} else if (irID == kPSIR_IPTC_Digest) {
tree->setKeyValue("PSIR:IPTC digest",
fromArgs("%.8X-%.8X-%.8X-%.8X",
@@ -1347,7 +1276,7 @@ DumpImageResources (XMP_Uns8 * psirOrigin, XMP_Uns32 psirLen, XMP_Uns32 fileOffs
static void
DumpOneIFD (int ifdIndex, XMP_Uns8 * ifdPtr, XMP_Uns8 * endPtr,
- XMP_Uns8 * tiffContent, XMP_Uns32 fileOffset, const char * label)
+ XMP_Uns8 * tiffContent, XMP_Uns32 fileOffset, const char * label, std::string path)
{
XMP_Uns8 * exifPtr = 0;
XMP_Uns8 * gpsPtr = 0;
@@ -1377,6 +1306,12 @@ DumpOneIFD (int ifdIndex, XMP_Uns8 * ifdPtr, XMP_Uns8 * endPtr,
XMP_Uns16 prevTag = 0;
XMP_Uns8 * fieldPtr = tiffContent+ifdOffset+2;
+
+ if (!path.empty())
+ path.append("/");
+
+ path.append(fromArgs("IFD%d", ifdIndex));
+
for (int i = 0; i < fieldCount; ++i, fieldPtr += 12) {
XMP_Uns16 fieldTag = TIFF_GetUns16 (fieldPtr);
@@ -1390,7 +1325,7 @@ DumpOneIFD (int ifdIndex, XMP_Uns8 * ifdPtr, XMP_Uns8 * endPtr,
if (valueLen <= 4) valuePtr = fieldPtr + 8;
//===================== adding key here
- tree->setKeyValue( fromArgs("TIFF:%d", fieldTag) );
+ tree->setKeyValue( fromArgs("%s/TIFF:%d",path.c_str(), fieldTag) );
if ((fieldType < 1) || (fieldType >= kTIFF_TypeEnd)) {
tree->addComment("type %d", fieldType);
@@ -1438,9 +1373,9 @@ DumpOneIFD (int ifdIndex, XMP_Uns8 * ifdPtr, XMP_Uns8 * endPtr,
xmpPtr = valuePtr;
xmpLen = valueLen;
} else {
- for (int i = 0; sTIFF_TagNames[i].tag != 0; ++i) {
- if (sTIFF_TagNames[i].tag == fieldTag) {
- tree->addComment("%s", sTIFF_TagNames[i].name);
+ for (int j = 0; sTIFF_TagNames[j].tag != 0; ++j) {
+ if (sTIFF_TagNames[j].tag == fieldTag) {
+ tree->addComment("%s", sTIFF_TagNames[j].name);
break;
}
}
@@ -1449,8 +1384,10 @@ DumpOneIFD (int ifdIndex, XMP_Uns8 * ifdPtr, XMP_Uns8 * endPtr,
XMP_Uns8 value8;
XMP_Uns16 value16;
XMP_Uns32 value32;
+ XMP_Uns32 denom;
std::string tempStr;
-
+ char cs[31];
+
switch (fieldType) {
case kTIFF_Uns8 :
@@ -1482,11 +1419,19 @@ DumpOneIFD (int ifdIndex, XMP_Uns8 * ifdPtr, XMP_Uns8 * endPtr,
break;
case kTIFF_URational :
- if (valueCount == 1) {
- value32 = TIFF_GetUns32 (valuePtr);
- XMP_Uns32 denom = TIFF_GetUns32 (valuePtr+4);
- tree->changeValue( "%u/%u" , value32,denom);
+ for( unsigned int j = 0; j < valueCount ; j++) {
+ value32 = TIFF_GetUns32 (valuePtr+(j*8));
+ denom = TIFF_GetUns32 (valuePtr+(j*8)+4);
+ snprintf(cs,30,"%u",value32);
+ tempStr += cs;
+ tempStr += "/";
+ snprintf(cs,30,"%u",denom);
+ tempStr += cs;
+ if(j < valueCount-1)
+ tempStr += ";";
}
+ if(tempStr.length() > 0)
+ tree->changeValue( tempStr );
break;
case kTIFF_Int8 :
@@ -1499,12 +1444,47 @@ DumpOneIFD (int ifdIndex, XMP_Uns8 * ifdPtr, XMP_Uns8 * endPtr,
break;
case kTIFF_Undef8 :
- if (valueCount == 1) {
+ if ( valueCount == 1 ) {
value8 = *valuePtr;
- tree->changeValue("0x%.2X", value8);
+ tree->changeValue( "0x%.2X", value8 );
+ } else if ( fieldTag == 36864 ) { // ExifVersion
+ tree->changeValue( "%.*s", valueCount, valuePtr );
+ } else if ( fieldTag == 37510 ) { // UserComment
+ XMP_Uns8 * encPtr = valuePtr;
+ valuePtr += 8;
+ valueCount -= 8;
+ sprintf(cs,"encoding = %.8s", encPtr );
+ tempStr += cs;
+ if ( ! CheckBytes ( encPtr, "UNICODE\0", 8 ) ) {
+ tree->changeValue( convert8Bit ( valuePtr, false, valueCount ) );
+ } else {
+ bool doBE = beTIFF;
+ if ( CheckBytes ( valuePtr, "\xFE\xFF", 2 ) ) {
+ doBE = true;
+ valuePtr += 2;
+ valueCount -= 2;
+ tempStr += ", BE BOM";
+ }
+ if ( CheckBytes ( valuePtr, "\xFF\xFE", 2 ) ) {
+ doBE = false;
+ valuePtr += 2;
+ valueCount -= 2;
+ tempStr += ", LE BOM";
+ }
+ if ( doBE ) {
+ tree->changeValue( convert16Bit( true, (XMP_Uns8*) valuePtr, false, valueCount ) );
+ //PrintOnlyASCII_16BE ( (XMP_Uns16*)valuePtr, valueCount, ", value =", false /* ! stopOnNUL */ );
+ } else {
+ tree->changeValue( convert16Bit( false, (XMP_Uns8*) valuePtr, false, valueCount ) );
+ //PrintOnlyASCII_16LE ( (XMP_Uns16*)valuePtr, valueCount, ", value =", false /* ! stopOnNUL */ );
+ }
+ }
}
+
+ if(tempStr.length() > 0)
+ tree->addComment( tempStr );
break;
-
+
case kTIFF_Int16 :
if (valueCount == 1) {
value16 = TIFF_GetUns16 (valuePtr);
@@ -1522,7 +1502,7 @@ DumpOneIFD (int ifdIndex, XMP_Uns8 * ifdPtr, XMP_Uns8 * endPtr,
case kTIFF_SRational :
if (valueCount == 1) {
value32 = TIFF_GetUns32 (valuePtr);
- XMP_Uns32 denom = TIFF_GetUns32 (valuePtr+4);
+ denom = TIFF_GetUns32 (valuePtr+4);
tree->changeValue("%d/%d", *((XMP_Int32*)&value32), *((XMP_Int32*)&denom));
}
break;
@@ -1551,22 +1531,22 @@ DumpOneIFD (int ifdIndex, XMP_Uns8 * ifdPtr, XMP_Uns8 * endPtr,
if (exifPtr != 0) {
DumpIFDChain (exifPtr, endPtr, tiffContent,
- (fileOffset + (exifPtr - tiffContent)), "TIFF tag #34665 (Exif IFD)");
+ (fileOffset + (exifPtr - tiffContent)), "TIFF tag #34665 (Exif IFD)", path+"/TIFF:34665");
}
if (gpsPtr != 0) {
DumpIFDChain (gpsPtr, endPtr, tiffContent,
- (fileOffset + (gpsPtr - tiffContent)), "TIFF tag #34853 (GPS Info IFD)");
+ (fileOffset + (gpsPtr - tiffContent)), "TIFF tag #34853 (GPS Info IFD)", path+"/TIFF:34853");
}
if (interopPtr != 0) {
DumpIFDChain (interopPtr, endPtr, tiffContent,
- (fileOffset + (interopPtr - tiffContent)), "TIFF tag #40965 (Interoperability IFD)");
+ (fileOffset + (interopPtr - tiffContent)), "TIFF tag #40965 (Interoperability IFD)", path+"/TIFF:40965");
}
if (makerNotePtr != 0) {
DumpIFDChain (makerNotePtr, endPtr, tiffContent,
- (fileOffset + (makerNotePtr - tiffContent)), "TIFF tag #37500 (Maker Note)");
+ (fileOffset + (makerNotePtr - tiffContent)), "TIFF tag #37500 (Maker Note)", path+"/TIFF:37500");
}
if (iptcPtr != 0) {
@@ -1589,7 +1569,7 @@ DumpOneIFD (int ifdIndex, XMP_Uns8 * ifdPtr, XMP_Uns8 * endPtr,
static void
DumpIFDChain (XMP_Uns8 * startPtr, XMP_Uns8 * endPtr,
- XMP_Uns8 * tiffContent, XMP_Uns32 fileOrigin, const char * label)
+ XMP_Uns8 * tiffContent, XMP_Uns32 fileOrigin, const char * label, std::string path)
{
XMP_Uns8 * ifdPtr = startPtr;
XMP_Uns32 ifdOffset = startPtr - tiffContent;
@@ -1603,7 +1583,7 @@ DumpIFDChain (XMP_Uns8 * startPtr, XMP_Uns8 * endPtr,
}
XMP_Uns16 fieldCount = TIFF_GetUns16 (ifdPtr);
- DumpOneIFD (ifdIndex, ifdPtr, endPtr, tiffContent, fileOrigin, label);
+ DumpOneIFD (ifdIndex, ifdPtr, endPtr, tiffContent, fileOrigin, label, path);
ifdOffset = TIFF_GetUns32 (ifdPtr+2+(12*fieldCount));
ifdPtr = tiffContent + ifdOffset;
@@ -1614,7 +1594,7 @@ DumpIFDChain (XMP_Uns8 * startPtr, XMP_Uns8 * endPtr,
// =================================================================================================
static void
-DumpTIFF (XMP_Uns8 * tiffContent, XMP_Uns32 tiffLen, XMP_Uns32 fileOffset, const char * label)
+DumpTIFF (XMP_Uns8 * tiffContent, XMP_Uns32 tiffLen, XMP_Uns32 fileOffset, const char * label, std::string path)
{
// ! TIFF can be nested because of the Photoshop 6 weiredness. Save and restore the procs.
GetUns16_Proc save_GetUns16 = TIFF_GetUns16;
@@ -1622,11 +1602,13 @@ DumpTIFF (XMP_Uns8 * tiffContent, XMP_Uns32 tiffLen, XMP_Uns32 fileOffset, const
GetUns64_Proc save_GetUns64 = TIFF_GetUns64;
if (CheckBytes(tiffContent,"II\x2A\x00",4)) {
+ beTIFF = false;
TIFF_GetUns16 = GetUns16LE;
TIFF_GetUns32 = GetUns32LE;
TIFF_GetUns64 = GetUns64LE;
tree->addComment("Little endian ");
} else if (CheckBytes(tiffContent,"MM\x00\x2A",4)) {
+ beTIFF = true;
TIFF_GetUns16 = GetUns16BE;
TIFF_GetUns32 = GetUns32BE;
TIFF_GetUns64 = GetUns64BE;
@@ -1639,7 +1621,7 @@ DumpTIFF (XMP_Uns8 * tiffContent, XMP_Uns32 tiffLen, XMP_Uns32 fileOffset, const
tree->addComment("TIFF from %s, offset %d (0x%X), size %d", label, fileOffset, fileOffset, tiffLen);
XMP_Uns32 ifdOffset = TIFF_GetUns32 (tiffContent+4);
- DumpIFDChain (tiffContent+ifdOffset, tiffContent+tiffLen, tiffContent, fileOffset, label);
+ DumpIFDChain (tiffContent+ifdOffset, tiffContent+tiffLen, tiffContent, fileOffset, label, path);
TIFF_GetUns16 = save_GetUns16;
TIFF_GetUns32 = save_GetUns32;
@@ -1731,15 +1713,18 @@ DumpJPEG (XMP_Uns8 * jpegContent, XMP_Uns32 jpegLen)
tree->addComment(fromArgs("size %d, APP%d, \"%s\"", segLen, minorKind, segName));
if ((minorKind == 1) &&
((memcmp(segName,"Exif\0\0",6) == 0) || (memcmp(segName,"Exif\0\xFF",6) == 0))) {
- tree->addComment("Exif");
+ tree->addComment("EXIF");
+ tree->changeValue("EXIF");
exifPtr = segPtr + 4 + 6;
exifLen = segLen - 2 - 6;
} else if ((minorKind == 13) && (strcmp(segName,"Photoshop 3.0") == 0)) {
tree->addComment("PSIR");
+ tree->changeValue("PSIR");
psirPtr = segPtr + 4 + strlen(segName) + 1;
psirLen = (XMP_Uns16)(segLen - 2 - strlen(segName) - 1);
} else if ((minorKind == 1) && (strcmp(segName,"http://ns.adobe.com/xap/1.0/") == 0)) {
tree->addComment("XMP");
+ tree->changeValue("XMP");
xmpPtr = segPtr + 4 + strlen(segName) + 1;
xmpLen = (XMP_Uns16)(segLen - 2 - strlen(segName) - 1);
}
@@ -1824,7 +1809,7 @@ DumpJPEG (XMP_Uns8 * jpegContent, XMP_Uns32 jpegLen)
));
}
- if (exifPtr != 0) DumpTIFF (exifPtr, exifLen, (exifPtr - jpegContent), "JPEG Exif APP1");
+ if (exifPtr != 0) DumpTIFF (exifPtr, exifLen, (exifPtr - jpegContent), "JPEG Exif APP1", "JPEG:APP1");
if (psirPtr != 0) DumpImageResources (psirPtr, psirLen, (psirPtr - jpegContent), "JPEG Photoshop APP13");
if (xmpPtr != 0) DumpXMP (xmpPtr, xmpLen, (xmpPtr - jpegContent), "JPEG XMP APP1");
@@ -1832,40 +1817,6 @@ DumpJPEG (XMP_Uns8 * jpegContent, XMP_Uns32 jpegLen)
// =================================================================================================
-static XMP_Uns32
-DumpISOFileType (XMP_Uns8 * ftypPtr, XMP_Uns32 ftypLimit)
-{
- XMP_Uns32 ftypLen = GetUns32BE (ftypPtr);
- XMP_Uns32 ftypTag = GetUns32BE (ftypPtr+4);
-
- if (ftypLen == 0) ftypLen = ftypLimit;
-
- if ((ftypLen < 16) || (ftypLen > ftypLimit) || (ftypTag != kISOTag_ftyp)) {
- tree->addComment( "** Invalid File Type box: tag = '%.4s' (0x%.8X), size = %d **",
- ftypPtr+4, ftypTag, ftypLen);
- return ftypLimit; // Force caller to skip the rest of the file.
- }
-
- XMP_Uns32 ftypVersion = GetUns32BE (ftypPtr+12);
-
- tree->comment("Major Brand '%.4s', version %d",ftypPtr+8, ftypVersion);
- tree->comment("Compatible brands:");
-
- XMP_Uns8 * compatPtr = ftypPtr + 16;
- XMP_Uns8 * compatEnd = ftypPtr + ftypLen;
-
- if (compatPtr >= compatEnd)
- tree->addComment("<none>");
-
- for (; compatPtr < compatEnd; compatPtr += 4) {
- tree->addComment("'%.4s'", compatPtr);
- }
-
- return ftypLen;
-} // DumpISOFileType
-
-// =================================================================================================
-
static const XMP_Uns8 kUUID_XMP[16] =
{ 0xBE, 0x7A, 0xCF, 0xCB, 0x97, 0xA9, 0x42, 0xE8, 0x9C, 0x71, 0x99, 0x94, 0x91, 0xE3, 0xAF, 0xAC };
static const XMP_Uns8 kUUID_Exif[16] =
@@ -1875,314 +1826,443 @@ static const XMP_Uns8 kUUID_IPTC[16] =
static const XMP_Uns8 kUUID_PSIR[16] =
{ 0x2C, 0x4C, 0x01, 0x00, 0x85, 0x04, 0x40, 0xB9, 0xA0, 0x3E, 0x56, 0x21, 0x48, 0xD6, 0xDF, 0xEB };
-static XMP_Uns8 * sISOPtr_XMP = 0; // ! Rather ugly, but what the heck.
-static XMP_Uns32 sISOLen_XMP = 0;
-static XMP_Int32 sISOPos_XMP = 0; // *** Should be 64 bits.
-
-static XMP_Uns8 * sISOPtr_Exif = 0;
-static XMP_Uns32 sISOLen_Exif = 0;
-static XMP_Uns32 sISOPos_Exif = 0;
-
-static XMP_Uns8 * sISOPtr_IPTC = 0;
-static XMP_Uns32 sISOLen_IPTC = 0;
-static XMP_Uns32 sISOPos_IPTC = 0;
-
-static XMP_Uns8 * sISOPtr_PSIR = 0;
-static XMP_Uns32 sISOLen_PSIR = 0;
-static XMP_Uns32 sISOPos_PSIR = 0;
-
// -------------------------------------------------------------------------------------------------
+/**
+ * helper routine to get past the version and flags field...
+ */
static void
-CleanISOLegacy()
+digestISOFullBoxExtension ( LFA_FileRef file, std::string isoPath, XMP_Int64& remainingSize, XMP_Uns8& version, XMP_Uns32& flags )
{
- if (sISOPtr_XMP != 0) free (sISOPtr_XMP);
- sISOPtr_XMP = 0;
- sISOLen_XMP = 0;
- sISOPos_XMP = 0;
-
- if (sISOPtr_Exif != 0) free (sISOPtr_Exif);
- sISOPtr_Exif = 0;
- sISOLen_Exif = 0;
- sISOPos_Exif = 0;
-
- if (sISOPtr_IPTC != 0) free (sISOPtr_IPTC);
- sISOPtr_IPTC = 0;
- sISOLen_IPTC = 0;
- sISOPos_IPTC = 0;
+ version = LFA_ReadUns8( file );
+ flags = 0;
- if (sISOPtr_PSIR != 0) free (sISOPtr_PSIR);
- sISOPtr_PSIR = 0;
- sISOLen_PSIR = 0;
- sISOPos_PSIR = 0;
+ LFA_Read( file, &flags, 3, true ); // read only 3 byte!
+ flags = flags >> 8; // (move to bit 0-23)
+ remainingSize -= 4;
-} // CleanISOLegacy
-
-// =================================================================================================
+ tree->setKeyValue( isoPath + "version", fromArgs( "%d", version) );
+ tree->setKeyValue( isoPath + "flags", fromArgs( "0x%.8X", flags) );
+}
-static XMP_Uns32
-DumpISOBoxInfo ( LFA_FileRef file, XMP_Uns32 parent, XMP_Uns32 boxOffset, XMP_Uns32 boxLimit,
- bool isQT = false, bool doPop = true )
+static void
+digestInternationalTextSequence ( LFA_FileRef file, std::string isoPath, XMP_Int64* remainingSize )
{
- XMP_Uns32 boxLen, boxTag, contentOffset;
- XMP_Int64 bigLen = 0;
-
- if ( (boxOffset > boxLimit) || ((boxLimit-boxOffset) < 8) ) {
- tree->comment ( "** No room for box in %d..%d, %d **", boxOffset, boxLimit, (boxLimit-boxOffset) );
- return boxLimit; // Off track, stop looking for boxes.
- }
-
- LFA_Seek ( file, boxOffset, SEEK_SET );
- LFA_Read ( file, &boxLen, 4, true);
- LFA_Read ( file, &boxTag, 4, true);
-
- boxLen = GetUns32BE ( &boxLen );
- contentOffset = 8;
-
- if ( boxLen == 0 ) {
- boxLen = boxLimit;
- } else if ( boxLen == 1 ) {
- LFA_Read ( file, &bigLen, 8, true);
- bigLen = GetUns64BE ( &bigLen );
- boxLen = Low32(bigLen); // ! HACK!
- contentOffset = 16;
- }
+ XMP_Int64 miniBoxStringSize = tree->digest16u(file,isoPath+"size",true,true);
+ tree->digest16u(file,isoPath+"language code",true,true);
+ (*remainingSize) -= 4;
+ if ( (*remainingSize) != miniBoxStringSize )
+ tree->addComment("WARNING: boxSize and miniBoxSize differ!");
+ tree->digestString( file, isoPath+"value", miniBoxStringSize, false );
+}
- if ( boxLen < 8 ) {
- tree->comment ( "** Bad box size of %d **", boxLen );
- return boxLimit; // Off track, stop looking for boxes.
- }
-
- tree->pushNode ( fromArgs("'%.4s'", &boxTag) );
+/**
+* dumps one *or several* (ohter while loop) ISO Boxes within the indicated space:
+*
+* maxBoxLen is :== fileLen on top-level, otherwise available length of outer box (exluding header size naturally)
+*
+* (NB: reading (and displaying) box types, compat brands and other 4-letter stuff
+* as LE is somehow easier (might need adjustment for PPC though)
+*
+* practices:
+* compensate endianess using MakeUns32BE() prior to use as string, NOT for numeric compare
+*/
+static void
+DumpISOBoxes ( LFA_FileRef file, XMP_Uns32 maxBoxLen, std::string _isoPath )
+{
+ XMP_Int64 endOfThisLevel = LFA_Tell(file) + maxBoxLen;
+ std::string origIsoPath( _isoPath );
+ std::string isoPath( _isoPath );
- boxTag = GetUns32BE ( &boxTag ); // ! After string print before hex print. needed below, too, to make comparisons work.
- if ( bigLen == 0 ) {
- tree->addComment( fromArgs( "hex value: %X, offset %d (0x%X), size %d", boxTag, boxOffset, boxOffset, boxLen ) );
- } else {
- tree->addComment( fromArgs( "hex value: %X, offset %d (0x%X), size %llu (0x %X_%.8X)",boxTag, boxOffset, boxOffset, bigLen, High32(bigLen), Low32(bigLen) ));
- }
+ while ( LFA_Tell(file) < endOfThisLevel )
+ {
+ XMP_Int64 boxHeaderSize = 8;
- // only go deeper into boxes that matter:
- if ( (boxTag == kISOTag_moov) || (boxTag == kISOTag_udta) ||
- (boxTag == kISOTag_trak) || (boxTag == kISOTag_meta) ) {
-
- XMP_Uns32 childPos, childLen;
- XMP_Uns32 childLimit = boxOffset + boxLen;
- if ( boxTag == kISOTag_meta ) {
- // reasoning here:
- // meta boxes with parent udta have versions
- // (as any ISO-part-12 file should, thus "!isQT or ...")
- // other meta boxes (i.e. under 'trak') do not
- if ( (! isQT) || (parent == kISOTag_udta) ) {
- contentOffset += 4; // Skip the version and flags.
- } else {
- tree->addComment ( "\t** QT hack for 'meta' with no version/flags **" );
- }
- }
+ //assertMsg("No space for ISO box header", boxHeaderSize <= maxBoxLen );
- for ( childPos = (boxOffset + contentOffset); childPos < childLimit; childPos += childLen ) {
- childLen = DumpISOBoxInfo ( file, boxTag, childPos, childLimit, isQT );
+ //// certainly not enough room for another box?
+ // could be a 32bit zero trailing a udta
+ // or, uhm, something garbage-ish...
+ if ( LFA_Tell(file) + boxHeaderSize > endOfThisLevel )
+ {
+ XMP_Int64 numUnusedBytes = (LFA_Tell(file) + boxHeaderSize - endOfThisLevel);
+ tree->digestString( file, isoPath+"unused", numUnusedBytes, false );
+ tree->addComment( "'free' since too small for a box" );
+
+ bool ok;
+ LFA_Seek( file, maxBoxLen, SEEK_CUR, &ok );
+ assertMsg("skippind to-small space failed (truncated file?)", ok );
+ continue; // could just as well: return
}
- } else if ( boxTag == kISOTag_ilst ) {
+ XMP_Int64 boxPos= LFA_Tell( file ); // store here, output below
+ XMP_Int64 boxSize = tree->digest32u( file, "", true ); // NB: 32bit <- 64bit
+ XMP_Uns32 boxType = tree->digest32u( file, "", false );
- // The Apple 'ilst' metadata appears to have a simple 2 level structure. 'Appears to' because
- // we do not have official documentation. The 'ilst' box is a simple container Box (no
- // version/flags), as are the boxes that it contains. It looks like those subboxes are the
- // metadata items. The sub-box type is the metadata item name, the sub-box contains just a
- // 'data' box with the value.
-
- // *** The inner loop is hardwired to assume the sub-box has an 8 byte header.
-
- XMP_Uns32 downOnePos, downOneLen;
- XMP_Uns32 downOneLimit = boxOffset + boxLen;
-
- for ( downOnePos = (boxOffset + contentOffset); downOnePos < downOneLimit; downOnePos += downOneLen ) {
-
- downOneLen = DumpISOBoxInfo ( file, boxTag, downOnePos, downOneLimit, isQT, false /* no pop */ );
-
- XMP_Uns32 downTwoPos, downTwoLen;
- XMP_Uns32 downTwoLimit = downOnePos + downOneLen;
+ switch (boxSize)
+ {
+ case 0:
+ // A value of zero says that the box extends to the end of the file.
+ boxSize = ( maxBoxLen - boxPos ); // *** could be errorneous below top-level
+ break;
+ case 1:
+ // A value of 1 says that a 64-bit big endian size is written after the box type field, the data follows.
+ boxSize = LFA_ReadUns64_BE( file );
+ boxHeaderSize += 8;
+ break;
+ default:
+ break;
+ }
- for ( downTwoPos = (downOnePos + 8); downTwoPos < downTwoLimit; downTwoPos += downTwoLen ) {
- downTwoLen = DumpISOBoxInfo ( file, boxTag, downTwoPos, downTwoLimit, isQT );
- }
-
- tree->popNode();
+ std::string boxString( fromArgs( "%.4s" , &boxType ) );
+ // substitute mac-copyright signs with an easier-to-handle "(c)"
+ if ( boxString.at(0) == 0xA9 )
+ boxString = std::string("(c)") + boxString.substr(1);
+ isoPath = origIsoPath + boxString + "/";
- }
+ // TEMP
+ // Log::info("pushing %s, endOfThisLevel: 0x%X", isoPath.c_str(), endOfThisLevel );
- } else if ( boxTag == kQTTag_XMP_ ) {
+ tree->pushNode( isoPath );
+ tree->addComment("offset 0x%I64X, size 0x%I64X", boxPos , boxSize);
- tree->addComment ( "QuickTime XMP" );
- if ( sISOPtr_XMP != 0 ) tree->addComment ( "** Redundant QuickTime XMP **" );
-
- sISOLen_XMP = boxLen - 8;
- sISOPtr_XMP = (XMP_Uns8*) malloc ( sISOLen_XMP );
- sISOPos_XMP = LFA_Tell ( file );
- LFA_Read ( file, sISOPtr_XMP, sISOLen_XMP, true );
+ // endOfBoxPos saves the hassle of keeping the remainingSize up-to-date
+ // (which is only needed and only done, if usefull for the specific box)
+ XMP_Int64 remainingSize = boxSize - boxHeaderSize;
+ XMP_Int64 endOfBoxPos = LFA_Tell(file) + remainingSize;
- } else if ( (boxTag == kISOTag_uuid) && (boxLen >= 24) ) {
+ // ---------------------------------------------
+ // for FullBoxes:
+ XMP_Uns8 version = 255;
+ XMP_Uns32 flags = 0xFFFFFF;
- XMP_Uns8 uuid[16];
- LFA_Read ( file, &uuid, 16, true);
+ switch ( boxType )
+ {
+ // container boxes (FULL), that contain (relevant) boxes:
+ case 0x6174656D: // meta, FULLBOX
+ if ( isoPath != "moov/udta/meta/" )
+ break; //no area of interest (and navigate around some malformed files)
- tree->addComment ( "UUID" );
- for ( int i = 0; i < 16; ++i ) {
- if ( (i & 3) == 0 ) tree->addComment ( " " );
- tree->addComment ( "%.2X", uuid[i] );
- }
+ digestISOFullBoxExtension( file, isoPath, remainingSize, version, flags );
+ // intentionally: no break.
- if ( CheckBytes ( uuid, kUUID_XMP, 16 ) ) {
+ // container boxes (all non-FULL), that contain (relevant) boxes:
+ case 0x666E696D: // minf - "simple container, no direct content"
+ if ( boxString == "minf" && isoPath != "moov/trak/mdia/minf/" )
+ break;
+ case 0x6C627473: // stbl is a simple container, no direct content
+ {
+ TimeCodeTrack = false; // assume until we known better by a relevant
+ // moov/trak/mdia/minf/stbl/stsd/ of format tmcd
+ if ( boxString == "stbl" && isoPath != "moov/trak/mdia/minf/stbl/")
+ break;
+ }
+ case 0x766F6F6D: // moov
+ case 0x6169646D: // mdia
+ case 0x61746475: // udta - user data
+ case 0x6B617274: // trak - track
+ case 0x74736C69: // ilst (contains cprt box)
+ {
+ // store and restore current position to not depend
+ // on sub-level mischief...
+ bool ok;
+ XMP_Int64 keep = LFA_Tell( file );
+ DumpISOBoxes( file, remainingSize, isoPath );
+ LFA_Seek( file, keep, SEEK_SET, &ok );
+ assertMsg( "seek failed", ok );
+ }
+ break;
- tree->addComment ( "ISO Base Media XMP" );
- if ( sISOPtr_XMP != 0 ) tree->addComment ( "** Redundant ISO Base Media XMP **" );
+ // known boxes, that need content extraction
+ case 0x70797466: // ftyp - file type
+ {
+ XMP_Uns32 majorBrand = LFA_ReadUns32_LE( file );
+ XMP_Uns32 minorVersion = LFA_ReadUns32_LE( file );
+
+ //Log::info( fromArgs( "major Brand: '%.4s' (0x%.8X)" , &majorBrand, MakeUns32BE(majorBrand) ));
+ //Log::info( fromArgs( "minor Version: 0x%.8X" , MakeUns32BE(minorVersion) ) );
+ tree->setKeyValue( isoPath + "majorBrand",
+ fromArgs( "%.4s", &majorBrand ),
+ fromArgs( "0x%.8X" , MakeUns32BE(majorBrand) ) );
+ tree->setKeyValue( isoPath + "minorVersion",
+ fromArgs( "0x%.8X", MakeUns32BE(minorVersion) ) );
+
+ remainingSize -= 4 + 4;
+ //Log::info( fromArgs( "remaining Size: %d" , remainingSize ) );
+
+ while ( remainingSize >= 4 )
+ {
+ LFA_ReadUns32_LE( file );
+ // TODO: Concatenate for KeyValue...
+ //XMP_Uns32 compatVersion = LFA_ReadUns32_LE( file );
+ //Log::info( fromArgs( "compatible brand: '%.4s' (0x%.8X)" , &compatVersion, MakeUns32BE(compatVersion) ));
+ remainingSize -= 4;
+ }
- sISOLen_XMP = boxLen - 24;
- sISOPtr_XMP = (XMP_Uns8*) malloc ( sISOLen_XMP );
- sISOPos_XMP = LFA_Tell ( file );
- LFA_Read ( file, sISOPtr_XMP, sISOLen_XMP, true );
+ // odd bytes left?
+ if ( remainingSize > 0 )
+ tree->addComment( "WARNING: %d bytes left, considering FREE", remainingSize );
- } else if ( CheckBytes ( uuid, kUUID_Exif, 16 ) ) {
+ }
+ break;
+ case 0x61746164: // data (within itunes Metadata)
+ // all data atoms start with two common fields: a type indicator, and a locale indicator.
+ // each of these fields is four bytes long:
+ tree->digest32u(file, isoPath+ "type", true, true);
+ tree->digest32u(file, isoPath+ "locale", true, true);
+ remainingSize -= 8;
+ // rest is actual contents:
+ tree->digestString( file, isoPath + "value", remainingSize, false );
+ break;
+ case 0x64697575: // uuid
+ XMP_Uns8 uid[16];
+ tree->digest( file, isoPath + "uuidValue" , uid , 16 );
+ if ( ! strncmp( (const char*) kUUID_XMP, (const char*) uid, 16 ))
+ tree->addComment(" - the XMP UUID !");
- tree->addComment ( "ISO Base Media Exif" );
- if ( sISOPtr_Exif != 0 ) tree->addComment ( "** Redundant ISO Base Media Exif **" );
+ break;
- sISOLen_Exif = boxLen - 24;
- sISOPtr_Exif = (XMP_Uns8*) malloc ( sISOLen_Exif );
- sISOPos_Exif = LFA_Tell ( file );
- LFA_Read ( file, sISOPtr_Exif, sISOLen_Exif, true );
+ case 0x65657266: // free
+ tree->addComment( "free space" );
+ break;
- } else if ( CheckBytes ( uuid, kUUID_IPTC, 16 ) ) {
+ // FULL BOXES (w/o container boxes, above) **********************************
+ case 0x6468766D: // mvhd, FULLBOX, movie-header-box
+ {
+ digestISOFullBoxExtension( file, isoPath, remainingSize, version, flags );
+ if ( version == 1 )
+ {
+ tree->digest64u(file,isoPath+ "creation_time",true,true);
+ tree->digest64u(file,isoPath+ "modification_time",true,true);
+ tree->digest32u(file,isoPath+ "timescale",true,true);
+ tree->digest64u(file,isoPath+ "duration",true,true);
+ }
+ else if ( version == 0 )
+ {
+ tree->digest32u(file,isoPath+ "creation_time",true,true);
+ tree->digest32u(file,isoPath+ "modification_time",true,true);
+ tree->digest32u(file,isoPath+ "timescale",true,true);
+ tree->digest32u(file,isoPath+ "duration",true,true);
+ } else
+ {
+ tree->addComment("WARNING: unknown mvhd version!");
+ }
+ // COULDDO more fields, but not needed right now.
+ }
+ break;
+ case 0x726C6468: // hdlr - handler reference
+ {
+ if ( isoPath != "moov/trak/mdia/hdlr/")
+ break;
+ if ( remainingSize < 4*4 )
+ break; // box too small...
- tree->addComment ( "ISO Base Media IPTC" );
- if ( sISOPtr_IPTC != 0 ) tree->addComment ( "** Redundant ISO Base Media IPTC **" );
+ digestISOFullBoxExtension( file, isoPath, remainingSize, version, flags );
- sISOLen_IPTC = boxLen - 24;
- sISOPtr_IPTC = (XMP_Uns8*) malloc ( sISOLen_IPTC );
- sISOPos_IPTC = LFA_Tell ( file );
- LFA_Read ( file, sISOPtr_IPTC, sISOLen_IPTC, true );
+ tree->digestString( file, isoPath+"quickTimeType", 4, false ); // expecting: 'mhlr' - media handler
+ tree->digestString( file, isoPath+"subType", 4, false ); // expecting: 'tmcd' - timecode
+ tree->digestString( file, isoPath+"manufacturer", 4, false ); // e.g. 'appl'
- } else if ( CheckBytes ( uuid, kUUID_PSIR, 16 ) ) {
+ break; // rest doesn't bother us...
+ }
+ case 0x64737473: // stsd - timecode sample description table
+ {
+ if ( isoPath != "moov/trak/mdia/minf/stbl/stsd/")
+ break;
- tree->addComment ( "ISO Base Media PSIR" );
- if ( sISOPtr_PSIR != 0 ) tree->addComment ( "** Redundant ISO Base Media PSIR **" );
+ // version (1 byte), flags (3 byte) - must be 0
+ assertMsg("stbl: version and flags must be zero", 0 == tree->digest32u( file, "", true ) );
+ // entryCount - must be 0
+ assertMsg("stbl: at least one entry needed", 1 <= tree->digest32u( file, "", true ) );
+ remainingSize -= 8;
+
+ // only dump first occurence
+ // ensure there's enough bytes for at least on stbl:
+ if ( remainingSize < 29 )
+ break; // MPEG4/al_sbr_twi_22_1_fsaac22.mv4 has a box that is smaller than this...
+ // hence can only break, not throw
+ XMP_Int64 entrySize = (XMP_Int64) tree->digest32u( file, "", true, true );
+ std::string format = tree->digestString( file, "", 4, false ); // must be 'tmcd'
+ tree->pushNode( isoPath + format + "/" );
+ if ( format != "tmcd" )
+ {
+ tree->addComment("irrelevant node");
+ tree->popNode();
+ break; // different party...
+ }
+
+ TimeCodeTrack = true; // we're in the right track
+
+ Skip( file, 6 ); // [6] reserved bytes
+ tree->digest16u( file, isoPath+"dataReferenceIndex", true, true ); // (ignored)
+ tree->addOffset( file );
+ Skip( file, 4 ); // uint32 reserved
+ tree->digest32u( file, isoPath+"flags", true, true );
+ tree->digest32u( file, isoPath+"timeScale", true, true );
+ tree->digest32u( file, isoPath+"frameDuration", true, true );
+ Skip( file, 2 ); // skip ignored frame count, reserved
+
+ // ////////////////////////////// dig out 'trailing boxes'
+ // comparing "atom remains" vs. "entry" (probably must be '>='
+
+ // deduct the already digested...
+ entrySize -= 34; // ( 4+4+6+2+4+4+4+4+1+1 )
+ remainingSize -= 34; // (the atom-level value)
+
+ assertMsg( "entry Size must be 0 or positive", entrySize >= 0 );
+ assertMsg( "must not overreach atom", entrySize <= remainingSize );
+
+ XMP_Int64 endOfTrailingBoxes = LFA_Tell(file) + remainingSize;
+ while ( LFA_Tell(file) < endOfTrailingBoxes )
+ {
+ LFA_Tell( file );
+ DumpISOBoxes( file, entrySize, isoPath );
+ LFA_Tell( file );
+ }
- sISOLen_PSIR = boxLen - 24;
- sISOPtr_PSIR = (XMP_Uns8*) malloc ( sISOLen_PSIR );
- sISOPos_PSIR = LFA_Tell ( file );
- LFA_Read ( file, sISOPtr_PSIR, sISOLen_PSIR, true );
- }
+ assertMsg( "did not boil down to zero", LFA_Tell(file) == endOfTrailingBoxes );
- }
+ tree->popNode();
+ break;
+ }
- if ( doPop ) tree->popNode();
+ case 0x63737473: // stsc - timecode sample description table
+ {
+ if ( isoPath != "moov/trak/mdia/minf/stbl/stsc/")
+ break;
+ if ( !TimeCodeTrack )
+ {
+ tree->addComment("not tcmd -> not of interest");
+ break; // not of interest
+ }
- return boxLen;
+ // version (1 byte), flags (3 byte) - must be 0
+ assertMsg("stbl: version and flags must be zero", 0 == tree->digest32u( file, "", true ) );
+ // entryCount - must be 0
+ assertMsg("stbl: at least one entry needed", 1 <= tree->digest32u( file, "", true ) );
+ remainingSize -= 8;
-} // DumpISOBoxInfo
+ LFA_Tell( file );
-// =================================================================================================
+ tree->digest32u( file, isoPath+"firstChunkNo", true, true );
+ tree->digest32u( file, isoPath+"numSamplesPerChunk", true, true );
+ tree->digest32u( file, isoPath+"sampleDescriptionID", true, true );
+
+ break;
+ }
-static void
-DumpJPEG2000 (LFA_FileRef file, XMP_Uns32 jp2kLen)
-{
- // JPEG 2000 files are ISO base media file format,
- // ISO 14496-12, with an initial 12 byte signature.
+ case 0x6F637473: // stco - timecode sample description table
+ {
+ if ( isoPath != "moov/trak/mdia/minf/stbl/stco/")
+ break;
+ if ( !TimeCodeTrack )
+ break; // not of interest
+
+ // version (1 byte), flags (3 byte) - must be 0
+ assertMsg("stbl: version and flags must be zero", 0 == tree->digest32u( file, "", true ) );
+ // entryCount - must be 0
+ assertMsg("stbl: at least one entry needed", 1 <= tree->digest32u( file, "", true ) );
+ remainingSize -= 8;
+
+ XMP_Int64 absOffset = tree->digest32u( file, isoPath+"absFileOffset32", true, true );
+
+ // recklessly navigate to that timecode media sample, grab value, return to old position...
+ XMP_Int64 oldPos = LFA_Tell( file );
+ LFA_Seek( file, absOffset, SEEK_SET, 0 );
+ tree->digest32u( file, isoPath+"timecodeMediaSample", true, true );
+ LFA_Seek( file, oldPos, SEEK_SET, 0 );
- XMP_Uns8 buffer[4096];
- size_t bufLen;
+ }
+ case 0x34366F63: // co64 - timecode sample description table -> 64 bit offset
+ {
+ if ( isoPath != "moov/trak/mdia/minf/stbl/co64/")
+ break;
- LFA_Seek ( file, 12, SEEK_SET );
- bufLen = LFA_Read ( file, buffer, sizeof(buffer), false);
+ tree->digest64u( file, isoPath+"absFileOffset64", true, true );
+ break;
+ }
- XMP_Uns32 ftypLen = DumpISOFileType ( buffer, bufLen );
+ case 0x74727063: // cprt, FULLBOX
+ if ( isoPath == "moov/udta/cprt/")
+ {
- XMP_Uns32 boxPos, boxLen;
+ digestISOFullBoxExtension( file, isoPath, remainingSize, version, flags );
- for ( boxPos = ftypLen+12; boxPos < jp2kLen; boxPos += boxLen ) {
- boxLen = DumpISOBoxInfo ( file, 0, boxPos, jp2kLen );
- }
+ // 1/8 byte ISO language padding = 1-bit value set to 0
+ // 1 7/8 bytes content language = 3 * 5-bits ISO 639-2 language code less 0x60
+ // - example code for english = 0x15C7
+ tree->digest16u(file,isoPath+"content language",true,true);
+ tree->addComment("(0x15C7 == english)");
+ // zero-terminated, actual string:
+ tree->digestString( file, isoPath+"value", 0 );
+ } else
+ {
+ // ISO - copyright (?)
+ // a container box, hunt for 'data' atom by recursion:
+ bool ok;
+ XMP_Int64 keep = LFA_Tell( file );
+ DumpISOBoxes( file, remainingSize, isoPath );
+ LFA_Seek( file, keep, SEEK_SET, &ok );
+ assertMsg( "seek failed", ok );
+ }
+ break;
+ case 0x64686B74: // tkhd, FULLBOX
+ case 0x6E61656D: // mean, FULLBOX
+ case 0x656D616E: // name, FULLBOX
+ {
+ if ( isoPath == "moov/trak/mdia/minf/stbl/stsd/name/" ) // this regrettably is a diffrent animal (international text sequence)
+ digestInternationalTextSequence( file, isoPath, &remainingSize );
+ else
+ digestISOFullBoxExtension( file, isoPath, remainingSize, version, flags );
+ }
+ break;
- if ( sISOPtr_Exif != 0 ) {
- DumpTIFF ( sISOPtr_Exif, sISOLen_Exif, sISOPos_Exif, "JPEG 2000 Exif" );
- }
+ // (c)-style quicktime boxes and boxes of no interest:
+ default:
+ if ( (boxType & 0xA9) == 0xA9) // (c)something
+ {
+ if ( 0 == isoPath.compare( 0 , 20, "moov/udta/meta/ilst/"))
+ { // => iTunes metadata (hunt for data childs)
+ // a container box, hunt for 'data' atom by recursion:
+ bool ok;
+ XMP_Int64 keep = LFA_Tell( file );
+ DumpISOBoxes( file, remainingSize, isoPath );
+ LFA_Seek( file, keep, SEEK_SET, &ok );
+ assertMsg( "seek failed", ok );
+ }
+ else if ( 0 == isoPath.compare( 0 , 10, "moov/udta/" ))
+ { // => Quicktime metadata "international text sequence" ( size, language code, value )
+ digestInternationalTextSequence( file, isoPath, &remainingSize );
+ } else
+ {
+ tree->addComment("WARNING: unknown flavor of (c)*** boxes, neither QT nor iTunes");
+ }
+ break;
+ }
+ //boxes of no interest:
- if ( sISOPtr_IPTC != 0) {
- DumpIPTC ( sISOPtr_IPTC, sISOLen_IPTC, sISOPos_IPTC, "JPEG 2000 IPTC" );
- }
+ break;
+ }
- if ( sISOPtr_PSIR != 0 ) {
- DumpImageResources ( sISOPtr_PSIR, sISOLen_PSIR, sISOPos_PSIR, "JPEG 2000 PSIR" );
- }
+ bool ok;
+ LFA_Seek( file, endOfBoxPos, SEEK_SET, &ok );
+ assertMsg("End-of-Box Seek failed (truncated file?)", ok );
- if ( sISOPtr_XMP != 0 ) {
- DumpXMP ( sISOPtr_XMP, sISOLen_XMP, sISOPos_XMP, "JPEG 2000 XMP" );
- }
+ tree->popNode();
- CleanISOLegacy();
-} // DumpJPEG2000
+ } // while
-// =================================================================================================
+} // DumpISOBoxes()
// attempt to combine dumping of mpeg-4 and quicktime (mov) into one routine...
static void
-DumpISO ( LFA_FileRef file, XMP_Uns32 fileLen, bool isQT )
+DumpISO ( LFA_FileRef file, XMP_Uns32 fileLen )
{
- // MPEG-4
- // uses the ISO base media file format, ISO 14496-12. The base structure is a sequence of
- // boxes, a box can have nested boxes. The box structure is:
- // A big endian UInt32 box size, the offset to the following box
- // A big endian UInt32 box type, generally 4 ASCII characters
- // The box data
- // A box size of 0 means the last box in the file. If the box size is 1 a big endian UInt64 size
- // is present between the box type and data. A common subtype is a UUID box. These have a type
- // of uuid, followed by a 16 byte UUID, followed by other box data.
- //
- // The XMP is in a top level UUID box with the UUID BE7ACFCB 97A942E8 9C719994 91E3AFAC.
-
- // MOV
- // A QuickTime file happens to be identical to the ISO base media file format at the raw level.
- // * The chunks are called "atoms" instead of "boxes"
- // * A leading 'ftyp' atom is not required.
-
- XMP_Uns32 boxPos = 0; //assume null (presence of ftyp box will correct)
- XMP_Uns32 boxLen;
-
- // TODO: make obsolete
- XMP_Uns8 buffer[4096];
- size_t bufLen;
- LFA_Seek ( file, 0, SEEK_SET );
- bufLen = LFA_Read ( file, buffer, sizeof(buffer) );
-
- //peek at first 4 bytes
- Rewind(file);
- Skip(file,4);
-
- XMP_Uns32 temp = tree->digest32u( file , "" , true, true );
- bool hasFtyp = ( kISOTag_ftyp == temp);
- Rewind(file,4);
-
- //in Quicktime, a leading 'ftyp' atom is optional,
- //in MPEG-4 it is mandatory:
- if (! isQT)
- assertMsg("MPEG-4 File must have a first 'ftyp' box!", hasFtyp );
-
- // if existing, go parse it:
- if( hasFtyp )
- boxPos = DumpISOFileType ( buffer, bufLen );
-
- // iterate rest of boxes
- for ( ; boxPos < fileLen; boxPos += boxLen ) {
- boxLen = DumpISOBoxInfo ( file, 0, boxPos, fileLen, isQT);
- }
-
- // final memory test
- assertMsg("Unexpected Exif (somewhere in file)", sISOPtr_Exif == 0);
- assertMsg("Unexpected IPTC (somewhere in file)", sISOPtr_IPTC == 0);
- assertMsg("Unexpected PSIR (somewhere in file)", sISOPtr_PSIR == 0);
+ TimeCodeTrack = false;
- CleanISOLegacy(); //free's some memory
+ // see specificition at https://zerowing.corp.adobe.com/display/XMP/Embedding+Spec+MPEG4
+ DumpISOBoxes(file, fileLen, "" );
+ assertMsg("truncated file/last box reached beyond end?", LFA_Tell(file) == fileLen );
}
// =================================================================================================
@@ -2397,12 +2477,12 @@ static void DumpASFContentEncryption (LFA_FileRef file, XMP_Uns32 objOffset, XMP
LFA_Read ( file, &fieldSize, 4, true);
fieldSize = GetUns32LE (&fieldSize);
CaptureFileData (file, 0, fieldSize);
- PrintOnlyASCII_8 (sDataPtr, fieldSize, "Key ID");
+ PrintOnlyASCII_8 (sDataPtr, fieldSize);
LFA_Read ( file, &fieldSize, 4, true);
fieldSize = GetUns32LE (&fieldSize);
CaptureFileData (file, 0, fieldSize);
- PrintOnlyASCII_8 (sDataPtr, fieldSize, "License URL");
+ PrintOnlyASCII_8 (sDataPtr, fieldSize);
} // DumpASFContentEncryption
@@ -2830,171 +2910,314 @@ DumpUCF(LFA_FileRef file, XMP_Int64 len)
tree->setKeyValue("file data","",fromArgs("skipping %u bytes",compressed_size));
Skip(file,compressed_size);
}
-
-
-
-
tree->popNode();
}
- /////////////////////////////////////////////////////////////////////
- // :
tree->pushNode("");
tree->popNode();
-
- /////////////////////////////////////////////////////////////////////
- // :
-
- /////////////////////////////////////////////////////////////////////
- // :
-
- /////////////////////////////////////////////////////////////////////
- // :
-
-
} // DumpUCF
// =================================================================================================
-static const char * kRIFF_Lists[] = { "INFO", "hdrl", "Tdat", "Cdat", 0 };
-
-static const char * kRIFF_LegacyText[] =
+// AVI and WAV files are RIFF based. Although they have usage differences, we can have a common
+// dumper. This might need changes for other RIFF-based files, e.g. for specific legacy
+// metadata. RIFF is a chunky format. AVI and WAV have an outermost RIFF chunk. The XMP is in a
+// top level "_PMX" chunk. Legacy metadata for WAV is in a top level LIST/INFO chunk. Legacy
+// metadata for AVI is in a variety of places, don't have specs at present. Free space can be
+// JUNK or JUNQ.
+//
+// A RIFF chunk contains:
+// - A 4 byte chunk ID, typically ASCII letters
+// - A little endian UInt32 size of the chunk data
+// - The chunk data
+// - Pad byte if the chunk data length is odd (added on 2007-03-22)
+
+// The ID is written in "reading order", e.g. the 'R' in "RIFF" is first in the file. Chunks
+// must start on even offsets. A pad byte of 0 is written after the data if necessary. The size
+// does not include the pad, nor the ID and size fields. Some chunks contain nested chunks,
+// notably the RIFF and LIST chunks do. These have the layout:
+// - A 4 byte chunk ID, typically ASCII letters
+// - A little endian UInt32 size of the chunk data
+// - A 4 byte usage ID, typically ASCII letters
+// - The nested chunks
+
+// reads maxSize bytes from file (not "up to", exactly fullSize)
+// puts it into a string, sets respective tree property
+static void setFixedBEXTField ( LFA_FileRef file, std::string propName, XMP_Int64 fullSize )
{
- // INFO list items, taken from Exif 2.2, ASCII with terminating nul:
- "IARL", "IART", "ICMS", "ICMT", "ICOP", "ICRD", "ICRP", "IDIM", "IDPI", "IENG", "IGNR",
- "IKEY", "ILGT", "IMED", "INAM", "IPLT", "IPRD", "ISBJ", "ISFT", "ISHP", "ISRC", "ISRF", "ITCH",
- // AVI legacy items, taken from AVI handler code:
- "ISMT", "tc_O", "tc_A", "rn_O", "rn_A", "cmnt",
- // WAV legacy items, taken from WAV handler code:
- "DISP",
- // Final sentinel
- 0
-};
-
-// =================================================================================================
+ char* descriptionBuffer = new char[ fullSize + 2 ];
+ LFA_Read( file, descriptionBuffer, fullSize, true );
+ descriptionBuffer[fullSize]='\0'; // tack on, in case not contained
+ // parse till first \0
+ std::string description( descriptionBuffer );
+ tree->setKeyValue( propName, description) ;
+ delete[] descriptionBuffer;
+}
-static bool
-IsKnownRIFFList ( const char * listID )
+static void
+DumpRIFFChunk ( LFA_FileRef file, XMP_Int64 parentEnd, std::string origChunkPath )
{
- for ( size_t i = 0; kRIFF_Lists[i] != 0; ++i ) {
- if ( strncmp ( listID, kRIFF_Lists[i], 4 ) == 0 ) return true;
- }
-
- return false;
-
-} // IsKnownRIFFList
-// =================================================================================================
+ while ( LFA_Tell(file) < parentEnd )
+ {
+ bool isOutermost = origChunkPath.empty();
-static bool
-IsKnownRIFFLegacy ( const char * chunkID )
-{
- for ( size_t i = 0; kRIFF_LegacyText[i] != 0; ++i ) {
- if ( strncmp ( chunkID, kRIFF_LegacyText[i], 4 ) == 0 ) return true;
- }
+ XMP_Int64 chunkPos= LFA_Tell( file );
+ XMP_Uns32 chunkID = tree->digest32u( file, "", false );
+ std::string idString( fromArgs( "%.4s" , &chunkID ) );
+ XMP_Int64 chunkSize = tree->digest32u( file, "", false ) // NB: XMPInt64 <- XMPUns32
+ + 8; //adding size of id and length field itself
- return false;
+ // calculate size if size field seems broken
+ if (chunkSize > parentEnd)
+ chunkSize = parentEnd - chunkPos;
-} // IsKnownRIFFLegacy
+ std::string chunkPath = isOutermost ? ( idString ) : (origChunkPath + "/" + idString);
+
+ // check special case of trailing bytes not in a valid RIFF structure
+ if ( isOutermost && idString != "RIFF")
+ {
+ //dump undefined bytes till the end of the file
+ tree->pushNode("** unknown bytes **");
+ tree->addOffset( file );
+ chunkSize = parentEnd - chunkPos; // get size through calculation (and not from size bytes)
+ tree->addComment("size: 0x%llX", chunkSize );
+ Skip( file, chunkSize-8 ); // Already read the 8 byte header.
+ tree->popNode();
+ }
+ else
+ {
+ // only RIFF and LIST contain subchunks...
+ bool hasSubChunks = (idString == "RIFF") || (idString == "LIST");
+ bool skipper=false;
+ if (hasSubChunks)
+ {
+ //get inner ID 'type' as in 'listType', 'fileType', ...
+ XMP_Uns32 chunkType = tree->digest32u( file );
+ std::string typeString( fromArgs( "%.4s" , &chunkType ) );
+
+ if ( isOutermost )
+ assertMsg("level-0 chunk must be AVI, AVIX or WAVE",
+ ( typeString == "AVI " ) || ( typeString == "AVIX" ) || ( typeString == "WAVE" ) );
+
+ chunkPath = chunkPath + ":" + typeString;
+
+ tree->pushNode( chunkPath );
+ tree->addComment("offset 0x%llX, size 0x%llX", chunkPos , chunkSize);
+
+ if ( ( idString + ":" + typeString == "LIST:INFO" ) ||
+ ( idString + ":" + typeString == "LIST:Tdat" ) ||
+ ( idString + ":" + typeString == "RIFF:AVI " ) ||
+ ( idString + ":" + typeString == "RIFF:AVIX" ) ||
+ ( idString + ":" + typeString == "RIFF:WAVE" ) ||
+ ( idString + ":" + typeString == "LIST:hdrl" ) ||
+ ( idString + ":" + typeString == "LIST:strl" ) ||
+ ( idString + ":" + typeString == "LIST:movi" )
+ )
+ {
+ DumpRIFFChunk( file, LFA_Tell(file) + chunkSize - 12, chunkPath ); // recurse!
+ }
+ else
+ {
+ Skip( file, chunkSize - 12 ); // skip it !
+ }
+ tree->popNode();
+ }
+ else if (idString.length() == 4) // check that we got a valid idString
+ {
+
+ // now that LIST:movi gets dumped,
+ // skip some very frequent, irrelevant chunks,
+ // otherwise the dump becomes unusably long...
+ std::string firstTwo = idString.substr(0,2);
+ std::string secondTwo = idString.substr(2,2);
+ if ( secondTwo == "db" || secondTwo == "dc" || secondTwo == "wb" ) // nb: _could_ colidde, requiring additional numeric test
+ {
+ skipper = true;
+ }
+
+ if ( ! skipper )
+ {
+ tree->pushNode( chunkPath );
+ //Log::info( chunkPath );
+ tree->addComment("offset 0x%llX, size 0x%llX", chunkPos , chunkSize);
+ }
-// =================================================================================================
+ // tackle chunks of interest //////////////////////////////////////////////
+ bool isListInfo =
+ ( (origChunkPath == "RIFF:WAVE/LIST:INFO" || origChunkPath == "RIFF:AVI /LIST:INFO" )
+ && idString.at(0) == 'I' ); // so far all mapping relevant props begin with "I"
-static XMP_Uns32
-DumpRIFFChunk ( LFA_FileRef file, XMP_Uns32 chunkOffset, XMP_Uns32 parentEnd, size_t nesting )
-{
- // AVI and WAV files are RIFF based. Although they have usage differences, we can have a common
- // dumper. This might need changes for other RIFF-based files, e.g. for specific legacy
- // metadata. RIFF is a chunky format. AVI and WAV have an outermost RIFF chunk. The XMP is in a
- // top level "_PMX" chunk. Legacy metadata for WAV is in a top level LIST/INFO chunk. Legacy
- // metadata for AVI is in a variety of places, don't have specs at present. Free space can be
- // JUNK or JUNQ.
- //
- // A RIFF chunk contains:
- // - A 4 byte chunk ID, typically ASCII letters
- // - A little endian UInt32 size of the chunk data
- // - The chunk data
- // - Pad byte if the chunk data length is odd (added on 2007-03-22)
-
- // The ID is written in "reading order", e.g. the 'R' in "RIFF" is first in the file. Chunks
- // must start on even offsets. A pad byte of 0 is written after the data if necessary. The size
- // does not include the pad, nor the ID and size fields. Some chunks contain nested chunks,
- // notably the RIFF and LIST chunks do. These have the layout:
- // - A 4 byte chunk ID, typically ASCII letters
- // - A little endian UInt32 size of the chunk data
- // - A 4 byte usage ID, typically ASCII letters
- // - The nested chunks
+ bool isListTdat = (origChunkPath == "RIFF:WAVE/LIST:Tdat" || origChunkPath == "RIFF:AVI /LIST:Tdat")
+ && idString.at(0) != 'J' ; // just exclude JUNK/Q
- LFA_Seek ( file, chunkOffset, SEEK_SET );
- char chunkID[4];
- LFA_Read ( file, chunkID, 4, true );
+ bool isDispChunk =
+ ( ( origChunkPath == "RIFF:WAVE" || origChunkPath == "RIFF:AVI ")
+ && idString == "DISP" );
- XMP_Uns32 chunkLen = tree->digest32u(file);
+ bool isBextChunk =
+ ( ( origChunkPath == "RIFF:WAVE" || origChunkPath == "RIFF:AVI ")
+ && idString == "bext" );
- bool isRIFF = CheckBytes ( chunkID, "RIFF", 4 );
- bool isLIST = CheckBytes ( chunkID, "LIST", 4 );
+ bool isXMPchunk = false; //assume beforehand
+ if ( idString == "_PMX" )
+ { // detour first, to detect xmp in wrong places
+ assertMsg( "XMP packet found in wrong place!",
+ ( origChunkPath == "RIFF:WAVE" || "RIFF:AVI" || "RIFF:AVIX" ) ); //be very linient here.
+ isXMPchunk = true;
+ }
- if ( isRIFF | isLIST ) {
- char subID[4];
- LFA_Read ( file, subID, 4, true );
+ // deal with chunks of interest /////////////////////////////////////////////
+ // a little prelude for disp chunk
+ if ( isDispChunk )
+ {
+ XMP_Uns32 dispChunkType = LFA_ReadUns32_LE(file);
+ // only dispChunks starting with a 0x0001 are of interest to us.
+ // others do exist and are not an error
- bool knownList = false;
- if ( isLIST ) knownList = IsKnownRIFFList ( subID );
+ if ( dispChunkType != 0x0001 )
+ isDispChunk = false;
- tree->pushNode ( "%.4s:%.4s" , chunkID, subID );
- tree->addComment ( "offset 0x%X, size %d%s",
- chunkOffset, chunkLen, (((chunkLen & 1) == 0) ? "" : " (has pad byte)") );
+ chunkSize -= 4;
+ }
- if ( (chunkOffset + chunkLen) > parentEnd ) {
- tree->addComment ( "Chunk too long, extends beyond parent" );
- }
+ if ( isListInfo || isListTdat || isDispChunk )
+ {
+ // dump that string:
+ std::string value;
- if ( (isRIFF && (nesting == 0)) || (knownList && (nesting == 1)) ) {
+ if ( chunkSize > 8 ) // aka skip for empty chunks
+ {
+ // first check if the string is zero terminated
+ LFA_Seek( file , chunkSize - 8 - 1, SEEK_CUR ); // jump to last char
+ bool zeroTerm = (LFA_ReadUns8( file ) == 0);
+ LFA_Seek( file , -(chunkSize - 8 ), SEEK_CUR ); //jump back
+ // some strings are zero-terminated (so despite initial length they are "c-strings"
+ // others are not ( "pascal strings" if you will.
+ // must cater to both: zero-terminated-ness should not affect resulting value.
+
+ // Samy: also dump out the zero termination. Needed for testing
+
+ if (zeroTerm)
+ {
+ // read string without zero (last char)
+ value = tree->digestString( file, "" , chunkSize - 8 - 1, false );
+ tree->addComment(" zero terminated");
+ LFA_ReadUns8( file ); // skip the zero
+ }
+ else
+ {
+ // read string including last char
+ value = tree->digestString( file, "" , chunkSize - 8 , false );
+ tree->addComment(" not zero terminated");
+ }
+ tree->changeValue( value );
+ }
- XMP_Uns32 childLimit = chunkOffset + 8 + chunkLen;
- XMP_Uns32 childOffset, childLen;
+ tree->changeValue( value );
+ }
+ else if ( isXMPchunk )
+ {
+ tree->pushNode("XMP packet");
+
+ tree->addOffset( file );
+ tree->addComment("packet size: 0x%llX", chunkSize - 8 );
+ Skip( file, chunkSize - 8 );
+ tree->addComment("packet end: 0x%llX", LFA_Tell( file ) );
+
+ tree->popNode();
+ }
+ else if ( isBextChunk )
+ {
+ tree->pushNode("bext chunk");
+ tree->addOffset( file );
+ tree->addComment("packet size: 0x%llX", chunkSize - 8 );
+
+ // I assume that the minimum BEXT chunk size is 602:
+ // > 8 + ( 256+32+32+10+8+4+4+2+64+190+0 )
+ // ans = 610
+ const XMP_Int64 MIN_BEXT_SIZE = 610;
+ assertMsg("minimum Berx Chunk Size", chunkSize >= MIN_BEXT_SIZE );
+ XMP_Int64 BEXT_CodingHistorySize = chunkSize - MIN_BEXT_SIZE;
+
+ setFixedBEXTField ( file, chunkPath+".Description" , 256 );
+ setFixedBEXTField ( file, chunkPath+".Originator" , 32 );
+ setFixedBEXTField ( file, chunkPath+".OriginatorReference" , 32 );
+ setFixedBEXTField ( file, chunkPath+".OriginationDate" , 10 );
+ setFixedBEXTField ( file, chunkPath+".OriginationTime" , 8 );
+
+ tree->digest32u( file, chunkPath+".TimeReferenceLow", false, true ); // DWORD == 32 Bit
+ tree->digest32u( file, chunkPath+".TimeReferenceHigh", false, true ); // DWORD == 32 Bit
+
+ tree->digest16u( file, chunkPath+".Version", false, true );
+
+ // UMID has 64 bytes:
+ tree->digestString(file, chunkPath+".UMID",64);
+ //tree->digest32u( file, chunkPath+".UMID_0-4", false, true );
+ //tree->setKeyValue( "UMID_5-59" );
+ //Skip( file, 64 - 4 - 4 );
+ //tree->digest32u( file, chunkPath+".UMID_60-63", false, true );
+
+ tree->setKeyValue( chunkPath+".Reserved" );
+ Skip( file, 190 );
+
+ if ( BEXT_CodingHistorySize )
+ {
+ setFixedBEXTField ( file, chunkPath+".CodingHistory" , BEXT_CodingHistorySize );
+
+ //tree->setKeyValue( chunkPath+".CodingHistory" ); // not bothering details.
+ tree->addComment( "( 0x%llx bytes ) ", BEXT_CodingHistorySize );
+ //Skip( file, BEXT_CodingHistorySize );
+ }
- for ( childOffset = (chunkOffset + 12); childOffset < childLimit; childOffset += (8 + childLen) ) {
- childLen = DumpRIFFChunk ( file, childOffset, childLimit, nesting+1 );
+ tree->addComment("packet end: 0x%llX", LFA_Tell( file ) );
+ tree->popNode();
+ }
+ else
+ {
+ Skip( file, chunkSize - 8 ); // skip remainder of chunk ( id, length already digested )
+ assertMsg( fromArgs( "inner chunk size too big, curPos:0x%llx, parentEnd:0x%llx",
+ LFA_Tell(file),
+ parentEnd ),
+ LFA_Tell(file) <= parentEnd );
+ }
+
+ if ( ! skipper )
+ tree->popNode();
}
-
- if ( childOffset != childLimit ) {
- tree->addComment ( "Unexpected end to nested chunks at offset %u (0x%X)", childOffset, childOffset );
+ else
+ {
+ //dump undefined bytes in LIST
+ tree->pushNode("** unknown bytes **");
+ tree->addOffset( file );
+ tree->addComment("size: 0x%llX", chunkSize );
+ Skip( file, chunkSize - 8 );
+ tree->popNode();
}
- }
- tree->popNode();
- } else {
- tree->pushNode ( "%.4s",chunkID );
- tree->addComment ( "offset 0x%X, size %d%s",
- chunkOffset, chunkLen, (((chunkLen & 1) == 0) ? "" : " (has pad byte)") );
-
- if ( (chunkLen > 0) && IsKnownRIFFLegacy (chunkID) ) {
- CaptureFileData ( file, (chunkOffset + 8), chunkLen );
- std::string legacyField = convert8Bit ( sDataPtr, false, chunkLen );
- tree->changeValue ( legacyField.substr(0,30) + ((legacyField.size()>30)?"...":"") );
- }
- if ( (chunkLen > 0) && CheckBytes ( chunkID, "_PMX", 4 ) ) {
- tree->pushNode( "XMP packet" );
+ if ( LFA_Tell(file) % 2 == 1 ) // if odd file position, add pad byte.
{
- tree->addComment( "xmp at offset 0x%X, length 0x%X", (chunkOffset + 8), chunkLen );
+ XMP_Uns8 padByte = LFA_ReadUns8( file );
+ if (!skipper)
+ {
+ if ( 0 != padByte )
+ tree->addComment(" (non-zero pad byte!)");
+ else
+ tree->addComment(" (pad byte)");
+ // Samy: add pad byte comment
+ }
}
- tree->popNode();
}
- tree->popNode();
- }
+ } // while
- return (chunkLen + (chunkLen & 1)); // Account for the pad byte.
} // DumpRIFFChunk
// =================================================================================================
static void
-DumpRIFF ( LFA_FileRef file, XMP_Uns32 fileLen )
+DumpRIFF ( LFA_FileRef file, XMP_Int64 fileLen )
{
- XMP_Uns32 riffLen = DumpRIFFChunk ( file, 0, fileLen, 0 );
- riffLen += 8; // Include the chunk header.
-
- assertMsg( fromArgs("Unexpected end to RIFF at offset %d (0x%X)", riffLen, riffLen), riffLen == fileLen );
+ DumpRIFFChunk ( file, fileLen, "" );
}
// =================================================================================================
@@ -3096,7 +3319,7 @@ DumpPNGChunk ( LFA_FileRef file, XMP_Uns32 pngLen, XMP_Uns32 chunkOffset )
XMP_Uns8 * keywordPtr = sDataPtr;
size_t keywordLen = strlen ((char*)keywordPtr);
- PrintOnlyASCII_8 ( keywordPtr, keywordLen, "," );
+ PrintOnlyASCII_8 ( keywordPtr, keywordLen);
if ( (chunkType == kPNG_iTXt) && (keywordLen == 17) && CheckBytes (keywordPtr, "XML:com.adobe.xmp", 18) ) {
@@ -3501,7 +3724,14 @@ static XMP_Uns32 DumpScriptDataValue ( LFA_FileRef file, XMP_Uns32 sdOffset, boo
ReadSDValue ( 2 );
u16 = GetUns16BE ( &buffer[0] );
ReadSDValue ( u16 );
- tree->addComment ( "string (%d) = \"%.*s\"", u16, u16, buffer );
+ //Samy: restrict to string under 4k. This was crashing for large XMP packets before, because addComment
+ // uses a fixed buffer with the size 4096
+ if (int(u16) < 4096 )
+ {
+ tree->addComment ( "string (%d) = \"%.*s\"", u16, u16, buffer );
+ } else {
+ tree->addComment ( "string (%d) ", u16 );
+ }
if ( buffer[u16-1] != 0 ) tree->addComment ( "value lacks trailing nul" );
if ( isOnXMP ) CaptureXMPF ( file, (sdOffset+1+2), u16 );
return (sdOffset + 1 + 2 + u16);
@@ -3920,29 +4150,25 @@ PrintID3Encoding ( XMP_Uns8 encoding, XMP_Uns8 * strPtr )
{
bool bigEndian = true;
- tree->addComment ( "encoding %d", encoding );
-
switch ( encoding ) {
case 0 :
- tree->addComment ( " (ISO Latin-1)" );
+ tree->addComment ( "Latin1" );
break;
case 1 :
if ( *strPtr == 0xFF ) bigEndian = false;
- tree->addComment ( " (UTF-16 with BOM, %s endian)", (bigEndian ? "big" : "little") );
+ tree->addComment ( "UTF-16 with BOM, %s)", (bigEndian ? "BE" : "LE") );
break;
case 2 :
- tree->addComment ( " (UTF-16 big endian)" );
+ tree->addComment ( "UTF-16 BE" );
break;
case 3 :
- tree->addComment ( " (UTF-8)" );
+ tree->addComment ( "UTF-8" );
break;
default :
- tree->addComment ( " (** unknown **)" );
+ tree->addComment ( "** unknown **" );
break;
}
-
return bigEndian;
-
} // PrintID3Encoding
// =================================================================================================
@@ -3975,19 +4201,44 @@ static void
DumpMP3 (LFA_FileRef file, XMP_Uns32 /*mp3Len*/)
{
// ** We're ignoring the effects of the unsync flag, and not checking the CRC (if present).
-
assert (sizeof(ID3_Header) == 10);
assert (sizeof(ID3_FrameHeader) == 10);
- // Dump the basic ID3 header.
+ // Detect ID3v1 header:
+ if ( LFA_Measure( file ) > 128 )
+ {
+ LFA_Seek( file, -128, SEEK_END );
+ XMP_Uns32 tagID = 0xFFFFFF00 & LFA_ReadUns32_BE( file );
+ if ( tagID == 0x54414700 ) // must be "TAG"
+ {
+ // Dump ID3v1 header:
+ tree->pushNode("ID3v1");
+ Rewind( file, 1 ); // read one byte too many...
+
+ tree->digestString( file, "ID3v1:title", 30, false, true );
+ tree->digestString( file, "ID3v1:artist", 30, false, true );
+ tree->digestString( file, "ID3v1:album", 30, false, true );
+ tree->digestString( file, "ID3v1:year", 4, false, true );
+ tree->digestString( file, "ID3v1:comment", 30, false, true );
+ tree->digest( file, "ID3v1:genreNo", 0, 1 );
+
+ // ID3v1.1 trackNo byte dance:
+ Rewind( file, 3);
+ LFA_Tell( file );
+ if ( LFA_ReadUns8( file ) == 0 )
+ tree->digest( file, "ID3v1:trackNo", 0, 1 );
- ID3_Header id3Head;
+ tree->popNode();
+ }
+ }
+ // Dump ID3v2 header:
+ ID3_Header id3Head;
LFA_Seek (file, 0, SEEK_SET);
LFA_Read ( file, &id3Head, sizeof(id3Head), true);
if (! CheckBytes (id3Head.id3, "ID3", 3)) {
- tree->addComment(" No ID3v2 section");
+ tree->setKeyValue("No ID3v2 tag");
return;
}
@@ -3995,8 +4246,8 @@ DumpMP3 (LFA_FileRef file, XMP_Uns32 /*mp3Len*/)
XMP_Uns32 framePos = sizeof(id3Head); // The offset of the next (first) ID3 frame.
XMP_Uns32 frameEnd = framePos + id3Len;
- tree->addComment(" ID3v2.%d.%d, size %d, flags 0x%.2X",
- id3Head.vMajor, id3Head.vMinor, id3Len, id3Head.flags);
+ tree->pushNode("ID3v2.%d.%d, size %d, flags 0x%.2X",id3Head.vMajor, id3Head.vMinor, id3Len, id3Head.flags);
+
if (id3Head.flags != 0) {
tree->addComment("%s%s%s%s",
((id3Head.flags & 0x80) ? ", unsync" : ""),
@@ -4005,22 +4256,21 @@ DumpMP3 (LFA_FileRef file, XMP_Uns32 /*mp3Len*/)
((id3Head.flags & 0x10) ? ", has footer" : ""));
}
-
if ((id3Head.vMajor != 3) && (id3Head.vMajor != 4)) {
tree->addComment(" ** Unrecognized major version tree.");
+ tree->popNode();
return;
}
bool hasExtHeader = ((id3Head.flags & 0x40) != 0);
// Dump the extended header if present.
-
if (hasExtHeader) {
-
XMP_Uns32 extHeaderLen;
- LFA_Read ( file, &extHeaderLen, 4, true );
- extHeaderLen = GetID3Size (id3Head.vMajor, &extHeaderLen);
+
+ extHeaderLen = tree->digest32u(file);
+ extHeaderLen = GetID3Size (id3Head.vMajor, &extHeaderLen);
framePos += (4 + extHeaderLen);
if (id3Head.vMajor == 3) {
@@ -4035,81 +4285,94 @@ DumpMP3 (LFA_FileRef file, XMP_Uns32 /*mp3Len*/)
frameEnd -= padLen;
- tree->addComment(" Extended header size %d, flags 0x%.4X, pad size %d",
+ tree->pushNode("Extended header MajorV3 size %d, flags 0x%.4X, pad size %d",
extHeaderLen, extHeaderFlags, padLen);
if (extHeaderFlags & 0x8000) {
XMP_Uns32 crc;
LFA_Read ( file, &crc, 4, true );
crc = GetUns32BE (&crc);
- tree->addComment("CRC 0x%.8X", crc);
+ tree->setKeyValue( "CRC" , fromArgs("0x%.8X", crc) );
}
+ tree->popNode();
} else if (id3Head.vMajor == 4) {
XMP_Uns8 flagCount;
LFA_Read ( file, &flagCount, 1, true );
- tree->addComment(" Extended header size %d, flag count %d", extHeaderLen, flagCount);
+ tree->pushNode("Extended header MajorV4 size %d, flag count %d", extHeaderLen, flagCount);
for (size_t i = 0; i < flagCount; ++i) {
XMP_Uns8 flag;
LFA_Read ( file, &flag, 1, true );
- tree->addComment("0x%.2X", flag);
+ tree->setKeyValue( fromArgs( "Flag %.2d", flag ) , fromArgs( "0x%.2X", flag ) );
}
-
+ tree->popNode();
+ } else
+ { // ADDED
+ tree->addComment("unknown major version !");
}
}
- // Dump the ID3 frames.
-
+ ////////////////////////////////////////////////////
+ // Dump the ID3 frames
while (framePos < frameEnd) {
-
if ((frameEnd - framePos) < 10) break; // Assume into ID3v2.4 padding.
ID3_FrameHeader frameHead;
LFA_Seek (file, framePos, SEEK_SET);
LFA_Read ( file, &frameHead, sizeof(frameHead), true);
- if (CheckBytes (frameHead.id, "\x0\x0\x0\x0", 4)) break; // Assume into ID3v2.4 padding.
+ if (CheckBytes (frameHead.id, "\x0", 1))
+ break; // Assume into ID3v2.4 padding.
+ // FIXED: there could be just 1 or 2 or 3 bytes of padding total !!
frameHead.size = GetID3Size (id3Head.vMajor, &frameHead.size);
frameHead.flags = GetUns16BE (&frameHead.flags);
- tree->addComment(" '%.4s', offset %d (0x%X), size %d, flags 0x%.2X",
- frameHead.id, framePos, framePos, frameHead.size, frameHead.flags);
-
- if (frameHead.id[0] == 'T') {
+ tree->setKeyValue(
+ fromArgs( "ID3v2:%.4s",frameHead.id),
+ "", //no value yet, tree->changeValue() below
+ fromArgs( "offset %d (0x%X), size %d, flags 0x%.2X",
+ framePos, framePos, frameHead.size, frameHead.flags)
+ );
+ if ( frameHead.size == 0 )
+ {
+ // NOTHING TO DO HERE.
+ // i.e. on 0-byte frames, including known ones...
+ // ( i.e. the testcase of a (errorneous) TCON 0 byte frame )
+ }
+ else if ( (frameHead.id[0] == 'T') ||(frameHead.id[0] == 'W')) { // Text and URL fields
CaptureFileData (file, 0, frameHead.size);
- XMP_Uns8 encoding = sDataPtr[0];
-
- bool bigEndian = PrintID3Encoding (encoding, (sDataPtr + 1));
+ XMP_Uns8 encoding = 0;
+ XMP_Uns8 skip = 0;
+ if ( frameHead.id[0] == 'T' ) // URL field has no encoding byte
+ {
+ encoding = sDataPtr[0];
+ skip = 1;
+ }
+ bool bigEndian = PrintID3Encoding (encoding, (sDataPtr + skip));
if ((encoding == 0) || (encoding == 3)) {
- PrintOnlyASCII_8 ((sDataPtr + 1), (frameHead.size - 1), " ");
+ tree->changeValue( convert8Bit( sDataPtr + skip, false, frameHead.size-skip ) );
} else if ((encoding == 1) || (encoding == 2)) {
- XMP_Uns16 * valuePtr = (XMP_Uns16*) (sDataPtr + 1);
- if (bigEndian) {
- PrintOnlyASCII_16BE (valuePtr, (frameHead.size - 1), " ");
- } else {
- PrintOnlyASCII_16LE (valuePtr, (frameHead.size - 1), " ");
- }
+ tree->changeValue( convert16Bit( bigEndian, sDataPtr + skip, false, (frameHead.size - skip) ) );
}
} else if (CheckBytes (frameHead.id, "PRIV", 4) && (frameHead.size >= 4)) {
-
- CaptureFileData (file, 0, frameHead.size);
- PrintOnlyASCII_8 (sDataPtr, strlen((char*)sDataPtr), ",");
+ // checking on the XMP packet
+ CaptureFileData (file, 0, frameHead.size); //NB: has side effect: sDataLen, sDataMax, sDataPtr
+ tree->changeValue( convert8Bit(sDataPtr,false,strlen((char*)sDataPtr) ));
if (CheckBytes (sDataPtr, "XMP\x0", 4)) {
CaptureXMPF (file, (framePos + 10 + 4), (frameHead.size - 4));
}
- } else if (CheckBytes (frameHead.id, "COMM", 4)) {
-
+ } else if (CheckBytes (frameHead.id, "COMM", 4) || CheckBytes (frameHead.id, "USLT", 4)) {
CaptureFileData (file, 0, frameHead.size);
- XMP_Uns8 * frameEnd = sDataPtr + frameHead.size;
+ XMP_Uns8 * frameEnd2 = sDataPtr + frameHead.size;
XMP_Uns8 encoding = sDataPtr[0];
char * lang = (char*) (sDataPtr + 1);
@@ -4118,49 +4381,50 @@ DumpMP3 (LFA_FileRef file, XMP_Uns32 /*mp3Len*/)
bool bigEndian = PrintID3Encoding (encoding, (sDataPtr + 4));
if ((encoding == 0) || (encoding == 3)) {
-
XMP_Uns8 * descrPtr = sDataPtr + 4;
XMP_Uns8 * valuePtr = descrPtr;
- while (*valuePtr != 0) ++valuePtr;
+
+ while (*valuePtr != 0)
+ {
+ ++valuePtr;
+ }
++valuePtr;
- PrintOnlyASCII_8 (descrPtr, (valuePtr - descrPtr - 1), " ");
- PrintOnlyASCII_8 (valuePtr, (frameEnd - valuePtr), " ");
+ size_t descrBytes = valuePtr - descrPtr - 1;
+ tree->changeValue( convert8Bit( valuePtr, false, frameEnd2 - valuePtr ) );
+ if (CheckBytes (frameHead.id, "COMM", 4)) {
+ tree->setKeyValue("ID3v2:COMM-descr", convert8Bit( descrPtr, false, descrBytes ).c_str() );
+ }
} else if ((encoding == 1) || (encoding == 2)) {
XMP_Uns16 * descrPtr = (XMP_Uns16*) (sDataPtr + 4);
XMP_Uns16 * valuePtr = descrPtr;
- while (*valuePtr != 0) ++valuePtr;
+ while (*valuePtr != 0)
+ {
+ ++valuePtr;
+ }
++valuePtr;
size_t descrBytes = 2 * (valuePtr - descrPtr - 1);
- size_t valueBytes = 2 * ((XMP_Uns16*)frameEnd - valuePtr);
-
- if (bigEndian) {
- PrintOnlyASCII_16BE (descrPtr, descrBytes, " ");
- PrintOnlyASCII_16BE (valuePtr, valueBytes, " ");
- } else {
- PrintOnlyASCII_16LE (descrPtr, descrBytes, " ");
- PrintOnlyASCII_16LE (valuePtr, valueBytes, " ");
- }
+ size_t valueBytes = 2 * ((XMP_Uns16*)frameEnd2 - valuePtr);
+ tree->changeValue( convert16Bit( bigEndian, (XMP_Uns8*) valuePtr, false, valueBytes ) );
+ tree->setKeyValue("ID3v2:COMM-descr", convert16Bit( bigEndian, (XMP_Uns8*) descrPtr, false, descrBytes ).c_str() );
}
-
}
-
framePos += (sizeof(ID3_FrameHeader) + frameHead.size);
-
}
if (framePos < frameEnd) {
- tree->addComment(" Padding assumed, offset %d (0x%X), size %d",
- framePos, framePos, (frameEnd - framePos));
+ tree->setKeyValue("","",
+ fromArgs("Padding assumed, offset %d (0x%X), size %d",framePos, framePos, (frameEnd - framePos))
+ );
}
if (sXMPPtr != 0) DumpXMP ("ID3 'PRIV' \"XMP\" frame");
-
+ tree->popNode();
} // DumpMP3
// =================================================================================================
@@ -4170,7 +4434,7 @@ PacketScan (LFA_FileRef file, XMP_Int64 fileLen)
{
try {
- QEScanner scanner (fileLen);
+ XMPScanner scanner (fileLen);
LFA_Seek (file, 0, SEEK_SET);
XMP_Uns8 buffer [64*1024];
@@ -4183,13 +4447,13 @@ PacketScan (LFA_FileRef file, XMP_Int64 fileLen)
}
size_t snipCount = scanner.GetSnipCount();
- QEScanner::SnipInfoVector snips (snipCount);
+ XMPScanner::SnipInfoVector snips (snipCount);
scanner.Report (snips);
size_t packetCount = 0;
for (size_t s = 0; s < snipCount; ++s) {
- if (snips[s].fState == QEScanner::eValidPacketSnip) {
+ if (snips[s].fState == XMPScanner::eValidPacketSnip) {
++packetCount;
CaptureXMPF (file, (XMP_Uns32)snips[s].fOffset, (XMP_Uns32)snips[s].fLength);
DumpXMP ("packet scan");
@@ -4288,9 +4552,15 @@ namespace DumpFile_NS {
// -------------------------------------------------------------------------------------------------
-void DumpFile::Scan (std::string filename, TagTree &tagTree)
+void DumpFile::Scan (std::string filename, TagTree &tagTree, bool resetTree)
{
DumpFile_NS::selfTest(); //calls selftest (will happen only once per runtime, optimization done)
+
+ if( resetTree )
+ {
+ tagTree.reset();
+ }
+
tree = &tagTree; // static "global" helper to avoid looping throug 'tree' 24x7
// Read the first 4K of the file into a local buffer and determine the file format.
@@ -4326,7 +4596,7 @@ void DumpFile::Scan (std::string filename, TagTree &tagTree)
if ( format == kXMP_JPEGFile ) {
tagTree.pushNode ( "Dumping JPEG file" );
- tagTree.addComment ( "size %d(0x%X)", fileLen, fileLen );
+ tagTree.addComment ( "size %lld (0x%llx)", fileLen, fileLen );
{
fileContent = (XMP_Uns8*) malloc(fileLen);
LFA_Seek ( fileRef, 0, SEEK_SET );
@@ -4338,7 +4608,7 @@ void DumpFile::Scan (std::string filename, TagTree &tagTree)
} else if ( format == kXMP_PhotoshopFile ) {
tagTree.pushNode ( "Dumping Photoshop file" );
- tagTree.addComment ( "size %d (0x%X)", fileLen, fileLen );
+ tagTree.addComment ( "size %lld (0x%llx)", fileLen, fileLen );
{
fileContent = (XMP_Uns8*) malloc(fileLen);
LFA_Seek ( fileRef, 0, SEEK_SET );
@@ -4350,91 +4620,84 @@ void DumpFile::Scan (std::string filename, TagTree &tagTree)
} else if ( format == kXMP_TIFFFile ) {
tagTree.pushNode ( "Dumping TIFF file" );
- tagTree.addComment ( "size %d (0x%X)", fileLen, fileLen );
+ tagTree.addComment ( "size %lld (0x%llx)", fileLen, fileLen );
{
fileContent = (XMP_Uns8*) malloc(fileLen);
LFA_Seek ( fileRef, 0, SEEK_SET );
LFA_Read ( fileRef, fileContent, fileLen, true);
- DumpTIFF ( fileContent, fileLen, 0, "TIFF file" );
+ DumpTIFF ( fileContent, fileLen, 0, "TIFF file" , "");
}
tagTree.popNode();
- } else if ( format == kXMP_JPEG2KFile ) {
-
- tagTree.pushNode ( "Dumping JPEG 2000 file" );
- tagTree.addComment ( "size %d (0x%X)", fileLen, fileLen );
- DumpJPEG2000 (fileRef, fileLen );
- tagTree.popNode();
-
} else if ( format == kXMP_WMAVFile ) {
tagTree.pushNode ( "Dumping ASF (WMA/WMV) file" );
- tagTree.addComment ( "size %d (0x%X)", fileLen, fileLen );
+ tagTree.addComment ( "size %lld (0x%llx)", fileLen, fileLen );
DumpASF ( fileRef, fileLen );
tagTree.popNode();
} else if ( format == kXMP_AVIFile ) {
tagTree.pushNode ( "Dumping AVI file" );
- tagTree.addComment ( "size %d (0x%X)", fileLen, fileLen );
+ tagTree.addComment ( "size %lld (0x%llx)", fileLen, fileLen );
DumpRIFF ( fileRef, fileLen );
tagTree.popNode();
} else if ( format == kXMP_WAVFile ) {
tagTree.pushNode ( "Dumping WAV file" );
- tagTree.addComment ( "size %d (0x%X)", fileLen, fileLen );
+ tagTree.addComment ( "size %lld (0x%llx)", fileLen, fileLen );
DumpRIFF ( fileRef, fileLen );
tagTree.popNode();
- } else if ( format == kXMP_MPEG4File || format == kXMP_MOVFile ) {
-
- bool isQT = (format == kXMP_MOVFile);
-
- tagTree.pushNode ( "Dumping ISO (MPEG-4 or Quicktime) file" );
- tagTree.addComment ( "size %d (0x%X)", fileLen, fileLen );
- DumpISO( fileRef, fileLen, isQT );
+ } else if ( format == kXMP_MPEG4File || format == kXMP_MOVFile || format == kXMP_JPEG2KFile ) {
+ // all ISO formats ( MPEG4, MOV, JPEG2000) handled jointly,
+ // - no longer relying on any advance "isQT" flagging
+ tagTree.pushNode ( "ISO file" );
+ Log::info("size: %d", fileLen );
+ tagTree.addComment ( "size %I64d (0x%I64X)", fileLen, fileLen );
+ DumpISO( fileRef, fileLen );
tagTree.popNode();
} else if ( format == kXMP_PNGFile ) {
tagTree.pushNode ( "Dumping PNG file" );
- tagTree.addComment ( "size %d (0x%X)", fileLen, fileLen );
+ tagTree.addComment ( "size %lld (0x%llx)", fileLen, fileLen );
DumpPNG ( fileRef, fileLen );
tagTree.popNode();
} else if ( format == kXMP_InDesignFile ) {
tagTree.pushNode ( "Dumping InDesign file" );
- tagTree.addComment ( "size %d (0x%X)", fileLen, fileLen );
+ tagTree.addComment ( "size %lld (0x%llx)", fileLen, fileLen );
DumpInDesign ( fileRef, fileLen );
tagTree.popNode();
} else if ( format == kXMP_SWFFile ) {
tagTree.pushNode ( "Dumping SWF file" );
- tagTree.addComment ( "size %d (0x%X)", fileLen, fileLen );
+ tagTree.addComment ( "size %lld (0x%llx)", fileLen, fileLen );
DumpSWF ( fileRef, fileLen );
tagTree.popNode();
} else if ( format == kXMP_FLVFile ) {
tagTree.pushNode ( "Dumping FLV file" );
- tagTree.addComment ( "size %d (0x%X)", fileLen, fileLen );
+ tagTree.addComment ( "size %lld (0x%llx)", fileLen, fileLen );
DumpFLV ( fileRef, fileLen );
tagTree.popNode();
} else if ( format == kXMP_MP3File ) {
tagTree.pushNode ( "Dumping MP3 file" );
- tagTree.addComment ( "size %d (0x%X)", fileLen, fileLen );
+ tagTree.addComment ( "size %lld (0x%llx)", fileLen, fileLen );
DumpMP3 ( fileRef, fileLen );
tagTree.popNode();
} else if ( format == kXMP_UCFFile ) {
tagTree.pushNode ( "Dumping UCF (Universal Container Format) file" );
- tagTree.addComment ( "size %d (0x%X)", fileLen, fileLen );
+ tagTree.addComment ( "size %lld (0x%llx)", fileLen, fileLen );
DumpUCF ( fileRef, fileLen );
tagTree.popNode();
@@ -4456,3 +4719,12 @@ void DumpFile::Scan (std::string filename, TagTree &tagTree)
LFA_Close(fileRef);
} // DumpFile
+
+
+void DumpFile::dumpFile( std::string filename )
+{
+ TagTree localTree;
+ DumpFile::Scan( filename , localTree ); // (important test in itself for validity)
+ localTree.dumpTree();
+}
+
diff --git a/samples/source/common/DumpFile.h b/samples/source/common/DumpFile.h
index 05857f8..d5ef41f 100644
--- a/samples/source/common/DumpFile.h
+++ b/samples/source/common/DumpFile.h
@@ -1,5 +1,5 @@
// =================================================================================================
-// Copyright 2002-2008 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
@@ -14,7 +14,10 @@
class DumpFile {
public:
- static void Scan(std::string filename, TagTree &tagTree);
+ static void Scan( std::string filename, TagTree &tagTree, bool resetTree = true );
+
+ /* dumps file to output, no strings attached Log::info() */
+ static void dumpFile( std::string filename );
};
#endif
diff --git a/samples/source/common/Log.cpp b/samples/source/common/Log.cpp
index fe132ba..d75336e 100644
--- a/samples/source/common/Log.cpp
+++ b/samples/source/common/Log.cpp
@@ -1,5 +1,5 @@
// =================================================================================================
-// Copyright 2005-2008 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
@@ -35,6 +35,12 @@ FILE* Log::logfile=NULL;
bool Log::noErrorsOrWarnings=true;
bool Log::mute=false;
bool Log::muteWarnings=false;
+
+// strings for skipped test information
+std::string Log::skippedTestsAll;
+std::string Log::skippedTestsWin;
+std::string Log::skippedTestsMac;
+std::string Log::skippedTestsUnix;
//////////////////////////////////////////////////////////////////////////////////////
/* standard log, use filename=NULL or filename="" or do not construct at all
@@ -184,6 +190,43 @@ void Log::important(std::string s1)
info(s1.c_str());
}
+void Log::error( std::string s1 )
+{
+ error( s1.c_str() );
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// skipped tests
+void Log::printSkippedTest(const char* filename)
+{
+ std::string completeString = "<html><head><title>Skipped Tests:</title><head><body>";
+ if (!skippedTestsAll.empty())
+ completeString.append("<br><br><table border=\"1\"><caption>all Plattforms:</caption><tr><th>Path to Test</th><th>comment</th></tr>"+skippedTestsAll + "</table>");
+ if (!skippedTestsWin.empty())
+ completeString.append("<br><br><table border=\"1\"><caption>Windows:</caption><tr><th>Path to Test</th><th>comment</th></tr>"+skippedTestsWin+ "</table>");
+ if (!skippedTestsMac.empty())
+ completeString.append("<br><br><table border=\"1\"><caption>Macintosh:</caption><tr><th>Path to Test</th><th>comment</th></tr>"+skippedTestsMac+ "</table>");
+ if (!skippedTestsUnix.empty())
+ completeString.append("<br><br><table border=\"1\"><caption>UNIX:</caption><tr><th>Path to Test</th><th>comment</th></tr>"+skippedTestsUnix+ "</table>");
+
+ completeString.append("</body></html>");
+ // print to console if mute is off
+ /*if (!Log::mute)
+ printf(completeString.c_str());*/
+
+ //print result to file
+ FILE* outputFile=fopen(filename,"wt");
+ if (!outputFile) {
+ printf("could not open output file %s for writing",filename); //do in addition to make sure it's output
+ throw LOG_Exception("could not open output file for writing");
+ }
+ fprintf( outputFile, "%s", completeString.c_str());
+ if(outputFile) {
+ fclose(outputFile);
+ }
+
+}
+
/////////////////////////////////////////////////////////////////////////////////
// mute vs. verbose
@@ -206,4 +249,3 @@ void Log::setVerboseWarnings()
{
Log::muteWarnings = false;
}
-
diff --git a/samples/source/common/Log.h b/samples/source/common/Log.h
index 3bd370c..db8d35c 100644
--- a/samples/source/common/Log.h
+++ b/samples/source/common/Log.h
@@ -1,5 +1,5 @@
// =================================================================================================
-// Copyright 2005-2008 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
@@ -60,6 +60,7 @@ class Log {
//convenience overloads:
static void info(std::string s1);
static void important(std::string s1);
+ static void error( std::string s1 );
// mute vs. verbose
static void setMute();
@@ -72,7 +73,16 @@ class Log {
// NB: public on intend, such that i.e. copy commands can decide to
// send their output to nirvana accordingly on mute==true;
// (getter/setter on a static boolean var is fairly pointless)
-
+
+ // strings to store the skipped test for different plattforms
+ static std::string skippedTestsAll;
+ static std::string skippedTestsWin;
+ static std::string skippedTestsMac;
+ static std::string skippedTestsUnix;
+
+ // function to print the skipped tests on console and into a log file
+ static void printSkippedTest(const char* filename);
+
private:
static bool singletonInstanciated;
static FILE* logfile;
@@ -80,6 +90,8 @@ class Log {
//should only be temporarily used, i.e. on selftests that
//inevitable yield (during selftet: bogus) warnings
static bool muteWarnings;
+
+
};
#endif
diff --git a/samples/source/common/OutputUtils.cpp b/samples/source/common/OutputUtils.cpp
deleted file mode 100644
index 0c59339..0000000
--- a/samples/source/common/OutputUtils.cpp
+++ /dev/null
@@ -1,227 +0,0 @@
-// =================================================================================================
-// Copyright 2005-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 <string>
-#include <stdarg.h>
-
-#include "OutputUtils.h"
-#include "Log.h"
-
-
-using namespace std;
-
-namespace Utils {
- /* hand over the fileFormat and get a fileFormatName (2-3 Letters ie. PDF)
- * and a Description (i.e. "Portable Document Format") back.
- * see also public/include/XMP_Const.h
- */
- void fileFormatNameToString ( XMP_FileFormat fileFormat, std::string & fileFormatName, std::string & fileFormatDesc )
- {
- fileFormatName.erase();
- fileFormatDesc.erase();
- switch(fileFormat) {
- case kXMP_PDFFile:
- fileFormatName = "PDF ";fileFormatDesc = "Portable Document Format";break;
- case kXMP_PostScriptFile:
- fileFormatName = "PS ";fileFormatDesc = "Post Script";break;
- case kXMP_EPSFile:
- fileFormatName = "EPS ";fileFormatDesc = "Encapsulated Post Script";break;
- case kXMP_JPEGFile:
- fileFormatName = "JPEG";fileFormatDesc = "Joint Photographic Experts Group";break;
- case kXMP_JPEG2KFile:
- fileFormatName = "JPX ";fileFormatDesc = "JPEG 2000";break;
- case kXMP_TIFFFile:
- fileFormatName = "TIFF";fileFormatDesc = "Tagged Image File Format";break;
- case kXMP_GIFFile:
- fileFormatName = "GIF ";fileFormatDesc = "Graphics Interchange Format";break;
- case kXMP_PNGFile:
- fileFormatName = "PNG ";fileFormatDesc = "Portable Network Graphic";break;
- case kXMP_MOVFile:
- fileFormatName = "MOV ";fileFormatDesc = "Quicktime";break;
- case kXMP_AVIFile:
- fileFormatName = "AVI ";fileFormatDesc = "Quicktime";break;
- case kXMP_CINFile:
- fileFormatName = "CIN ";fileFormatDesc = "Cineon";break;
- case kXMP_WAVFile:
- fileFormatName = "WAV ";fileFormatDesc = "WAVE Form Audio Format";break;
- case kXMP_MP3File:
- fileFormatName = "MP3 ";fileFormatDesc = "MPEG-1 Audio Layer 3";break;
- case kXMP_SESFile:
- fileFormatName = "SES ";fileFormatDesc = "Audition session";break;
- case kXMP_CELFile:
- fileFormatName = "CEL ";fileFormatDesc = "Audition loop";break;
- case kXMP_MPEGFile:
- fileFormatName = "MPEG";fileFormatDesc = "Motion Pictures Experts Group";break;
- case kXMP_MPEG2File:
- fileFormatName = "MP2 ";fileFormatDesc = "MPEG-2";break;
- case kXMP_WMAVFile:
- fileFormatName = "WMAV";fileFormatDesc = "Windows Media Audio and Video";break;
- case kXMP_HTMLFile:
- fileFormatName = "HTML";fileFormatDesc = "HyperText Markup Language";break;
- case kXMP_XMLFile:
- fileFormatName = "XML ";fileFormatDesc = "Extensible Markup Language";break;
- case kXMP_TextFile:
- fileFormatName = "TXT ";fileFormatDesc = "text";break;
- case kXMP_PhotoshopFile:
- fileFormatName = "PSD ";fileFormatDesc = "Photoshop Document";break;
- case kXMP_IllustratorFile:
- fileFormatName = "AI ";fileFormatDesc = "Adobe Illustrator";break;
- case kXMP_InDesignFile:
- fileFormatName = "INDD";fileFormatDesc = "Adobe InDesign";break;
- case kXMP_AEProjectFile:
- fileFormatName = "AEP ";fileFormatDesc = "AfterEffects Project";break;
- case kXMP_AEFilterPresetFile:
- fileFormatName = "FFX ";fileFormatDesc = "AfterEffects Filter Preset";break;
- case kXMP_EncoreProjectFile:
- fileFormatName = "NCOR";fileFormatDesc = "Encore Project";break;
- case kXMP_PremiereProjectFile:
- fileFormatName = "PPRJ";fileFormatDesc = "Premier Project";break;
- case kXMP_SWFFile:
- fileFormatName = "SWF ";fileFormatDesc = "Shockwave Flash";break;
- case kXMP_PremiereTitleFile:
- fileFormatName = "PRTL";fileFormatDesc = "Premier Title";break;
- default:
- fileFormatName = " ";fileFormatDesc = "Unkown file format";break;
- }
- } //fileFormatNameToString
-
- /**
- * GetFormatInfo-Flags (aka Handler-Flags)
- * find this in XMP_Const.h under "Options for GetFormatInfo"
- */
- std::string XMPFiles_FormatInfoToString ( const XMP_OptionBits options) {
- std::string outString;
-
- if( options & kXMPFiles_CanInjectXMP )
- outString.append(" CanInjectXMP");
- if( options & kXMPFiles_CanExpand )
- outString.append(" CanExpand");
- if( options & kXMPFiles_CanRewrite )
- outString.append(" CanRewrite");
- if( options & kXMPFiles_PrefersInPlace )
- outString.append(" PrefersInPlace");
- if( options & kXMPFiles_CanReconcile )
- outString.append(" CanReconcile");
- if( options & kXMPFiles_AllowsOnlyXMP )
- outString.append(" AllowsOnlyXMP");
- if( options & kXMPFiles_ReturnsRawPacket )
- outString.append(" ReturnsRawPacket");
- if( options & kXMPFiles_ReturnsTNail )
- outString.append(" ReturnsTNail");
- if( options & kXMPFiles_HandlerOwnsFile )
- outString.append(" HandlerOwnsFile");
- if( options & kXMPFiles_AllowsSafeUpdate )
- outString.append(" AllowsSafeUpdate");
-
- if (outString.empty()) outString=" (none)";
- return outString;
- }
-
- /**
- * openOptions to String, find this in
- * XMP_Const.h under "Options for OpenFile"
- */
- std::string XMPFiles_OpenOptionsToString ( const XMP_OptionBits options) {
- std::string outString;
- if( options & kXMPFiles_OpenForRead)
- outString.append(" OpenForRead");
- if( options & kXMPFiles_OpenForUpdate)
- outString.append(" OpenForUpdate");
- if( options & kXMPFiles_OpenOnlyXMP)
- outString.append(" OpenOnlyXMP");
- if( options & kXMPFiles_OpenCacheTNail)
- outString.append(" OpenCacheTNail");
- if( options & kXMPFiles_OpenStrictly)
- outString.append(" OpenStrictly");
- if( options & kXMPFiles_OpenUseSmartHandler)
- outString.append(" OpenUseSmartHandler");
- if( options & kXMPFiles_OpenUsePacketScanning)
- outString.append(" OpenUsePacketScanning");
- if( options & kXMPFiles_OpenLimitedScanning)
- outString.append(" OpenLimitedScanning");
-
- if (outString.empty()) outString=" (none)";
- return outString;
- }
-
-
-
- std::string fromArgs(const char* format, ...)
- {
- //note: format and ... are somehow "used up", i.e. dumping them
- // via vsprintf _and_ via printf brought up errors on Mac (only)
- // i.e. %d %X stuff looking odd (roughly like signed vs unsigned...)
- // buffer reuse is fine, just dont use format/... twice.
-
- char buffer[4096]; //should be big enough but no guarantees..
- va_list args;
- va_start(args, format);
- vsprintf(buffer, format, args);
- va_end(args);
-
- return std::string(buffer);
- }
-
-
-
- //just the callback-Function
- XMP_Status DumpToString( void * refCon, XMP_StringPtr outStr, XMP_StringLen outLen )
- {
- XMP_Status status = 0;
- std::string* dumpString = static_cast < std::string* > ( refCon );
- dumpString->append (outStr, outLen); // less effective: ( std::string(outStr).substr(0,outLen) );
- return status;
- }
-
- //full output to Log
- void dumpXMP(SXMPMeta* xmpMeta){
- std::string dump;
- xmpMeta->Sort();
- xmpMeta->DumpObject(DumpToString, &dump);
- Log::info("%s",dump.c_str());
- }
-
- //same but with Comment
- void dumpXMP(std::string comment, SXMPMeta* xmpMeta){
- Log::info("%s",comment.c_str());
- std::string dump;
- xmpMeta->Sort();
- xmpMeta->DumpObject(DumpToString, &dump);
- Log::info("%s",dump.c_str());
- }
-
- // dump Aliases ****************************
- //full output to Log
- void dumpAliases()
- {
- std::string dump;
- SXMPMeta::DumpAliases(DumpToString,&dump);
- Log::info("List of aliases:");
- Log::info("%s",dump.c_str());
- }
-
- void DumpXMP_DateTime( const XMP_DateTime & date ) {
- static std::string tempStr;
- tempStr.clear();
- SXMPUtils::ConvertFromDate( date, &tempStr );
- Log::info("%s\nYear: '%d' Month: '%d' Day: '%d' Hour: '%d' Min: '%d' Sec: '%d' Nanos: '%d' TZSign: '%d' TZHour: '%d' TZMinute: '%d'\n", tempStr.c_str(), date.year, date.month, date.day, date.hour, date.minute, date.second, date.nanoSecond, date.tzSign, date.tzHour, date.tzMinute );
- }
-
-
-
-
- void removeNLines(std::string* s, int n){
- for(int i=0;i<n;i++)
- s->erase(0,s->find_first_of("\n")+1);
- }
-
-
-
-} /*namespace Utils*/
-
diff --git a/samples/source/common/OutputUtils.h b/samples/source/common/OutputUtils.h
deleted file mode 100644
index 6597ed8..0000000
--- a/samples/source/common/OutputUtils.h
+++ /dev/null
@@ -1,50 +0,0 @@
-// =================================================================================================
-// Copyright 2005-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.
-//
-// =================================================================================================
-
-#ifndef __XMPQE_QEOUTPUTUTILS_h__
-#define __XMPQE_QEOUTPUTUTILS_h__ 1
-
-//XMP related
-#define TXMP_STRING_TYPE std::string
-
-#include "XMP.hpp"
-
-namespace Utils{
-
-
- void fileFormatNameToString( XMP_FileFormat fileFormat, std::string &fileFormatName, std::string &fileFormatDesc );
-
- std::string XMPFiles_FormatInfoToString ( const XMP_OptionBits options);
- std::string XMPFiles_OpenOptionsToString ( const XMP_OptionBits options);
-
- std::string fromArgs(const char* format, ...);
-
- //just the DumpToString Callback routine
- // - (taken over from dfranzen resp. DevTestProto)
- // - /*outLen*/ ==> avoid unused warning
- XMP_Status DumpToString ( void * refCon, XMP_StringPtr outStr, XMP_StringLen /*outLen*/ );
-
- // dumps xmp (using Log::info())
- // based on dfranzen's conglomerate of dump-functions in XMPTestCase.cpp
- // no return value, will Log::error() on problems
- void dumpXMP(SXMPMeta* xmpMeta);
- void dumpXMP(std::string comment, SXMPMeta* xmpMeta);
- void dumpAliases();
-
- //remove n first lines from a string
- // handy i.e. for first line of xmp-rdf packets prior to KGO,
- // since firt line may contain -date- or actual date,
- // (debug) or 7 spaces, etc...
- void removeNLines(std::string* s, int n=1);
-
- void DumpXMP_DateTime( const XMP_DateTime & date );
-
-}
-
-#endif
diff --git a/samples/source/common/QEScanner.cpp b/samples/source/common/QEScanner.cpp
deleted file mode 100644
index 5553495..0000000
--- a/samples/source/common/QEScanner.cpp
+++ /dev/null
@@ -1,1469 +0,0 @@
-// =================================================================================================
-// Copyright 2002-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.
-//
-// 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 WIN_ENV
- #pragma warning ( disable : 4786 ) // The VC++ debugger can't handle long symbol names.
- #pragma warning ( disable : 4127 ) // conditional expression is constant
-#endif
-
-
-#include "QEScanner.hpp"
-
-#include <cassert>
-#include <string>
-#include <iostream>
-#include <iomanip>
-#include <fstream>
-#include <cstdlib>
-
-
-#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
-// =============
-
-QEScanner::PacketMachine::PacketMachine ( SInt64 bufferOffset, const void * bufferOrigin, SInt64 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
-// ==============
-
-QEScanner::PacketMachine::~PacketMachine ()
-{
-
- // An empty placeholder.
-
-} // ~PacketMachine
-
-
-// =================================================================================================
-// AssociateBuffer
-// ===============
-
-void
-QEScanner::PacketMachine::AssociateBuffer ( SInt64 bufferOffset, const void * bufferOrigin, SInt64 bufferLength )
-{
-
- fBufferOffset = bufferOffset;
- fBufferOrigin = (const char *) bufferOrigin;
- fBufferPtr = fBufferOrigin + fBufferOverrun;
- fBufferLimit = fBufferOrigin + bufferLength;
-
-} // AssociateBuffer
-
-
-// =================================================================================================
-// ResetMachine
-// ============
-
-void
-QEScanner::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
-// ============
-
-QEScanner::PacketMachine::TriState
-QEScanner::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
-// ===========
-
-QEScanner::PacketMachine::TriState
-QEScanner::PacketMachine::MatchString ( PacketMachine * ths, const char * literal )
-{
- const int bytesPerChar = ths->fBytesPerChar;
- const char * litPtr = literal + ths->fPosition;
- const size_t charsToGo = strlen ( literal ) - ths->fPosition;
- size_t 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
-// =========
-
-QEScanner::PacketMachine::TriState
-QEScanner::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
-// ==============
-
-QEScanner::PacketMachine::TriState
-QEScanner::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
-// ===============
-
-QEScanner::PacketMachine::TriState
-QEScanner::PacketMachine::MatchCloseQuote ( PacketMachine * ths, const char * /* unused */ )
-{
-
- return MatchChar ( ths, &ths->fQuoteChar );
-
-} // MatchCloseQuote
-
-
-// =================================================================================================
-// CaptureAttrName
-// ===============
-
-QEScanner::PacketMachine::TriState
-QEScanner::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 = 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.
-
-QEScanner::PacketMachine::TriState
-QEScanner::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.
-
-QEScanner::PacketMachine::TriState
-QEScanner::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
-};
-
-QEScanner::PacketMachine::TriState
-QEScanner::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
-// ==============
-
-QEScanner::PacketMachine::TriState
-QEScanner::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;
- size_t count = ths->fAttrValue.size();
- size_t 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
-// =============
-
-QEScanner::PacketMachine::TriState
-QEScanner::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
-// ==============
-
-QEScanner::PacketMachine::TriState
-QEScanner::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.
-
-QEScanner::PacketMachine::TriState
-QEScanner::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 SInt64 currLength = (ths->fBufferOffset + (ths->fBufferPtr - ths->fBufferOrigin)) - ths->fPacketStart;
-
- if ( (ths->fBytesAttr != -1) && (ths->fBytesAttr != currLength) ) {
- if ( ths->fBytesAttr < currLength ) {
- ths->fBogusPacket = true; // The bytes attribute value is too small.
- } else {
- ths->fPosition = (signed long)(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;
-
- }
-
- ths->fPacketLength = (ths->fBufferOffset + (ths->fBufferPtr - ths->fBufferOrigin)) - ths->fPacketStart;
- 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 QEScanner::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.
-
-QEScanner::PacketMachine::TriState
-QEScanner::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
-QEScanner::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.
-
-QEScanner::PacketMachine::TriState
-QEScanner::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
-// ============
-
-QEScanner::InternalSnip::InternalSnip ( SInt64 offset, SInt64 length )
-{
-
- fInfo.fOffset = offset;
- fInfo.fLength = length;
-
-} // InternalSnip
-
-
-// =================================================================================================
-// InternalSnip
-// ============
-
-QEScanner::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
-// =============
-
-QEScanner::InternalSnip::~InternalSnip ()
-{
-} // ~InternalSnip
-
-
-
-// =================================================================================================
-// =================================================================================================
-// class QEScanner
-// ================
-
-
-// =================================================================================================
-// DumpSnipList
-// ============
-
-static const char * snipStateName [6] = { "not-seen", "pending", "raw-data", "good-packet", "partial", "bad-packet" };
-
-void
-QEScanner::DumpSnipList ( const char * title )
-{
-#if 1
- 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;
- }
-#endif
-} // DumpSnipList
-
-
-// =================================================================================================
-// PrevSnip and NextSnip
-// =====================
-
-QEScanner::InternalSnipIterator
-QEScanner::PrevSnip ( InternalSnipIterator snipPos )
-{
-
- InternalSnipIterator prev = snipPos;
- return --prev;
-
-} // PrevSnip
-
-QEScanner::InternalSnipIterator
-QEScanner::NextSnip ( InternalSnipIterator snipPos )
-{
-
- InternalSnipIterator next = snipPos;
- return ++next;
-
-} // NextSnip
-
-
-// =================================================================================================
-// QEScanner
-// ==========
-//
-// Initialize the scanner object with one "not seen" snip covering the whole stream.
-
-QEScanner::QEScanner ( SInt64 streamLength ) :
-
- fStreamLength ( streamLength )
-
-{
- InternalSnip rootSnip ( 0, streamLength );
-
- if ( streamLength > 0 ) fInternalSnips.push_front ( rootSnip ); // Be nice for empty files.
- // DumpSnipList ( "New QEScanner" );
-
-} // QEScanner
-
-
-// =================================================================================================
-// ~QEScanner
-// ===========
-
-QEScanner::~QEScanner()
-{
-
-} // ~QEScanner
-
-
-// =================================================================================================
-// GetSnipCount
-// ============
-
-size_t
-QEScanner::GetSnipCount ()
-{
-
- return fInternalSnips.size();
-
-} // GetSnipCount
-
-
-// =================================================================================================
-// StreamAllScanned
-// ================
-
-bool
-QEScanner::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
-QEScanner::SplitInternalSnip ( InternalSnipIterator snipPos, SInt64 relOffset, SInt64 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 SInt64 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
-// ==================
-
-QEScanner::InternalSnipIterator
-QEScanner::MergeInternalSnips ( InternalSnipIterator firstPos, InternalSnipIterator secondPos )
-{
-
- firstPos->fInfo.fLength += secondPos->fInfo.fLength;
- fInternalSnips.erase ( secondPos );
- return firstPos;
-
-} // MergeInternalSnips
-
-
-// =================================================================================================
-// Scan
-// ====
-
-void
-QEScanner::Scan ( const void * bufferOrigin, SInt64 bufferOffset, SInt64 bufferLength )
-{
- SInt64 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 SInt64 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
-QEScanner::Report ( SnipInfoVector& snips )
-{
- const size_t count = fInternalSnips.size();
- InternalSnipIterator snipPos = fInternalSnips.begin();
-
- size_t 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/samples/source/common/QEScanner.hpp b/samples/source/common/QEScanner.hpp
deleted file mode 100644
index 0c24dce..0000000
--- a/samples/source/common/QEScanner.hpp
+++ /dev/null
@@ -1,331 +0,0 @@
-#ifndef __QEScanner_hpp__
-#define __QEScanner_hpp__
-
-// =================================================================================================
-// Copyright 2002-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.
-//
-// 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 <list>
-#include <vector>
-#include <string>
-#include <memory>
-#include <stdexcept>
-
-// =================================================================================================
-// The QEScanner 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 QEScanner object.
-// =================================================================================================
-
-class QEScanner {
-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 QEScanner 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.
-
- typedef unsigned char UInt8;
- typedef unsigned long UInt32;
- typedef long long SInt64;
-
- 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 UInt8 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 UInt8 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) & QEScanner::eChar16BitMask) != 0 )
- #define CharFormIs32Bit(f) ( ((int)(f) & QEScanner::eChar32BitMask) != 0 )
-
- #define CharFormIsBigEndian(f) ( ((int)(f) & QEScanner::eCharLittleEndianMask) == 0 )
- #define CharFormIsLittleEndian(f) ( ((int)(f) & QEScanner::eCharLittleEndianMask) != 0 )
-
- struct SnipInfo {
-
- SInt64 fOffset; // The byte offset of this snip within the input stream.
- SInt64 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.
- SInt64 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, SInt64 offset, SInt64 length ) :
- fOffset ( offset ),
- fLength ( length ),
- fState ( state ),
- fOutOfOrder ( false ),
- fAccess ( ' ' ),
- fCharForm ( eChar8Bit ),
- fEncodingAttr ( "" ),
- fBytesAttr( -1 )
- { }
-
- };
-
- typedef std::vector<SnipInfo> SnipInfoVector;
-
- QEScanner ( SInt64 streamLength );
- // Constructs a new QEScanner object for a stream with the given length.
-
- ~QEScanner();
-
- size_t 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, SInt64 bufferOffset, SInt64 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: // QEScanner
-
- 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() {}; // Let everything default.
- InternalSnip ( SInt64 offset, SInt64 length );
- InternalSnip ( const InternalSnip & );
- ~InternalSnip();
-
- }; // InternalSnip
-
- typedef std::list<InternalSnip> InternalSnipList;
- typedef InternalSnipList::iterator InternalSnipIterator;
-
- class PacketMachine {
- public:
-
- SInt64 fPacketStart; // Byte offset relative to the entire stream.
- SInt64 fPacketLength; // Length in bytes to the end of the trailer processing instruction.
- SInt64 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 ( SInt64 bufferOffset, const void * bufferOrigin, SInt64 bufferLength );
-
- PacketMachine ( SInt64 bufferOffset, const void * bufferOrigin, SInt64 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
-
- };
-
- SInt64 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*/ size_t 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
-
- SInt64 fStreamLength;
- InternalSnipList fInternalSnips;
-
- void
- SplitInternalSnip ( InternalSnipIterator snipPos, SInt64 relOffset, SInt64 newLength );
-
- InternalSnipIterator
- MergeInternalSnips ( InternalSnipIterator firstPos, InternalSnipIterator secondPos );
-
- void
- DumpSnipList ( const char * title );
-
- InternalSnipIterator
- PrevSnip ( InternalSnipIterator snipPos );
-
- InternalSnipIterator
- NextSnip ( InternalSnipIterator snipPos );
-
-}; // QEScanner
-
-
-#endif // __QEScanner_hpp__
diff --git a/samples/source/common/TagTree.cpp b/samples/source/common/TagTree.cpp
index 2870057..9a5ac23 100644
--- a/samples/source/common/TagTree.cpp
+++ b/samples/source/common/TagTree.cpp
@@ -1,5 +1,5 @@
// =================================================================================================
-// Copyright 2005-2008 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
@@ -23,6 +23,8 @@
#include "TagTree.h"
#include <stdarg.h>
+// silent by default
+bool TagTree::verbose = false;
#if WIN_ENV
//should preferably be within a #if XMP_WinBuild, not doable here...
@@ -58,24 +60,40 @@ TagTree::TagTree()
rootNode.key="rootkey"; //all never seen
rootNode.value="rootvalue";
rootNode.comment="rootcomment";
- tagMap.clear(); // making extra sure it's clear, avoid OS X std:: issues
- nodeStack.clear();
- nodeStack.push_back(&rootNode);
- lastNode=NULL;
+ reset();
}
TagTree::~TagTree()
{
+
+}
+
+void TagTree::reset()
+{
tagMap.clear();
nodeStack.clear();
+ rootNode.children.clear();
nodeStack.push_back(&rootNode);
- //any further cleanup needed?
+ lastNode = NULL;
}
-void TagTree::setKeyValue(const std::string key,const std::string value, const std::string comment)
+
+// verbosity control:
+void TagTree::setMute()
+{
+ TagTree::verbose = false;
+}
+
+void TagTree::setVerbose()
+{
+ TagTree::verbose = true;
+}
+
+
+void TagTree::setKeyValue(const std::string key,const std::string value, const std::string _comment)
{
Node* pCurNode=*nodeStack.rbegin(); //current Node
- pCurNode->children.push_back(Node(key,value,comment));
+ pCurNode->children.push_back(Node(key,value, _comment));
if(key.size()==0) { // standalone comment?
if (value.size()!=0) // must have no value
@@ -95,6 +113,9 @@ void TagTree::setKeyValue(const std::string key,const std::string value, const s
//add to Map -----------------------------------
lastNode=&*(pCurNode->children.rbegin());
tagMap[extkey]=lastNode;
+
+ if ( verbose )
+ Log::info( " setKeyValue( %s |-> %s) [%s]", key.c_str(), value.c_str(), _comment.c_str() );
}
void TagTree::digest(LFA_FileRef file,const std::string key /*=NULL*/,
@@ -115,7 +136,8 @@ void TagTree::digest(LFA_FileRef file,const std::string key /*=NULL*/,
else
value=new char[numOfBytes+1];
- if (numOfBytes != LFA_Read ( file, value, numOfBytes, true)) // saying 1,4 guarantes read as ordered (4,1 would not)
+ // require all == false => leave the throwing to this routine
+ if (numOfBytes != LFA_Read ( file, value, numOfBytes, false)) // saying 1,4 guarantes read as ordered (4,1 would not)
Log::error("could not read %d number of files (End of File reached?)",numOfBytes);
char* out=new char[2 + numOfBytes*3 + 5]; //'0x12 34 45 78 ' length formula: 2 ("0x") + numOfBytes x 3 + 5 (padding)
@@ -125,7 +147,7 @@ void TagTree::digest(LFA_FileRef file,const std::string key /*=NULL*/,
XMP_Int64 i; // *)
for (i=0; i < numOfBytes; i++)
snprintf(&out[2+i*3],4,"%.2X ",value[i]); //always must allow that extra 0-byte on mac (overwritten again and again)
- snprintf(&out[2+i*3],1,"\0"); // *) using i one more time (needed while bug 1613297 regarding snprintf not fixed)
+ snprintf(&out[2+i*3],1,"%c",'\0'); // *) using i one more time (needed while bug 1613297 regarding snprintf not fixed)
setKeyValue(key,out);
}
@@ -139,14 +161,14 @@ void TagTree::digest(LFA_FileRef file,const std::string key /*=NULL*/,
XMP_Int64 TagTree::digest64s(LFA_FileRef file,const std::string key /* ="" */ , bool BigEndian /*=false*/ )
{
XMP_Int64 r;
- if (8 != LFA_Read ( file, &r, 8, true))
+ if (8 != LFA_Read ( file, &r, 8, false)) // require all == false => leave the throwing to this routine
Log::error("could not read 8-byte value from file (end of file?)");
if ( ((kBigEndianHost==1) && !BigEndian ) || ((kBigEndianHost==0) && BigEndian )) // "XOR"
Flip8(&r);
if (!key.empty()) {
char out[25]; //longest is "18446744073709551615", 21 chars ==> 25
- snprintf(out,24,"%d",r); //signed, mind the trailing \0 on Mac btw
+ snprintf(out,24,"%lld",r); //signed, mind the trailing \0 on Mac btw
setKeyValue(key,out);
}
return r;
@@ -155,7 +177,7 @@ XMP_Int64 TagTree::digest64s(LFA_FileRef file,const std::string key /* ="" */ ,
XMP_Uns64 TagTree::digest64u(LFA_FileRef file,const std::string key /* ="" */, bool BigEndian /*=false*/,bool hexDisplay /*=false*/ )
{
XMP_Uns64 r;
- if (8 != LFA_Read ( file, &r, 8, true))
+ if (8 != LFA_Read ( file, &r, 8, false)) // require all == false => leave the throwing to this routine
Log::error("could not read 8-byte value from file (end of file?)");
if ( ((kBigEndianHost==1) && !BigEndian ) || ((kBigEndianHost==0) && BigEndian )) // "XOR"
Flip8(&r);
@@ -168,16 +190,16 @@ XMP_Uns64 TagTree::digest64u(LFA_FileRef file,const std::string key /* ="" */, b
snprintf(out , 24 , "%I64u" , r);
#else
// MAC, UNIX
- snprintf(out , 24 , "%0xllu" , r);
+ snprintf(out , 24 , "%llu" , r);
#endif
}
else
{
//not working, upper 32 bit empty:
#if WIN_ENV
- snprintf( out , 24 , "0x%I64.16X" , r );
+ snprintf( out , 24 , "0x%.16I64X" , r );
#else
- snprintf( out , 24 , "0x%ll.16X" , r );
+ snprintf( out , 24 , "0x%.16llX" , r );
#endif
}
setKeyValue(key,out);
@@ -189,7 +211,7 @@ XMP_Uns64 TagTree::digest64u(LFA_FileRef file,const std::string key /* ="" */, b
XMP_Int32 TagTree::digest32s(LFA_FileRef file,const std::string key /* ="" */ , bool BigEndian /*=false*/ )
{
XMP_Int32 r;
- if (4 != LFA_Read ( file, &r, 4, true))
+ if (4 != LFA_Read ( file, &r, 4, false)) // require all == false => leave the throwing to this routine
Log::error("could not read 4-byte value from file (end of file?)");
if ( ((kBigEndianHost==1) && !BigEndian ) || ((kBigEndianHost==0) && BigEndian )) // "XOR"
Flip4(&r);
@@ -204,7 +226,7 @@ XMP_Int32 TagTree::digest32s(LFA_FileRef file,const std::string key /* ="" */ ,
XMP_Uns32 TagTree::digest32u(LFA_FileRef file,const std::string key /* ="" */, bool BigEndian /*=false*/,bool hexDisplay /*=false*/ )
{
XMP_Uns32 r;
- if (4 != LFA_Read ( file, &r, 4, true))
+ if (4 != LFA_Read ( file, &r, 4, false)) // require all == false => leave the throwing to this routine
Log::error("could not read 4-byte value from file (end of file?)");
if ( ((kBigEndianHost==1) && !BigEndian ) || ((kBigEndianHost==0) && BigEndian )) // "XOR"
Flip4(&r);
@@ -222,7 +244,7 @@ XMP_Uns32 TagTree::digest32u(LFA_FileRef file,const std::string key /* ="" */, b
XMP_Int16 TagTree::digest16s(LFA_FileRef file,const std::string key /* ="" */ , bool BigEndian /*=false*/ )
{
XMP_Int16 r;
- if (2 != LFA_Read ( file, &r, 2, true))
+ if (2 != LFA_Read ( file, &r, 2, false)) // require all == false => leave the throwing to this routine
Log::error("could not read 2-byte value from file (end of file?)");
if ( ((kBigEndianHost==1) && !BigEndian ) || ((kBigEndianHost==0) && BigEndian )) // "XOR"
Flip2(&r);
@@ -237,7 +259,7 @@ XMP_Int16 TagTree::digest16s(LFA_FileRef file,const std::string key /* ="" */ ,
XMP_Uns16 TagTree::digest16u(LFA_FileRef file,const std::string key /* ="" */, bool BigEndian /*=false*/,bool hexDisplay /*=false*/ )
{
XMP_Uns16 r;
- if (2 != LFA_Read ( file, &r, 2, true))
+ if (2 != LFA_Read ( file, &r, 2, false)) // require all == false => leave the throwing to this routine
Log::error("could not read 2-byte value from file (end of file?)");
if ( ((kBigEndianHost==1) && !BigEndian ) || ((kBigEndianHost==0) && BigEndian )) // "XOR"
Flip2(&r);
@@ -340,7 +362,7 @@ void TagTree::digest16u(XMP_Uns16* returnValue, LFA_FileRef file,const std::stri
//////////////////////////////////////////////////////////////////////////////////////////
-std::string TagTree::digestString(LFA_FileRef file,const std::string key /*=""*/, size_t length /* =0 */, bool verifyZeroTerm /* =false */)
+std::string TagTree::digestString(LFA_FileRef file,const std::string key /*=""*/, size_t length /* =0 */, bool verifyZeroTerm /* =false */, bool allowEarlyZeroTerm /* =false */ )
{
std::string r(256,'\0'); //give some room in advance (performance)
r.clear(); // safety measure (may be needed on mac)
@@ -350,6 +372,15 @@ std::string TagTree::digestString(LFA_FileRef file,const std::string key /*=""*/
for ( XMP_Uns32 i = 0; ( i<length ) || (length==0) ; i++ )
{
XMP_Uns8 ch = (XMP_Uns8)LFA_GetChar(file);
+
+ // allow early zero termination (useful for fixed length field that may or may not end prematurely)
+ if ( allowEarlyZeroTerm && ( ch == 0 ) && ( length != 0 ) )
+ {
+ i++;
+ LFA_Seek( file, length - i, SEEK_CUR ); // compensate for skipped bytes
+ break;
+ }
+
if ( (0x20 <= ch) && (ch <= 0x7E) )
{ //outside-case
if ( outside )
@@ -357,13 +388,13 @@ std::string TagTree::digestString(LFA_FileRef file,const std::string key /*=""*/
r.push_back(ch);
outside = false;
} else {
+ if ( (length==0) && (ch == '\0' ) )
+ break; // lenght zero => watch for zero termination...
if ( !outside )
r.push_back('<'); //first inside
else if (!((length==0) && (ch =='\0')))
r.push_back(' '); //further inside (except very last)
outside = true;
- if ( (length==0) && (ch == '\0' ) )
- break; // lenght zero => watch for zero termination...
char tmp[4];
sprintf(tmp, "%.2X", ch );
r+=tmp;
@@ -386,9 +417,9 @@ std::string TagTree::digestString(LFA_FileRef file,const std::string key /*=""*/
return r;
}
-void TagTree::comment(const std::string comment)
+void TagTree::comment(const std::string _comment)
{
- setKeyValue("","",comment);
+ setKeyValue("","", _comment);
}
void TagTree::comment(const char* format, ...)
@@ -411,6 +442,9 @@ void TagTree::pushNode(const std::string key)
//_and_ push reference to that one on stack
Node* pCurNode=*nodeStack.rbegin();
nodeStack.push_back( &*pCurNode->children.rbegin() );
+
+ if ( verbose )
+ Log::info( "pushing %d: %s",nodeStack.size(), key.c_str() );
}
//formatstring wrapper
@@ -439,6 +473,8 @@ void TagTree::addOffset(LFA_FileRef file)
// - addTag()'s will now go to the prior Node
void TagTree::popNode()
{
+ // FOR DEBUGGING Log::info( "pop %d",nodeStack.size() );
+
if (nodeStack.size() <= 1)
Log::error("nodeStack underflow: %d",nodeStack.size());
nodeStack.pop_back();
@@ -473,13 +509,16 @@ void TagTree::changeValue(const char* format, ...)
}
-void TagTree::addComment(const std::string comment)
+void TagTree::addComment(const std::string _comment)
{
if (!lastNode)
Log::error("lastnode NULL");
lastNode->comment=lastNode->comment +
( (lastNode->comment.size())?",":"") //only add comma, if there already is...
- +comment;
+ + _comment;
+
+ if ( verbose )
+ Log::info( " addComment: %s", _comment.c_str() );
}
void TagTree::addComment(const char* format, ...)
@@ -516,13 +555,16 @@ void TagTree::dumpTree(bool commentsFlag /*true*/,Node* pNode /*null*/ ,unsigned
std::string indent(depth*2,' '); //read: tab 4
if (commentsFlag) {
- Log::info( "%s%s%s%s%s%s%s%s%s", //fancy formatting foo='bar' [re,do]
+ Log::info( "%s%s%s%s%s%s%s%s%s%s", //fancy formatting foo='bar' [re,do]
indent.c_str(),
pNode->key.c_str(),
pNode->value.size()?" = '":"",
pNode->value.c_str(),
pNode->value.size()?"'":"",
( pNode->key.size() + pNode->value.size() > 0 ) ? " ":"",
+ // standalong comments don't need extra indentation:
+ pNode->comment.size() && ( pNode->key.size() || pNode->value.size() )?
+ ((std::string("\n ")+indent).c_str()) : "",
pNode->comment.size() && ( pNode->key.size() || pNode->value.size() ) ?"[":"",
//standalone comments don't need brackets
pNode->comment.c_str(),
@@ -606,6 +648,47 @@ std::string TagTree::getComment(const std::string key)
return tagMap[key]->comment;
}
+unsigned int TagTree::getSubNodePos( const std::string nodeKey, const std::string parentKey, int skip )
+{
+ Node* parent = NULL;
+
+ if( parentKey.empty() )
+ {
+ parent = &rootNode.children.front();
+ }
+ else
+ {
+ if ( ! hasNode( parentKey ) )
+ Log::error( "parent key %s does not exist", parentKey.c_str() );
+
+ parent = tagMap[parentKey];
+ }
+
+ unsigned int pos = 1;
+ NodeListIter iter;
+ for( iter = parent->children.begin();
+ iter != parent->children.end() && ( !( (iter->key == (parentKey.empty() ? nodeKey : parentKey + nodeKey)) && skip--<=0 )) ;
+ iter++, pos++ );
+
+ if( iter == parent->children.end() )
+ pos = 0;
+
+ return pos;
+}
+
+
+XMP_Int64 TagTree::getNodeSize( const std::string nodeKey )
+{
+ string tmp = tagMap[nodeKey]->comment;
+ size_t startpos = tmp.find( "size" ) + 5;
+ if( startpos == string::npos )
+ return 0;
+ tmp = tmp.substr( startpos, tmp.find_first_of( ",", startpos ) );
+
+ return strtol( tmp.c_str(), NULL, 0 );
+}
+
+
bool TagTree::hasNode(const std::string key)
{
if ( tagMap.count(key)==0 )
diff --git a/samples/source/common/TagTree.h b/samples/source/common/TagTree.h
index 7ec490c..0891711 100644
--- a/samples/source/common/TagTree.h
+++ b/samples/source/common/TagTree.h
@@ -1,5 +1,5 @@
// =================================================================================================
-// Copyright 2005-2008 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
@@ -38,6 +38,7 @@ class TagTree {
private:
struct Node; //forward-looking declaration
typedef std::list<Node> NodeList;
+ typedef std::list<Node>::iterator NodeListIter;
struct Node {
std::string key; // node structure name resp. tag name
@@ -54,10 +55,10 @@ private:
}
//the one to use
- Node(std::string key, std::string value, std::string comment) {
- this->key=key;
- this->value=value;
- this->comment=comment;
+ Node(std::string _key, std::string _value, std::string _comment) {
+ this->key = _key;
+ this->value = _value;
+ this->comment = _comment;
children.clear();
}
};
@@ -77,11 +78,20 @@ private:
NodeStack nodeStack;
Node rootNode; //TODO: ("root","");
-
+
+ // control verbosity to ease debugging:
+ static bool verbose;
+
public:
TagTree();
~TagTree();
+ void reset();
+
+ // verbosity control (mute by default ) ===================================
+ void setMute();
+ void setVerbose();
+
//input functions =========================================================
void pushNode(const std::string key);
@@ -147,11 +157,11 @@ public:
//(length is counted w/o trailing zero termination, i.e. length("hans")==4 )
// TODO: length default = 0 not yet implemented
// verifyZeroTerm
- // - has an effect only if a length!=0 is given
+ // - has an effect only if a length!=0 is given (otherwise things go up to the 0 anyway)
// - in this case, asserts that the string has a terminating zero
// (_after_ <length> bytes, otherwise that byte is not read which has an impact on the filepointer!)
// would throw if any zero (\0) is encountered prior to that
- std::string digestString(LFA_FileRef file,const std::string key="", size_t length=0, bool verifyZeroTerm=false );
+ std::string digestString(LFA_FileRef file,const std::string key="", size_t length=0, bool verifyZeroTerm=false, bool allowEarlyZeroTerm=false );
// (wrappers)
// standalone comment
@@ -178,6 +188,8 @@ public:
std::string getValue(const std::string key);
std::string getComment(const std::string key);
+ unsigned int getSubNodePos( const std::string nodeKey, const std::string parentKey = "", int skip = 0 );
+ XMP_Int64 getNodeSize( const std::string nodeKey );
//returns true if there is such a node, false if not, error if it happens to be key-value pair
bool hasNode(const std::string key);
diff --git a/samples/source/common/globals.h b/samples/source/common/globals.h
index 291ceb3..af9d033 100644
--- a/samples/source/common/globals.h
+++ b/samples/source/common/globals.h
@@ -1,10 +1,10 @@
// =================================================================================================
-// Copyright 2005-2008 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.
-//
+//
// =================================================================================================
#ifndef XMPQE_GLOBALS_H
@@ -18,12 +18,12 @@
#endif
#ifdef WIN_ENV
- #define XMPQE_LITTLE_ENDIAN
+ #define XMPQE_LITTLE_ENDIAN 1
#elif (defined(MAC_ENV) || defined(UNIX_ENV))
#if __BIG_ENDIAN__
- #define XMPQE_BIG_ENDIAN
+ #define XMPQE_BIG_ENDIAN 1
#elif __LITTLE_ENDIAN__
- #define XMPQE_LITTLE_ENDIAN
+ #define XMPQE_LITTLE_ENDIAN 1
#else
#error "Neither __BIG_ENDIAN__ nor __LITTLE_ENDIAN__ is set"
#endif
@@ -36,8 +36,52 @@
const char OMNI_CSTRING[]={0x41,0xE4,0xB8,0x80,0x42,0xE4,0xBA,0x8C,0x43,0xC3,0x96,0x44,0xF0,0x90,0x81,0x91,0x45,'\0'};
const char BOM_CSTRING[]={0xEF,0xBB,0xBF,'\0'}; // nb: forgetting the '\0' is a very evil mistake.
const std::string OMNI_STRING(OMNI_CSTRING);
- //if plain utf8 conversion, mac/win local encoding is a different story...
+ // if plain utf8 conversion, mac/win local encoding is a different story...
const std::string OMNI_BUGINESE("A<E4 B8 80>B<E4 BA 8C>C<C3 96>D<F0 90 81 91>E");
+ // if utf16LE
+ const std::string OMNI_BUGINESE_16LE("A<00 4E>B<8C 4E>C<D6 00>D<00 D8 51 DC>E");
+ // if utf16 BE
+ const std::string OMNI_BUGINESE_16BE("A<4E 00>B<4E 8C>C<00 D6>D<D8 00 DC 51>E");
+
+ // degraded version of omni-strings
+ // (sometimes useful for asserts after roundtrips, for setting values see OMNI_STRING in TestCase.h)
+ // ==> filed bug 2302354 on this issue.
+ #if WIN_ENV
+ // a *wrongly* degraded omni-string (non-BMP-char to ??)
+ const std::string DEG_OMNI_BUGINESE("A?B?C<C3 96>D??E");
+ // ditto albeit MacRoman encoding
+ const std::string MAC_OMNI_BUGINESE("A?B?C<85>D??E");
+ #else
+ // a *correctly* degraded omni-string (non-BMP-char to ?)
+ const std::string DEG_OMNI_BUGINESE("A?B?C<C3 96>D?E");
+ // ditto albeit MacRoman encoding
+ const std::string MAC_OMNI_BUGINESE("A?B?C<85>D?E");
+ #endif
+ // -> #issue# the non-BMP character in OMNI_STRING between D and E gets converted
+ // into two question marks (wrong) , not into one (correct)
+
+ const char OEAEUE_UTF8_CSTRING[]={0xC3, 0x84, 0xC3, 0x96, 0xC3, 0x9C,'\0'};
+ const std::string AEOEUE_UTF8(OEAEUE_UTF8_CSTRING);
+ const std::string AEOEUE_UTF8_BUGINESE("<C3 84 C3 96 C3 9C>");
+
+ const std::string AEOEUE_WIN_LOCAL_BUGINESE("<C4 D6 DC>");
+ const std::string AEOEUE_MAC_LOCAL_BUGINESE("<80 85 86>");
+
+ const std::string AEOEUE_WIN_MOJIBAKE_BUGINESE("<E2 82 AC E2 80 A6 E2 80 A0>");
+ const std::string AEOEUE_MAC_MOJIBAKE_BUGINESE("<C6 92 C3 B7 E2 80 B9>");
+ const std::string AEOEUE_LATIN1_MOJIBAKE_BUGINESE("<C2 80 C2 85 C2 86>");
+
+ #if MAC_ENV
+ const std::string AEOEUE_WIN_LOCAL_TO_UTF8 = AEOEUE_MAC_MOJIBAKE_BUGINESE;
+ const std::string AEOEUE_MAC_LOCAL_TO_UTF8 = AEOEUE_UTF8_BUGINESE;
+ #elif WIN_ENV
+ const std::string AEOEUE_WIN_LOCAL_TO_UTF8 = AEOEUE_UTF8_BUGINESE;
+ const std::string AEOEUE_MAC_LOCAL_TO_UTF8 = AEOEUE_WIN_MOJIBAKE_BUGINESE;
+ #else
+ // windows local encoding will work for UNIX (Latin1), but mac will result MOJIBAKE
+ const std::string AEOEUE_WIN_LOCAL_TO_UTF8 = AEOEUE_UTF8_BUGINESE;
+ const std::string AEOEUE_MAC_LOCAL_TO_UTF8 = AEOEUE_LATIN1_MOJIBAKE_BUGINESE;
+ #endif
#endif // XMPQE_GLOBALS_H
diff --git a/samples/source/dumpfile/main.cpp b/samples/source/dumpfile/main.cpp
index d1541a6..327a41e 100644
--- a/samples/source/dumpfile/main.cpp
+++ b/samples/source/dumpfile/main.cpp
@@ -1,5 +1,5 @@
// =================================================================================================
-// Copyright 2004-2008 Adobe Systems Incorporated
+// Copyright 2003 Adobe Systems Incorporated
// All Rights Reserved.
//
// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms
@@ -18,9 +18,9 @@
//sanity check platform/endianess
#include "globals.h"
-#ifdef WIN_ENV
-#pragma warning ( disable : 4267 ) // suppress string conversion warning
-// #pragma warning ( disable : 1234 ) // say what you do here
+#if XMP_WinBuild
+ #pragma warning ( disable : 4267 ) // suppress string conversion warning
+ // #pragma warning ( disable : 1234 ) // say what you do here
#endif
//only define in one non-public-source, non-header(.cpp) place
@@ -46,21 +46,40 @@ const int DUMPFILEVERSION=2;
#include "LargeFileAccess.hpp"
-void LFA_Throw ( const char* msg, int id )
+//void LFA_Throw ( const char* msg, int id )
+//{
+ //switch(id)
+ //{
+ // case kLFAErr_InternalFailure:
+ // Log::error("LFA User Abort:%s", msg);
+ // break;
+ // case kLFAErr_ExternalFailure:
+ // Log::error("LFA External Failure:%s", msg);
+ // break;
+ // case kLFAErr_UserAbort:
+ // Log::error("LFA User Abort:%s", msg);
+ // break;
+ // default:
+ // Log::error("LFA unknown error:%s", msg); //should not occur
+ // break;
+ //}
+//}
+#define XMP_Throw(msg,id) { throw XMP_Error ( id, msg ); }
+void LFA_Throw ( const char* msg, int id )
{
switch(id)
{
case kLFAErr_InternalFailure:
- Log::error("LFA User Abort:%s", msg);
+ XMP_Throw(msg,kXMPErr_InternalFailure);
break;
case kLFAErr_ExternalFailure:
- Log::error("LFA External Failure:%s", msg);
+ XMP_Throw(msg,kXMPErr_ExternalFailure);
break;
case kLFAErr_UserAbort:
- Log::error("LFA User Abort:%s", msg);
+ XMP_Throw(msg,kXMPErr_UserAbort);
break;
default:
- Log::error("LFA unknown error:%s", msg); //should not occur
+ XMP_Throw(msg,kXMPErr_UnknownException);
break;
}
}
@@ -105,10 +124,10 @@ void printUsageLong() {
Log::info("");
Log::info("-tree Shows the tree structure of the file. (default)");
Log::info("");
- Log::info("-keys Shows only the key-value nodes found in the file, as a list with no");
+ Log::info("-keys Shows only the key-value nodes found in the file, as a list with no");
Log::info(" hierarchical structure, in alphabetical order by key.");
Log::info("");
- Log::info("-list Shows only the key-value nodes found in the file, as a list with no");
+ Log::info("-list Shows only the key-value nodes found in the file, as a list with no");
Log::info(" hierarchical structure, in the order of parsing.");
Log::info("");
Log::info("-nocomments Supresses the comment portion of key-value nodes.");
diff --git a/samples/source/xmpcommand/Actions.cpp b/samples/source/xmpcommand/Actions.cpp
index 2324b10..d0e5e32 100644
--- a/samples/source/xmpcommand/Actions.cpp
+++ b/samples/source/xmpcommand/Actions.cpp
@@ -1,5 +1,5 @@
// =================================================================================================
-// Copyright 2004-2008 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
@@ -23,7 +23,6 @@ const char * XMP_EXE_VERSION= "4.4";
#include "globals.h"
#include "Log.h"
-#include "OutputUtils.h"
#include "Actions.h" //must come after XMP.hpp/XMP_Const.h
@@ -90,6 +89,146 @@ const char * XMP_EXE_VERSION= "4.4";
return content;
}
+ /* hand over the fileFormat and get a fileFormatName (2-3 Letters ie. PDF)
+ * and a Description (i.e. "Portable Document Format") back.
+ * see also public/include/XMP_Const.h
+ */
+ void fileFormatNameToString ( XMP_FileFormat fileFormat, std::string & fileFormatName, std::string & fileFormatDesc )
+ {
+ fileFormatName.erase();
+ fileFormatDesc.erase();
+ switch(fileFormat) {
+ case kXMP_PDFFile:
+ fileFormatName = "PDF ";fileFormatDesc = "Portable Document Format";break;
+ case kXMP_PostScriptFile:
+ fileFormatName = "PS ";fileFormatDesc = "Post Script";break;
+ case kXMP_EPSFile:
+ fileFormatName = "EPS ";fileFormatDesc = "Encapsulated Post Script";break;
+ case kXMP_JPEGFile:
+ fileFormatName = "JPEG";fileFormatDesc = "Joint Photographic Experts Group";break;
+ case kXMP_JPEG2KFile:
+ fileFormatName = "JPX ";fileFormatDesc = "JPEG 2000";break;
+ case kXMP_TIFFFile:
+ fileFormatName = "TIFF";fileFormatDesc = "Tagged Image File Format";break;
+ case kXMP_GIFFile:
+ fileFormatName = "GIF ";fileFormatDesc = "Graphics Interchange Format";break;
+ case kXMP_PNGFile:
+ fileFormatName = "PNG ";fileFormatDesc = "Portable Network Graphic";break;
+ case kXMP_MOVFile:
+ fileFormatName = "MOV ";fileFormatDesc = "Quicktime";break;
+ case kXMP_AVIFile:
+ fileFormatName = "AVI ";fileFormatDesc = "Quicktime";break;
+ case kXMP_CINFile:
+ fileFormatName = "CIN ";fileFormatDesc = "Cineon";break;
+ case kXMP_WAVFile:
+ fileFormatName = "WAV ";fileFormatDesc = "WAVE Form Audio Format";break;
+ case kXMP_MP3File:
+ fileFormatName = "MP3 ";fileFormatDesc = "MPEG-1 Audio Layer 3";break;
+ case kXMP_SESFile:
+ fileFormatName = "SES ";fileFormatDesc = "Audition session";break;
+ case kXMP_CELFile:
+ fileFormatName = "CEL ";fileFormatDesc = "Audition loop";break;
+ case kXMP_MPEGFile:
+ fileFormatName = "MPEG";fileFormatDesc = "Motion Pictures Experts Group";break;
+ case kXMP_MPEG2File:
+ fileFormatName = "MP2 ";fileFormatDesc = "MPEG-2";break;
+ case kXMP_WMAVFile:
+ fileFormatName = "WMAV";fileFormatDesc = "Windows Media Audio and Video";break;
+ case kXMP_HTMLFile:
+ fileFormatName = "HTML";fileFormatDesc = "HyperText Markup Language";break;
+ case kXMP_XMLFile:
+ fileFormatName = "XML ";fileFormatDesc = "Extensible Markup Language";break;
+ case kXMP_TextFile:
+ fileFormatName = "TXT ";fileFormatDesc = "text";break;
+ case kXMP_PhotoshopFile:
+ fileFormatName = "PSD ";fileFormatDesc = "Photoshop Document";break;
+ case kXMP_IllustratorFile:
+ fileFormatName = "AI ";fileFormatDesc = "Adobe Illustrator";break;
+ case kXMP_InDesignFile:
+ fileFormatName = "INDD";fileFormatDesc = "Adobe InDesign";break;
+ case kXMP_AEProjectFile:
+ fileFormatName = "AEP ";fileFormatDesc = "AfterEffects Project";break;
+ case kXMP_AEFilterPresetFile:
+ fileFormatName = "FFX ";fileFormatDesc = "AfterEffects Filter Preset";break;
+ case kXMP_EncoreProjectFile:
+ fileFormatName = "NCOR";fileFormatDesc = "Encore Project";break;
+ case kXMP_PremiereProjectFile:
+ fileFormatName = "PPRJ";fileFormatDesc = "Premier Project";break;
+ case kXMP_SWFFile:
+ fileFormatName = "SWF ";fileFormatDesc = "Shockwave Flash";break;
+ case kXMP_PremiereTitleFile:
+ fileFormatName = "PRTL";fileFormatDesc = "Premier Title";break;
+ case kXMP_UnknownFile:
+ fileFormatName = " ";fileFormatDesc = "Unkown file format";break;
+ default:
+ fileFormatName = " ";fileFormatDesc = "no known format constant";break;
+ }
+ } //fileFormatNameToString
+
+ /**
+ * GetFormatInfo-Flags (aka Handler-Flags)
+ * find this in XMP_Const.h under "Options for GetFormatInfo"
+ */
+ std::string XMPFiles_FormatInfoToString ( const XMP_OptionBits options) {
+ std::string outString;
+
+ if( options & kXMPFiles_CanInjectXMP )
+ outString.append(" CanInjectXMP");
+ if( options & kXMPFiles_CanExpand )
+ outString.append(" CanExpand");
+ if( options & kXMPFiles_CanRewrite )
+ outString.append(" CanRewrite");
+ if( options & kXMPFiles_PrefersInPlace )
+ outString.append(" PrefersInPlace");
+ if( options & kXMPFiles_CanReconcile )
+ outString.append(" CanReconcile");
+ if( options & kXMPFiles_AllowsOnlyXMP )
+ outString.append(" AllowsOnlyXMP");
+ if( options & kXMPFiles_ReturnsRawPacket )
+ outString.append(" ReturnsRawPacket");
+ if( options & kXMPFiles_HandlerOwnsFile )
+ outString.append(" HandlerOwnsFile");
+ if( options & kXMPFiles_AllowsSafeUpdate )
+ outString.append(" AllowsSafeUpdate");
+
+ if (outString.empty()) outString=" (none)";
+ return outString;
+ }
+
+ /**
+ * openOptions to String, find this in
+ * XMP_Const.h under "Options for OpenFile"
+ */
+ std::string XMPFiles_OpenOptionsToString ( const XMP_OptionBits options) {
+ std::string outString;
+ if( options & kXMPFiles_OpenForRead)
+ outString.append(" OpenForRead");
+ if( options & kXMPFiles_OpenForUpdate)
+ outString.append(" OpenForUpdate");
+ if( options & kXMPFiles_OpenOnlyXMP)
+ outString.append(" OpenOnlyXMP");
+ if( options & kXMPFiles_OpenStrictly)
+ outString.append(" OpenStrictly");
+ if( options & kXMPFiles_OpenUseSmartHandler)
+ outString.append(" OpenUseSmartHandler");
+ if( options & kXMPFiles_OpenUsePacketScanning)
+ outString.append(" OpenUsePacketScanning");
+ if( options & kXMPFiles_OpenLimitedScanning)
+ outString.append(" OpenLimitedScanning");
+
+ if (outString.empty()) outString=" (none)";
+ return outString;
+ }
+
+ //just the callback-Function
+ XMP_Status DumpToString( void * refCon, XMP_StringPtr outStr, XMP_StringLen outLen )
+ {
+ XMP_Status status = 0;
+ std::string* dumpString = static_cast < std::string* > ( refCon );
+ dumpString->append (outStr, outLen);
+ return status;
+ }
+
// ********************************************************************************
void Actions::version() {
@@ -100,21 +239,21 @@ void Actions::version() {
Log::info("%s", filesVersion.message);
Log::info("Executable Version:%s", XMP_EXE_VERSION);
-#ifdef WIN_ENV
+#if XMP_WinBuild
Log::info("compiled on Windows on %s, %s",__DATE__,__TIME__);
#endif
-#ifdef MAC_ENV
+#if XMP_MacBuild
Log::info("compiled on Mac on %s, %s",__DATE__,__TIME__);
#endif
-#ifndef NDEBUG
+#if NDEBUG
Log::info("Configuration: debug");
#else
Log::info("Configuration: release");
#endif
Log::info("Edition: Public SDK");
-#if defined(XMPQE_BIG_ENDIAN)
+#if XMPQE_BIG_ENDIAN
Log::info("Big endian machine");
-#elif defined(XMPQE_LITTLE_ENDIAN)
+#elif XMPQE_LITTLE_ENDIAN
Log::info("Little endian machine");
#else
Log::warn("unknown Endian !!!");
@@ -144,7 +283,7 @@ void Actions::info() {
out_filepath.c_str(),this->mediafile.c_str());
//openOptions (also verify that identical to what's been used)
- std::string openFlagsString=Utils::XMPFiles_OpenOptionsToString(out_openFlags);
+ std::string openFlagsString=XMPFiles_OpenOptionsToString(out_openFlags);
Log::info("openFlags: %s (%X)", openFlagsString.c_str(), out_openFlags);
if ( generalOpenFlags != out_openFlags )
Log::warn("out_openFlags (0x%X) differ from those used for open (0x%X)",
@@ -152,12 +291,12 @@ void Actions::info() {
//FileFormat (resolves, usually by way of extension unless specified)
std::string fileFormatName, fileFormatDescription;
- Utils::fileFormatNameToString(out_fileFormat,fileFormatName,fileFormatDescription);
+ fileFormatNameToString(out_fileFormat,fileFormatName,fileFormatDescription);
Log::info("fileFormat: %s (%s,0x%X)",
fileFormatDescription.c_str(),fileFormatName.c_str(),out_fileFormat);
//FormatInfo aka "HandlerFlags" (show what is possible with this format)
- std::string formatInfoString=Utils::XMPFiles_FormatInfoToString(out_handlerFlags);
+ std::string formatInfoString=XMPFiles_FormatInfoToString(out_handlerFlags);
Log::info("formatInfo:%s (0x%X)", formatInfoString.c_str(), out_handlerFlags);
xmpFile.CloseFile(generalCloseFlags); //(enabled again now that bug 1352603 is fixed)
@@ -209,7 +348,9 @@ void Actions::dump() {
if ( !xmpFile.GetXMP(&xmpMeta,0,0))
Log::warn("file contains no XMP. - says xmpFile.GetXMP()");
else {
- Utils::dumpXMP(&xmpMeta);
+ std::string dump;
+ xmpMeta.DumpObject(DumpToString, &dump);
+ Log::info( dump );
}
xmpFile.CloseFile(generalCloseFlags); //(enabled again now that bug 1352603 is fixed)
@@ -217,42 +358,6 @@ void Actions::dump() {
///////////////////////////////////////////////////////////////////////////////////////
-void Actions::thumb() {
- if(!this->switch_nocheck) verifyFileIsReadable(this->mediafile); //need readability
- XMP_FileFormat xmpFileFormat=kXMP_UnknownFile;
-
- SXMPFiles xmpFile;
- XMP_ThumbnailInfo tnailInfo;
- if ( !xmpFile.OpenFile(this->mediafile,
- xmpFileFormat,
- generalOpenFlags | kXMPFiles_OpenCacheTNail) )
- Log::error("error opening file %s",this->mediafile.c_str());
-
- if ( !xmpFile.GetThumbnail(&tnailInfo)) {
- Log::warn("no thumbnail found. - says xmpFile.GetThumbnail()");
- //TODO: comment-in again once bug 1352603 fixed
- //xmpFile.CloseFile(generalCloseFlags);
- return;
- }
- else {
- //FileFormat (resolves, usually by way of extension unless specified)
- std::string fileFormatName, fileFormatDescription;
- Utils::fileFormatNameToString(tnailInfo.fileFormat,fileFormatName,fileFormatDescription);
- Log::info("fileFormat: %s (%s,0x%X)",
- fileFormatDescription.c_str(),fileFormatName.c_str(),tnailInfo.fileFormat);
-
- Log::info("full image size:%dx%d",tnailInfo.fullWidth,tnailInfo.fullHeight);
- Log::info("full orientation:0x%X",tnailInfo.fullOrientation);
- Log::info("thumbnail size:%dx%d (%d bytes)",tnailInfo.tnailWidth,tnailInfo.tnailHeight,tnailInfo.tnailSize);
- Log::info("thumbnail orientation:0x%X",tnailInfo.tnailOrientation);
- Log::info("thumbnail format 0x%X",tnailInfo.tnailFormat);
- }
-
- xmpFile.CloseFile(generalCloseFlags); //(enabled again now that bug 1352603 is fixed)
-}
-
-
-///////////////////////////////////////////////////////////////////////////////////////
void Actions::put() {
//need read and write
if(!this->switch_nocheck) verifyFileIsWritable(this->mediafile);
@@ -293,7 +398,7 @@ void Actions::put() {
} else { //handle the error
//FileFormat (resolves, usually by way of extension unless specified TODO)
std::string fileFormatName, fileFormatDescription;
- Utils::fileFormatNameToString(out_fileFormat,fileFormatName,fileFormatDescription);
+ fileFormatNameToString(out_fileFormat,fileFormatName,fileFormatDescription);
Log::error("the File %s of type %s neither contains XMP nor can it be injected (at least kXMPFiles_CanInjectXMP not returned)",
this->mediafile.c_str(), fileFormatName.c_str());
};
@@ -315,9 +420,6 @@ void Actions::doAction() {
case Actions::INFO:
info();
break;
- case Actions::THUMB: //GetThumbnail()
- thumb();
- break;
case Actions::PUT: //PutXMP()
put();
break;
diff --git a/samples/source/xmpcommand/Actions.h b/samples/source/xmpcommand/Actions.h
index 084c260..f43afbc 100644
--- a/samples/source/xmpcommand/Actions.h
+++ b/samples/source/xmpcommand/Actions.h
@@ -1,5 +1,5 @@
// =================================================================================================
-// Copyright 2004-2008 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
@@ -11,7 +11,7 @@
class Actions {
public:
- enum ACTION_TYPE { NONE, VERSION, INFO, PUT, GET, DUMP, THUMB};
+ enum ACTION_TYPE { NONE, VERSION, INFO, PUT, GET, DUMP };
//the params to be set:
bool switch_safe; //asking for closing file with kXMPFiles_UpdateSafely (safe+rename that is)
@@ -36,7 +36,6 @@ class Actions {
private:
void version(void);
void info(void);
- void thumb(void);
void put();
void get(void);
void dump(void);
diff --git a/samples/source/xmpcommand/PrintUsage.cpp b/samples/source/xmpcommand/PrintUsage.cpp
index 3931e18..7560cbb 100644
--- a/samples/source/xmpcommand/PrintUsage.cpp
+++ b/samples/source/xmpcommand/PrintUsage.cpp
@@ -1,5 +1,5 @@
// =================================================================================================
-// Copyright 2004-2008 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
@@ -60,8 +60,6 @@ namespace XMPQE {
Log::info("");
Log::info("Actions:");
Log::info(" info <mediafile> Prints basic information about the file.");
- Log::info(" thumb <mediafile> Prints information about the native thumbnail ");
- Log::info(" if one is included, or informs that none is available.");
Log::info(" put <xmpfile> <mediafile> Injects the XMP contained in the specified xmpfile ");
Log::info(" into the specified mediafile.");
Log::info(" get <mediafile> Retrieves the XMP Packet contained in the specified mediafile.");
diff --git a/samples/source/xmpcommand/PrintUsage.h b/samples/source/xmpcommand/PrintUsage.h
index a48b24a..982fe41 100644
--- a/samples/source/xmpcommand/PrintUsage.h
+++ b/samples/source/xmpcommand/PrintUsage.h
@@ -1,5 +1,5 @@
// =================================================================================================
-// Copyright 2004-2008 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
diff --git a/samples/source/xmpcommand/main.cpp b/samples/source/xmpcommand/XMPCommand.cpp
index 2dc4374..a89904c 100644
--- a/samples/source/xmpcommand/main.cpp
+++ b/samples/source/xmpcommand/XMPCommand.cpp
@@ -1,5 +1,5 @@
// =================================================================================================
-// Copyright 2005-2008 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
@@ -34,16 +34,23 @@
//QE related
#include "Actions.h"
#include "Log.h"
+#include "LargeFileAccess.hpp"
// -help output
#include "PrintUsage.h"
+
namespace XMPQE {
void InitDependencies()
{
if ( ! SXMPMeta::Initialize() )
Log::error( "XMPMeta::Initialize failed!" );
- if ( ! SXMPFiles::Initialize() )
+ XMP_OptionBits options = 0;
+ #if UNIX_ENV
+ options |= kXMPFiles_ServerMode;
+ #endif
+
+ if ( ! SXMPFiles::Initialize ( options ) )
Log::error( "XMPFiles::Initialize failed!" );
}
@@ -140,8 +147,6 @@ int main ( int argc, const char * argv[] )
if ( !strcmp("info",argv[i])) {
action.actiontype= Actions::INFO;
- } else if ( !strcmp("thumb",argv[i])) {
- action.actiontype= Actions::THUMB;
} else if ( !strcmp("put",argv[i]) || !strcmp("inject",argv[i]) ) {
action.actiontype= Actions::PUT;
} else if ( !strcmp("get",argv[i]) || !strcmp("extract",argv[i]) ) {