#------------------------------------------------------------------------------
# File:         Microsoft.pm
#
# Description:  Definitions for custom Microsoft tags
#
# Revisions:    2010/10/01 - P. Harvey Created
#               2011/10/05 - PH Added ProcessXtra()
#               2021/02/23 - PH Added abiltity to write Xtra tags
#
# References:   1) http://research.microsoft.com/en-us/um/redmond/groups/ivm/hdview/hdmetadataspec.htm
#------------------------------------------------------------------------------

package Image::ExifTool::Microsoft;

use strict;
use vars qw($VERSION %codePage);
use Image::ExifTool qw(:DataAccess :Utils);
use Image::ExifTool::XMP;

$VERSION = '1.23';

sub ProcessXtra($$$);
sub WriteXtra($$$);
sub CheckXtra($$$);

# list of code pages used by Microsoft
# (ref http://msdn.microsoft.com/en-us/library/dd317756(VS.85).aspx)
%codePage = (
     37 => 'IBM EBCDIC US-Canada',
    437 => 'DOS United States',
    500 => 'IBM EBCDIC International',
    708 => 'Arabic (ASMO 708)',
    709 => 'Arabic (ASMO-449+, BCON V4)',
    710 => 'Arabic - Transparent Arabic',
    720 => 'DOS Arabic (Transparent ASMO)',
    737 => 'DOS Greek (formerly 437G)',
    775 => 'DOS Baltic',
    850 => 'DOS Latin 1 (Western European)',
    852 => 'DOS Latin 2 (Central European)',
    855 => 'DOS Cyrillic (primarily Russian)',
    857 => 'DOS Turkish',
    858 => 'DOS Multilingual Latin 1 with Euro',
    860 => 'DOS Portuguese',
    861 => 'DOS Icelandic',
    862 => 'DOS Hebrew',
    863 => 'DOS French Canadian',
    864 => 'DOS Arabic',
    865 => 'DOS Nordic',
    866 => 'DOS Russian (Cyrillic)',
    869 => 'DOS Modern Greek',
    870 => 'IBM EBCDIC Multilingual/ROECE (Latin 2)',
    874 => 'Windows Thai (same as 28605, ISO 8859-15)',
    875 => 'IBM EBCDIC Greek Modern',
    932 => 'Windows Japanese (Shift-JIS)',
    936 => 'Windows Simplified Chinese (PRC, Singapore)',
    949 => 'Windows Korean (Unified Hangul Code)',
    950 => 'Windows Traditional Chinese (Taiwan)',
    1026 => 'IBM EBCDIC Turkish (Latin 5)',
    1047 => 'IBM EBCDIC Latin 1/Open System',
    1140 => 'IBM EBCDIC US-Canada with Euro',
    1141 => 'IBM EBCDIC Germany with Euro',
    1142 => 'IBM EBCDIC Denmark-Norway with Euro',
    1143 => 'IBM EBCDIC Finland-Sweden with Euro',
    1144 => 'IBM EBCDIC Italy with Euro',
    1145 => 'IBM EBCDIC Latin America-Spain with Euro',
    1146 => 'IBM EBCDIC United Kingdom with Euro',
    1147 => 'IBM EBCDIC France with Euro',
    1148 => 'IBM EBCDIC International with Euro',
    1149 => 'IBM EBCDIC Icelandic with Euro',
    1200 => 'Unicode UTF-16, little endian',
    1201 => 'Unicode UTF-16, big endian',
    1250 => 'Windows Latin 2 (Central European)',
    1251 => 'Windows Cyrillic',
    1252 => 'Windows Latin 1 (Western European)',
    1253 => 'Windows Greek',
    1254 => 'Windows Turkish',
    1255 => 'Windows Hebrew',
    1256 => 'Windows Arabic',
    1257 => 'Windows Baltic',
    1258 => 'Windows Vietnamese',
    1361 => 'Korean (Johab)',
    10000 => 'Mac Roman (Western European)',
    10001 => 'Mac Japanese',
    10002 => 'Mac Traditional Chinese',
    10003 => 'Mac Korean',
    10004 => 'Mac Arabic',
    10005 => 'Mac Hebrew',
    10006 => 'Mac Greek',
    10007 => 'Mac Cyrillic',
    10008 => 'Mac Simplified Chinese',
    10010 => 'Mac Romanian',
    10017 => 'Mac Ukrainian',
    10021 => 'Mac Thai',
    10029 => 'Mac Latin 2 (Central European)',
    10079 => 'Mac Icelandic',
    10081 => 'Mac Turkish',
    10082 => 'Mac Croatian',
    12000 => 'Unicode UTF-32, little endian',
    12001 => 'Unicode UTF-32, big endian',
    20000 => 'CNS Taiwan',
    20001 => 'TCA Taiwan',
    20002 => 'Eten Taiwan',
    20003 => 'IBM5550 Taiwan',
    20004 => 'TeleText Taiwan',
    20005 => 'Wang Taiwan',
    20105 => 'IA5 (IRV International Alphabet No. 5, 7-bit)',
    20106 => 'IA5 German (7-bit)',
    20107 => 'IA5 Swedish (7-bit)',
    20108 => 'IA5 Norwegian (7-bit)',
    20127 => 'US-ASCII (7-bit)',
    20261 => 'T.61',
    20269 => 'ISO 6937 Non-Spacing Accent',
    20273 => 'IBM EBCDIC Germany',
    20277 => 'IBM EBCDIC Denmark-Norway',
    20278 => 'IBM EBCDIC Finland-Sweden',
    20280 => 'IBM EBCDIC Italy',
    20284 => 'IBM EBCDIC Latin America-Spain',
    20285 => 'IBM EBCDIC United Kingdom',
    20290 => 'IBM EBCDIC Japanese Katakana Extended',
    20297 => 'IBM EBCDIC France',
    20420 => 'IBM EBCDIC Arabic',
    20423 => 'IBM EBCDIC Greek',
    20424 => 'IBM EBCDIC Hebrew',
    20833 => 'IBM EBCDIC Korean Extended',
    20838 => 'IBM EBCDIC Thai',
    20866 => 'Russian/Cyrillic (KOI8-R)',
    20871 => 'IBM EBCDIC Icelandic',
    20880 => 'IBM EBCDIC Cyrillic Russian',
    20905 => 'IBM EBCDIC Turkish',
    20924 => 'IBM EBCDIC Latin 1/Open System with Euro',
    20932 => 'Japanese (JIS 0208-1990 and 0121-1990)',
    20936 => 'Simplified Chinese (GB2312)',
    20949 => 'Korean Wansung',
    21025 => 'IBM EBCDIC Cyrillic Serbian-Bulgarian',
    21027 => 'Extended Alpha Lowercase (deprecated)',
    21866 => 'Ukrainian/Cyrillic (KOI8-U)',
    28591 => 'ISO 8859-1 Latin 1 (Western European)',
    28592 => 'ISO 8859-2 (Central European)',
    28593 => 'ISO 8859-3 Latin 3',
    28594 => 'ISO 8859-4 Baltic',
    28595 => 'ISO 8859-5 Cyrillic',
    28596 => 'ISO 8859-6 Arabic',
    28597 => 'ISO 8859-7 Greek',
    28598 => 'ISO 8859-8 Hebrew (Visual)',
    28599 => 'ISO 8859-9 Turkish',
    28603 => 'ISO 8859-13 Estonian',
    28605 => 'ISO 8859-15 Latin 9',
    29001 => 'Europa 3',
    38598 => 'ISO 8859-8 Hebrew (Logical)',
    50220 => 'ISO 2022 Japanese with no halfwidth Katakana (JIS)',
    50221 => 'ISO 2022 Japanese with halfwidth Katakana (JIS-Allow 1 byte Kana)',
    50222 => 'ISO 2022 Japanese JIS X 0201-1989 (JIS-Allow 1 byte Kana - SO/SI)',
    50225 => 'ISO 2022 Korean',
    50227 => 'ISO 2022 Simplified Chinese',
    50229 => 'ISO 2022 Traditional Chinese',
    50930 => 'EBCDIC Japanese (Katakana) Extended',
    50931 => 'EBCDIC US-Canada and Japanese',
    50933 => 'EBCDIC Korean Extended and Korean',
    50935 => 'EBCDIC Simplified Chinese Extended and Simplified Chinese',
    50936 => 'EBCDIC Simplified Chinese',
    50937 => 'EBCDIC US-Canada and Traditional Chinese',
    50939 => 'EBCDIC Japanese (Latin) Extended and Japanese',
    51932 => 'EUC Japanese',
    51936 => 'EUC Simplified Chinese',
    51949 => 'EUC Korean',
    51950 => 'EUC Traditional Chinese',
    52936 => 'HZ-GB2312 Simplified Chinese',
    54936 => 'Windows XP and later: GB18030 Simplified Chinese (4 byte)',
    57002 => 'ISCII Devanagari',
    57003 => 'ISCII Bengali',
    57004 => 'ISCII Tamil',
    57005 => 'ISCII Telugu',
    57006 => 'ISCII Assamese',
    57007 => 'ISCII Oriya',
    57008 => 'ISCII Kannada',
    57009 => 'ISCII Malayalam',
    57010 => 'ISCII Gujarati',
    57011 => 'ISCII Punjabi',
    65000 => 'Unicode (UTF-7)',
    65001 => 'Unicode (UTF-8)',
);

# tags written by Microsoft HDView (ref 1)
%Image::ExifTool::Microsoft::Stitch = (
    PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
    WRITE_PROC => \&Image::ExifTool::WriteBinaryData,
    CHECK_PROC => \&Image::ExifTool::CheckBinaryData,
    WRITABLE => 1,
    FORMAT => 'float',
    FIRST_ENTRY => 0,
    GROUPS => { 0 => 'MakerNotes', 2 => 'Image' },
    NOTES => q{
        Information found in the Microsoft custom EXIF tag 0x4748, as written by
        Windows Live Photo Gallery.
    },
    0 => {
        Name => 'PanoramicStitchVersion',
        Format => 'int32u',
    },
    1 => {
        Name => 'PanoramicStitchCameraMotion',
        Format => 'int32u',
        PrintConv => {
            2 => 'Rigid Scale',
            3 => 'Affine',
            4 => '3D Rotation',
            5 => 'Homography',
        },
    },
    2 => {
        Name => 'PanoramicStitchMapType',
        Format => 'int32u',
        PrintConv => {
            0 => 'Perspective',
            1 => 'Horizontal Cylindrical',
            2 => 'Horizontal Spherical',
            257 => 'Vertical Cylindrical',
            258 => 'Vertical Spherical',
        },
    },
    3 => 'PanoramicStitchTheta0',
    4 => 'PanoramicStitchTheta1',
    5 => 'PanoramicStitchPhi0',
    6 => 'PanoramicStitchPhi1',
);

# Microsoft Photo schema properties (MicrosoftPhoto) (ref PH)
%Image::ExifTool::Microsoft::XMP = (
    %Image::ExifTool::XMP::xmpTableDefaults,
    GROUPS => { 0 => 'XMP', 1 => 'XMP-microsoft', 2 => 'Image' },
    NAMESPACE => 'MicrosoftPhoto',
    TABLE_DESC => 'XMP Microsoft',
    VARS => { ID_FMT => 'none' },
    NOTES => q{
        Microsoft Photo 1.0 schema XMP tags.  This is likely not a complete list,
        but represents tags which have been observed in sample images.  The actual
        namespace prefix is "MicrosoftPhoto", but ExifTool shortens this in the
        family 1 group name.
    },
    CameraSerialNumber => { },
    DateAcquired       => { Groups => { 2 => 'Time' }, %Image::ExifTool::XMP::dateTimeInfo },
    FlashManufacturer  => { },
    FlashModel         => { },
    LastKeywordIPTC    => { List => 'Bag' },
    LastKeywordXMP     => { List => 'Bag' },
    LensManufacturer   => { },
    LensModel          => { Avoid => 1 },
    Rating => {
        Name => 'RatingPercent',
        Notes => q{
            XMP-xmp:Rating values of 1,2,3,4 and 5 stars correspond to RatingPercent
            values of 1,25,50,75 and 99 respectively
        },
    },
    CreatorAppId             => { Name => 'CreatorAppID' },
    CreatorOpenWithUIOptions => { },
    ItemSubType              => { },
);

# Microsoft Photo 1.1 schema properties (MP1 - written as 'prefix0' by MSPhoto) (ref PH)
%Image::ExifTool::Microsoft::MP1 = (
    %Image::ExifTool::XMP::xmpTableDefaults,
    GROUPS => { 0 => 'XMP', 1 => 'XMP-MP1', 2 => 'Image' },
    NAMESPACE => 'MP1',
    TABLE_DESC => 'XMP Microsoft Photo',
    VARS => { ID_FMT => 'none' },
    NOTES => q{
        Microsoft Photo 1.1 schema XMP tags which have been observed.
    },
    PanoramicStitchCameraMotion => {
        PrintConv => {
            'RigidScale' => 'Rigid Scale',
            'Affine'     => 'Affine',
            '3DRotation' => '3D Rotation',
            'Homography' => 'Homography',
        },
    },
    PanoramicStitchMapType => {
        PrintConv => {
            'Perspective'            => 'Perspective',
            'Horizontal-Cylindrical' => 'Horizontal Cylindrical',
            'Horizontal-Spherical'   => 'Horizontal Spherical',
            'Vertical-Cylindrical'   => 'Vertical Cylindrical',
            'Vertical-Spherical'     => 'Vertical Spherical',
        },
    },
    PanoramicStitchPhi0   => { Writable => 'real' },
    PanoramicStitchPhi1   => { Writable => 'real' },
    PanoramicStitchTheta0 => { Writable => 'real' },
    PanoramicStitchTheta1 => { Writable => 'real' },
    WhiteBalance0         => { Writable => 'real' },
    WhiteBalance1         => { Writable => 'real' },
    WhiteBalance2         => { Writable => 'real' },
    Brightness            => { Avoid => 1 },
    Contrast              => { Avoid => 1 },
    CameraModelID         => { Avoid => 1 },
    ExposureCompensation  => { Avoid => 1 },
    PipelineVersion       => { },
    StreamType            => { },
);

# Microsoft Photo 1.2 schema properties (MP) (ref PH)
# (also ref http://msdn.microsoft.com/en-us/library/windows/desktop/ee719905(v=vs.85).aspx)
my %sRegions = (
    STRUCT_NAME => 'Microsoft Regions',
    NAMESPACE   => 'MPReg',
    NOTES => q{
        Note that PersonLiveIdCID element is called PersonLiveCID according to the
        Microsoft specification, but in practice their software actually writes
        PersonLiveIdCID, so ExifTool uses this too.
    },
    Rectangle         => { },
    PersonDisplayName => { },
    PersonEmailDigest => { },
    PersonLiveIdCID   => { },  # (see https://exiftool.org/forum/index.php?topic=4274.msg20368#msg20368)
    PersonSourceID    => { },
);
%Image::ExifTool::Microsoft::MP = (
    %Image::ExifTool::XMP::xmpTableDefaults,
    GROUPS => { 0 => 'XMP', 1 => 'XMP-MP', 2 => 'Image' },
    NAMESPACE => 'MP',
    TABLE_DESC => 'XMP Microsoft Photo',
    VARS => { ID_FMT => 'none' },
    NOTES => q{
        Microsoft Photo 1.2 schema XMP tags which have been observed.
    },
    RegionInfo => {
        Name => 'RegionInfoMP',
        Struct => {
            STRUCT_NAME => 'Microsoft RegionInfo',
            NAMESPACE   => 'MPRI',
            Regions   => { Struct => \%sRegions, List => 'Bag' },
            DateRegionsValid => {
                Writable => 'date',
                Shift => 'Time',
                Groups => { 2 => 'Time'},
                PrintConv => '$self->ConvertDateTime($val)',
                PrintConvInv => '$self->InverseDateTime($val,undef,1)',
            },
        },
    },
    # remove "MP" from tag name (was added only to avoid conflict with XMP-mwg-rs:RegionInfo)
    RegionInfoRegions                  => { Flat => 1, Name => 'RegionInfoRegions' },
    RegionInfoDateRegionsValid         => { Flat => 1, Name => 'RegionInfoDateRegionsValid' },
    # shorten flattened Regions tag names to make them easier to use
    RegionInfoRegionsRectangle         => { Flat => 1, Name => 'RegionRectangle' },
    RegionInfoRegionsPersonDisplayName => { Flat => 1, Name => 'RegionPersonDisplayName' },
    RegionInfoRegionsPersonEmailDigest => { Flat => 1, Name => 'RegionPersonEmailDigest' },
    RegionInfoRegionsPersonLiveIdCID   => { Flat => 1, Name => 'RegionPersonLiveIdCID' },
    RegionInfoRegionsPersonSourceID    => { Flat => 1, Name => 'RegionPersonSourceID' },
);

# Xtra tags written in MP4 files written by Microsoft Windows Media Player
# (ref http://msdn.microsoft.com/en-us/library/windows/desktop/dd562330(v=VS.85).aspx)
# Note: These tags are closely related to tags in Image::ExifTool::ASF::ExtendedDescr
#       and Image::ExifTool::WTV::Metadata
%Image::ExifTool::Microsoft::Xtra = (
    PROCESS_PROC => \&ProcessXtra,
    WRITE_PROC => \&WriteXtra,
    CHECK_PROC => \&CheckXtra,
    WRITE_GROUP => 'Microsoft',
    AVOID => 1,
    GROUPS => { 0 => 'QuickTime', 2 => 'Video' },
    VARS => { ID_FMT => 'none' },
    NOTES => q{
        Tags found in the Microsoft "Xtra" atom of QuickTime videos.  Tag ID's are
        not shown because some are unruly GUID's.  Currently most of these tags are
        not writable because the Microsoft documentation is poor and samples were
        not available, but more tags may be made writable in the future if samples
        are provided.  Note that writable tags in this table are are flagged to
        "Avoid", which means that other more common tags will be written instead if
        possible unless the Microsoft group is specified explicitly.
    },
    Abstract                    => { },
    AcquisitionTime             => { Groups => { 2 => 'Time' } },
    AcquisitionTimeDay          => { Groups => { 2 => 'Time' } },
    AcquisitionTimeMonth        => { Groups => { 2 => 'Time' } },
    AcquisitionTimeYear         => { Groups => { 2 => 'Time' } },
    AcquisitionTimeYearMonth    => { Groups => { 2 => 'Time' } },
    AcquisitionTimeYearMonthDay => { Groups => { 2 => 'Time' } },
    AlbumArtistSortOrder        => { },
    AlbumID                     => { },
    AlbumIDAlbumArtist          => { },
    AlbumTitleSortOrder         => { },
    AlternateSourceURL          => { },
    AudioBitrate                => { },
    AudioFormat                 => { },
    Author                      => { Groups => { 2 => 'Author' } },
    AuthorSortOrder             => { },
    AverageLevel                => { },
    Bitrate                     => { },
    BuyNow                      => { },
    BuyTickets                  => { },
    CallLetters                 => { },
    CameraManufacturer          => { },
    CameraModel                 => { },
    CDTrackEnabled              => { },
    Channels                    => { },
    chapterNum                  => { },
    Comment                     => { },
    ContentDistributorDuration  => { },
    Copyright                   => { Groups => { 2 => 'Author' } },
    Count                       => { },
    CurrentBitrate              => { },
    Description                 => { Writable => 'Unicode', Avoid => 1 },
    DisplayArtist               => { },
    DLNAServerUDN               => { },
    DLNASourceURI               => { },
    DRMKeyID                    => { },
    DRMIndividualizedVersion    => { },
    DTCPIPHost                  => { },
    DTCPIPPort                  => { },
    Duration                    => { },
    DVDID                       => { },
    Event                       => { },
    FileSize                    => { },
    FileType                    => { },
    FourCC                      => { },
    FormatTag                   => { },
    FrameRate                   => { },
    Frequency                   => { },
    IsNetworkFeed               => { },
    Is_Protected                => 'IsProtected',
    IsVBR                       => { },
    LeadPerformer               => { },
    LibraryID                   => { },
    LibraryName                 => { },
    Location                    => { },
    MediaContentTypes           => { },
    MediaType                   => { },
    ModifiedBy                  => { },
    MoreInfo                    => { },
    PartOfSet                   => { },
    PeakValue                   => { },
    PixelAspectRatioX           => { },
    PixelAspectRatioY           => { },
    PlaylistIndex               => { },
    Provider                    => { },
    ProviderLogoURL             => { },
    ProviderURL                 => { },
    RadioBand                   => { },
    RadioFormat                 => { },
    RatingOrg                   => { },
    RecordingTime               => { Groups => { 2 => 'Time' } },
    RecordingTimeDay            => { Groups => { 2 => 'Time' } },
    RecordingTimeMonth          => { Groups => { 2 => 'Time' } },
    RecordingTimeYear           => { Groups => { 2 => 'Time' } },
    RecordingTimeYearMonth      => { Groups => { 2 => 'Time' } },
    RecordingTimeYearMonthDay   => { Groups => { 2 => 'Time' } },
    ReleaseDate                 => { Groups => { 2 => 'Time' } },
    ReleaseDateDay              => { Groups => { 2 => 'Time' } },
    ReleaseDateMonth            => { Groups => { 2 => 'Time' } },
    ReleaseDateYear             => { Groups => { 2 => 'Time' } },
    ReleaseDateYearMonth        => { Groups => { 2 => 'Time' } },
    ReleaseDateYearMonthDay     => { Groups => { 2 => 'Time' } },
    RequestState                => { },
    ShadowFilePath              => { },
    SourceURL                   => { },
    Subject                     => { },
    SyncState                   => { },
    Sync01                      => { },
    Sync02                      => { },
    Sync03                      => { },
    Sync04                      => { },
    Sync05                      => { },
    Sync06                      => { },
    Sync07                      => { },
    Sync08                      => { },
    Sync09                      => { },
    Sync10                      => { },
    Sync11                      => { },
    Sync12                      => { },
    Sync13                      => { },
    Sync14                      => { },
    Sync15                      => { },
    Sync16                      => { },
    SyncOnly                    => { },
    Temporary                   => { },
    Title                       => { },
    titleNum                    => { },
    TitleSortOrder              => { },
    TotalDuration               => { },
    TrackingID                  => { },
    UserCustom1                 => { },
    UserCustom2                 => { },
    UserEffectiveRating         => { },
    UserLastPlayedTime          => { },
    UserPlayCount               => { },
    UserPlaycountAfternoon      => { },
    UserPlaycountEvening        => { },
    UserPlaycountMorning        => { },
    UserPlaycountNight          => { },
    UserPlaycountWeekday        => { },
    UserPlaycountWeekend        => { },
    UserRating                  => { },
    UserServiceRating           => { },
    VideoBitrate                => { },
    VideoFormat                 => { },
    'WM/AlbumArtist'            => { Name => 'AlbumArtist', Writable => 'Unicode' }, # (NC)
    'WM/AlbumCoverURL'          => { Name => 'AlbumCoverURL', Writable => 'Unicode' }, # (NC)
    'WM/AlbumTitle'             => { Name => 'AlbumTitle',  Writable => 'Unicode' }, # (NC)
    'WM/BeatsPerMinute'         => 'BeatsPerMinute',
    'WM/Category'               => { Name => 'Category',    Writable => 'Unicode', List => 1 },
    'WM/Composer'               => { Name => 'Composer',    Writable => 'Unicode' }, # (NC)
    'WM/Conductor'              => { Name => 'Conductor',   Writable => 'Unicode', List => 1 },
    'WM/ContentDistributor'     => { Name => 'ContentDistributor', Writable => 'Unicode' },
    'WM/ContentDistributorType' => 'ContentDistributorType',
    'WM/ContentGroupDescription'=> 'ContentGroupDescription',
    'WM/Director'               => { Name => 'Director',    Writable => 'Unicode', List => 1 },
    'WM/EncodingTime'           => {
        Name => 'EncodingTime',
        Groups => { 2 => 'Time' },
        Shift => 'Time',
        Writable => 'date',
        PrintConv => '$self->ConvertDateTime($val)',
        PrintConvInv => '$self->InverseDateTime($val)',
    },
    'WM/Genre'                  => 'Genre',
    'WM/GenreID'                => 'GenreID',
    'WM/InitialKey'             => { Name => 'InitialKey',  Writable => 'Unicode' },
    'WM/Language'               => 'Language',
    'WM/Lyrics'                 => 'Lyrics',
    'WM/MCDI'                   => 'MCDI',
    'WM/MediaClassPrimaryID'    => {
        Name => 'MediaClassPrimaryID',
        Writable => 'GUID',
        PrintConv => { #http://msdn.microsoft.com/en-us/library/windows/desktop/dd757960(v=vs.85).aspx
            'D1607DBC-E323-4BE2-86A1-48A42A28441E' => 'Music',
            'DB9830BD-3AB3-4FAB-8A37-1A995F7FF74B' => 'Video',
            '01CD0F29-DA4E-4157-897B-6275D50C4F11' => 'Audio (not music)',
            'FCF24A76-9A57-4036-990D-E35DD8B244E1' => 'Other (not audio or video)',
        },
    },
    'WM/MediaClassSecondaryID' => {
        Name => 'MediaClassSecondaryID',
        Writable => 'GUID',
        PrintConv => { #http://msdn.microsoft.com/en-us/library/windows/desktop/dd757960(v=vs.85).aspx
            'E0236BEB-C281-4EDE-A36D-7AF76A3D45B5' => 'Audio Book',
            '3A172A13-2BD9-4831-835B-114F6A95943F' => 'Spoken Word',
            '6677DB9B-E5A0-4063-A1AD-ACEB52840CF1' => 'Audio News',
            '1B824A67-3F80-4E3E-9CDE-F7361B0F5F1B' => 'Talk Show',
            '1FE2E091-4E1E-40CE-B22D-348C732E0B10' => 'Video News',
            'D6DE1D88-C77C-4593-BFBC-9C61E8C373E3' => 'Web-based Video',
            '00033368-5009-4AC3-A820-5D2D09A4E7C1' => 'Sound Clip from Game',
            'F24FF731-96FC-4D0F-A2F5-5A3483682B1A' => 'Song from Game',
            'E3E689E2-BA8C-4330-96DF-A0EEEFFA6876' => 'Music Video',
            'B76628F4-300D-443D-9CB5-01C285109DAF' => 'Home Movie',
            'A9B87FC9-BD47-4BF0-AC4F-655B89F7D868' => 'Feature Film',
            'BA7F258A-62F7-47A9-B21F-4651C42A000E' => 'TV Show',
            '44051B5B-B103-4B5C-92AB-93060A9463F0' => 'Corporate Video',
            '0B710218-8C0C-475E-AF73-4C41C0C8F8CE' => 'Home Video from Pictures',
            '00000000-0000-0000-0000-000000000000' => 'Unknown Content', #PH
        },
    },
    'WM/MediaOriginalBroadcastDateTime' => {
        Name => 'MediaOriginalBroadcastDateTime',
        Groups => { 2 => 'Time' },
        PrintConv => '$self->ConvertDateTime($val)',
    },
    'WM/MediaOriginalChannel'   => 'MediaOriginalChannel',
    'WM/MediaStationName'       => 'MediaStationName',
    'WM/Mood'                   => { Name => 'Mood',        Writable => 'Unicode' },
    'WM/OriginalAlbumTitle'     => { Name => 'OriginalAlbumTitle',  Writable => 'Unicode' }, # (NC)
    'WM/OriginalArtist'         => { Name => 'OriginalArtist',      Writable => 'Unicode' }, # (NC)
    'WM/OriginalLyricist'       => { Name => 'OriginalLyricist',    Writable => 'Unicode' }, # (NC)
    'WM/ParentalRating'         => { Name => 'ParentalRating',      Writable => 'Unicode' },
    'WM/PartOfSet'              => 'PartOfSet',
    'WM/Period'                 => { Name => 'Period',      Writable => 'Unicode' },
    'WM/Producer'               => { Name => 'Producer',    Writable => 'Unicode', List => 1 },
    'WM/ProtectionType'         => 'ProtectionType',
    'WM/Provider'               => { Name => 'Provider',    Writable => 'Unicode' }, # (NC)
    'WM/ProviderRating'         => 'ProviderRating',
    'WM/ProviderStyle'          => 'ProviderStyle',
    'WM/Publisher'              => { Name => 'Publisher',   Writable => 'Unicode' }, # (multiple entries separated by semicolon)
    'WM/SharedUserRating'       => { Name => 'SharedUserRating', Writable => 'int64u' },
    'WM/SubscriptionContentID'  => 'SubscriptionContentID',
    'WM/SubTitle'               => { Name => 'Subtitle',    Writable => 'Unicode' },
    'WM/SubTitleDescription'    => 'SubtitleDescription',
    'WM/TrackNumber'            => 'TrackNumber',
    'WM/UniqueFileIdentifier'   => 'UniqueFileIdentifier',
    'WM/VideoFrameRate'         => 'VideoFrameRate',
    'WM/VideoHeight'            => 'VideoHeight',
    'WM/VideoWidth'             => 'VideoWidth',
    'WM/WMCollectionGroupID'    => 'WMCollectionGroupID',
    'WM/WMCollectionID'         => 'WMCollectionID',
    'WM/WMContentID'            => 'WMContentID',
    'WM/WMShadowFileSourceDRMType' => 'WMShadowFileSourceDRMType',
    'WM/WMShadowFileSourceFileType' => 'WMShadowFileSourceFileType',
    'WM/Writer'                 => { Name => 'Writer',  Groups => { 2 => 'Author' }, Writable => 'Unicode' }, # (NC)
    'WM/Year'                   => { Name => 'Year',    Groups => { 2 => 'Time' } },
    'WM/PromotionURL'           => { Name => 'PromotionURL',Writable => 'Unicode' },
    'WM/AuthorURL'              => { Name => 'AuthorURL', Groups => { 2 => 'Author' }, Writable => 'Unicode' },
    'WM/EncodedBy',             => { Name => 'EncodedBy',   Writable => 'Unicode' },

    # I can't find documentation for the following tags in videos,
    # but the tag ID's correspond to Microsoft property GUID+ID's
    # References:
    #  http://msdn.microsoft.com/en-us/library/cc251929%28v=prot.10%29.aspx
    #  http://multi-rename-script.googlecode.com/svn-history/r4/trunk/plugins/ShellDetails/ShellDetails.ini
    # I have observed only 1 so far:
    '{2CBAA8F5-D81F-47CA-B17A-F8D822300131} 100' => {
        Name => 'DateAcquired', # (seems to be when videos are downloaded from the camera)
        Groups => { 2 => 'Time' },
        Shift => 'Time',
        Writable => 'vt_filetime',
        PrintConv => '$self->ConvertDateTime($val)',
        PrintConvInv => '$self->InverseDateTime($val,undef)',
    },
    # the following have not yet been observed...
    '{B725F130-47EF-101A-A5F1-02608C9EEBAC} 10'    => 'Name',
    '{B725F130-47EF-101A-A5F1-02608C9EEBAC} 12'    => 'Size',
    '{B725F130-47EF-101A-A5F1-02608C9EEBAC} 4'     => 'Type',
    '{B725F130-47EF-101A-A5F1-02608C9EEBAC} 14'    => {
        Name => 'DateModified',
        Groups => { 2 => 'Time' },
        PrintConv => '$self->ConvertDateTime($val)',
    },
    '{B725F130-47EF-101A-A5F1-02608C9EEBAC} 15'    => {
        Name => 'DateCreated',
        Groups => { 2 => 'Time' },
        PrintConv => '$self->ConvertDateTime($val)',
    },
    '{B725F130-47EF-101A-A5F1-02608C9EEBAC} 16'    => {
        Name => 'DateAccessed',
        Groups => { 2 => 'Time' },
        PrintConv => '$self->ConvertDateTime($val)',
    },
    '{B725F130-47EF-101A-A5F1-02608C9EEBAC} 13'    => 'Attributes',
    '{D8C3986F-813B-449C-845D-87B95D674ADE} 2'     => 'Status',
    '{9B174B34-40FF-11D2-A27E-00C04FC30871} 4'     => 'Owner',
    '{F29F85E0-4FF9-1068-AB91-08002B27B3D9} 4'     => {
        Name => 'Author',
        Groups => { 2 => 'Author' },
    },
    '{F29F85E0-4FF9-1068-AB91-08002B27B3D9} 2'     => 'Title',
    '{F29F85E0-4FF9-1068-AB91-08002B27B3D9} 3'     => 'Subject',
    '{D5CDD502-2E9C-101B-9397-08002B2CF9AE} 2'     => 'Category',
    '{F29F85E0-4FF9-1068-AB91-08002B27B3D9} 14'    => 'Pages',
    '{F29F85E0-4FF9-1068-AB91-08002B27B3D9} 6'     => 'Comments',
    '{64440492-4C8B-11D1-8B70-080036B11A03} 11'    => {
        Name => 'Copyright',
        Groups => { 2 => 'Author' },
    },
    '{56A3372E-CE9C-11D2-9F0E-006097C686F6} 2'     => 'Artist',
    '{56A3372E-CE9C-11D2-9F0E-006097C686F6} 4'     => 'AlbumTitle',
    '{56A3372E-CE9C-11D2-9F0E-006097C686F6} 5'     => {
        Name => 'Year',
        Groups => { 2 => 'Time' },
    },
    '{56A3372E-CE9C-11D2-9F0E-006097C686F6} 7'     => 'TrackNumber',
    '{56A3372E-CE9C-11D2-9F0E-006097C686F6} 11'    => 'Genre',
    '{64440490-4C8B-11D1-8B70-080036B11A03} 3'     => 'Duration',
    '{64440490-4C8B-11D1-8B70-080036B11A03} 4'     => 'Bitrate',
    '{AEAC19E4-89AE-4508-B9B7-BB867ABEE2ED} 2'     => 'Protected',
    '{14B81DA1-0135-4D31-96D9-6CBFC9671A99} 272'   => 'CameraModel',
    '{14B81DA1-0135-4D31-96D9-6CBFC9671A99} 36867' => {
        Name => 'DatePictureTaken',
        Groups => { 2 => 'Time' },
        PrintConv => '$self->ConvertDateTime($val)',
    },
    '{6444048F-4C8B-11D1-8B70-080036B11A03} 13'    => 'Dimensions',
    '{6444048F-4C8B-11D1-8B70-080036B11A03} 3'     => 'Untitled0',
    '{6444048F-4C8B-11D1-8B70-080036B11A03} 4'     => 'Untitled1',
    '{6D748DE2-8D38-4CC3-AC60-F009B057C557} 2'     => 'EpisodeName',
    '{6D748DE2-8D38-4CC3-AC60-F009B057C557} 3'     => 'ProgramDescription',
    '{F29F85E0-4FF9-1068-AB91-08002B27B3D9} 12'    => 'Untitled2',
    '{64440490-4C8B-11D1-8B70-080036B11A03} 6'     => 'AudioSampleSize',
    '{64440490-4C8B-11D1-8B70-080036B11A03} 5'     => 'AudioSampleRate',
    '{64440490-4C8B-11D1-8B70-080036B11A03} 7'     => 'Channels',
    '{D5CDD502-2E9C-101B-9397-08002B2CF9AE} 15'    => 'Company',
    '{0CEF7D53-FA64-11D1-A203-0000F81FEDEE} 3'     => 'Description',
    '{0CEF7D53-FA64-11D1-A203-0000F81FEDEE} 4'     => 'FileVersion',
    '{0CEF7D53-FA64-11D1-A203-0000F81FEDEE} 7'     => 'ProductName',
    '{0CEF7D53-FA64-11D1-A203-0000F81FEDEE} 8'     => 'ProductVersion',
    '{F29F85E0-4FF9-1068-AB91-08002B27B3D9} 5'     => 'Keywords',
    '{28636AA6-953D-11D2-B5D6-00C04FD918D0} 11'    => 'Type',
    '{6D24888F-4718-4BDA-AFED-EA0FB4386CD8} 100'   => 'OfflineStatus',
    '{A94688B6-7D9F-4570-A648-E3DFC0AB2B3F} 100'   => 'OfflineAvailability',
    '{28636AA6-953D-11D2-B5D6-00C04FD918D0} 9'     => 'PerceivedType',
    '{1E3EE840-BC2B-476C-8237-2ACD1A839B22} 3'     => 'Kinds',
    '{56A3372E-CE9C-11D2-9F0E-006097C686F6} 36'    => 'Conductors',
    '{64440492-4C8B-11D1-8B70-080036B11A03} 9'     => 'Rating',
    '{14B81DA1-0135-4D31-96D9-6CBFC9671A99} 271'   => 'CameraMaker',
    '{F29F85E0-4FF9-1068-AB91-08002B27B3D9} 18'    => 'ProgramName',
    '{293CA35A-09AA-4DD2-B180-1FE245728A52} 100'   => 'Duration',
    '{BFEE9149-E3E2-49A7-A862-C05988145CEC} 100'   => 'IsOnline',
    '{315B9C8D-80A9-4EF9-AE16-8E746DA51D70} 100'   => 'IsRecurring',
    '{F6272D18-CECC-40B1-B26A-3911717AA7BD} 100'   => 'Location',
    '{D55BAE5A-3892-417A-A649-C6AC5AAAEAB3} 100'   => 'OptionalAttendeeAddresses',
    '{09429607-582D-437F-84C3-DE93A2B24C3C} 100'   => 'OptionalAttendees',
    '{744C8242-4DF5-456C-AB9E-014EFB9021E3} 100'   => 'OrganizerAddress',
    '{AAA660F9-9865-458E-B484-01BC7FE3973E} 100'   => 'OrganizerName',
    '{72FC5BA4-24F9-4011-9F3F-ADD27AFAD818} 100'   => 'ReminderTime',
    '{0BA7D6C3-568D-4159-AB91-781A91FB71E5} 100'   => 'RequiredAttendeeAddresses',
    '{B33AF30B-F552-4584-936C-CB93E5CDA29F} 100'   => 'RequiredAttendees',
    '{00F58A38-C54B-4C40-8696-97235980EAE1} 100'   => 'Resources',
    '{5BF396D4-5EB2-466F-BDE9-2FB3F2361D6E} 100'   => 'Free-busyStatus',
    '{9B174B35-40FF-11D2-A27E-00C04FC30871} 3'     => 'TotalSize',
    '{E3E0584C-B788-4A5A-BB20-7F5A44C9ACDD} 9'     => 'AccountName',
    '{28636AA6-953D-11D2-B5D6-00C04FD918D0} 5'     => 'Computer',
    '{9AD5BADB-CEA7-4470-A03D-B84E51B9949E} 100'   => 'Anniversary',
    '{CD102C9C-5540-4A88-A6F6-64E4981C8CD1} 100'   => 'AssistantsName',
    '{9A93244D-A7AD-4FF8-9B99-45EE4CC09AF6} 100'   => 'AssistantsPhone',
    '{176DC63C-2688-4E89-8143-A347800F25E9} 47'    => 'Birthday',
    '{730FB6DD-CF7C-426B-A03F-BD166CC9EE24} 100'   => 'BusinessAddress',
    '{402B5934-EC5A-48C3-93E6-85E86A2D934E} 100'   => 'BusinessCity',
    '{B0B87314-FCF6-4FEB-8DFF-A50DA6AF561C} 100'   => 'BusinessCountry-Region',
    '{BC4E71CE-17F9-48D5-BEE9-021DF0EA5409} 100'   => 'BusinessPOBox',
    '{E1D4A09E-D758-4CD1-B6EC-34A8B5A73F80} 100'   => 'BusinessPostalCode',
    '{446F787F-10C4-41CB-A6C4-4D0343551597} 100'   => 'BusinessStateOrProvince',
    '{DDD1460F-C0BF-4553-8CE4-10433C908FB0} 100'   => 'BusinessStreet',
    '{91EFF6F3-2E27-42CA-933E-7C999FBE310B} 100'   => 'BusinessFax',
    '{56310920-2491-4919-99CE-EADB06FAFDB2} 100'   => 'BusinessHomePage',
    '{6A15E5A0-0A1E-4CD7-BB8C-D2F1B0C929BC} 100'   => 'BusinessPhone',
    '{BF53D1C3-49E0-4F7F-8567-5A821D8AC542} 100'   => 'CallbackNumber',
    '{8FDC6DEA-B929-412B-BA90-397A257465FE} 100'   => 'CarPhone',
    '{D4729704-8EF1-43EF-9024-2BD381187FD5} 100'   => 'Children',
    '{8589E481-6040-473D-B171-7FA89C2708ED} 100'   => 'CompanyMainPhone',
    '{FC9F7306-FF8F-4D49-9FB6-3FFE5C0951EC} 100'   => 'Department',
    '{F8FA7FA3-D12B-4785-8A4E-691A94F7A3E7} 100'   => 'E-mailAddress',
    '{38965063-EDC8-4268-8491-B7723172CF29} 100'   => 'E-mail2',
    '{644D37B4-E1B3-4BAD-B099-7E7C04966ACA} 100'   => 'E-mail3',
    '{84D8F337-981D-44B3-9615-C7596DBA17E3} 100'   => 'E-mailList',
    '{CC6F4F24-6083-4BD4-8754-674D0DE87AB8} 100'   => 'E-mailDisplayName',
    '{F1A24AA7-9CA7-40F6-89EC-97DEF9FFE8DB} 100'   => 'FileAs',
    '{14977844-6B49-4AAD-A714-A4513BF60460} 100'   => 'FirstName',
    '{635E9051-50A5-4BA2-B9DB-4ED056C77296} 100'   => 'FullName',
    '{3C8CEE58-D4F0-4CF9-B756-4E5D24447BCD} 100'   => 'Gender',
    '{176DC63C-2688-4E89-8143-A347800F25E9} 70'    => 'GivenName',
    '{5DC2253F-5E11-4ADF-9CFE-910DD01E3E70} 100'   => 'Hobbies',
    '{98F98354-617A-46B8-8560-5B1B64BF1F89} 100'   => 'HomeAddress',
    '{176DC63C-2688-4E89-8143-A347800F25E9} 65'    => 'HomeCity',
    '{08A65AA1-F4C9-43DD-9DDF-A33D8E7EAD85} 100'   => 'HomeCountry-Region',
    '{7B9F6399-0A3F-4B12-89BD-4ADC51C918AF} 100'   => 'HomePOBox',
    '{8AFCC170-8A46-4B53-9EEE-90BAE7151E62} 100'   => 'HomePostalCode',
    '{C89A23D0-7D6D-4EB8-87D4-776A82D493E5} 100'   => 'HomeStateOrProvince',
    '{0ADEF160-DB3F-4308-9A21-06237B16FA2A} 100'   => 'HomeStreet',
    '{660E04D6-81AB-4977-A09F-82313113AB26} 100'   => 'HomeFax',
    '{176DC63C-2688-4E89-8143-A347800F25E9} 20'    => 'HomePhone',
    '{D68DBD8A-3374-4B81-9972-3EC30682DB3D} 100'   => 'IMAddresses',
    '{F3D8F40D-50CB-44A2-9718-40CB9119495D} 100'   => 'Initials',
    '{176DC63C-2688-4E89-8143-A347800F25E9} 6'     => 'JobTitle',
    '{97B0AD89-DF49-49CC-834E-660974FD755B} 100'   => 'Label',
    '{8F367200-C270-457C-B1D4-E07C5BCD90C7} 100'   => 'LastName',
    '{C0AC206A-827E-4650-95AE-77E2BB74FCC9} 100'   => 'MailingAddress',
    '{176DC63C-2688-4E89-8143-A347800F25E9} 71'    => 'MiddleName',
    '{176DC63C-2688-4E89-8143-A347800F25E9} 35'    => 'CellPhone',
    '{176DC63C-2688-4E89-8143-A347800F25E9} 74'    => 'Nickname',
    '{176DC63C-2688-4E89-8143-A347800F25E9} 7'     => 'OfficeLocation',
    '{508161FA-313B-43D5-83A1-C1ACCF68622C} 100'   => 'OtherAddress',
    '{6E682923-7F7B-4F0C-A337-CFCA296687BF} 100'   => 'OtherCity',
    '{8F167568-0AAE-4322-8ED9-6055B7B0E398} 100'   => 'OtherCountry-Region',
    '{8B26EA41-058F-43F6-AECC-4035681CE977} 100'   => 'OtherPOBox',
    '{95C656C1-2ABF-4148-9ED3-9EC602E3B7CD} 100'   => 'OtherPostalCode',
    '{71B377D6-E570-425F-A170-809FAE73E54E} 100'   => 'OtherStateOrProvince',
    '{FF962609-B7D6-4999-862D-95180D529AEA} 100'   => 'OtherStreet',
    '{D6304E01-F8F5-4F45-8B15-D024A6296789} 100'   => 'Pager',
    '{176DC63C-2688-4E89-8143-A347800F25E9} 69'    => 'PersonalTitle',
    '{C8EA94F0-A9E3-4969-A94B-9C62A95324E0} 100'   => 'City',
    '{E53D799D-0F3F-466E-B2FF-74634A3CB7A4} 100'   => 'Country-Region',
    '{DE5EF3C7-46E1-484E-9999-62C5308394C1} 100'   => 'POBox',
    '{18BBD425-ECFD-46EF-B612-7B4A6034EDA0} 100'   => 'PostalCode',
    '{F1176DFE-7138-4640-8B4C-AE375DC70A6D} 100'   => 'StateOrProvince',
    '{63C25B20-96BE-488F-8788-C09C407AD812} 100'   => 'Street',
    '{176DC63C-2688-4E89-8143-A347800F25E9} 48'    => 'PrimaryE-mail',
    '{176DC63C-2688-4E89-8143-A347800F25E9} 25'    => 'PrimaryPhone',
    '{7268AF55-1CE4-4F6E-A41F-B6E4EF10E4A9} 100'   => 'Profession',
    '{9D2408B6-3167-422B-82B0-F583B7A7CFE3} 100'   => 'Spouse',
    '{176DC63C-2688-4E89-8143-A347800F25E9} 73'    => 'Suffix',
    '{AAF16BAC-2B55-45E6-9F6D-415EB94910DF} 100'   => 'TTY-TTDPhone',
    '{C554493C-C1F7-40C1-A76C-EF8C0614003E} 100'   => 'Telex',
    '{E3E0584C-B788-4A5A-BB20-7F5A44C9ACDD} 18'    => 'Webpage',
    '{D5CDD502-2E9C-101B-9397-08002B2CF9AE} 27'    => 'Status',
    '{D5CDD502-2E9C-101B-9397-08002B2CF9AE} 26'    => 'ContentType',
    '{43F8D7B7-A444-4F87-9383-52271C9B915C} 100'   => {
        Name => 'DateArchived',
        Groups => { 2 => 'Time' },
        PrintConv => '$self->ConvertDateTime($val)',
    },
    '{72FAB781-ACDA-43E5-B155-B2434F85E678} 100'   => {
        Name => 'DateCompleted',
        Groups => { 2 => 'Time' },
        PrintConv => '$self->ConvertDateTime($val)',
    },
    '{14B81DA1-0135-4D31-96D9-6CBFC9671A99} 18258' => {
        Name => 'DateImported',
        Groups => { 2 => 'Time' },
        PrintConv => '$self->ConvertDateTime($val)',
    },
    '{276D7BB0-5B34-4FB0-AA4B-158ED12A1809} 100'   => 'ClientID',
    '{F334115E-DA1B-4509-9B3D-119504DC7ABB} 100'   => 'Contributors',
    '{F29F85E0-4FF9-1068-AB91-08002B27B3D9} 11'    => 'LastPrinted',
    '{F29F85E0-4FF9-1068-AB91-08002B27B3D9} 13'    => {
        Name => 'DateLastSaved',
        Groups => { 2 => 'Time' },
        PrintConv => '$self->ConvertDateTime($val)',
    },
    '{1E005EE6-BF27-428B-B01C-79676ACD2870} 100'   => 'Division',
    '{E08805C8-E395-40DF-80D2-54F0D6C43154} 100'   => 'DocumentID',
    '{D5CDD502-2E9C-101B-9397-08002B2CF9AE} 7'     => 'Slides',
    '{F29F85E0-4FF9-1068-AB91-08002B27B3D9} 10'    => 'TotalEditingTime',
    '{F29F85E0-4FF9-1068-AB91-08002B27B3D9} 15'    => 'WordCount',
    '{3F8472B5-E0AF-4DB2-8071-C53FE76AE7CE} 100'   => 'DueDate',
    '{C75FAA05-96FD-49E7-9CB4-9F601082D553} 100'   => 'EndDate',
    '{28636AA6-953D-11D2-B5D6-00C04FD918D0} 12'    => 'FileCount',
    '{41CF5AE0-F75A-4806-BD87-59C7D9248EB9} 100'   => 'WindowsFileName',
    '{67DF94DE-0CA7-4D6F-B792-053A3E4F03CF} 100'   => 'FlagColor',
    '{E3E0584C-B788-4A5A-BB20-7F5A44C9ACDD} 12'    => 'FlagStatus',
    '{9B174B35-40FF-11D2-A27E-00C04FC30871} 2'     => 'SpaceFree',
    '{6444048F-4C8B-11D1-8B70-080036B11A03} 7'     => 'BitDepth',
    '{6444048F-4C8B-11D1-8B70-080036B11A03} 5'     => 'HorizontalResolution',
    '{6444048F-4C8B-11D1-8B70-080036B11A03} 6'     => 'VerticalResolution',
    '{E3E0584C-B788-4A5A-BB20-7F5A44C9ACDD} 11'    => 'Importance',
    '{F23F425C-71A1-4FA8-922F-678EA4A60408} 100'   => 'IsAttachment',
    '{5CDA5FC8-33EE-4FF3-9094-AE7BD8868C4D} 100'   => 'IsDeleted',
    '{5DA84765-E3FF-4278-86B0-A27967FBDD03} 100'   => 'HasFlag',
    '{A6F360D2-55F9-48DE-B909-620E090A647C} 100'   => 'IsCompleted',
    '{346C8BD1-2E6A-4C45-89A4-61B78E8E700F} 100'   => 'Incomplete',
    '{E3E0584C-B788-4A5A-BB20-7F5A44C9ACDD} 10'    => 'ReadStatus',
    '{EF884C5B-2BFE-41BB-AAE5-76EEDF4F9902} 100'   => 'Shared',
    '{D0A04F0A-462A-48A4-BB2F-3706E88DBD7D} 100'   => {
        Name => 'Creator',
        Groups => { 2 => 'Author' },
    },
    '{F7DB74B4-4287-4103-AFBA-F1B13DCD75CF} 100'   => {
        Name => 'Date',
        Groups => { 2 => 'Time' },
        PrintConv => '$self->ConvertDateTime($val)',
    },
    '{B725F130-47EF-101A-A5F1-02608C9EEBAC} 2'     => 'FolderName',
    '{E3E0584C-B788-4A5A-BB20-7F5A44C9ACDD} 6'     => 'FolderPath',
    '{DABD30ED-0043-4789-A7F8-D013A4736622} 100'   => 'Folder',
    '{D4D0AA16-9948-41A4-AA85-D97FF9646993} 100'   => 'Participants',
    '{E3E0584C-B788-4A5A-BB20-7F5A44C9ACDD} 7'     => 'Path',
    '{DEA7C82C-1D89-4A66-9427-A4E3DEBABCB1} 100'   => 'ContactNames',
    '{95BEB1FC-326D-4644-B396-CD3ED90E6DDF} 100'   => 'EntryType',
    '{D5CDD502-2E9C-101B-9397-08002B2CF9AE} 28'    => 'Language',
    '{5CBF2787-48CF-4208-B90E-EE5E5D420294} 23'    => {
        Name => 'DateVisited',
        Groups => { 2 => 'Time' },
        PrintConv => '$self->ConvertDateTime($val)',
    },
    '{5CBF2787-48CF-4208-B90E-EE5E5D420294} 21'    => 'Description',
    '{B9B4B3FC-2B51-4A42-B5D8-324146AFCF25} 3'     => 'LinkStatus',
    '{B9B4B3FC-2B51-4A42-B5D8-324146AFCF25} 2'     => 'LinkTarget',
    '{5CBF2787-48CF-4208-B90E-EE5E5D420294} 2'     => 'URL',
    '{2E4B640D-5019-46D8-8881-55414CC5CAA0} 100'   => 'MediaCreated',
    '{DE41CC29-6971-4290-B472-F59F2E2F31E2} 100'   => {
        Name => 'DateReleased',
        Groups => { 2 => 'Time' },
        PrintConv => '$self->ConvertDateTime($val)',
    },
    '{64440492-4C8B-11D1-8B70-080036B11A03} 36'    => 'EncodedBy',
    '{64440492-4C8B-11D1-8B70-080036B11A03} 22'    => 'Producers',
    '{64440492-4C8B-11D1-8B70-080036B11A03} 30'    => 'Publisher',
    '{56A3372E-CE9C-11D2-9F0E-006097C686F6} 38'    => 'Subtitle',
    '{64440492-4C8B-11D1-8B70-080036B11A03} 34'    => 'UserWebURL',
    '{64440492-4C8B-11D1-8B70-080036B11A03} 23'    => 'Writers',
    '{E3E0584C-B788-4A5A-BB20-7F5A44C9ACDD} 21'    => 'Attachments',
    '{E3E0584C-B788-4A5A-BB20-7F5A44C9ACDD} 2'     => 'BccAddresses',
    '{E3E0584C-B788-4A5A-BB20-7F5A44C9ACDD} 3'     => 'BccNames',
    '{E3E0584C-B788-4A5A-BB20-7F5A44C9ACDD} 4'     => 'CcAddresses',
    '{E3E0584C-B788-4A5A-BB20-7F5A44C9ACDD} 5'     => 'CcNames',
    '{DC8F80BD-AF1E-4289-85B6-3DFC1B493992} 100'   => 'ConversationID',
    '{E3E0584C-B788-4A5A-BB20-7F5A44C9ACDD} 20'    => {
        Name => 'DateReceived',
        Groups => { 2 => 'Time' },
        PrintConv => '$self->ConvertDateTime($val)',
    },
    '{E3E0584C-B788-4A5A-BB20-7F5A44C9ACDD} 19'    => {
        Name => 'DateSent',
        Groups => { 2 => 'Time' },
        PrintConv => '$self->ConvertDateTime($val)',
    },
    '{E3E0584C-B788-4A5A-BB20-7F5A44C9ACDD} 13'    => 'FromAddresses',
    '{E3E0584C-B788-4A5A-BB20-7F5A44C9ACDD} 14'    => 'FromNames',
    '{9C1FCF74-2D97-41BA-B4AE-CB2E3661A6E4} 8'     => 'HasAttachments',
    '{0BE1C8E7-1981-4676-AE14-FDD78F05A6E7} 100'   => 'SenderAddress',
    '{0DA41CFA-D224-4A18-AE2F-596158DB4B3A} 100'   => 'SenderName',
    '{E3E0584C-B788-4A5A-BB20-7F5A44C9ACDD} 15'    => 'Store',
    '{E3E0584C-B788-4A5A-BB20-7F5A44C9ACDD} 16'    => 'ToAddresses',
    '{BCCC8A3C-8CEF-42E5-9B1C-C69079398BC7} 100'   => 'ToDoTitle',
    '{E3E0584C-B788-4A5A-BB20-7F5A44C9ACDD} 17'    => 'ToNames',
    '{FDF84370-031A-4ADD-9E91-0D775F1C6605} 100'   => 'Mileage',
    '{56A3372E-CE9C-11D2-9F0E-006097C686F6} 13'    => 'AlbumArtist',
    '{56A3372E-CE9C-11D2-9F0E-006097C686F6} 35'    => 'Beats-per-minute',
    '{64440492-4C8B-11D1-8B70-080036B11A03} 19'    => 'Composers',
    '{56A3372E-CE9C-11D2-9F0E-006097C686F6} 34'    => 'InitialKey',
    '{56A3372E-CE9C-11D2-9F0E-006097C686F6} 39'    => 'Mood',
    '{56A3372E-CE9C-11D2-9F0E-006097C686F6} 37'    => 'PartOfSet',
    '{64440492-4C8B-11D1-8B70-080036B11A03} 31'    => 'Period',
    '{4776CAFA-BCE4-4CB1-A23E-265E76D8EB11} 100'   => 'Color',
    '{64440492-4C8B-11D1-8B70-080036B11A03} 21'    => 'ParentalRating',
    '{10984E0A-F9F2-4321-B7EF-BAF195AF4319} 100'   => 'ParentalRatingReason',
    '{9B174B35-40FF-11D2-A27E-00C04FC30871} 5'     => 'SpaceUsed',
    '{D35F743A-EB2E-47F2-A286-844132CB1427} 100'   => 'ExifVersion',
    '{14B81DA1-0135-4D31-96D9-6CBFC9671A99} 18248' => 'Event',
    '{14B81DA1-0135-4D31-96D9-6CBFC9671A99} 37380' => 'ExposureBias',
    '{14B81DA1-0135-4D31-96D9-6CBFC9671A99} 34850' => 'ExposureProgram',
    '{14B81DA1-0135-4D31-96D9-6CBFC9671A99} 33434' => 'ExposureTime',
    '{14B81DA1-0135-4D31-96D9-6CBFC9671A99} 33437' => 'F-stop',
    '{14B81DA1-0135-4D31-96D9-6CBFC9671A99} 37385' => 'FlashMode',
    '{14B81DA1-0135-4D31-96D9-6CBFC9671A99} 37386' => 'FocalLength',
    '{A0E74609-B84D-4F49-B860-462BD9971F98} 100'   => 'FocalLength35mm',
    '{14B81DA1-0135-4D31-96D9-6CBFC9671A99} 34855' => 'ISOSpeed',
    '{E6DDCAF7-29C5-4F0A-9A68-D19412EC7090} 100'   => 'LensMaker',
    '{E1277516-2B5F-4869-89B1-2E585BD38B7A} 100'   => 'LensModel',
    '{14B81DA1-0135-4D31-96D9-6CBFC9671A99} 37384' => 'LightSource',
    '{08F6D7C2-E3F2-44FC-AF1E-5AA5C81A2D3E} 100'   => 'MaxAperture',
    '{14B81DA1-0135-4D31-96D9-6CBFC9671A99} 37383' => 'MeteringMode',
    '{14B81DA1-0135-4D31-96D9-6CBFC9671A99} 274'   => 'Orientation',
    '{6D217F6D-3F6A-4825-B470-5F03CA2FBE9B} 100'   => 'ProgramMode',
    '{49237325-A95A-4F67-B211-816B2D45D2E0} 100'   => 'Saturation',
    '{14B81DA1-0135-4D31-96D9-6CBFC9671A99} 37382' => 'SubjectDistance',
    '{EE3D3D8A-5381-4CFA-B13B-AAF66B5F4EC9} 100'   => 'WhiteBalance',
    '{9C1FCF74-2D97-41BA-B4AE-CB2E3661A6E4} 5'     => 'Priority',
    '{39A7F922-477C-48DE-8BC8-B28441E342E3} 100'   => 'Project',
    '{6D748DE2-8D38-4CC3-AC60-F009B057C557} 7'     => 'ChannelNumber',
    '{6D748DE2-8D38-4CC3-AC60-F009B057C557} 12'    => 'ClosedCaptioning',
    '{6D748DE2-8D38-4CC3-AC60-F009B057C557} 13'    => 'Rerun',
    '{6D748DE2-8D38-4CC3-AC60-F009B057C557} 14'    => 'SAP',
    '{4684FE97-8765-4842-9C13-F006447B178C} 100'   => 'BroadcastDate',
    '{A5477F61-7A82-4ECA-9DDE-98B69B2479B3} 100'   => 'RecordingTime',
    '{6D748DE2-8D38-4CC3-AC60-F009B057C557} 5'     => 'StationCallSign',
    '{1B5439E7-EBA1-4AF8-BDD7-7AF1D4549493} 100'   => 'StationName',
    '{560C36C0-503A-11CF-BAA1-00004C752A9A} 2'     => 'AutoSummary',
    '{560C36C0-503A-11CF-BAA1-00004C752A9A} 3'     => 'Summary',
    '{49691C90-7E17-101A-A91C-08002B2ECDA9} 3'     => 'SearchRanking',
    '{F8D3F6AC-4874-42CB-BE59-AB454B30716A} 100'   => 'Sensitivity',
    '{EF884C5B-2BFE-41BB-AAE5-76EEDF4F9902} 200'   => 'SharedWith',
    '{668CDFA5-7A1B-4323-AE4B-E527393A1D81} 100'   => 'Source',
    '{48FD6EC8-8A12-4CDF-A03E-4EC5A511EDDE} 100'   => 'StartDate',
    '{D37D52C6-261C-4303-82B3-08B926AC6F12} 100'   => 'BillingInformation',
    '{084D8A0A-E6D5-40DE-BF1F-C8820E7C877C} 100'   => 'Complete',
    '{08C7CC5F-60F2-4494-AD75-55E3E0B5ADD0} 100'   => 'TaskOwner',
    '{28636AA6-953D-11D2-B5D6-00C04FD918D0} 14'    => 'TotalFileSize',
    '{0CEF7D53-FA64-11D1-A203-0000F81FEDEE} 9'     => 'LegalTrademarks',
    '{64440491-4C8B-11D1-8B70-080036B11A03} 10'    => 'VideoCompression',
    '{64440492-4C8B-11D1-8B70-080036B11A03} 20'    => 'Directors',
    '{64440491-4C8B-11D1-8B70-080036B11A03} 8'     => 'DataRate',
    '{64440491-4C8B-11D1-8B70-080036B11A03} 4'     => 'FrameHeight',
    '{64440491-4C8B-11D1-8B70-080036B11A03} 6'     => 'FrameRate',
    '{64440491-4C8B-11D1-8B70-080036B11A03} 3'     => 'FrameWidth',
    '{64440491-4C8B-11D1-8B70-080036B11A03} 43'    => 'TotalBitrate',
);

#------------------------------------------------------------------------------
# check new value for Xtra tag
# Inputs: 0) ExifTool object ref, 1) tagInfo hash ref, 2) raw value ref
# Returns: error string, or undef on success
sub CheckXtra($$$)
{
    my ($et, $tagInfo, $valPt) = @_;
    my $format = $$tagInfo{Writable};
    return 'Unknown format' unless $format;
    if ($format =~ /^int/) {
        return 'Not an integer' unless Image::ExifTool::IsInt($$valPt);
    } elsif ($format ne 'Unicode') {
        my @vals = ($$valPt);
        return 'Invalid format' unless WriteXtraValue($et, $tagInfo, \@vals);
    }
    return undef;
}

#------------------------------------------------------------------------------
# Decode value(s) in Microsoft Xtra tag
# Inputs: 0) ExifTool object ref, 1) value data
# Returns: Scalar context: decoded value, List context: 0) decoded value, 1) format string
sub ReadXtraValue($$)
{
    my ($et, $data) = @_;
    my ($format, $i, @vals);
    
    return undef if length($data) < 10;

    # (version flags according to the reference, but looks more like a count - PH)
    my $count = Get32u(\$data, 0);
    # point to start of first value (after 4-byte count, 4-byte length and 2-byte type)
    my $valPos = 10;
    for ($i=0; ;) {
        # (stored value includes size of $valLen and $valType, so subtract 6)
        my $valLen = Get32u(\$data, $valPos - 6) - 6;
        last if $valPos + $valLen > length($data);
        my $valType = Get16u(\$data, $valPos - 2);
        my $val = substr($data, $valPos, $valLen);
        # Note: all dumb Microsoft values are little-endian inside a big-endian-format file
        SetByteOrder('II');
        if ($valType == 8) {
            $format = 'Unicode';
            $val = $et->Decode($val, 'UCS2');
        } elsif ($valType == 19 and $valLen == 8) {
            $format = 'int64u';
            $val = Get64u(\$val, 0);
        } elsif ($valType == 21 and $valLen == 8) {
            $format = 'date';
            $val = Get64u(\$val, 0);
            # convert time from 100 ns intervals since Jan 1, 1601
            $val = $val * 1e-7 - 11644473600 if $val;
            # (the Nikon S100 uses UTC timezone, same as ASF - PH)
            $val = Image::ExifTool::ConvertUnixTime($val, 1);
        } elsif ($valType == 72 and $valLen == 16) {
            $format = 'GUID';
            $val = uc unpack('H*',pack('NnnNN',unpack('VvvNN',$val)));
            $val =~ s/(.{8})(.{4})(.{4})(.{4})/$1-$2-$3-$4-/;
        } elsif ($valType == 65 and $valLen > 4) { #PH (empirical)
            $format = 'variant';
            require Image::ExifTool::FlashPix;
            my $vPos = 0; # (necessary because ReadFPXValue updates this)
            # read entry as a VT_VARIANT (use FlashPix module for this)
            $val = Image::ExifTool::FlashPix::ReadFPXValue($et, \$val, $vPos,
                   Image::ExifTool::FlashPix::VT_VARIANT(), $valLen, 1);
        } else {
            $format = "Unknown($valType)";
        }
        SetByteOrder('MM'); # back to native QuickTime byte ordering
        push @vals, $val;
        last if ++$i >= $count;
        $valPos += $valLen + 6; # step to next value
        last if $valPos > length($data);
    }
    return wantarray ? (\@vals, $format) : \@vals;
}

#------------------------------------------------------------------------------
# Write a Microsoft Xtra value
# Inputs: 0) ExifTool object ref, 1) tagInfo ref, 2) reference to list of values
# Returns: new value binary data (or empty string)
sub WriteXtraValue($$$)
{
    my ($et, $tagInfo, $vals) = @_;
    my $format = $$tagInfo{Writable};
    my $buff = '';
    my $count = 0;
    my $val;
    foreach $val (@$vals) {
        SetByteOrder('II');
        my ($type, $dat);
        if ($format eq 'Unicode') {
            $dat = $et->Encode($val,'UCS2','II') . "\0\0";  # (must be null terminated)
            $type = 8;
        } elsif ($format eq 'int64u') {
            if (Image::ExifTool::IsInt($val)) {
                $dat = Set64u($val);
                $type = 19;
            }
        } elsif ($format eq 'date') {
            $dat = Image::ExifTool::GetUnixTime($val, 1);   # (convert to UTC, NC)
            if ($dat) {
                # 100ns intervals since Jan 1, 1601
                $dat = Set64u(($dat + 11644473600) * 1e7);
                $type = 21;
            }
        } elsif ($format eq 'vt_filetime') { # 'date' value inside a VT_VARIANT
            $dat = Image::ExifTool::GetUnixTime($val);  # (leave as local time, NC)
            if ($dat) {
                # 100ns intervals since Jan 1, 1601
                $dat = Set32u(64) . Set64u(($dat + 11644473600) * 1e7);
                $type = 65;
            }
        } elsif ($format eq 'GUID') {
            ($dat = $val) =~ tr/-//d;
            if (length($dat) == 32) {
                $dat = pack('VvvNN',unpack('NnnNN',pack('H*', $dat)));
                $type = 72;
            }
        } else {
            $et->Warn("Error converting value for Microsoft:$$tagInfo{Name}");
        }
        SetByteOrder('MM');
        if (defined $type) {
            ++$count;
            $buff .= Set32u(length($dat)+6) . Set16u($type) . $dat;
        }
    }
    return $count ? Set32u($count) . $buff : '';
}

#------------------------------------------------------------------------------
# Add new values to list
# Inputs: 0) ExifTool ref, 1) new value list ref, 2) nvHash ref
# Returns: true if something was added
sub AddNewValues($$$)
{
    my ($et, $vals, $nvHash) = @_;
    my @newVals = $et->GetNewValue($nvHash) or return undef;
    if ($$et{OPTIONS}{Verbose} > 1) {
        $et->VPrint(1, "  + Microsoft:$$nvHash{TagInfo}{Name} = $_\n") foreach @newVals;
    }
    push @$vals, @newVals;
    return 1;
}

#------------------------------------------------------------------------------
# Write tags to a Microsoft Xtra MP4 atom
# Inputs: 0) ExifTool object ref, 1) source dirInfo ref, 2) tag table ref
# Returns: Microsoft Xtra data block (may be empty if no Xtra data) or undef on error
sub WriteXtra($$$)
{
    my ($et, $dirInfo, $tagTablePtr) = @_;
    $et or return 1;      # allow dummy access

    my $delGroup = ($$et{DEL_GROUP} and $$et{DEL_GROUP}{Microsoft});
    my $newTags = $et->GetNewTagInfoHash($tagTablePtr);

    return undef unless $delGroup or %$newTags;  # don't rewrite if nothing to do

    my $dataPt = $$dirInfo{DataPt};
    my $dataLen = length $$dataPt;
    my $newData = '';
    my $pos = 0;
    my ($err, %done, $changed, $tag);

    if ($delGroup) {
        $changed = 1 if $dataLen;
        my $empty = '';
        $dataPt = $empty;
        $dataLen = 0;
    }
    for (;;) {
        last if $pos + 4 > $dataLen;
        my $size = Get32u($dataPt, $pos); # (includes $size word)
        ($size < 8 or $pos + $size > $dataLen) and $err=1, last;
        my $tagLen = Get32u($dataPt, $pos + 4);
        $tagLen + 18 > $size and $err=1, last;
        $tag = substr($$dataPt, $pos + 8, $tagLen);
        my @newVals;
        while ($$newTags{$tag}) {
            my $nvHash = $et->GetNewValueHash($$newTags{$tag});
            $$nvHash{CreateOnly} and delete($$newTags{$tag}), last; # don't edit this tag
            my $valPos = $pos + 8 + $tagLen;
            my $valLen = $size - 8 - $tagLen;
            my $val = ReadXtraValue($et, substr($$dataPt, $valPos, $valLen));
            foreach $val (@$val) {
                my $overwrite = $et->IsOverwriting($nvHash, $val);
                $overwrite or push(@newVals, $val), next;
                $et->VPrint(1, "  - Microsoft:$$newTags{$tag}{Name} = $val\n");
                next if $done{$tag};
                $done{$tag} = 1;
                AddNewValues($et, \@newVals, $nvHash);
            }
            # add to the end of the list if this was a List-type tag and we didn't delete anything
            if (not $done{$tag} and $$newTags{$tag}{List}) {
                AddNewValues($et, \@newVals, $nvHash) or last;
                $done{$tag} = 1;
            }
            last;   # (it was a cheap goto)
        }
        if ($done{$tag}) {
            $changed = 1;
            # write changed values
            my $buff = WriteXtraValue($et, $$newTags{$tag}, \@newVals);
            if (length $buff) {
                $newData .= Set32u(8+length($tag)+length($buff)) . Set32u(length($tag)) . $tag . $buff;
            }
        } else {
            # nothing changed; just copy over
            $newData .= substr($$dataPt, $pos, $size);
        }
        $pos += $size;  # step to next entry
    }
    if ($err) {
        $et->Warn('Microsoft Xtra format error');
        return undef;
    }
    # add any new tags
    foreach $tag (sort keys %$newTags) {
        next if $done{$tag};
        my $nvHash = $et->GetNewValueHash($$newTags{$tag});
        next unless $$nvHash{IsCreating} and not $$nvHash{EditOnly};
        my @newVals;
        AddNewValues($et, \@newVals, $nvHash) or next;
        my $buff = WriteXtraValue($et, $$newTags{$tag}, \@newVals);
        if (length $buff) {
            $newData .= Set32u(8+length($tag)+length($buff)) . Set32u(length($tag)) . $tag . $buff;
            $changed = 1;
        }
    }
    if ($changed) {
        ++$$et{CHANGED};
    } else {
        undef $newData;
    }
    return $newData;
}

#------------------------------------------------------------------------------
# Extract information from Xtra MP4 atom
# Inputs: 0) ExifTool object ref, 1) dirInfo ref, 2) tag table ref
# Returns: 1 on success
# Reference: http://code.google.com/p/mp4v2/ [since removed from trunk]
sub ProcessXtra($$$)
{
    my ($et, $dirInfo, $tagTablePtr) = @_;
    my $dataPt = $$dirInfo{DataPt};
    my $dataPos = $$dirInfo{Base} || 0;
    my $dataLen = $$dirInfo{DataLen};
    my $pos = 0;
    $et->VerboseDir('Xtra', 0, $dataLen);
    for (;;) {
        last if $pos + 4 > $dataLen;
        my $size = Get32u($dataPt, $pos); # (includes $size word)
        last if $size < 8 or $pos + $size > $dataLen;
        my $tagLen = Get32u($dataPt, $pos + 4);
        last if $tagLen + 18 > $size;
        my $valLen = $size - 8 - $tagLen;
        if ($tagLen > 0 and $valLen > 0) {
            my $tag = substr($$dataPt, $pos + 8, $tagLen);
            my $valPos = $pos + 8 + $tagLen;
            my ($val, $format) = ReadXtraValue($et, substr($$dataPt, $valPos, $valLen));
            last unless defined $val;
            $val = $$val[0] if @$val == 1;
            my $tagInfo = $et->GetTagInfo($tagTablePtr, $tag);
            unless ($tagInfo) {
                # generate tag information for unrecognized tags
                my $name = $tag;
                $name =~ s{^WM/}{};
              # $name =~ tr/-_A-Za-z0-9//dc;
                if ($name =~ /^[-\w]+$/) {
                    $tagInfo = { Name => ucfirst($name) };
                    AddTagToTable($tagTablePtr, $tag, $tagInfo);
                    $et->VPrint(0, $$et{INDENT}, "[adding Microsoft:$tag]\n");
                }
            }
            my $count = ref $val ? scalar @$val : 1;
            $et->HandleTag($tagTablePtr, $tag, $val,
                TagInfo => $tagInfo,
                DataPt  => $dataPt,
                DataPos => $dataPos,
                Start   => $valPos,
                Size    => $valLen,
                Format  => $format,
                Extra   => " count=$count",
            );
        }
        $pos += $size;  # step to next entry
    }
    return 1;
}

1;  # end

__END__

=head1 NAME

Image::ExifTool::Microsoft - Definitions for custom Microsoft tags

=head1 SYNOPSIS

This module is used by Image::ExifTool

=head1 DESCRIPTION

This module contains definitions required by Image::ExifTool to interpret
Microsoft-specific EXIF and XMP tags, and routines to read/write Microsoft
Xtra tags in videos.

=head1 AUTHOR

Copyright 2003-2025, Phil Harvey (philharvey66 at gmail.com)

This library is free software; you can redistribute it and/or modify it
under the same terms as Perl itself.

=head1 REFERENCES

=over 4

=item L<http://research.microsoft.com/en-us/um/redmond/groups/ivm/hdview/hdmetadataspec.htm>

=back

=head1 SEE ALSO

L<Image::ExifTool::TagNames/Microsoft Tags>,
L<Image::ExifTool(3pm)|Image::ExifTool>

=cut

