[Helix-client-dev] CR/CN: Implemented -xmlout program option, added support for -xmlin pictures [GMPMetaEditor branch]

Petar Basic <[email protected]> Mon, 18 Jan 2010 21:44:28 +0100
Newsgroups gmane.comp.multimedia.helix.devel
Message-ID <[email protected]>
Modified by: pbasic at real.com
Date: 2010/01/18
Project: GMPMetaEditor (meta3gp.exe)

Synopsis:
Implemented -xmlout program option, added support for -xmlin pictures
[GMPMetaEditor branch]

Details:
1.) -xmlout option writes extracted or injected metadata into the
specified XML file.

2.) Added support for XML input file elements:
"albums/album/image/fileName", "albums/album/image/fileName2".

Files Modified:
datatype/common/util/pub/metautil.h
datatype/common/util/metautil.cpp
datatype/tools/dtdriver/apps/meta3gp/HXXmlInputParser.h
datatype/tools/dtdriver/apps/meta3gp/HXXmlInputParser.cpp
datatype/tools/dtdriver/apps/meta3gp/main.cpp

Platforms and Profiles Affected:
All

Image Size and Heap Use impact:
None

Platforms and Profiles Build Verified:
system id: win32-i386-vc7, sunos-5.10-sparc-studio11
profile: helix-client-all-defines

Platforms and Profiles Functionality Verified:
x86 Windows XP SP2
Sparc SunOS 5.10

Branch:
GMPMetaEditor

Copyright assignment:
I am a RealNetworks employee or contractor.

_______________________________________________
Helix-client-dev mailing list
[email protected]
http://lists.helixcommunity.org/mailman/listinfo/helix-client-dev
datatype_common_util.diff (application/octet-stream, 23.3 KB)
Index: metautil.cpp
===================================================================
RCS file: /cvsroot/datatype/common/util/metautil.cpp,v
retrieving revision 1.13
diff -d -H -w -U30 -r1.13 metautil.cpp
--- metautil.cpp	6 Jan 2010 17:38:26 -0000	1.13
+++ metautil.cpp	18 Jan 2010 21:24:09 -0000
@@ -2777,86 +2777,86 @@
             res = HXR_OK;
         }
         fclose(fp);
     }
     return res;
 }
 
 MetaInfo::MetaInfo()
 {
     Clear();
 }
 
 MetaInfo::~MetaInfo()
 {
     ClearKeywords();
     ClearPictures();
 }
 
 MetaInfo::MetaInfo(const MetaInfo& rhs)
 {
     *this = rhs;
 }
 
 MetaInfo& MetaInfo::operator=(const MetaInfo& rhs)
 {
     Clear();
     UpdateItems(rhs);
     return *this;
 }
 
-void MetaInfo::Dump(UINT32 metaInfoSetFlags)
+void MetaInfo::Dump(UINT32 metaInfoSetFlags) const
 {
     // Generic
     if(metaInfoSetFlags & METAINFO_SET_GENERIC)
     {
         Dump(_GENERIC_META_INFO_TITLE_KEY, m_stringItems[StringMetaItem_Title]);
         Dump(_GENERIC_META_INFO_COMPOSER_KEY, m_stringItems[StringMetaItem_Composer]);
         Dump(_GENERIC_META_INFO_COPYRIGHT_KEY, m_stringItems[StringMetaItem_Copyright]);
         Dump(_GENERIC_META_INFO_DESCRIPTION_KEY, m_stringItems[StringMetaItem_Description]);
         Dump(_GENERIC_META_INFO_ARTIST_KEY, m_stringItems[StringMetaItem_Artist]);
         Dump(_GENERIC_META_INFO_GENRE_KEY, m_stringItems[StringMetaItem_Genre]);
         Dump(_GENERIC_META_INFO_ALBUM_KEY, m_stringItems[StringMetaItem_Album]);
         Dump(_GENERIC_META_INFO_UITS_KEY, m_stringItems[StringMetaItem_UITSData], FALSE);
 
         Dump(_GENERIC_META_INFO_TRACK_NUMBER_KEY, m_ulongItems[UIntMetaItem_TrackNumber],
                                                 m_ulongItemValidFlags[UIntMetaItem_TrackNumber]);
         Dump(_GENERIC_META_INFO_RECORDING_YEAR_KEY, m_ulongItems[UIntMetaItem_RecordingYear],
                                                 m_ulongItemValidFlags[UIntMetaItem_RecordingYear]);
 
         for(UINT32 i = 0; i < GetPictureCount(); i++)
         {
             printf(_GENERIC_META_INFO_PICTURE_KEY": ", int(i));
             ID3Tools::APICFrame* pFrame = GetPictureByIndex(i);
             if(pFrame)
             {
-                printf("PictureType=%d, MimeType=%s, PictureData=%d bytes\n",
+                printf("PictureType=%d, MimeType=%s, PictureSize=%d bytes\n",
                     pFrame->GetPictureType(),
                     (const char*)pFrame->GetMimeType(),
                     pFrame->GetPictureDataSize());
             }
         }
     }
 
     // iTunes
     if(metaInfoSetFlags & METAINFO_SET_ITUNES_SPECIFIC)
     {
         Dump(_ITUNES_META_INFO_TRACK_SUBTITLE_KEY, m_stringItems[StringMetaItem_TrackSubtitle]);
         Dump(_ITUNES_META_INFO_GENERATOR_TOOL_KEY, m_stringItems[StringMetaItem_GeneratorTool]);
         Dump(_ITUNES_META_INFO_ENCODED_BY_KEY, m_stringItems[StringMetaItem_EncodedBy]);
         Dump(_ITUNES_META_INFO_GROUPING_KEY, m_stringItems[StringMetaItem_Grouping]);
         Dump(_ITUNES_META_INFO_ALBUM_ARTIST_KEY, m_stringItems[StringMetaItem_AlbumArtist]);
         Dump(_ITUNES_META_INFO_LYRICS_KEY, m_stringItems[StringMetaItem_Lyrics]);
         Dump(_ITUNES_META_INFO_BEATS_PER_MINUTE_KEY, m_ulongItems[UIntMetaItem_BeatsPerMinute],
                                             m_ulongItemValidFlags[UIntMetaItem_BeatsPerMinute]);
         Dump(_ITUNES_META_INFO_PART_OF_COMPILATION_KEY, m_ulongItems[UIntMetaItem_PartOfCompilation],
                                             m_ulongItemValidFlags[UIntMetaItem_PartOfCompilation]);
         Dump(_ITUNES_META_INFO_EXPLICIT_CONTENT_KEY, m_ulongItems[UIntMetaItem_ExplicitContent],
                                             m_ulongItemValidFlags[UIntMetaItem_ExplicitContent]);
         Dump(_ITUNES_META_INFO_TRACK_COUNT_KEY, m_ulongItems[UIntMetaItem_TrackCount],
                                             m_ulongItemValidFlags[UIntMetaItem_TrackCount]);
         Dump(_ITUNES_META_INFO_DISC_NUMBER_KEY, m_ulongItems[UIntMetaItem_DiscNumber],
                                             m_ulongItemValidFlags[UIntMetaItem_DiscNumber]);
         Dump(_ITUNES_META_INFO_DISC_COUNT_KEY, m_ulongItems[UIntMetaItem_DiscCount],
                                             m_ulongItemValidFlags[UIntMetaItem_DiscCount]);
     }
 
@@ -2864,60 +2864,206 @@
     if(metaInfoSetFlags & METAINFO_SET_3GPP_SPECIFIC)
     {
         Dump(_3GPP_META_INFO_GLOBAL_LANGUAGE_ENCODING_KEY, m_stringItems[StringMetaItem_GlobalLanguageEncoding]);
         Dump(_3GPP_META_INFO_RATING_ENTITY_KEY, m_stringItems[StringMetaItem_RatingEntity]);
         Dump(_3GPP_META_INFO_RATING_CRITERIA_KEY, m_stringItems[StringMetaItem_RatingCriteria]);
         Dump(_3GPP_META_INFO_RATING_INFO_KEY, m_stringItems[StringMetaItem_RatingInfo]);
         Dump(_3GPP_META_INFO_CLASSIFICATION_ENTITY_KEY, m_stringItems[StringMetaItem_ClassificationEntity]);
         Dump(_3GPP_META_INFO_CLASSIFICATION_TABLE_KEY, m_ulongItems[UIntMetaItem_ClassificationTable],
                                                 m_ulongItemValidFlags[UIntMetaItem_ClassificationTable]);
         Dump(_3GPP_META_INFO_CLASSIFICATION_INFO_KEY, m_stringItems[StringMetaItem_ClassificationInfo]);
         Dump(_3GPP_META_INFO_LOCATION_NAME_KEY, m_stringItems[StringMetaItem_LocationName]);
         Dump(_3GPP_META_INFO_LOCATION_ASTRONOMICAL_BODY_KEY, m_stringItems[StringMetaItem_LocationAstronomicalBody]);
         Dump(_3GPP_META_INFO_LOCATION_ADDITIONAL_NOTES_KEY, m_stringItems[StringMetaItem_LocationAdditionalNotes]);
         Dump(_3GPP_META_INFO_LOCATION_ROLE_KEY, m_ulongItems[UIntMetaItem_LocationRole],
                                         m_ulongItemValidFlags[UIntMetaItem_LocationRole]);
         Dump(_3GPP_META_INFO_LOCATION_LONGITUDE_KEY, m_stringItems[StringMetaItem_LocationLongitude]);
         Dump(_3GPP_META_INFO_LOCATION_LATITUDE_KEY, m_stringItems[StringMetaItem_LocationLatitude]);
         Dump(_3GPP_META_INFO_LOCATION_ALTITUDE_KEY, m_stringItems[StringMetaItem_LocationAltitude]);
 
         for(UINT32 i = 0; i < GetKeywordCount(); i++)
         {
             printf(_3GPP_META_INFO_KEYWORD_KEY": ", int(i));
             EncodedString kwd;
             GetKeyword(i, kwd);
             kwd.Dump();
             printf("\n");
         }
     }
 }
 
+void MetaInfo::DumpElementToXMLString(CHXString& out, const CHXString& ws,
+                                const EncodedString& src, const CHXString& elementName,
+                                const CHXString& attributes)
+{
+    if(!src.IsEmpty() || !attributes.IsEmpty())
+    {
+        out += ws + "<" + elementName;
+
+        if(!attributes.IsEmpty())
+        {
+            out += " " + attributes;
+        }
+
+        if(src.IsEmpty())
+        {
+            out += "/>";
+        }
+        else
+        {
+            out += ">";
+            out += src.GetData();
+            out += "</" + elementName + ">";
+        }
+        out += "\n";
+    }
+}
+
+void MetaInfo::DumpStringItemToXMLString(CHXString& out, const CHXString& ws,
+                                    eStringMetaItem item, const CHXString& elementName,
+                                    const CHXString& attributes) const
+{
+    DumpElementToXMLString(out, ws, m_stringItems[item], elementName, attributes);
+}
+
+void MetaInfo::DumpUIntToXMLString(CHXString& out, const CHXString& ws,
+                                    UINT32 src, const CHXString& elementName,
+                                    const CHXString& attributes)
+{
+    CHXString s;
+    s.AppendULONG(src);
+    EncodedString es((const char*)s);
+    DumpElementToXMLString(out, ws, es, elementName, attributes);
+}
+
+void MetaInfo::DumpUIntItemToXMLString(CHXString& out, const CHXString& ws,
+                                    eUIntMetaItem item, const CHXString& elementName,
+                                    const CHXString& attributes) const
+{
+    if(m_ulongItemValidFlags[item])
+    {
+        DumpUIntToXMLString(out, ws, m_ulongItems[item], elementName, attributes);
+    }
+}
+
+void MetaInfo::DumpToXMLString(CHXString& out,
+                            const CHXString& uitsFilename, const CHXString& picOutFilenamePrefix,
+                            UINT32 metaInfoSetFlags, UINT32 tabLevel, UINT32 tabWidth) const
+{
+    CHXString ws0 = CHXString(' ', tabLevel * tabWidth);
+    CHXString ws1 = CHXString(' ', (tabLevel + 1) * tabWidth);
+
+    out += ws0 + "<metadata>\n";
+
+    // Generic
+    DumpStringItemToXMLString(out, ws1, StringMetaItem_Title, "title");
+    DumpStringItemToXMLString(out, ws1, StringMetaItem_Composer, "composer");
+    DumpStringItemToXMLString(out, ws1, StringMetaItem_Copyright, "copyright");
+    DumpStringItemToXMLString(out, ws1, StringMetaItem_Description, "description");
+    DumpStringItemToXMLString(out, ws1, StringMetaItem_Artist, "artist");
+    DumpStringItemToXMLString(out, ws1, StringMetaItem_Genre, "genre");
+    DumpStringItemToXMLString(out, ws1, StringMetaItem_Album, "album");
+    DumpUIntItemToXMLString(out, ws1, UIntMetaItem_TrackNumber, "trackNumber");
+    DumpUIntItemToXMLString(out, ws1, UIntMetaItem_RecordingYear, "year");
+
+    // UITS
+    if(!m_stringItems[StringMetaItem_UITSData].IsEmpty())
+    {
+        CHXString attUits = "size=\"";
+        attUits.AppendULONG(m_stringItems[StringMetaItem_UITSData].GetByteLength());
+        attUits += "\"";
+        if(!uitsFilename.IsEmpty())
+        {
+            attUits += " path=\"" + uitsFilename + "\"";
+        }
+        DumpElementToXMLString(out, ws1, EncodedString(), "uits", attUits);
+    }
+
+    // Pictures
+    for(UINT32 i = 0; i < GetPictureCount(); i++)
+    {
+        ID3Tools::APICFrame* pFrame = GetPictureByIndex(i);
+        if(pFrame)
+        {
+            CHXString attPic = "size=\"";
+            attPic.AppendULONG(pFrame->GetPictureDataSize());
+            attPic += "\" pictureType=\"";
+            attPic.AppendULONG(pFrame->GetPictureType());
+            attPic += "\" type=\"" + pFrame->GetMimeType() + "\"";
+            if(!picOutFilenamePrefix.IsEmpty())
+            {
+                attPic += " path=\"" + MakePictureFilename(*pFrame, i, picOutFilenamePrefix) + "\"";
+            }
+            DumpElementToXMLString(out, ws1, EncodedString(), "picture", attPic);
+        }
+    }
+
+    // iTunes
+    DumpStringItemToXMLString(out, ws1, StringMetaItem_TrackSubtitle, "subtitle");
+    DumpStringItemToXMLString(out, ws1, StringMetaItem_GeneratorTool, "generatorTool");
+    DumpStringItemToXMLString(out, ws1, StringMetaItem_EncodedBy, "encodedBy");
+    DumpStringItemToXMLString(out, ws1, StringMetaItem_Grouping, "grouping");
+    DumpStringItemToXMLString(out, ws1, StringMetaItem_AlbumArtist, "albumArtist");
+    DumpStringItemToXMLString(out, ws1, StringMetaItem_Lyrics, "lyrics");
+    DumpUIntItemToXMLString(out, ws1, UIntMetaItem_BeatsPerMinute, "bpm");
+    DumpUIntItemToXMLString(out, ws1, UIntMetaItem_PartOfCompilation, "partOfCompilation");
+    DumpUIntItemToXMLString(out, ws1, UIntMetaItem_ExplicitContent, "explicitContent");
+    DumpUIntItemToXMLString(out, ws1, UIntMetaItem_TrackCount, "trackCount");
+    DumpUIntItemToXMLString(out, ws1, UIntMetaItem_DiscNumber, "discNumber");
+    DumpUIntItemToXMLString(out, ws1, UIntMetaItem_DiscCount, "discCount");
+
+    // 3GPP
+    DumpStringItemToXMLString(out, ws1, StringMetaItem_GlobalLanguageEncoding, "language");
+    DumpStringItemToXMLString(out, ws1, StringMetaItem_RatingEntity, "ratingEntity");
+    DumpStringItemToXMLString(out, ws1, StringMetaItem_RatingCriteria, "ratingCriteria");
+    DumpStringItemToXMLString(out, ws1, StringMetaItem_RatingInfo, "ratingInfo");
+    DumpStringItemToXMLString(out, ws1, StringMetaItem_ClassificationEntity, "classificationEntity");
+    DumpUIntItemToXMLString(out, ws1, UIntMetaItem_ClassificationTable, "classificationTable");
+    DumpStringItemToXMLString(out, ws1, StringMetaItem_ClassificationInfo, "classificationInfo");
+    DumpStringItemToXMLString(out, ws1, StringMetaItem_LocationName, "locationName");
+    DumpStringItemToXMLString(out, ws1, StringMetaItem_LocationAstronomicalBody, "locationAstronomicalBody");
+    DumpStringItemToXMLString(out, ws1, StringMetaItem_LocationAdditionalNotes, "locationAdditionalNotes");
+    DumpUIntItemToXMLString(out, ws1, UIntMetaItem_LocationRole, "locationRole");
+    DumpStringItemToXMLString(out, ws1, StringMetaItem_LocationLongitude, "locationLongitude");
+    DumpStringItemToXMLString(out, ws1, StringMetaItem_LocationLatitude, "locationLatitude");
+    DumpStringItemToXMLString(out, ws1, StringMetaItem_LocationAltitude, "locationAltitude");
+
+    for(UINT32 i = 0; i < GetKeywordCount(); i++)
+    {
+        EncodedString kwd;
+        GetKeyword(i, kwd);
+        DumpElementToXMLString(out, ws1, kwd, "keyword");
+    }
+
+    out += ws0 + "</metadata>\n";
+}
+
 void MetaInfo::Clear()
 {
     for(int iStrItem = StringMetaItem_Begin_; iStrItem < StringMetaItem_End_; iStrItem++)
     {
         m_stringItems[iStrItem].Clear();
     }
 
     for(int iUIntItem = UIntMetaItem_Begin_; iUIntItem < UIntMetaItem_End_; iUIntItem++)
     {
         m_ulongItems[iUIntItem] = 0;
         m_ulongItemValidFlags[iUIntItem] = FALSE;
     }
 
     ClearKeywords();
     ClearPictures();
 }
 
 void MetaInfo::ClearItems(UINT32 metaInfoSetFlags)
 {
     for(int iStrItem = StringMetaItem_Begin_; iStrItem < StringMetaItem_End_; iStrItem++)
     {
         if(IsItemSupportedByAnySet((eStringMetaItem)iStrItem, metaInfoSetFlags))
         {
             m_stringItems[iStrItem].Clear();
         }
     }
 
     for(int iUIntItem = UIntMetaItem_Begin_; iUIntItem < UIntMetaItem_End_; iUIntItem++)
     {
         if(IsItemSupportedByAnySet((eUIntMetaItem)iUIntItem, metaInfoSetFlags))
@@ -3395,60 +3541,83 @@
     return HXR_OK;
 }
 
 UINT32 MetaInfo::GetPictureCount() const
 {
     return (UINT32)m_aPictures.GetSize();
 }
 
 ID3Tools::APICFrame* MetaInfo::GetPictureByIndex(UINT32 index) const
 {
     if(index >= (UINT32)m_aPictures.GetSize())
     {
         return 0;
     }
     return (ID3Tools::APICFrame*)m_aPictures.GetAt(index);
 }
 
 ID3Tools::APICFrame* MetaInfo::GetPictureByType(UINT8 APICFramePictureType) const
 {
     for(int i = 0; i < m_aPictures.GetSize(); i++)
     {
         ID3Tools::APICFrame* pFrame = (ID3Tools::APICFrame*)m_aPictures.GetAt(i);
         if(pFrame && (pFrame->GetPictureType() == APICFramePictureType))
         {
             return pFrame;
         }
     }
     return 0;
 }
 
+CHXString MetaInfo::MakePictureFilename(const ID3Tools::APICFrame& frame, UINT32 index, const CHXString& prefix)
+{
+    CHXString filename = prefix.IsEmpty() ? "picture" : prefix;
+    filename.AppendULONG(index);
+    filename += "_type";
+    filename.AppendULONG(frame.GetPictureType());
+
+    if(frame.GetMimeType() == "image/jpeg")
+    {
+        filename += ".jpg";
+    }
+    else if(frame.GetMimeType() == "image/png")
+    {
+        filename += ".png";
+    }
+    else
+    {
+        filename += ".bin";
+    }
+
+    return filename;
+}
+
 HX_RESULT MetaInfo::ClearPictures()
 {
     ClearAPICFrameArray(m_aPictures);
     return HXR_OK;
 }
 
 HX_RESULT MetaInfo::AddPicture(ID3Tools::APICFrame* pFrame, UINT32 typeConflictFlags)
 {
     if(!pFrame)
     {
         return HXR_INVALID_PARAMETER;
     }
 
     ID3Tools::APICFrame* pOldFrame = GetPictureByType(pFrame->GetPictureType());
     if(pOldFrame)
     {
         HXBOOL bResolved = FALSE;
         HX_RESULT retVal = HXR_OK;
         
         if(typeConflictFlags & APTCF_FirstChanceOverwrite)
         {
             retVal = RemovePicture(pOldFrame->GetPictureType());
             bResolved = TRUE;
         }
 
         if(SUCCEEDED(retVal) && !bResolved && (typeConflictFlags & APTCF_SecondChanceAssignFreeType))
         {
             UINT8 freePicType = 0;
             retVal = FindFreePictureType(freePicType);
             if(SUCCEEDED(retVal))
Index: pub/metautil.h
===================================================================
RCS file: /cvsroot/datatype/common/util/pub/metautil.h,v
retrieving revision 1.8
diff -d -H -w -U30 -r1.8 metautil.h
--- pub/metautil.h	6 Jan 2010 17:38:28 -0000	1.8
+++ pub/metautil.h	18 Jan 2010 21:24:10 -0000
@@ -674,116 +674,135 @@
     static HXBOOL IsItemSupportedByAllSets(eArrayMetaItem itemName, UINT32 metaInfoSetFlags)
     {
         return (GetSetsWhichSupportItem(itemName) & metaInfoSetFlags) != metaInfoSetFlags;
     }
 
     static UINT32 GetFlavorsWhichSupportItem(eArrayMetaItem itemName);
     static HXBOOL IsItemSupportedByAnyFlavor(eArrayMetaItem itemName, UINT32 metaFlavorFlags)
     {
         return (GetFlavorsWhichSupportItem(itemName) & metaFlavorFlags) != 0;
     }
     static HXBOOL IsItemSupportedByAllFlavors(eArrayMetaItem itemName, UINT32 metaFlavorFlags)
     {
         return (GetFlavorsWhichSupportItem(itemName) & metaFlavorFlags) != metaFlavorFlags;
     }
     static HXBOOL IsKeywordValidForFlavors(const EncodedString& keyword, UINT32 metaFlavorFlags);
 
     static HXBOOL IsAPICFrameValidForFlavors(const ID3Tools::APICFrame& frame, UINT32 metaFlavorFlags);
     static HXBOOL IsAPICPictureTypeValidForFlavors(UINT32 pictureType, UINT32 metaFlavorFlags);
 
     static HX_RESULT ScanXMLStringEncoding(const UINT8* pXML, UINT32 uXMLSize, HX_TEXT_ENCODING_TYPE& outEncoding);
     static HX_RESULT LoadXMLStringFromFile(const char* szFileName, EncodedString& outXML);
     static HX_RESULT SaveXMLStringToFile(const char* szFileName, const EncodedString& sXML);
 
 public:
     MetaInfo();
     ~MetaInfo();
 
     MetaInfo(const MetaInfo& rhs);
     MetaInfo& operator=(const MetaInfo& rhs);
 
-    void Dump(UINT32 metaInfoSetFlags = METAINFO_SET_ALL);
+    void Dump(UINT32 metaInfoSetFlags = METAINFO_SET_ALL) const;
+    void DumpToXMLString(CHXString& out,
+                    const CHXString& uitsFilename = "", const CHXString& picOutFilenamePrefix = "",
+                    UINT32 metaInfoSetFlags = METAINFO_SET_ALL, UINT32 tabLevel = 0, UINT32 tabWidth = 2) const;
 
     void Clear();
     void ClearItems(UINT32 metaInfoSetFlags = METAINFO_SET_ALL);
     HX_RESULT UpdateItems(const MetaInfo& src, UINT32 metaInfoSetFlags = METAINFO_SET_ALL);
 
     HX_RESULT CopyToValues(IUnknown* pContext, IHXValues* pValues, HXBOOL bBlast, UINT32 metaInfoSetFlags = METAINFO_SET_ALL) const;
     HX_RESULT CopyFromValues(IHXValues* pValues, HXBOOL bBlast, UINT32 metaInfoSetFlags = METAINFO_SET_ALL);
 
     // Getters always update <out>.
     // String getters return success if <out> is empty.
     // UINT getters return success if the value copied to <out> is considered valid.
     HX_RESULT GetStringItem(eStringMetaItem itemName, EncodedString& out) const;
     HX_RESULT SetStringItem(eStringMetaItem itemName, const EncodedString& val, UINT32 metaFlavorFlags = METADATA_FLAVOR_ALL);
 
     HX_RESULT GetUIntItem(eUIntMetaItem itemName, UINT32& out) const;
     HX_RESULT SetUIntItem(eUIntMetaItem itemName, UINT32 val, HXBOOL bValid, UINT32 metaFlavorFlags = METADATA_FLAVOR_ALL);
 
     // 3GPP keywords
     UINT32 GetKeywordCount() const;
     HX_RESULT GetKeyword(UINT32 index, EncodedString& out) const;
     HX_RESULT ClearKeywords();
 
     // Returns FALSE if the byte-length of <val> is greater than 255 (maximum allowed by 3GPP).
     // Also, maximum number of keywords that can be written to 3GPP file is 255 (this method
     // does not check this to allow easier addition/removal, so this should be checked after
     // making changes by calling GetKeywordCount and taking appropriate actions).
     HX_RESULT AddKeyword(const EncodedString& val);
     HX_RESULT RemoveKeyword(const EncodedString& val);
 
     // APIC frames (attached pictures)
     UINT32 GetPictureCount() const;
     ID3Tools::APICFrame* GetPictureByIndex(UINT32 index) const;
     ID3Tools::APICFrame* GetPictureByType(UINT8 APICFramePictureType) const;
+    static CHXString MakePictureFilename(const ID3Tools::APICFrame& frame, UINT32 index, const CHXString& prefix);
 
     HX_RESULT ClearPictures();
 
     enum eAddPictureTypeConflictFlags
     {
         APTCF_FirstChanceOverwrite       = 0x00000001,
         APTCF_SecondChanceAssignFreeType = 0x00000002,
         APTCF_ThirdChanceOverwrite       = 0x00000004
     };
     // Returns HXR_INVALID_VERSION if the conflict was not resolved
     HX_RESULT AddPicture(ID3Tools::APICFrame* pFrame, UINT32 typeConflictFlags = 0);
 
     HX_RESULT RemovePicture(UINT8 APICFramePictureType);
     HX_RESULT FindFreePictureType(UINT8& outPictureType);
     HX_RESULT ConsolidatePictureTypes();
 
 private:
     HX_RESULT SetGlobalLanguageEncoding(const EncodedString& val);
     static HX_RESULT SetStringAsciiMaxLen(EncodedString& dst, const EncodedString& val, UINT32 maxLen);
     static HX_RESULT SetStringFixedPointNum(EncodedString& dst, const EncodedString& val);
     static void Update(EncodedString& dst, const EncodedString& src);
     static void Dump(const char* propName, const EncodedString& val, HXBOOL bFullDump = TRUE);
+
+    static void DumpElementToXMLString(CHXString& out, const CHXString& ws,
+                                const EncodedString& src, const CHXString& elementName,
+                                const CHXString& attributes = "");
+
+    void DumpStringItemToXMLString(CHXString& out, const CHXString& ws,
+                                    eStringMetaItem item, const CHXString& elementName,
+                                    const CHXString& attributes = "") const;
+
+    static void DumpUIntToXMLString(CHXString& out, const CHXString& ws, UINT32 src,
+                                const CHXString& elementName, const CHXString& attributes = "");
+
+    void DumpUIntItemToXMLString(CHXString& out, const CHXString& ws, eUIntMetaItem item,
+                                const CHXString& elementName, const CHXString& attributes = "") const;
+
     void CopyString(eStringMetaItem dstItemName, HXBOOL bBlast, IHXValues* pSrcValues, const char* srcEnginePropName);
     void CopyString(IUnknown* pContext, IHXValues* pDstValues, HXBOOL bBlast,
                     const char* dstEnginePropName, eStringMetaItem srcItemName) const;
 
     static HXBOOL IsInRange(UINT32 val, UINT32 min, UINT32 max)
     {
         return (val >= min) && (val <= max);
     }
     static HXBOOL IsInRange16(UINT32 val)
     {
         return IsInRange(val, 0, 0x0000FFFF);
     }
     static HXBOOL IsInRange8(UINT32 val)
     {
         return IsInRange(val, 0, 0x000000FF);
     }
     static HXBOOL IsInRangeBoolean(UINT32 val)
     {
         return IsInRange(val, 0, 1);
     }
     static HX_RESULT SetUInt(UINT32& dstVal, HXBOOL& dstValid, UINT32 srcVal, HXBOOL srcValid,
                             UINT32 minVal = 0, UINT32 maxVal = 0xFFFFFFFF);
 
     static HX_RESULT SetUInt16(UINT32& dstVal, HXBOOL& dstValid, UINT32 srcVal, HXBOOL srcValid)
     {
         return SetUInt(dstVal, dstValid, srcVal, srcValid, 0, 0x0000FFFF);
     }
     static HX_RESULT SetUInt8(UINT32& dstVal, HXBOOL& dstValid, UINT32 srcVal, HXBOOL srcValid)
     {
         return SetUInt(dstVal, dstValid, srcVal, srcValid, 0, 0x000000FF);
datatype_tools_dtdriver_apps_meta3gp.diff (application/octet-stream, 62.2 KB)
Index: HXXmlInputParser.cpp
===================================================================
RCS file: /cvsroot/datatype/tools/dtdriver/apps/meta3gp/HXXmlInputParser.cpp,v
retrieving revision 1.2.2.1
diff -d -H -w -U30 -r1.2.2.1 HXXmlInputParser.cpp
--- HXXmlInputParser.cpp	14 Jan 2010 22:59:00 -0000	1.2.2.1
+++ HXXmlInputParser.cpp	18 Jan 2010 21:20:05 -0000
@@ -107,69 +107,68 @@
 {
     // add the name to the path
     m_sCurrentElementPath += "/";
     m_sCurrentElementPath += pName;
 
     return HXR_OK;
 }
 
 
 STDMETHODIMP CHXXmlInputParser::HandleEndElement(
     const char* pName,
     UINT32 ulLineNumber,
     UINT32 ulColumnNumber)
 {
     // remove name from path
     CHXString sName(pName);
     m_sCurrentElementPath = m_sCurrentElementPath.Left(m_sCurrentElementPath.GetLength() - sName.GetLength() -1);
 
     return HXR_OK;
 }
 
 
 STDMETHODIMP CHXXmlInputParser::HandleCharacterData(
     IHXBuffer* pBuffer,
     UINT32 ulLineNumber,
     UINT32 ulColumnNumber)
 {
     // With current parser implementation we will assume UTF8
     EncodedString strValue((const char*)pBuffer->GetBuffer(), HX_TEXT_ENCODING_TYPE_UTF8);
 
-    if(!strValue.IsEmpty())
+    if(!strValue.IsEmpty() && m_pResults && m_pPathDesc)
     {
-        // copy value for known paths
-        XmlPair* currentValue = m_pResults->GetAt(m_sCurrentElementPath);
-        if(currentValue != NULL)
+        if(m_pPathDesc->GetAt(m_sCurrentElementPath))
         {
-            currentValue->xmlValueUTF8 = strValue;
+            m_pResults->Add(new XmlPair(m_sCurrentElementPath, strValue));
         }
     }
+
     return HXR_OK;
 }
 
 
 STDMETHODIMP CHXXmlInputParser::HandleProcessingInstruction(
     const char* pTarget,
     IHXValues* pAttributes,
     UINT32 ulLineNumber,
     UINT32 ulColumnNumber)
 {
     return HXR_OK;
 }
 
 
 STDMETHODIMP CHXXmlInputParser::HandleUnparsedEntityDecl(
     const char* pEntityName,
     const char* pSystemID,
     const char* pPublicID,
     const char* pNotationName,
     UINT32 ulLineNumber,
     UINT32 ulColumnNumber)
 {
     return HXR_OK;
 }
 
 
 STDMETHODIMP CHXXmlInputParser::HandleNotationDecl(
     const char* pNotationName,
     const char* pSystemID,
     const char* pPublicID,
@@ -213,49 +212,50 @@
 {
     HX_RESULT hResult;
 
     IUnknown* pUnk = NULL;
     hResult = pClassFactory->CreateInstance(CLSID_IHXXMLParser, (void**)&pUnk);
     if(hResult != HXR_OK)
     {
 	    return hResult;
     }
 
     HX_RELEASE(m_pXMLParser);
     hResult = pUnk->QueryInterface(IID_IHXXMLParser, (void**)&m_pXMLParser);
     if(hResult != HXR_OK)
     {
         HX_RELEASE(pUnk);
 	    return hResult;
     }
 
     HX_RELEASE(pUnk);
 
     hResult = m_pXMLParser->Init(this, NULL, TRUE);
     if(hResult != HXR_OK)
     {
 	    return hResult;
     }
 
     return HXR_OK;
 }
 
 
-STDMETHODIMP CHXXmlInputParser::Start(CParsedXmlPairs* pResults)
+STDMETHODIMP CHXXmlInputParser::Start(CParsedXmlPairs* pResults, CXmlPathDescs* pPathDesc)
 {
     m_pResults = pResults;
+    m_pPathDesc = pPathDesc;
     m_ParsingResult = HXR_OK;
 
     return HXR_OK;
 }
 
 
 STDMETHODIMP CHXXmlInputParser::Parse(IHXBuffer* pBuffer, HXBOOL bIsFinal)
 {
     return m_pXMLParser->Parse(pBuffer, bIsFinal);
 }
 
 
 STDMETHODIMP CHXXmlInputParser::EndParse()
 {
     return m_ParsingResult;
 }
Index: HXXmlInputParser.h
===================================================================
RCS file: /cvsroot/datatype/tools/dtdriver/apps/meta3gp/HXXmlInputParser.h,v
retrieving revision 1.1.2.2
diff -d -H -w -U30 -r1.1.2.2 HXXmlInputParser.h
--- HXXmlInputParser.h	14 Jan 2010 22:59:00 -0000	1.1.2.2
+++ HXXmlInputParser.h	18 Jan 2010 21:20:06 -0000
@@ -21,109 +21,153 @@
  * it created. 
  *  
  * This file, and the files included with this file, is distributed and made 
  * available on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 
  * EXPRESS OR IMPLIED, AND REALNETWORKS HEREBY DISCLAIMS ALL SUCH WARRANTIES, 
  * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS 
  * FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 
  * 
  * Technology Compatibility Kit Test Suite(s) Location: 
  *    http://www.helixcommunity.org/content/tck 
  * 
  * Contributor(s): 
  *  
  * ***** END LICENSE BLOCK ***** */ 
 
 
 /****************************************************************************
  *  Includes
  */
 
 #ifndef _HXXMLINPUTPARSER_H_
 #define _HXXMLINPUTPARSER_H_
 
 #include "hxcom.h"
 #include "hxstring.h"
 #include "hxxml.h"
 #include "carray.h"
 #include "encstr.h"
 
 
-struct XmlPair
+struct XmlPathDesc
 {
-    XmlPair(const CHXString& path, const EncodedString& valueUTF8)
-        : xmlPath(path)
-        , xmlValueUTF8(valueUTF8)
+    XmlPathDesc(const CHXString& _path)
+        : path(_path)
+    { }
+    CHXString path;
+};
+
+class CXmlPathDescs
+{
+public:
+    ~CXmlPathDescs()
     {
+        for(int i = 0; i < descs.GetSize(); i++)
+        {
+            delete (XmlPathDesc*)descs.GetAt(i);
     }
-    CHXString xmlPath;
-    EncodedString xmlValueUTF8;
+    }
+
+    void Add(XmlPathDesc* d)
+    {
+        if(d)
+        {
+            descs.Add(d);
+        }
+    }
+
+    XmlPathDesc* GetAt(const CHXString& xmlPath)
+    {
+        for(int i = 0; i < descs.GetSize(); i++)
+        {
+            XmlPathDesc *d = (XmlPathDesc*)descs.GetAt(i);
+            if(d && (d->path == xmlPath))
+            {
+                return d;
+            }
+        }
+
+        return NULL;
+    }
+
+private:
+    CHXPtrArray descs;
+};
+
+struct XmlPair
+{
+    XmlPair(const CHXString& _path, const EncodedString& _valueUTF8)
+        : path(_path)
+        , valueUTF8(_valueUTF8)
+    { }
+    CHXString path;
+    EncodedString valueUTF8;
 };
 
 class CParsedXmlPairs
 {
 public:
     ~CParsedXmlPairs()
     {
         for(int i = 0; i < xmlPairs.GetSize(); i++)
         {
             delete (XmlPair*)xmlPairs.GetAt(i);
         }
     }
 
     void Add(XmlPair* pair)
     {
         xmlPairs.Add(pair);
     }
 
     XmlPair* GetAt(const CHXString& xmlPath)
     {
         for(int i = 0; i < xmlPairs.GetSize(); i++)
         {
             XmlPair *pair = (XmlPair*)xmlPairs.GetAt(i);
-            if (pair->xmlPath == xmlPath)
+            if(pair->path == xmlPath)
             {
                 return pair;
             }
         }
 
         return NULL;
     }
 
     void Dump()
     {
         for(int i = 0; i < xmlPairs.GetSize(); i++)
         {
             XmlPair *pair = (XmlPair*)xmlPairs.GetAt(i);
 
-            printf("%s = [", (const char*)pair->xmlPath);
-            EncStrUtils::PrintTextUTF8(pair->xmlValueUTF8.GetData());
+            printf("%s = [", (const char*)pair->path);
+            EncStrUtils::PrintTextUTF8(pair->valueUTF8.GetData());
             printf("].\n");
         }
     }
 
     CHXPtrArray* GetAllPairs()
     {
         return &xmlPairs;
     }
 
 private:
     CHXPtrArray xmlPairs;
 };
 
 /*
  * CHXXmlInputParser class implements XML upgrade manifest parser.
  */
 class CHXXmlInputParser : 
     public IHXXMLParserResponse
 {
 public:
     /*
      * Class constructor. Initializes a new instance of the CHXXmlInputParser class.
      */
     CHXXmlInputParser();
 
     /**************************************************************************
      *  IHXContextUser methods
      **************************************************************************/
     STDMETHOD(QueryInterface)	(THIS_
 				REFIID riid,
@@ -244,61 +288,63 @@
     STDMETHOD(HandleUnparsedDoctypeDecl)   (THIS_
 					    const char* /*IN*/  pDoctype,
 					    const char* /*IN*/  pSystemID,
 					    const char* /*IN*/  pPublicID,
 					    UINT32	/*IN*/	ulLineNumber,
 					    UINT32	/*IN*/	ulColumnNumber);
 
     /************************************************************************
      *	Method:
      *	    IHXXMLParser::HandleDefault
      *	Purpose:
      *	    Called with unhandled entitiy data
      *	    Line/column numbers are for the start of the entity
      *
      */
     STDMETHOD(HandleDefault)		    (THIS_
 					    IHXBuffer*	/*IN*/	pBuffer,
 					    UINT32	/*IN*/	ulLineNumber,
 					    UINT32	/*IN*/	ulColumnNumber);
 
     /*
      * Initializes a parser component.
      */
     STDMETHOD(Init)                         (THIS_
                                             IHXCommonClassFactory*   /*IN*/  pClassFactory);
 
     /*
      * Starts (resets) the parser.
      */
     STDMETHOD(Start)                        (THIS_
-                                            CParsedXmlPairs* pResults);
+                                            CParsedXmlPairs* pResults,
+                                            CXmlPathDescs* pPathDesc);
 
     /*
      * Parses one buffer of XML data.
      */
     STDMETHOD(Parse)                        (THIS_
                                             IHXBuffer* pBuffer,
                                             HXBOOL bIsFinal);
 
     /*
      * Returns parsed manifest and releases the internal reference.
      */
     STDMETHOD(EndParse)                     (THIS_);
 
 protected:
     /*
      * Component destructor.
      */
     ~CHXXmlInputParser();
 
 
 protected:
-    LONG32		                m_lRefCount;                        // Reference count.
-    IHXXMLParser*               m_pXMLParser;                       // XML parser.
-    HX_RESULT                   m_ParsingResult;                    // Result of the parsing operation.
-    CHXString                   m_sCurrentElementPath;              // Path of the current XML element.
-    CParsedXmlPairs*             m_pResults;                         // Parsed results
+    LONG32 m_lRefCount;
+    IHXXMLParser* m_pXMLParser;
+    HX_RESULT m_ParsingResult;
+    CHXString m_sCurrentElementPath;
+    CParsedXmlPairs* m_pResults;
+    CXmlPathDescs* m_pPathDesc;
 };
 
 
 #endif
Index: main.cpp
===================================================================
RCS file: /cvsroot/datatype/tools/dtdriver/apps/meta3gp/main.cpp,v
retrieving revision 1.7.2.4
diff -d -H -w -U30 -r1.7.2.4 main.cpp
--- main.cpp	17 Jan 2010 17:36:55 -0000	1.7.2.4
+++ main.cpp	18 Jan 2010 21:20:09 -0000
@@ -126,62 +126,63 @@
 #define TMAIN main
 #define TCHXStringCtor(Val) CHXString(Val)
 #define TUTF8EncodedStringCtor(Val) EncodedString((const char*)::UTF8FromCCP(Val), HX_TEXT_ENCODING_TYPE_UTF8)
 
 #endif //USE_WIDE_CHAR_COMMAND_LINE
 
 
 /****************************************************************************
  *  Build-time messages
  */
 #ifdef _WINDOWS
 
 #ifdef USE_WIDE_CHAR_COMMAND_LINE
 #   pragma message("Using wide-string command-line arguments")
 #else
 #   pragma message("Using legacy-string command-line arguments")
 #endif
 
 #endif //_WINDOWS
 
 
 /****************************************************************************
  *  DLLAccessPath Variable
  */
 ENABLE_DLLACCESS_PATHS(g_Meta3GPAccessPath);
 
 
 /****************************************************************************
  *  Defines
  */
-#define PROGRAM_NAME_STRING         "RealNetworks meta-data editor"
-#define PROGRAM_COPYRIGHT_STRING    "Copyright (c) RealNetworks 2010"
+#define PROGRAM_VENDOR_STRING       "RealNetworks"
+#define PROGRAM_NAME_STRING         PROGRAM_VENDOR_STRING" meta-data editor"
+#define PROGRAM_COPYRIGHT_STRING    "Copyright (c) "PROGRAM_VENDOR_STRING" 2010"
 
 // input/output control
 #ifdef _WINDOWS
 #define OPTION_STRING_ASYNCMODE      "dev-asyncmode"
 #endif //_WINDOWS
 
 #define OPTION_STRING_DEFMETAPROC    "dev-defmetaproc"
 #define OPTION_STRING_INPUTFILE      "if"
 #define OPTION_STRING_OUTPUTFILE     "of"
 #define OPTION_STRING_XMLINPUTFILE   "xmlin"
 #define OPTION_STRING_XMLOUTPUTFILE  "xmlout"
 #define OPTION_STRING_CLEARMETADATA  "metaclear"
 #define OPTION_STRING_METASTYLE      "metastyle"
 #define OPTION_STRING_UTF16OUTPUT    "utf16"
 #define OPTION_STRING_ID3V240OUTPUT  "id3v240"
 #define OPTION_STRING_ID3V230OUTPUT  "id3v230"
 #define OPTION_STRING_ID3V2NOOUTPUT  "id3v2no"
 #define OPTION_STRING_OVERWRITEFILE  "overwrite"
 #define OPTION_STRING_PRINTMETADATA  "print"
 #define OPTION_STRING_HELP           "help"
 #define OPTION_STRING_GENERATEHASH   "audiohash"
 #define OPTION_STRING_LOGFILE        "log"
 
 // common metadata
 #define OPTION_STRING_TITLE            "title"
 #define OPTION_STRING_ARTIST           "artist"
 #define OPTION_STRING_ALBUM            "album"
 #define OPTION_STRING_GENRE            "genre"
 #define OPTION_STRING_COMPOSER         "composer"
 #define OPTION_STRING_COMMENT          "comment"
@@ -247,147 +248,195 @@
 #define RC_ERROR_MISSING_ENV_VAR                   21
 #define RC_ERROR_CANNOT_LOAD_DLL                   22
 #define RC_ERROR_CANNOT_INIT_PROGRAM_OBJECT        23
 
 #define RC_ERROR_CANNOT_OPEN_INPUT_XML_FILE        31
 #define RC_ERROR_BAD_INPUT_XML_FILE                32
 #define RC_ERROR_INPUT_MEDIA_FILE_NOT_SPECIFIED    33
 #define RC_ERROR_CANNOT_OPEN_INPUT_MEDIA_FILE      34
 #define RC_ERROR_OUTPUT_MEDIA_FILE_EXISTS          35
 #define RC_ERROR_SAME_INPUT_OUTPUT_MEDIA_FILE      36
 #define RC_ERROR_CANNOT_OPEN_INPUT_PICTURE_FILE    37
 #define RC_ERROR_UNSUPPORTED_INPUT_PICTURE_TYPE    38
 #define RC_ERROR_CANNOT_OPEN_INPUT_UITS_FILE       39
 #define RC_ERROR_UNSUPPORTED_INPUT_UITS_ENCODING   40
 #define RC_ERROR_OUTPUT_PICTURE_FILE_EXISTS        41
 #define RC_ERROR_CANNOT_OUTPUT_PICTURE_FILE        42
 #define RC_ERROR_OUTPUT_UITS_FILE_EXISTS           43
 #define RC_ERROR_CANNOT_OUTPUT_UITS_FILE           44
 #define RC_ERROR_OUTPUT_XML_FILE_EXISTS            45
 #define RC_ERROR_CANNOT_OUTPUT_XML_FILE            46
 
 #define RC_ERROR_PROCESSING_MEDIA_FILE             100
 
 
 /****************************************************************************
 *  Help display utilities
 */
 class OptionDesc
 {
 public:
-    OptionDesc(const char* option_ = 0, const char* option_args_ = 0, const char* desc_ = 0, 
-        int arg_count_ = 0, const char* xpath_ = 0)
+    // xpath_list_ is an array of CHXString pointers.
+    OptionDesc(const char* option_ = 0, const char* option_args_ = 0,
+            const char* desc_ = 0, int arg_count_ = 0,
+            const char* xpath_ = 0, CHXPtrArray* xpath_list_ = 0)
     {
         option = option_;
         option_args = option_args_;
         desc = desc_;
         arg_count = arg_count_;
-        xpath = xpath_;
+
+        if(xpath_list_)
+        {
+            xpaths = xpath_list_;
+        }
+        else
+        {
+            xpaths = new CHXPtrArray;
+        }
+
+        if(xpath_)
+        {
+            xpaths->Add(new CHXString(xpath_));
+        }
+    }
+
+    ~OptionDesc()
+    {
+        for(int i = 0; i < xpaths->GetSize(); i++)
+        {
+            delete (CHXString*)xpaths->GetAt(i);
+        }
+        delete xpaths;
     }
 
     UINT32 GetLeftColumnWidth()
     {
         UINT32 len = 0;
         if(option)
         {
             len += 2 + strlen(option); //leading whitespace and option prefix
             if(option_args)
             {
                 len += 1 + strlen(option_args); //whitespace separator and option args
             }
         }
         return len;
     }
 
     CHXString BuildLine(UINT32 leftColumnWidth)
     {
         CHXString out = "";
         if(option)
         {
             out += " -"; //leading whitespace and option prefix
             out += option;
             if(option_args)
             {
                 out += " "; //whitespace separator
                 out += option_args;
             }
             if(desc)
             {
                 INT32 diff = leftColumnWidth - strlen(out);
                 for(int d = 0; d < diff; d++)
                 {
                     out += " ";
                 }
                 out += desc;
             }
         }
         return out;
     }
 
     const char* GetOptionString() const
     {
         return option ? option : "";
     }
 
-    const char* GetXPath() const
+    int GetNumberOfArguments()
     {
-        return xpath ? xpath : "";
+        return arg_count;
     }
 
-    int GetNumberOfArguments()
+    UINT32 GetNumXPaths() const
     {
-        return arg_count;
+        return xpaths->GetSize();
+    }
+
+    CHXString GetXPath(int i) const
+    {
+        if(i <= xpaths->GetSize())
+        {
+            return *(CHXString*)xpaths->GetAt(i);
+        }
+        return CHXString();
+    }
+
+    HXBOOL MatchesXPath(const CHXString& xpath) const
+    {
+        for(int i = 0; i < xpaths->GetSize(); i++)
+        {
+            if(GetXPath(i) == xpath)
+            {
+                return TRUE;
+            }
+        }
+        return FALSE;
     }
 
 private:
     const char* option;
     const char* option_args;
     const char* desc;
     int arg_count;
-    const char* xpath;
+    CHXPtrArray* xpaths;
 };
 
 class OptionRegistry
 {
 public:
     ~OptionRegistry()
     {
         for(int i = 0; i < descs.GetSize(); i++)
         {
             delete (OptionDesc*)descs.GetAt(i);
         }
     }
 
     void Add(OptionDesc* od)
     {
+        if(od)
+        {
         descs.Add(od);
     }
+    }
 
     int CalcLeftColumnWidth()
     {
         int leftColumnWidth = 0;
 
         for(int i = 0; i < descs.GetSize(); i++)
         {
             OptionDesc* od = (OptionDesc*)descs.GetAt(i);
             int width = od->GetLeftColumnWidth();
             if(width > leftColumnWidth)
             {
                 leftColumnWidth = width;
             }
         }
         return leftColumnWidth;
     }
 
     void PrintColumns(int columnSpacing = 1)
     {
         int leftColumnWidth = CalcLeftColumnWidth() + columnSpacing;
 
         for(int i = 0; i < descs.GetSize(); i++)
         {
             OptionDesc* od = (OptionDesc*)descs.GetAt(i);
             CHXString line = od->BuildLine(leftColumnWidth);
             if(!line.IsEmpty())
             {
                 printf("%s\n", (const char*)line);
             }
         }
@@ -409,77 +458,81 @@
 
         for(int i = 0; i < descs.GetSize(); i++)
         {
             OptionDesc* od = (OptionDesc*)descs.GetAt(i);
             if(arg == od->GetOptionString())
             {
                 return TRUE;
             }
         }
         return FALSE;
     }
 
     OptionDesc* GetOptionDesc(CHXString& strArg) const
     {
         for(int i = 0; i < descs.GetSize(); i++)
         {
             OptionDesc* od = (OptionDesc*)descs.GetAt(i);
             if(strArg == od->GetOptionString())
             {
                 return od;
             }
         }
         return NULL;
     }
 
     OptionDesc* GetOptionDescWithPath(CHXString& strArg) const
     {
         for(int i = 0; i < descs.GetSize(); i++)
         {
             OptionDesc* od = (OptionDesc*)descs.GetAt(i);
-            if(strArg == od->GetXPath())
+            if(od->MatchesXPath(strArg))
             {
                 return od;
             }
         }
         return NULL;
     }
 
-    void InitializeXmlPairs(CParsedXmlPairs *xmlPairs)
+    void InitializeXmlPathDescs(CXmlPathDescs& xmlPathDescs)
     {
         for(int i = 0; i < descs.GetSize(); i++)
         {
             OptionDesc* desc = (OptionDesc*)descs.GetAt(i);
-            CHXString xpath(desc->GetXPath());
+
+            for(int iPath = 0; iPath < desc->GetNumXPaths(); iPath++)
+            {
+                CHXString xpath = desc->GetXPath(iPath);
             if(!xpath.IsEmpty())
             {
-                xmlPairs->Add(new XmlPair(xpath, EncodedString()));
+                    xmlPathDescs.Add(new XmlPathDesc(xpath));
+                }
             }
         }
     }
 
 private:
     CHXPtrArray descs;
 };
 
 void DisplayProgramInfo()
 {
     const char* pszDbg = "";
 #if defined(DEBUG) || defined(_DEBUG)
     pszDbg = ".debug";
 #endif
 
     printf("\n--- %s ---\n    %s\n    Version %s%s\n\n",
             PROGRAM_NAME_STRING, PROGRAM_COPYRIGHT_STRING, TARVER_STRING_VERSION, pszDbg);
 }
 
 void DisplayUsage(const TCHAR* argv0, OptionRegistry& optionReg)
 {
     CHXString exeName = CHXFileSpecUtils::GetCurrentApplication().GetName();
 
     printf("usage: %s [ -option [value]... ]...\n", (const char*)exeName);
     printf("with options:\n");
 
     optionReg.PrintColumns(2);
 }
 
 CHXString GetUTCTimeString()
@@ -680,61 +733,63 @@
         {
             delete (ID3Tools::APICFrame*)a[i];
         }
         a.RemoveAll();
     }
 
     // meta-data input params
     HXBOOL ClearMetaData;
     HXBOOL ClearKeywords;
     HXBOOL ClearPictures;
     HXBOOL GenerateHash;
     UINT32 InjectedMetaFlavors;
     MetaInfo InjectedMetaInfo;
     CHXPtrArray AddedKeywords; //EncodedString pointers
     CHXPtrArray RemovedKeywords; //EncodedString pointers
     CHXPtrArray AddedPictures; //ID3Tools::APICFrame pointers
     CHXPtrArray RemovedPictures; //Integers
 
     // meta-data output storage
     MetaInfo ExtractedMetaInfo;
     MetaInfo OutboundMetaInfo;
 
 #ifdef HELIX_FEATURE_METAEDIT_AUDIO_HASH
     unsigned char SHAHash[32];
     hash_state HashState;
 #endif
 };
 
 struct InputOption
 {
-    InputOption(const CHXString& optionName, const EncodedString& optionValueUTF8, const EncodedString& xmlOptionValueUTF8)
+    InputOption(const CHXString& optionName,
+                const EncodedString& optionValueUTF8 = EncodedString(),
+                const EncodedString& xmlOptionValueUTF8 = EncodedString())
         : OptionName(optionName)
         , OptionValueUTF8(optionValueUTF8)
         , XmlOptionValueUTF8(xmlOptionValueUTF8)
     {
     }
 
     // XML value has precedence over command-line value
     const EncodedString& GetEffectiveOptionValueUTF8() const
     {
         return XmlOptionValueUTF8.IsEmpty() ? OptionValueUTF8 : XmlOptionValueUTF8;
     }
 
     // XML value has precedence over command-line value
     CHXString GetEffectiveOptionValueCCP() const
     {
         const EncodedString& val = GetEffectiveOptionValueUTF8();
         return CHXString((const char*)CCPFromUTF8(val.GetData()));
     }
 
     CHXString OptionName;
     EncodedString OptionValueUTF8;
     EncodedString XmlOptionValueUTF8;
 };
 
 /****************************************************************************
 *  Param extractors
 */
 enum eCommandLineProcessorPhase
 {
     CLPP_IndependentOptions = 0,
@@ -1089,173 +1144,203 @@
     }
 
     if(bSkipArgOnly)
     {
         return HXR_OK;
     }
 
     if(MetaInfo::IsItemSupportedByAnyFlavor(MetaInfo::ArrayMetaItem_Pictures, metaFlavorFlags))
     {
         EncodedString sVal = option->OptionValueUTF8;
 
         UINT32 uVal = 0;
         if(PropTools::ExtractUInt32(uVal, sVal) &&
            MetaInfo::IsAPICPictureTypeValidForFlavors(uVal, metaFlavorFlags))
         {
             aRemovedPictureTypes.AddIfUnique((void*)uVal);
             return HXR_OK;
         }
 
         ReportInvalidValueForOption(option->OptionName, option->OptionValueUTF8);
         return HXR_PARSE_ERROR;
     }
 
     ReportUnsupportedMetaStyleOption(option->OptionName);
     return HXR_PARSE_ERROR;
 }
 
 HX_RESULT ProcessAddPictureOption(int& progRetCode, bool bSkipArgOnly, InputOption* option,
                                 CHXPtrArray& aAddedAPICFrames, UINT32 metaFlavorFlags)
 {
-    if(option->OptionValueUTF8.IsEmpty())
+    if(option->OptionValueUTF8.IsEmpty() && option->XmlOptionValueUTF8.IsEmpty())
     {
         return HXR_PARSE_ERROR;
     }
 
     if(bSkipArgOnly)
     {
         return HXR_OK;
     }
 
     if(MetaInfo::IsItemSupportedByAnyFlavor(MetaInfo::ArrayMetaItem_Pictures, metaFlavorFlags))
     {
         ID3Tools::APICFrame* pFrame = new ID3Tools::APICFrame;
         if(!pFrame)
         {
             progRetCode = RC_ERROR_CANNOT_INIT_PROGRAM_OBJECT;
             return HXR_OUTOFMEMORY;
         }
 
         // process image file
         HX_RESULT retVal = HXR_FAILED;
 
         CHXString sFileName = option->GetEffectiveOptionValueCCP();
         if(!sFileName.IsEmpty() && CHXFileSpecUtils::FileExists(CHXFileSpecifier(sFileName)))
         {
             // load image, accept only known MIME types
             retVal = pFrame->LoadPictureDataFromImageFile((const char*)sFileName, TRUE);
 
             // check if frame is acceptable
             if(SUCCEEDED(retVal) && !MetaInfo::IsAPICFrameValidForFlavors(*pFrame, metaFlavorFlags))
             {
+                // xml options override the command line, however, xml options can not be set using the flavor,
+                // so in this case, if the xml option has value which is not valid for flavor, we'll just ignore it.
+                if(option->XmlOptionValueUTF8.IsEmpty())
+                {
                 retVal = HXR_FAILED;
             }
+            }
 
             if(FAILED(retVal))
             {
                 printf("Unsupported picture type in file: %s\n", (const char*)sFileName);
                 progRetCode = RC_ERROR_UNSUPPORTED_INPUT_PICTURE_TYPE;
             }
         }
         else
         {
             printf("Failed to open picture file: %s\n", (const char*)sFileName);
             progRetCode = RC_ERROR_CANNOT_OPEN_INPUT_PICTURE_FILE;
         }
 
         // export frame
         if(SUCCEEDED(retVal) && pFrame)
         {
             if(!aAddedAPICFrames.AddIfUnique(pFrame))
             {
                 retVal = HXR_FAILED;
             }
         }
 
         if(FAILED(retVal))
         {
             // cleanup
             HX_DELETE(pFrame);
         }
 
         return retVal;
     }
 
     ReportUnsupportedMetaStyleOption(option->OptionName);
     return HXR_PARSE_ERROR;
 }
 
 // Only moves argVars position if successful
-HX_RESULT CopyOptionArgumentString(ArgVars& argVars, EncodedString& sOut, HXBOOL bOptional = FALSE)
+HX_RESULT CopyOptionArgumentString(ArgVars& argVars, EncodedString& sOut, HXBOOL bOptional)
 {
     sOut.Clear();
 
     const TCHAR* pVal = argVars.CurrentArg(1, bOptional, TRUE);
-    if(!pVal && !bOptional)
+    if(!pVal)
     {
-        return HXR_PARSE_ERROR;
+        return bOptional ? HXR_OK : HXR_PARSE_ERROR;
     }
 
     sOut = TUTF8EncodedStringCtor(pVal);
     argVars.MovePosition(1);
 
     return HXR_OK;
 }
 
 class InputOptions
 {
 public:
     InputOptions() 
         : position(0)
     {
     }
 
     ~InputOptions()
     {
         Clear();
     }
 
     void Clear()
     {
         for(int i = 0; i < options.GetSize(); i++)
         {
             delete (InputOption*)options.GetAt(i);
         }
 
         options.RemoveAll();
         position = 0;
     }
 
     void Add(InputOption* inputOpt)
     {
+        if(inputOpt)
+        {
         options.Add(inputOpt);
     }
+    }
+
+    void Remove(const char* strOptionName)
+    {
+        HXBOOL bDone = FALSE;
+        while(!bDone)
+        {
+            bDone = TRUE;
+
+            for(int i = 0; i < options.GetSize(); i++)
+            {
+                InputOption* inOpt = (InputOption*)options.GetAt(i);
+                if(inOpt && (inOpt->OptionName == strOptionName))
+                {
+                    options.RemoveAt(i);
+                    bDone = FALSE;
+
+                    // restart seek loop with new container contents
+                    break;
+                }
+            }
+        }
+    }
 
     void ResetPosition(int pos = 0)
     {
         position = pos;
     }
 
     HXBOOL ReachedEndPosition()
     {
         return (position >= options.GetSize());
     }
 
     void MovePosition(int step = 1)
     {
         position += step;
         if(position < 0)
         {
             position = 0;
         }
     }
 
     InputOption* GetOption(const CHXString& optionName) const
     {
         for(int i = 0; i < options.GetSize(); i++)
         {
             InputOption* option = (InputOption*)options.GetAt(i);
 
             if(option->OptionName == optionName)
             {
                 return option;
             }
@@ -1340,76 +1425,77 @@
             case 1:
             {
                 // one argument
                 retVal = CopyOptionArgumentString(argVars, optionArgument, FALSE);
                 break;
             }
 
             default:
                 // unsupported number of option arguments
                 retVal = HXR_PARSE_ERROR;
                 continue;
         }
 
         if(FAILED(retVal) && !bStrictMode)
         {
             retVal = HXR_OK;
             argVars.MovePosition(numArgs);
             continue;
         }
 
         if(SUCCEEDED(retVal))
         {
             inputOptions.Add(new InputOption(strOption, optionArgument, EncodedString()));
         }
     }
 
     return retVal;
 }
 
 HX_RESULT ParseXMLInputOptions(IHXCommonClassFactory* pCommonClassFactory, CHXXmlInputParser* pInputParser, 
-                               const CHXString& strInputXMLFilename, CParsedXmlPairs* parsedXmlResults)
+                               const CHXString& strInputXMLFileName, CParsedXmlPairs& parsedXmlResults,
+                               CXmlPathDescs& xmlPathDescs)
 {
-    if(strInputXMLFilename.IsEmpty())
+    if(strInputXMLFileName.IsEmpty())
     {
         return HXR_FAIL;
     }
 
-    FILE* fp = fopen((const char*)strInputXMLFilename, "r");
+    FILE* fp = fopen((const char*)strInputXMLFileName, "r");
     if(fp == NULL)
     {
-        printf("Unable to open file: %s\n", (const char*)strInputXMLFilename);
+        printf("Unable to open file: %s\n", (const char*)strInputXMLFileName);
         return HXR_FAIL;
     }
 
     HX_RESULT retVal = HXR_OK;
-    retVal = pInputParser->Start(parsedXmlResults);
+    retVal = pInputParser->Start(&parsedXmlResults, &xmlPathDescs);
 
     ULONG32 uParserBufferSize = 64 * 1024;
 
     if(SUCCEEDED(retVal))
     {
         // create temporary buffer for manifest chunks
         IHXBuffer* pBuffer = NULL;
         retVal = CreateBufferCCF(pBuffer, pCommonClassFactory);
         if(SUCCEEDED(retVal))
         {
             pBuffer->SetSize(uParserBufferSize);
 
             // read manifest, chunk by chunk
             while(true)
             {
                 size_t nItemsRead = fread(pBuffer->GetBuffer(), 1, uParserBufferSize, fp);
                 if(nItemsRead != 0)
                 {
                     if(uParserBufferSize != nItemsRead)
                     {
                         pBuffer->SetSize(nItemsRead);
                     }
 
                     // parse chunk
                     HXBOOL bIsFinal = uParserBufferSize != nItemsRead;
                     retVal = pInputParser->Parse(pBuffer, bIsFinal);
                     if(!SUCCEEDED(retVal))
                     {
                         break;
                     }
@@ -1425,60 +1511,107 @@
 
     if(SUCCEEDED(retVal))
     {
         // retrieve manifest
         retVal = pInputParser->EndParse();
     }
 
     fclose(fp);
 
     return retVal;
 }
 
 HXBOOL NeedOverwriteConfirmation(const char* pFileName, HXBOOL bOverwriteFile)
 {
     HXBOOL bNeedConfirmation = FALSE;
     if(pFileName)
     {
         if(CHXFileSpecUtils::FileExists(CHXFileSpecifier(pFileName)))
         {
             if(!bOverwriteFile)
             {
                 bNeedConfirmation = TRUE;
                 printf("Output file [%s] already exists. Use -%s option to force overwrite.\n",
                         pFileName, OPTION_STRING_OVERWRITEFILE);
             }
         }
     }
     return bNeedConfirmation;
 }
 
+/****************************************************************************
+*  XML output utilities
+*/
+HX_RESULT SaveMetadataToXML(const CHXString& filename, const MetaInfo& mi,
+                            const CHXString& srcFilename, const CHXString& uitsOutFilename,
+                            const CHXString& picOutFilenamePrefix)
+{
+    HX_RESULT retVal = HXR_FAILED;
+
+    if(!filename.IsEmpty())
+    {
+        FILE* fp = fopen((const char*)filename, "w");
+        if(fp)
+        {
+            // write UTF8 BOM
+            const UINT8 BOM_UTF8[3] = { 0xEF, 0xBB, 0xBF };
+            fwrite(BOM_UTF8, 1, 3, fp);
+
+            // begin surrounding elements
+            CHXString exeName = CHXFileSpecUtils::GetCurrentApplication().GetName();
+            exeName = exeName.Left(exeName.Find('.'));
+
+            fprintf(fp, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
+                        "<document type=\"audio meta-data\" date=\"%s\" creator=\"%s\" version=\"%s\" vendor=\"%s\">\n"
+                        "  <file path=\"%s\">\n",
+                        (const char*)GetUTCTimeString(), (const char*)exeName, TARVER_STRING_VERSION, PROGRAM_VENDOR_STRING,
+                        (const char*)srcFilename);
+
+            // write metadata elements
+            CHXString sMetadata;
+            mi.DumpToXMLString(sMetadata, uitsOutFilename, picOutFilenamePrefix, METAINFO_SET_ALL, 2, 2);
+            fprintf(fp, "%s", (const char*)sMetadata);
+
+            // end surrounding elements
+            fprintf(fp, "  </file>\n"
+                        "</document>\n");
+
+            // close file
+            fclose(fp);
+
+            retVal = HXR_OK;
+        }
+    }
+
+    return retVal;
+}
+
 
 /****************************************************************************
 *  Meta-data processor
 */
 class CProcessor : public IHXMetaDataProcessor
 {
 public:
     CProcessor(MetaProcessorVars& vars)
         : m_lRefCount(0)
         , m_vars(vars)
         , m_pContext(0)
     {
     }
     virtual ~CProcessor()
     {
         HX_RELEASE(m_pContext);
     }
     STDMETHOD(QueryInterface)(REFIID riid, void** ppvObj)
     {
         if(IsEqualIID(riid, IID_IUnknown))
         {
             AddRef();
             *ppvObj = (IUnknown*)(IHXMetaDataProcessor*)this;
             return HXR_OK;
         }
         if(IsEqualIID(riid, IID_IHXMetaDataProcessor))
         {
             AddRef();
             *ppvObj = (IHXMetaDataProcessor*)this;
             return HXR_OK;
@@ -1643,101 +1776,110 @@
 */
 int TMAIN(int argc, TCHAR* argv[])
 {
     // initialize variables
     HX_RESULT retVal = HXR_OK;
     int progRetCode = RC_SUCCESS;
 
     UINT32 startTime = 0;
     CHXFileSpecifier exeFileSpec;
     HXBOOL bUsingLog = FALSE;
 
     DLLAccess* pDLLAccess = 0;
     FPHXMEDIAPLATFORMOPEN fpHXMediaPlatformOpen = 0;
     FPHXCREATEMEDIAPLATFORM fpHXCreateMediaPlatform = 0;
     FPHXMEDIAPLATFORMCLOSE fpHXMediaPlatformClose = 0;
 
     IHXMediaPlatform* pMediaPlatform = 0;
     IHXCommonClassFactory* pCommonClassFactory = 0;
     CHXXmlInputParser* pInputParser = 0;
     IHXMetaDataEditor* pEditor = 0;
     CResponse* pResponse = 0;
     CProcessor* pProcessor = 0;
     IHXValues* pOptions = 0;
 
     char pDllPath[_MAX_PATH] = {0};
     char pDllFile[_MAX_PATH] = {0};
     const char* pszHelixPluginPath = 0;
 
     CHXString strInputFileName;
     CHXString strOutputFileName;
+    CHXString strOutputXMLFileName;
     CHXString strPictureFileNamePrefix;
     CHXString strUITSDataOutputFileName;
     CHXString strMetaStyleName;
 
     HXBOOL bDisplayHelp = FALSE;
     HXBOOL bOverwriteFile = FALSE;
     HXBOOL bPrintMetaData = FALSE;
     HXBOOL bExtractPicturesToFiles = FALSE;
     HXBOOL bUTF16Output = FALSE;
     UINT32 ulMetaDataID3VersionOutput = METADATAID3VERSION_240;
 
     MetaProcessorVars metaProcessorVars;
 
     // construct option registry
     OptionRegistry optionReg;
 
     optionReg.Add(new OptionDesc(OPTION_STRING_LOGFILE, "<filename>", "redirect console output to log file", 1));
     optionReg.Add(new OptionDesc(OPTION_STRING_INPUTFILE, "<filename>", "input file", 1, "/albums/album/track/mediaFile/fileName"));
     optionReg.Add(new OptionDesc(OPTION_STRING_OUTPUTFILE, "<filename>", "output file", 1, "/albums/album/track/outputFileName"));
     optionReg.Add(new OptionDesc(OPTION_STRING_XMLINPUTFILE, "<filename>", "XML file with input parameters", 1));
+    optionReg.Add(new OptionDesc(OPTION_STRING_XMLOUTPUTFILE, "<filename>", "metadata output XML file", 1));
     optionReg.Add(new OptionDesc(OPTION_STRING_OVERWRITEFILE, 0, "overwrite output file if it exists", 0));
     optionReg.Add(new OptionDesc(OPTION_STRING_CLEARMETADATA, 0, "clear all extracted metadata before injection", 0));
     optionReg.Add(new OptionDesc(OPTION_STRING_METASTYLE, "<3GPP | iTunes>", "flavor of injected metadata", 1));
     optionReg.Add(new OptionDesc(OPTION_STRING_UTF16OUTPUT, 0, "inject metadata strings in UTF16 format", 0));
     optionReg.Add(new OptionDesc(OPTION_STRING_ID3V240OUTPUT, 0, "inject ID3v2 version 2.4.0, default", 0));
     optionReg.Add(new OptionDesc(OPTION_STRING_ID3V230OUTPUT, 0, "inject ID3v2 version 2.3.0", 0));
     optionReg.Add(new OptionDesc(OPTION_STRING_ID3V2NOOUTPUT, 0, "skip injecting ID3v2 tag", 0));
     optionReg.Add(new OptionDesc(OPTION_STRING_PRINTMETADATA, 0, "print updated metadata", 0));
-    optionReg.Add(new OptionDesc(OPTION_STRING_PICTURE_EXTRACT, "[filename-prefix]", "save extracted pictures", -1));
+    optionReg.Add(new OptionDesc(OPTION_STRING_PICTURE_EXTRACT, "[filename prefix]", "save extracted pictures", -1));
     optionReg.Add(new OptionDesc(OPTION_STRING_GENERATEHASH, 0, "generate hash", 0));
     optionReg.Add(new OptionDesc());
     optionReg.Add(new OptionDesc(OPTION_STRING_TITLE, "<string>", "update title", 1, "/albums/album/track/name"));
     optionReg.Add(new OptionDesc(OPTION_STRING_ARTIST, "<string>", "update 3GPP performer, iTunes artist", 1, "/albums/album/track/artist/name"));
     optionReg.Add(new OptionDesc(OPTION_STRING_ALBUM, "<string>", "update album", 1, "/albums/album/name"));
     optionReg.Add(new OptionDesc(OPTION_STRING_GENRE, "<string>", "update genre", 1, "/albums/album/track/style/name"));
     optionReg.Add(new OptionDesc(OPTION_STRING_COMPOSER, "<string>", "update 3GPP author, iTunes composer", 1, "/albums/album/track/artist/composer"));
     optionReg.Add(new OptionDesc(OPTION_STRING_COMMENT, "<string>", "update 3GPP description, iTunes comment", 1, "/albums/album/pline"));
     optionReg.Add(new OptionDesc(OPTION_STRING_COPYRIGHT, "<string>", "update copyright", 1, "/albums/album/cline"));
     optionReg.Add(new OptionDesc(OPTION_STRING_TRACKNUMBER, "<integer 0..255>", "update track number", 1, "/albums/album/track/sequence"));
     optionReg.Add(new OptionDesc(OPTION_STRING_YEAR, "<integer 0..65536>", "update 3GPP recording year, iTunes release date", 1, "/albums/album/OrigReleaseYear"));
-    optionReg.Add(new OptionDesc(OPTION_STRING_PICTURE_ADD, "<filename>", "add picture", 1));
+    {
+        // Add picture
+        CHXPtrArray* pPictureAddXPaths = new CHXPtrArray;
+        pPictureAddXPaths->Add(new CHXString("/albums/album/image/fileName"));
+        pPictureAddXPaths->Add(new CHXString("/albums/album/image/fileName2"));
+
+        optionReg.Add(new OptionDesc(OPTION_STRING_PICTURE_ADD, "<filename>", "add picture", 1, 0, pPictureAddXPaths));
+    }
     optionReg.Add(new OptionDesc(OPTION_STRING_PICTURE_REMOVE, "<APIC picture type>", "remove picture", 1));
     optionReg.Add(new OptionDesc(OPTION_STRING_PICTURE_CLEAR, 0, "clear all extracted pictures", 0));
     optionReg.Add(new OptionDesc(OPTION_STRING_UITS_INSERT, "<filename>", "insert UITS signature from file", 1));
     optionReg.Add(new OptionDesc(OPTION_STRING_UITS_EXTRACT, "<filename>", "extract UITS signature into file", 1));
     optionReg.Add(new OptionDesc());
     optionReg.Add(new OptionDesc(OPTION_STRING_SOFTWARE, "<string>", "update iTunes generator tool", 1));
     optionReg.Add(new OptionDesc(OPTION_STRING_ENCODEDBY, "<string>", "update iTunes encoded by", 1));
     optionReg.Add(new OptionDesc(OPTION_STRING_GROUPING, "<string>", "update iTunes grouping", 1));
     optionReg.Add(new OptionDesc(OPTION_STRING_ALBUMARTIST, "<string>", "update iTunes album artist", 1, "/albums/album/artist/name"));
     optionReg.Add(new OptionDesc(OPTION_STRING_LYRICS, "<string>", "update iTunes lyrics", 1));
     optionReg.Add(new OptionDesc(OPTION_STRING_SUBTITLE, "<string>", "update iTunes track subtitle", 1));
     optionReg.Add(new OptionDesc(OPTION_STRING_BPM, "<integer>", "update iTunes beats per minute", 1));
     optionReg.Add(new OptionDesc(OPTION_STRING_PARTOFCOMP, "<0 | 1>", "update iTunes part of compilation", 1, "/albums/album/compilation"));
     optionReg.Add(new OptionDesc(OPTION_STRING_EXPLICIT, "<0 | 1>", "update iTunes content rating", 1, "/albums/album/explicit"));
     optionReg.Add(new OptionDesc(OPTION_STRING_TRACKSTOTAL, "<integer>", "update iTunes track count", 1, "/albums/album/trackCount"));
     optionReg.Add(new OptionDesc(OPTION_STRING_DISKNUMBER, "<integer>", "update iTunes disc number", 1, "/albums/album/track/discNumber"));
     optionReg.Add(new OptionDesc(OPTION_STRING_DISKSTOTAL, "<integer>", "update iTunes disc count", 1, "/albums/album/discCount"));
     optionReg.Add(new OptionDesc());
     optionReg.Add(new OptionDesc(OPTION_STRING_RATING_ENTITY, "<4 character code>", "update 3GPP rating entity", 1));
     optionReg.Add(new OptionDesc(OPTION_STRING_RATING_CRITERIA, "<4 character code>", "update 3GPP rating criteria", 1));
     optionReg.Add(new OptionDesc(OPTION_STRING_RATING_INFO, "<string>", "update 3GPP rating info", 1));
     optionReg.Add(new OptionDesc(OPTION_STRING_CLASSIFICATION_ENTITY, "<4 character code>", "update 3GPP classification entity", 1));
     optionReg.Add(new OptionDesc(OPTION_STRING_CLASSIFICATION_TABLE, "<integer 0..65535>", "update 3GPP classification table", 1));
     optionReg.Add(new OptionDesc(OPTION_STRING_CLASSIFICATION_INFO, "<string>", "update 3GPP classification info", 1));
     optionReg.Add(new OptionDesc(OPTION_STRING_KEYWORD_ADD, "<string>", "add 3GPP keyword", 1));
     optionReg.Add(new OptionDesc(OPTION_STRING_KEYWORD_REMOVE, "<string>", "remove 3GPP keyword", 1));
     optionReg.Add(new OptionDesc(OPTION_STRING_KEYWORD_CLEAR, 0, "clear all extracted 3GPP keywords", 0));
     optionReg.Add(new OptionDesc(OPTION_STRING_LOCATION_NAME, "<string>", "update 3GPP location name", 1));
     optionReg.Add(new OptionDesc(OPTION_STRING_LOCATION_ASTRONOMICAL_BODY, "<string>", "update 3GPP location astronomical body", 1));
     optionReg.Add(new OptionDesc(OPTION_STRING_LOCATION_ADDITIONAL_NOTES, "<string>", "update 3GPP location notes", 1));
@@ -1965,104 +2107,128 @@
 
     // initialize xml parser
     if(SUCCEEDED(retVal))
     {
         retVal = HXR_OUTOFMEMORY;
         pInputParser = new CHXXmlInputParser();
 
         if(pInputParser)
         {
             pInputParser->AddRef();
             retVal = HXR_OK;
         }
     }
 
     if(SUCCEEDED(retVal))
     {
         retVal = pInputParser->Init(pCommonClassFactory);
     }
 
     if(FAILED(retVal))
     {
         printf("Failed to initialize XML parser.\n");
         progRetCode = RC_ERROR_CANNOT_INIT_PROGRAM_OBJECT;
         goto cleanup;
     }
 
     // parse XML input file
     if(SUCCEEDED(retVal))
     {
         InputOption* pOptionXmlIn = inputOptions.GetOption(CHXString(OPTION_STRING_XMLINPUTFILE));
-        CHXString strInputXMLFilename = pOptionXmlIn ? pOptionXmlIn->GetEffectiveOptionValueCCP() : CHXString();
+        CHXString strInputXMLFileName = pOptionXmlIn ? pOptionXmlIn->GetEffectiveOptionValueCCP() : CHXString();
 
-        if(!strInputXMLFilename.IsEmpty() && !CHXFileSpecUtils::FileExists(CHXFileSpecifier(strInputXMLFilename)))
+        if(!strInputXMLFileName.IsEmpty() && !CHXFileSpecUtils::FileExists(CHXFileSpecifier(strInputXMLFileName)))
         {
-            printf("XML input file [%s] not found.\n", (const char*)strInputXMLFilename);
+            printf("XML input file [%s] not found.\n", (const char*)strInputXMLFileName);
             retVal = HXR_FAIL;
             progRetCode = RC_ERROR_CANNOT_OPEN_INPUT_XML_FILE;
         }
 
-        if(SUCCEEDED(retVal) && !strInputXMLFilename.IsEmpty())
+        if(SUCCEEDED(retVal) && !strInputXMLFileName.IsEmpty())
         {
-            printf("Processing XML input file [%s].\n", (const char*)strInputXMLFilename);
+            printf("Processing XML input file [%s].\n\n", (const char*)strInputXMLFileName);
 
             CParsedXmlPairs parsedXmlResults;
-            optionReg.InitializeXmlPairs(&parsedXmlResults);
 
-            retVal = ParseXMLInputOptions(pCommonClassFactory, pInputParser, strInputXMLFilename, &parsedXmlResults);
+            CXmlPathDescs xmlPathDescs;
+            optionReg.InitializeXmlPathDescs(xmlPathDescs);
+
+            retVal = ParseXMLInputOptions(pCommonClassFactory, pInputParser, strInputXMLFileName,
+                                          parsedXmlResults, xmlPathDescs);
 
             if(SUCCEEDED(retVal))
             {
+                HXBOOL bPictureOptionsUpdated = FALSE;
+
                 // merge parsed results into inputOptions
                 CHXPtrArray *xmlPairs = parsedXmlResults.GetAllPairs();
                 for(int i = 0; i < xmlPairs->GetSize(); i++)
                 {
                     XmlPair *pair = (XmlPair*)xmlPairs->GetAt(i);
-                    OptionDesc* optDesc = optionReg.GetOptionDescWithPath(pair->xmlPath);
+                    OptionDesc* optDesc = optionReg.GetOptionDescWithPath(pair->path);
 
                     // Add only results of XML file parsing which have non-empty value.
-                    if((optDesc != NULL) && !pair->xmlValueUTF8.IsEmpty())
+                    if(optDesc && !pair->valueUTF8.IsEmpty())
                     {
                         CHXString strName = optDesc->GetOptionString();
-                        InputOption* inOpt = inputOptions.GetOption(strName);
+                        HXBOOL bForceAddOption = FALSE;
 
-                        if(inOpt == NULL)
+                        // XML picture options invalidate command line picture options
+                        if(strName == OPTION_STRING_PICTURE_ADD)
                         {
-                            inputOptions.Add(new InputOption(strName, EncodedString(), pair->xmlValueUTF8));
+                            if(!bPictureOptionsUpdated)
+                            {
+                                bPictureOptionsUpdated = TRUE;
+
+                                inputOptions.Remove(OPTION_STRING_PICTURE_ADD);
+                                inputOptions.Remove(OPTION_STRING_PICTURE_REMOVE);
+                                inputOptions.Remove(OPTION_STRING_PICTURE_CLEAR);
+
+                                // implicit clear
+                                inputOptions.Add(new InputOption(OPTION_STRING_PICTURE_CLEAR));
+                            }
+                            bForceAddOption = TRUE;
+                        }
+
+                        // update command line options
+                        InputOption* inOpt = inputOptions.GetOption(strName);
+                        if(!inOpt || bForceAddOption)
+                        {
+                            inputOptions.Add(new InputOption(strName, EncodedString(), pair->valueUTF8));
                         }
                         else
                         {
-                            inOpt->XmlOptionValueUTF8 = pair->xmlValueUTF8;
+                            inOpt->XmlOptionValueUTF8 = pair->valueUTF8;
                         }
 
-                        printf("-%s taken from location [%s], value=[", (const char*)strName, (const char*)pair->xmlPath);
-                        EncStrUtils::PrintTextUTF8(pair->xmlValueUTF8.GetData());
+                        printf("-%s taken from location [%s], value=[", (const char*)strName, (const char*)pair->path);
+                        EncStrUtils::PrintTextUTF8(pair->valueUTF8.GetData());
                         printf("]\n");
                     }
                 }
 
                 printf("\n");
             }
             else
             {
                 printf("Error parsing XML input file.\n");
                 progRetCode = RC_ERROR_BAD_INPUT_XML_FILE;
             }
         }
     }
 
     // Process final combination of command-line and XML file input.
     // The first phase seeks for independent arguments, the second phase seeks for dependent arguments.
     eCommandLineProcessorPhase clpp = CLPP_IndependentOptions;
 
     while(SUCCEEDED(retVal) && (clpp != CLPP_Done))
     {
         for(inputOptions.ResetPosition(0); SUCCEEDED(retVal) && !inputOptions.ReachedEndPosition(); 
                 inputOptions.MovePosition(1))
         {
             InputOption* option = inputOptions.CurrentOption();
             CHXString strOption = option->OptionName;
 
             if(strOption == OPTION_STRING_HELP)
             {
                 bDisplayHelp = TRUE;
             }
@@ -2083,60 +2249,64 @@
                 // do nothing, already processed
             }
             else if(strOption == OPTION_STRING_INPUTFILE)
             {
                 strInputFileName = option->GetEffectiveOptionValueCCP();
             }
             else if(strOption == OPTION_STRING_OUTPUTFILE)
             {
                 strOutputFileName = option->GetEffectiveOptionValueCCP();
             }
             else if(strOption == OPTION_STRING_OVERWRITEFILE)
             {
                 bOverwriteFile = TRUE;
             }
             else if(strOption == OPTION_STRING_CLEARMETADATA)
             {
                 metaProcessorVars.ClearMetaData = TRUE;
             }
             else if(strOption == OPTION_STRING_PRINTMETADATA)
             {
                 bPrintMetaData = TRUE;
             }
             else if(strOption == OPTION_STRING_GENERATEHASH)
             {
                 metaProcessorVars.GenerateHash = TRUE;
             }
             else if(strOption == OPTION_STRING_XMLINPUTFILE)
             {
                 // do nothing, already processed
             }
+            else if(strOption == OPTION_STRING_XMLOUTPUTFILE)
+            {
+                strOutputXMLFileName = option->GetEffectiveOptionValueCCP();
+            }
             else if(strOption == OPTION_STRING_METASTYLE)
             {
                 strMetaStyleName = option->GetEffectiveOptionValueCCP();
 
                 if(clpp == CLPP_IndependentOptions)
                 {
                     if(SUCCEEDED(retVal) && !strMetaStyleName.IsEmpty())
                     {
                         if(!strMetaStyleName.CompareNoCase("3gpp"))
                         {
                             metaProcessorVars.InjectedMetaFlavors |= METADATA_FLAVOR_3GPP;
                         }
                         else if(!strMetaStyleName.CompareNoCase("itunes"))
                         {
                             metaProcessorVars.InjectedMetaFlavors |= METADATA_FLAVOR_ITUNES;
                         }
                         else
                         {
                             retVal = HXR_PARSE_ERROR;
                             progRetCode = RC_ERROR_BAD_COMMAND_LINE;
                             ReportInvalidValueForOption(OPTION_STRING_METASTYLE, option->GetEffectiveOptionValueUTF8());
                         }
                     }
                 }
             }
             else if(strOption == OPTION_STRING_UTF16OUTPUT)
             {
                 bUTF16Output = TRUE;
             }
             else if(strOption == OPTION_STRING_ID3V240OUTPUT)
@@ -2415,60 +2585,61 @@
                 }
                 else if(!sFileExt.CompareNoCase("m4a"))
                 {
                     metaProcessorVars.InjectedMetaFlavors |= METADATA_FLAVOR_ITUNES;
                 }
             }
         }
 
         // next phase
         clpp = (eCommandLineProcessorPhase)(int(clpp) + 1);
     }
 
     // display usage
     if(argc == 1)
     {
         // auto help display if started without options
         bDisplayHelp = TRUE;
     }
 
     if(bDisplayHelp)
     {
         DisplayUsage(argv[0], optionReg);
         goto cleanup;
     }
 
     // cleanup on command-line errors
     if(FAILED(retVal))
     {
         if(retVal == HXR_PARSE_ERROR)
         {
+            printf("Found command line error.\n");
             progRetCode = RC_ERROR_BAD_COMMAND_LINE;
         }
         goto cleanup;
     }
 
     // check for semantic errors
     if(SUCCEEDED(retVal) && strInputFileName.IsEmpty())
     {
         printf("Input file not specified.\n");
         retVal = HXR_PARSE_ERROR;
         progRetCode = RC_ERROR_INPUT_MEDIA_FILE_NOT_SPECIFIED;
     }
 
     // input file must exist
     if(SUCCEEDED(retVal) && !CHXFileSpecUtils::FileExists(CHXFileSpecifier(strInputFileName)))
     {
         printf("Input file [%s] not found.\n", (const char*)strInputFileName);
         retVal = HXR_FAIL;
         progRetCode = RC_ERROR_CANNOT_OPEN_INPUT_MEDIA_FILE;
     }
 
     if(SUCCEEDED(retVal) && !strOutputFileName.IsEmpty())
     {
         // writing to input file is not allowed
         if(SUCCEEDED(retVal) && (strInputFileName == strOutputFileName))
         {
             printf("File [%s] cannot be used for both input and output.\n", (const char*)strOutputFileName);
             retVal = HXR_FAIL;
             progRetCode = RC_ERROR_SAME_INPUT_OUTPUT_MEDIA_FILE;
         }
@@ -2631,155 +2802,170 @@
         // drive meta-info through meta-editor by using custom meta-data processor
         if(SUCCEEDED(retVal))
         {
             retVal = pEditor->Drive((const char*)strInputFileName, (const char*)strOutputFileName,
                                     (IHXMetaDataProcessor*)pProcessor,
                                     pOptions, (IHXMetaDataEditorResponse*)pResponse);
 
             if(SUCCEEDED(retVal) && !g_bUseDTDriverSynchronousMode)
             {
                 WaitAsyncDriveEnd();
                 retVal = pResponse->GetTerminationStatus();
             }
         }
     }
 
     if(FAILED(retVal))
     {
         printf("Failed to process input file [%s].\n", (const char*)strInputFileName);
         progRetCode = RC_ERROR_PROCESSING_MEDIA_FILE;
         goto cleanup;
     }
 
 #ifdef HELIX_FEATURE_METAEDIT_AUDIO_HASH
     if(metaProcessorVars.GenerateHash)
     {
         printf("--- Generated hash ---\n");
         for (int i=0; i<32; i++)
         {
             printf("%x", metaProcessorVars.SHAHash[i]);
         }
-        printf("\n");
+        printf("\n\n");
     }
 #endif
 
     // auto dump meta-data if extracting only
     if(strOutputFileName.IsEmpty() && !metaProcessorVars.GenerateHash)
     {
         bPrintMetaData = TRUE;
     }
 
     if(SUCCEEDED(retVal) && bPrintMetaData)
     {
         // dump extracted
         printf("--- Extracted meta-data ---\n");
         metaProcessorVars.ExtractedMetaInfo.Dump();
         printf("\n");
 
         // dump injected
         if(!g_bUseDefaultMetaEditorProcessor && !strOutputFileName.IsEmpty())
         {
             printf("--- Injected meta-data ---\n");
             metaProcessorVars.OutboundMetaInfo.Dump();
             printf("\n");
         }
     }
 
     // extract UITS data to file
+    HXBOOL bErrorSavingUITSData = FALSE;
     if(SUCCEEDED(retVal) && !strUITSDataOutputFileName.IsEmpty())
     {
         EncodedString sUITSData;
         if(SUCCEEDED(metaProcessorVars.ExtractedMetaInfo.GetStringItem(MetaInfo::StringMetaItem_UITSData, sUITSData))
             && !sUITSData.IsEmpty())
         {
-            printf("--- Extracting UITSData to file ---\n");
+            printf("--- Outputting UITSData to file ---\n");
             printf("Saving UITSData to [%s].\n", (const char*)strUITSDataOutputFileName);
 
             if(NeedOverwriteConfirmation((const char*)strUITSDataOutputFileName, bOverwriteFile))
             {
                 // report error but continue saving other data
                 progRetCode = RC_ERROR_OUTPUT_UITS_FILE_EXISTS;
+                bErrorSavingUITSData = TRUE;
             }
             else if(FAILED(MetaInfo::SaveXMLStringToFile((const char*)strUITSDataOutputFileName, sUITSData)))
             {
                 // report error but continue saving other data
                 printf("Failed to save UITSData.\n");
                 progRetCode = RC_ERROR_CANNOT_OUTPUT_UITS_FILE;
+                bErrorSavingUITSData = TRUE;
             }
             printf("\n");
         }
     }
 
     // extract pictures to files
+    HXBOOL bErrorSavingPictures = FALSE;
     if(SUCCEEDED(retVal) && bExtractPicturesToFiles && metaProcessorVars.ExtractedMetaInfo.GetPictureCount())
     {
-        printf("--- Extracting pictures to files ---\n");
+        printf("--- Outputting pictures to files ---\n");
         for(UINT32 iPic = 0; iPic < metaProcessorVars.ExtractedMetaInfo.GetPictureCount(); iPic++)
         {
             ID3Tools::APICFrame* pFrame = metaProcessorVars.ExtractedMetaInfo.GetPictureByIndex(iPic);
             if(pFrame)
             {
-                CHXString filename = strPictureFileNamePrefix.IsEmpty() ? "picture" : strPictureFileNamePrefix;
-                filename.AppendULONG(iPic);
-                filename += "_type";
-                filename.AppendULONG(pFrame->GetPictureType());
-
-                if(pFrame->GetMimeType() == "image/jpeg")
-                {
-                    filename += ".jpg";
-                }
-                else if(pFrame->GetMimeType() == "image/png")
-                {
-                    filename += ".png";
-                }
-                else
-                {
-                    filename += ".bin";
-                }
+                CHXString filename = MetaInfo::MakePictureFilename(*pFrame, iPic, strPictureFileNamePrefix);
 
                 printf("Saving picture %d to [%s].\n", iPic, (const char*)filename);
 
                 if(NeedOverwriteConfirmation((const char*)filename, bOverwriteFile))
                 {
                     // report error but continue saving other data
                     progRetCode = RC_ERROR_OUTPUT_PICTURE_FILE_EXISTS;
+                    bErrorSavingPictures = TRUE;
                 }
-                else
-                {
-                    if(FAILED(pFrame->SavePictureDataToImageFile((const char*)filename)))
+                else if(FAILED(pFrame->SavePictureDataToImageFile((const char*)filename)))
                     {
                         // report error but continue saving other data
                         printf("Failed to save picture %d.\n", iPic);
                         progRetCode = RC_ERROR_CANNOT_OUTPUT_PICTURE_FILE;
+                    bErrorSavingPictures = TRUE;
                     }
                 }
             }
+        printf("\n");
+    }
+
+    // save metadata to output XML
+    if(SUCCEEDED(retVal) && !strOutputXMLFileName.IsEmpty())
+    {
+        // save extracted data in extraction mode but injected data in injection mode
+        HXBOOL bSaveExtracted = strOutputFileName.IsEmpty();
+
+        printf("--- Outputting metadata to XML file ---\n");
+        printf("Saving %s metadata to [%s].\n",
+                bSaveExtracted ? "extracted" : "injected", (const char*)strOutputXMLFileName);
+
+        if(NeedOverwriteConfirmation((const char*)strOutputXMLFileName, bOverwriteFile))
+        {
+            // report error but continue saving other data
+            progRetCode = RC_ERROR_OUTPUT_XML_FILE_EXISTS;
+        }
+        else if(FAILED(SaveMetadataToXML(strOutputXMLFileName,
+                bSaveExtracted ? metaProcessorVars.ExtractedMetaInfo : metaProcessorVars.InjectedMetaInfo,
+                bSaveExtracted ? strInputFileName : strOutputFileName,
+                bErrorSavingUITSData ? "" : strUITSDataOutputFileName,
+                bErrorSavingUITSData ? "" : strPictureFileNamePrefix)))
+        {
+            // report error but continue saving other data
+            printf("Failed to save XML metadata file.\n");
+            progRetCode = RC_ERROR_CANNOT_OUTPUT_XML_FILE;
         }
         printf("\n");
     }
 
     if(SUCCEEDED(retVal))
     {
         UINT32 endTime = HX_GET_BETTERTICKCOUNT();
         printf("Done. Processing time: %f seconds.\n", (endTime - startTime) / 1000.0);
     }
 
     // cleanup objects
 cleanup:
     HX_RELEASE(pEditor);
     HX_RELEASE(pInputParser);
     HX_RELEASE(pProcessor);
     HX_RELEASE(pResponse);
     HX_RELEASE(pOptions);
     HX_RELEASE(pCommonClassFactory);
 
     if(pMediaPlatform)
     {
         pMediaPlatform->Close();
     }
     HX_RELEASE(pMediaPlatform);
 
     if(fpHXMediaPlatformClose)
     {
         fpHXMediaPlatformClose();
     }
     HX_DELETE(pDLLAccess);
Index: meta3gp.ver
===================================================================
RCS file: /cvsroot/datatype/tools/dtdriver/apps/meta3gp/Attic/meta3gp.ver,v
retrieving revision 1.1.2.3
diff -d -H -w -U30 -r1.1.2.3 meta3gp.ver
--- meta3gp.ver	14 Jan 2010 22:51:22 -0000	1.1.2.3
+++ meta3gp.ver	18 Jan 2010 21:20:09 -0000
@@ -1,15 +1,15 @@
 /* THIS FILE IS GENERATED BY THE BUILD SYSTEM -- DO NOT EDIT
  * Copyright (C) 1997-2002 RealNetworks Corporation. All rights reserved.
  */
 #ifdef _MACINTOSH
-#define TARVER_ULONG32_VERSION ((1<<28)|(0<<20)|(4<<12)|4)
-#define TARVER_ULONG64_VERSION ((1<<48)|(0<<32)|(4<<16)|4)
+#define TARVER_ULONG32_VERSION ((1<<28)|(0<<20)|(6<<12)|6)
+#define TARVER_ULONG64_VERSION ((1<<48)|(0<<32)|(6<<16)|6)
 #else
-#define TARVER_ULONG32_VERSION (UINT32)((1L<<28L)|(0L<<20L)|(4L<< 12L)|4L)
-#define TARVER_ULONG64_VERSION (UINT64)((1L<<48L)|(0L<<32L)|(4L<< 16L)|4L)
+#define TARVER_ULONG32_VERSION (UINT32)((1L<<28L)|(0L<<20L)|(6L<< 12L)|6L)
+#define TARVER_ULONG64_VERSION (UINT64)((1L<<48L)|(0L<<32L)|(6L<< 16L)|6L)
 #endif
-#define TARVER_LIST_VERSION 1,0,4,4
+#define TARVER_LIST_VERSION 1,0,6,6
 #define TARVER_MAJOR_VERSION 1
 #define TARVER_MINOR_VERSION 0
-#define TARVER_STRING_VERSION "1.0.4.4"
+#define TARVER_STRING_VERSION "1.0.6.6"
 #define TARVER_STR_BUILD_NAME ""