File Coverage

blib/lib/Image/ExifTool/PLIST.pm
Criterion Covered Total %
statement 180 224 80.3
branch 102 166 61.4
condition 47 86 54.6
subroutine 9 10 90.0
pod 0 5 0.0
total 338 491 68.8


line stmt bran cond sub pod time code
1             #------------------------------------------------------------------------------
2             # File: PLIST.pm
3             #
4             # Description: Read Apple PLIST information
5             #
6             # Revisions: 2013-02-01 - P. Harvey Created
7             #
8             # References: 1) http://www.apple.com/DTDs/PropertyList-1.0.dtd
9             # 2) http://opensource.apple.com/source/CF/CF-550/CFBinaryPList.c
10             #
11             # Notes: - Sony MODD files also use XML PLIST format, but with a few quirks
12             #
13             # - Decodes both the binary and XML-based PLIST formats
14             #------------------------------------------------------------------------------
15              
16             package Image::ExifTool::PLIST;
17              
18 9     9   4324 use strict;
  9         13  
  9         303  
19 9     9   31 use vars qw($VERSION);
  9         16  
  9         348  
20 9     9   47 use Image::ExifTool qw(:DataAccess :Utils);
  9         13  
  9         1815  
21 9     9   2962 use Image::ExifTool::XMP;
  9         23  
  9         786  
22 9     9   44 use Image::ExifTool::GPS;
  9         13  
  9         31535  
23              
24             $VERSION = '1.17';
25              
26             sub ExtractObject($$;$);
27             sub Get24u($$);
28              
29             # access routines to read various-sized integer/real values (add 0x100 to size for reals)
30             my %readProc = (
31             1 => \&Get8u,
32             2 => \&Get16u,
33             3 => \&Get24u,
34             4 => \&Get32u,
35             8 => \&Get64u,
36             0x104 => \&GetFloat,
37             0x108 => \&GetDouble,
38             );
39              
40             # recognize different types of PLIST files based on certain tags
41             my %plistType = (
42             adjustmentBaseVersion => 'AAE',
43             );
44              
45             # PLIST tags (generated on-the-fly for most tags)
46             %Image::ExifTool::PLIST::Main = (
47             PROCESS_PROC => \&ProcessPLIST,
48             GROUPS => { 0 => 'PLIST', 1 => 'XML', 2 => 'Document' },
49             VARS => { LONG_TAGS => 18 },
50             NOTES => q{
51             Apple Property List tags. ExifTool reads both XML and binary-format PLIST
52             files, and will extract any existing tags even if they aren't listed below.
53             These tags belong to the family 0 "PLIST" group, but family 1 group may be
54             either "XML" or "PLIST" depending on whether the format is XML or binary.
55             },
56             #
57             # tags found in PLIST information of QuickTime iTunesInfo iTunMOVI atom (ref PH)
58             #
59             'cast//name' => { Name => 'Cast', List => 1 },
60             'directors//name' => { Name => 'Directors', List => 1 },
61             'producers//name' => { Name => 'Producers', List => 1 },
62             'screenwriters//name' => { Name => 'Screenwriters', List => 1 },
63             'codirectors//name' => { Name => 'Codirectors', List => 1 }, # (NC)
64             'studio//name' => { Name => 'Studio', List => 1 }, # (NC)
65             #
66             # tags found in MODD files (ref PH)
67             #
68             'MetaDataList//DateTimeOriginal' => {
69             Name => 'DateTimeOriginal',
70             Description => 'Date/Time Original',
71             Groups => { 2 => 'Time' },
72             # Sony uses a "real" here -- number of days since Dec 31, 1899
73             ValueConv => 'IsFloat($val) ? ConvertUnixTime(($val - 25569) * 24 * 3600) : $val',
74             PrintConv => '$self->ConvertDateTime($val)',
75             },
76             'MetaDataList//Duration' => {
77             Name => 'Duration',
78             Groups => { 2 => 'Video' },
79             PrintConv => 'ConvertDuration($val)',
80             },
81             'MetaDataList//Geolocation/Latitude' => {
82             Name => 'GPSLatitude',
83             Groups => { 2 => 'Location' },
84             PrintConv => 'Image::ExifTool::GPS::ToDMS($self, $val, 1, "N")',
85             },
86             'MetaDataList//Geolocation/Longitude' => {
87             Name => 'GPSLongitude',
88             Groups => { 2 => 'Location' },
89             PrintConv => 'Image::ExifTool::GPS::ToDMS($self, $val, 1, "E")',
90             },
91             'MetaDataList//Geolocation/MapDatum' => {
92             Name => 'GPSMapDatum',
93             Groups => { 2 => 'Location' },
94             },
95             # slow motion stuff found in AAE files
96             'slowMotion/regions/timeRange/start/flags' => {
97             Name => 'SlowMotionRegionsStartTimeFlags',
98             PrintConv => { BITMASK => {
99             0 => 'Valid',
100             1 => 'Has been rounded',
101             2 => 'Positive infinity',
102             3 => 'Negative infinity',
103             4 => 'Indefinite',
104             }},
105             },
106             'slowMotion/regions/timeRange/start/value' => 'SlowMotionRegionsStartTimeValue',
107             'slowMotion/regions/timeRange/start/timescale' => 'SlowMotionRegionsStartTimeScale',
108             'slowMotion/regions/timeRange/start/epoch' => 'SlowMotionRegionsStartTimeEpoch',
109             'slowMotion/regions/timeRange/duration/flags' => {
110             Name => 'SlowMotionRegionsDurationFlags',
111             PrintConv => { BITMASK => {
112             0 => 'Valid',
113             1 => 'Has been rounded',
114             2 => 'Positive infinity',
115             3 => 'Negative infinity',
116             4 => 'Indefinite',
117             }},
118             },
119             'slowMotion/regions/timeRange/duration/value' => 'SlowMotionRegionsDurationValue',
120             'slowMotion/regions/timeRange/duration/timescale' => 'SlowMotionRegionsDurationTimeScale',
121             'slowMotion/regions/timeRange/duration/epoch' => 'SlowMotionRegionsDurationEpoch',
122             'slowMotion/regions' => 'SlowMotionRegions',
123             'slowMotion/rate' => 'SlowMotionRate',
124             # buried deep in live photo .mov file
125             'LivePhotoMetadataSetupDataVersion' => { },
126             'SystemVersion/ProductBuildVersion' => 'ProductBuildVersion',
127             'SystemVersion/ProductName' => 'ProductName',
128             'SystemVersion/ProductVersion' => 'ProductVersion',
129             'FrameworkVersions/CoreMotion' => 'CoreMotionVersion',
130             'FrameworkVersions/CMCaptureCore' => 'CMCaptureCoreVersion',
131             'FrameworkVersions/H16ISPServices' => 'H16ISPServicesVersion',
132             'FrameworkVersions/CoreMedia' => 'CoreMediaVersion',
133             XMLFileType => {
134             # recognize MODD files by their content
135             RawConv => q{
136             if ($val eq 'ModdXML' and $$self{FILE_TYPE} eq 'XMP') {
137             $self->OverrideFileType('MODD');
138             }
139             return $val;
140             },
141             },
142             adjustmentData => { # AAE file
143             Name => 'AdjustmentData',
144             CompressedPLIST => 1,
145             SubDirectory => { TagTable => 'Image::ExifTool::PLIST::Main' },
146             },
147             );
148              
149             #------------------------------------------------------------------------------
150             # We found a PLIST XML property name/value
151             # Inputs: 0) ExifTool object ref, 1) tag table ref
152             # 2) reference to array of XML property names (last is current property)
153             # 3) property value, 4) attribute hash ref (not used here)
154             # Returns: 1 if valid tag was found
155             sub FoundTag($$$$;$)
156             {
157 37     37 0 64 my ($et, $tagTablePtr, $props, $val, $attrs) = @_;
158 37 50       51 return 0 unless @$props;
159 37         86 my $verbose = $et->Options('Verbose');
160 37   100     90 my $keys = $$et{PListKeys} || ( $$et{PListKeys} = [] );
161              
162 37         56 my $prop = $$props[-1];
163 37 50       45 if ($verbose > 1) {
164 0         0 $et->VPrint(0, $$et{INDENT}, '[', join('/',@$props), ' = ',
165             $et->Printable($val), "]\n");
166             }
167             # un-escape XML character entities
168 37         71 $val = Image::ExifTool::XMP::UnescapeXML($val);
169              
170             # handle the various PLIST properties
171 37 100 66     124 if ($prop eq 'data') {
    100          
    100          
172 2 50 33     11 if ($val =~ /^[0-9a-f]+$/ and not length($val) & 0x01) {
173             # MODD files use ASCII-hex encoded "data"...
174 0         0 my $buff = pack('H*', $val);
175 0         0 $val = \$buff;
176             } else {
177             # ...but the PLIST DTD specifies Base64 encoding
178 2         7 $val = Image::ExifTool::XMP::DecodeBase64($val);
179             }
180             } elsif ($prop eq 'date') {
181 2         6 $val = Image::ExifTool::XMP::ConvertXMPDate($val);
182             } elsif ($prop eq 'true' or $prop eq 'false') {
183 1         3 $val = ucfirst $prop;
184             } else {
185             # convert from UTF8 to ExifTool Charset
186 32         69 $val = $et->Decode($val, 'UTF8');
187 32 100       53 if ($prop eq 'key') {
188 17 100       26 if (@$props <= 3) { # top-level key should be plist/dict/key
189 15         28 @$keys = ( $val );
190             } else {
191             # save key names to be used in tag name
192 2         1113 push @$keys, '' while @$keys < @$props - 3;
193 2         7 pop @$keys while @$keys > @$props - 2;
194 2         4 $$keys[@$props - 3] = $val;
195             }
196 17         33 return 0;
197             }
198             }
199              
200 20 100       32 return 0 unless @$keys; # can't store value if no associated key
201              
202 18         38 my $tag = join '/', @$keys; # generate tag ID from 'key' values
203 18         26 my $tagInfo = $$tagTablePtr{$tag};
204 18 100       24 unless ($tagInfo) {
205 15 50       37 $et->VPrint(0, $$et{INDENT}, "[adding $tag]\n") if $verbose;
206             # generate tag name from ID
207 15         16 my $name = $tag;
208 15         22 $name =~ s{^MetaDataList//}{}; # shorten long MODD metadata tag names
209 15         18 $name =~ s{//name$}{}; # remove unnecessary MODD "name" property
210 15         26 $name =~ s/([^A-Za-z])([a-z])/$1\u$2/g; # capitalize words
211 15         22 $name =~ tr/-_a-zA-Z0-9//dc; # remove illegal characters
212 15         48 $tagInfo = { Name => ucfirst($name), List => 1 };
213 15 100       26 if ($prop eq 'date') {
214 2         6 $$tagInfo{Groups}{2} = 'Time';
215 2         5 $$tagInfo{PrintConv} = '$self->ConvertDateTime($val)';
216             }
217 15         30 AddTagToTable($tagTablePtr, $tag, $tagInfo);
218             }
219             # allow list-behaviour only for consecutive tags with the same ID
220 18 100 100     61 if ($$et{LastPListTag} and $$et{LastPListTag} ne $tagInfo) {
221 14         36 delete $$et{LIST_TAGS}{$$et{LastPListTag}};
222             }
223 18         26 $$et{LastPListTag} = $tagInfo;
224             # override file type if applicable
225 18 50 66     38 $et->OverrideFileType($plistType{$tag}) if $plistType{$tag} and $$et{FILE_TYPE} eq 'XMP';
226             # handle compressed PLIST/JSON data
227 18         30 my $proc;
228 18 50 66     39 if ($$tagInfo{CompressedPLIST} and ref $val eq 'SCALAR' and $$val !~ /^bplist00/) {
      66        
229 0 0       0 if (eval { require IO::Uncompress::RawInflate }) {
  0         0  
230 0         0 my $inflated;
231 0 0       0 if (IO::Uncompress::RawInflate::rawinflate($val => \$inflated)) {
232 0         0 $val = \$inflated;
233             } else {
234 0         0 $et->Warn("Error inflating PLIST::$$tagInfo{Name}");
235             }
236             } else {
237 0         0 $et->Warn('Install IO::Uncompress to decode compressed PLIST data');
238             }
239             }
240             # save the tag
241 18         48 $et->HandleTag($tagTablePtr, $tag, $val, ProcessProc => $proc);
242              
243 18         40 return 1;
244             }
245              
246             #------------------------------------------------------------------------------
247             # Get big-endian 24-bit integer
248             # Inputs: 0) data ref, 1) offset
249             # Returns: integer value
250             sub Get24u($$)
251             {
252 0     0 0 0 my ($dataPt, $off) = @_;
253 0         0 return unpack 'N', "\0" . substr($$dataPt, $off, 3);
254             }
255              
256             #------------------------------------------------------------------------------
257             # Extract object from binary PLIST file at the current file position (ref 2)
258             # Inputs: 0) ExifTool ref, 1) PLIST info ref, 2) parent tag ID (undef for top)
259             # Returns: the object, or undef on error
260             sub ExtractObject($$;$)
261             {
262 81     81 0 118 my ($et, $plistInfo, $parent) = @_;
263 81         88 my $raf = $$plistInfo{RAF};
264 81         74 my ($buff, $val);
265              
266 81 50       141 $raf->Read($buff, 1) == 1 or return undef;
267 81         91 my $type = ord($buff) >> 4;
268 81         89 my $size = ord($buff) & 0x0f;
269 81 100 100     271 if ($type == 0) { # null/bool/fill
    100 100        
    50          
270 1         7 $val = { 0x00=>'', 0x08=>'True', 0x09=>'False', 0x0f=>'' }->{$size};
271             } elsif ($type == 1 or $type == 2 or $type == 3) { # int, float or date
272 23         30 $size = 1 << $size;
273 23 100       67 my $proc = ($type == 1 ? $readProc{$size} : $readProc{$size + 0x100}) or return undef;
    50          
274 23 50       46 $val = &$proc(\$buff, 0) if $raf->Read($buff, $size) == $size;
275 23 100 66     61 if ($type == 3 and defined $val) { # date
276             # dates are referenced to Jan 1, 2001 (11323 days from Unix time zero)
277 3         14 $val = Image::ExifTool::ConvertUnixTime($val + 11323 * 24 * 3600, 1);
278 3         8 $$plistInfo{DateFormat} = 1;
279             }
280             } elsif ($type == 8) { # UID
281 0         0 ++$size;
282 0 0       0 $raf->Read($buff, $size) == $size or return undef;
283 0         0 my $proc = $readProc{$size};
284 0 0       0 if ($proc) {
    0          
285 0         0 $val = &$proc(\$buff, 0);
286             } elsif ($size == 16) {
287 0         0 require Image::ExifTool::ASF;
288 0         0 $val = Image::ExifTool::ASF::GetGUID($buff);
289             } else {
290 0         0 $val = "0x" . unpack 'H*', $buff;
291             }
292             } else {
293             # $size is the size of the remaining types
294 57 100       95 if ($size == 0x0f) {
295             # size is stored in extra integer object
296 5         18 $size = ExtractObject($et, $plistInfo);
297 5 50 33     45 return undef unless defined $size and $size =~ /^\d+$/;
298             }
299 57 100 66     161 if ($type == 4) { # data
    100 66        
    100          
    50          
300 2 50 33     20 if ($size < 1000000 or $et->Options('Binary')) {
301 2 50       5 $raf->Read($buff, $size) == $size or return undef;
302             } else {
303 0         0 $buff = "Binary data $size bytes";
304             }
305 2         4 $val = \$buff; # (return reference for binary data)
306             } elsif ($type == 5) { # ASCII string
307 40 50       69 $raf->Read($val, $size) == $size or return undef;
308             } elsif ($type == 6) { # UCS-2BE string
309 1         3 $size *= 2;
310 1 50       3 $raf->Read($buff, $size) == $size or return undef;
311 1         4 $val = $et->Decode($buff, 'UTF16'); # (might as well support surrogates too)
312             } elsif ($type == 10 or $type == 12 or $type == 13) { # array, set or dict
313             # the remaining types store a list of references
314 14         24 my $refSize = $$plistInfo{RefSize};
315 14         18 my $refProc = $$plistInfo{RefProc};
316 14 100       25 my $num = $type == 13 ? $size * 2 : $size;
317 14         17 my $len = $num * $refSize;
318 14 50       26 $raf->Read($buff, $len) == $len or return undef;
319 14         19 my $table = $$plistInfo{Table};
320 14         20 my ($i, $ref, @refs, @array);
321 14         24 for ($i=0; $i<$num; ++$i) {
322 68         88 my $ref = &$refProc(\$buff, $i * $refSize);
323 68 50       94 return 0 if $ref >= @$table;
324 68         98 push @refs, $ref;
325             }
326 14 100       25 if ($type == 13) { # dict
327             # prevent infinite recursion
328 9 50 66     28 if (defined $parent and length $parent > 1000) {
329 0         0 $et->Warn('Possible deep recursion while parsing PLIST');
330 0         0 return undef;
331             }
332 9         14 my $tagTablePtr = $$plistInfo{TagTablePtr};
333 9         23 my $verbose = $et->Options('Verbose');
334 9         14 $val = { }; # initialize return dictionary (will stay empty if tags are saved)
335 9         17 for ($i=0; $i<$size; ++$i) {
336             # get the entry key
337 29 50       67 $raf->Seek($$table[$refs[$i]], 0) or return undef;
338 29         98 my $key = ExtractObject($et, $plistInfo);
339 29 50 33     108 next unless defined $key and length $key; # silently ignore bad dict entries
340             # get the entry value
341 29 50       56 $raf->Seek($$table[$refs[$i+$size]], 0) or return undef;
342             # generate an ID for this tag
343 29 100       54 my $tag = defined $parent ? "$parent/$key" : $key;
344 29         41 undef $$plistInfo{DateFormat};
345 29         49 my $obj = ExtractObject($et, $plistInfo, $tag);
346 29 50       45 next if not defined $obj;
347 29 50       43 unless ($tagTablePtr) {
348             # make sure this is a valid structure field name
349 0 0 0     0 if (not defined $key or $key !~ /^[-_a-zA-Z0-9]+$/) {
    0          
350 0         0 $key = "Tag$i"; # (generate fake tag name if it had illegal characters)
351             } elsif ($key !~ /^[_a-zA-Z]/) {
352 0         0 $key = "_$key"; # (must begin with alpha or underline)
353             }
354 0 0       0 $$val{$key} = $obj if defined $obj;
355 0         0 next;
356             }
357 29 100       55 next if ref($obj) eq 'HASH';
358 24         64 my $tagInfo = $et->GetTagInfo($tagTablePtr, $tag);
359 24 50       37 unless ($tagInfo) {
360 0 0       0 $et->VPrint(0, $$et{INDENT}, "[adding $tag]\n") if $verbose;
361 0         0 my $name = $tag;
362 0         0 $name =~ s/([^A-Za-z])([a-z])/$1\u$2/g; # capitalize words
363 0         0 $name =~ tr/-_a-zA-Z0-9//dc; # remove illegal characters
364 0 0 0     0 $name = 'Tag'.ucfirst($name) if length($name) < 2 or $name =~ /^[-0-9]/;
365 0         0 $tagInfo = { Name => ucfirst($name), List => 1 };
366 0 0       0 if ($$plistInfo{DateFormat}) {
367 0         0 $$tagInfo{Groups}{2} = 'Time';
368 0         0 $$tagInfo{PrintConv} = '$self->ConvertDateTime($val)';
369             }
370 0         0 AddTagToTable($tagTablePtr, $tag, $tagInfo);
371             }
372             # allow list-behaviour only for consecutive tags with the same ID
373 24 100 66     89 if ($$et{LastPListTag} and $$et{LastPListTag} ne $tagInfo) {
374 22         51 delete $$et{LIST_TAGS}{$$et{LastPListTag}};
375             }
376 24         34 $$et{LastPListTag} = $tagInfo;
377 24         56 $et->HandleTag($tagTablePtr, $tag, $obj);
378             }
379             } else {
380             # extract the referenced objects
381 5         9 foreach $ref (@refs) {
382 10 50       15 $raf->Seek($$table[$ref], 0) or return undef; # seek to this object
383 10         25 $val = ExtractObject($et, $plistInfo, $parent);
384 10 100 66     31 next unless defined $val and ref $val ne 'HASH';
385 9         17 push @array, $val;
386             }
387 5         12 $val = \@array;
388             }
389             }
390             }
391 81         141 return $val;
392             }
393              
394             #------------------------------------------------------------------------------
395             # Process binary PLIST data (ref 2)
396             # Inputs: 0) ExifTool object ref, 1) DirInfo ref, 2) tag table ref
397             # Returns: 1 on success (and returns plist value as $$dirInfo{Value})
398             sub ProcessBinaryPLIST($$;$)
399             {
400 8     8 0 14 my ($et, $dirInfo, $tagTablePtr) = @_;
401 8         12 my ($i, $buff, @table);
402 8         13 my $dataPt = $$dirInfo{DataPt};
403              
404 8 100       36 $et->VerboseDir('Binary PLIST') unless $$dirInfo{NoVerboseDir};
405 8         20 SetByteOrder('MM');
406              
407 8 100       29 if ($dataPt) {
408 7         13 my $start = $$dirInfo{DirStart};
409 7 100 33     35 if ($start or ($$dirInfo{DirLen} and $$dirInfo{DirLen} != length $$dataPt)) {
      66        
410 1   50     5 my $buf2 = substr($$dataPt, $start || 0, $$dirInfo{DirLen});
411 1         4 $$dirInfo{RAF} = File::RandomAccess->new(\$buf2);
412             } else {
413 6         33 $$dirInfo{RAF} = File::RandomAccess->new($dataPt);
414             }
415 7   100     23 my $strt = $$dirInfo{DirStart} || 0;
416             }
417             # read and parse the trailer
418 8 50       19 my $raf = $$dirInfo{RAF} or return 0;
419 8 50 33     19 $raf->Seek(-32,2) and $raf->Read($buff,32)==32 or return 0;
420 8         24 my $intSize = Get8u(\$buff, 6);
421 8         16 my $refSize = Get8u(\$buff, 7);
422 8         36 my $numObj = Get64u(\$buff, 8);
423 8         19 my $topObj = Get64u(\$buff, 16);
424 8         17 my $tableOff = Get64u(\$buff, 24);
425              
426 8 50       20 return 0 if $topObj >= $numObj;
427 8 50       28 my $intProc = $readProc{$intSize} or return 0;
428 8 50       20 my $refProc = $readProc{$refSize} or return 0;
429              
430             # read and parse the offset table
431 8         13 my $tableSize = $intSize * $numObj;
432 8 50 33     56 $raf->Seek($tableOff, 0) and $raf->Read($buff, $tableSize) == $tableSize or return 0;
433 8         23 for ($i=0; $i<$numObj; ++$i) {
434 69         99 push @table, &$intProc(\$buff, $i * $intSize);
435             }
436 8         45 my %plistInfo = (
437             RAF => $raf,
438             RefSize => $refSize,
439             RefProc => $refProc,
440             Table => \@table,
441             TagTablePtr => $tagTablePtr,
442             );
443             # position file pointer at the top object, and extract it
444 8 50       19 $raf->Seek($table[$topObj], 0) or return 0;
445 8         26 $$dirInfo{Value} = ExtractObject($et, \%plistInfo);
446 8 50       42 return defined $$dirInfo{Value} ? 1 : 0;
447             }
448              
449             #------------------------------------------------------------------------------
450             # Extract information from a PLIST file (binary, XML or JSON format)
451             # Inputs: 0) ExifTool object ref, 1) dirInfo ref, 2) tag table ref
452             # Returns: 1 on success, 0 if this wasn't valid PLIST
453             sub ProcessPLIST($$;$)
454             {
455 7     7 0 19 my ($et, $dirInfo, $tagTablePtr) = @_;
456 7         17 my $dataPt = $$dirInfo{DataPt};
457 7   50     33 my $start = $$dirInfo{DirStart} || 0;
458 7         12 my ($result, $notXML);
459              
460 7 100       19 if ($dataPt) {
461 1         4 pos($$dataPt) = $start;
462 1 50       39 $notXML = 1 unless $$dataPt =~ /\G
463             }
464 7 100       16 unless ($notXML) {
465             # process XML PLIST data using the XMP module
466 6         22 $$dirInfo{XMPParseOpts}{FoundProc} = \&FoundTag;
467 6         34 $result = Image::ExifTool::XMP::ProcessXMP($et, $dirInfo, $tagTablePtr);
468 6         15 delete $$dirInfo{XMPParseOpts};
469 6 100       18 return $result if $result;
470             }
471 5         8 my $buff;
472 5         11 my $raf = $$dirInfo{RAF};
473 5 100       13 if ($raf) {
474 4 50 33     15 $raf->Seek(0,0) and $raf->Read($buff, 64) or return 0;
475 4         8 $dataPt = \$buff;
476             } else {
477 1 50       3 return 0 unless $dataPt;
478             }
479 5         18 pos($$dataPt) = $start;
480 5 100 33     40 if ($$dataPt =~ /\Gbplist0/) { # binary PLIST
    50 33        
    50          
481             # binary PLIST file
482 2         7 my $tagTablePtr = GetTagTable('Image::ExifTool::PLIST::Main');
483 2         7 $et->SetFileType('PLIST', 'application/x-plist');
484 2         4 $$et{SET_GROUP1} = 'PLIST';
485 2 50       7 unless (ProcessBinaryPLIST($et, $dirInfo, $tagTablePtr)) {
486 0         0 $et->Error('Error reading binary PLIST file');
487             }
488 2         4 delete $$et{SET_GROUP1};
489 2         4 $result = 1;
490             } elsif ($$dataPt =~ /^\{"/) { # JSON PLIST
491 0 0       0 $raf and $raf->Seek(0);
492 0         0 require Image::ExifTool::JSON;
493 0         0 $result = Image::ExifTool::JSON::ProcessJSON($et, $dirInfo);
494             } elsif ($$et{FILE_EXT} and $$et{FILE_EXT} eq 'PLIST' and
495             $$dataPt =~ /^\xfe\xff\x00/)
496             {
497             # (have seen very old PLIST files encoded as UCS-2BE with leading BOM)
498 0         0 $et->Error('Old PLIST format currently not supported');
499 0         0 $result = 1;
500             }
501 5         16 return $result;
502             }
503              
504             1; # end
505              
506             __END__