exiftool_vendored 13.22.0 → 13.25.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,462 @@
1
+ #------------------------------------------------------------------------------
2
+ # File: PCAP.pm
3
+ #
4
+ # Description: Read CAP, PCAP and PCAPNG Packet Capture files
5
+ #
6
+ # Revisions: 2025-03-03 - P. Harvey Created
7
+ #
8
+ # References: 1) https://www.ietf.org/archive/id/draft-gharris-opsawg-pcap-01.html
9
+ # 2) https://www.ietf.org/archive/id/draft-ietf-opsawg-pcapng-02.html
10
+ # 3) https://formats.kaitai.io/microsoft_network_monitor_v2/
11
+ #------------------------------------------------------------------------------
12
+
13
+ package Image::ExifTool::PCAP;
14
+
15
+ use strict;
16
+ use vars qw($VERSION);
17
+ use Image::ExifTool qw(:DataAccess :Utils);
18
+
19
+ $VERSION = '1.00';
20
+
21
+ %Image::ExifTool::PCAP::Main = (
22
+ GROUPS => { 0 => 'File', 1 => 'File', 2 => 'Other' },
23
+ VARS => { NO_LOOKUP => 1 }, # omit tags from lookup
24
+ NOTES => 'Tags extracted from CAP, PCAP and PCAPNG Packet Capture files.',
25
+ # (Note: All string values are UTF-8 but I'm going to be lazy and not bother decoding them)
26
+ ByteOrder => {
27
+ PrintConv => {
28
+ II => 'Little-endian (Intel, II)',
29
+ MM => 'Big-endian (Motorola, MM)',
30
+ },
31
+ },
32
+ PCAPVersion => { },
33
+ LinkType => {
34
+ PrintConvColumns => 2,
35
+ PrintConv => {
36
+ # (see https://datatracker.ietf.org/doc/html/draft-richardson-opsawg-pcaplinktype-01 for a full list)
37
+ 0 => 'BSD Loopback',
38
+ 1 => 'IEEE 802.3 Ethernet',
39
+ 2 => 'Experimental 4Mb Ethernet',
40
+ 3 => 'AX.25',
41
+ 4 => 'PRONET',
42
+ 5 => 'MIT CHAOSNET',
43
+ 6 => 'IEEE 802.5',
44
+ 7 => 'ARCNET BSD',
45
+ 8 => 'SLIP',
46
+ 9 => 'PPP',
47
+ 10 => 'fddI',
48
+ # 11-49 not to be used
49
+ 50 => 'PPP HDLC',
50
+ 51 => 'PPP Ethernet',
51
+ # 52-98 not to be used
52
+ 99 => 'Symantec Firewall',
53
+ 100 => 'ATM RFC 1483',
54
+ 101 => 'Raw',
55
+ 102 => 'SLIP BSD/OS',
56
+ 103 => 'PPP BSD/OS',
57
+ 104 => 'Cisco PPP with HDLC',
58
+ 105 => 'IEEE 802.11',
59
+ 106 => 'ATM Classical IP',
60
+ 107 => 'Frame Relay',
61
+ 108 => 'OpenBSD Loopback',
62
+ 109 => 'OpenBSD IPSEC',
63
+ 110 => 'ATM LANE 802.3',
64
+ 111 => 'NetBSD HIPPI',
65
+ 112 => 'NetBSD HDLC',
66
+ 113 => 'Linux SLL',
67
+ 114 => 'Apple LocalTalk',
68
+ 115 => 'Acorn Econet',
69
+ 116 => 'OpenBSD ipfilter',
70
+ 117 => 'OpenBSD pflog',
71
+ 118 => 'Cisco IOS',
72
+ 119 => 'IEEE 802.11 Prism',
73
+ 120 => 'IEEE 802.11 Aironet',
74
+ 121 => 'Siemens HiPath HDLC',
75
+ 122 => 'IP-over-Fibre',
76
+ 123 => 'SunATM',
77
+ 124 => 'RapidIO',
78
+ 125 => 'PCI Express',
79
+ 126 => 'Xilinx Aurora',
80
+ 127 => 'IEEE 802.11 Radiotap',
81
+ 128 => 'Tazmen Sniffer',
82
+ 129 => 'ARCNET Linux',
83
+ 130 => 'Juniper MLPPP',
84
+ 131 => 'Juniper MLFR',
85
+ 132 => 'Juniper ES',
86
+ 133 => 'Juniper GGSN',
87
+ 134 => 'Juniper MFR',
88
+ 135 => 'Juniper ATM2',
89
+ 136 => 'Juniper Services',
90
+ 137 => 'Juniper ATM1',
91
+ 138 => 'Apple IP-over-IEEE 1394',
92
+ 139 => 'MTP2 with PHDR',
93
+ 140 => 'MTP2',
94
+ 141 => 'MTP3',
95
+ 142 => 'SCCP',
96
+ 143 => 'DOCSIS',
97
+ 144 => 'Linux IrDA',
98
+ 145 => 'IBM SP',
99
+ 146 => 'IBM SN',
100
+ # 147-162 - reserved
101
+ 163 => 'IEEE 802.11 AVS',
102
+ 164 => 'Juniper Monitor',
103
+ 165 => 'BACnet MS/TP',
104
+ 166 => 'PPP PPPD',
105
+ 167 => 'Juniper PPPOE',
106
+ 168 => 'Juniper PPPOE ATM',
107
+ 169 => 'GPRS LLC',
108
+ 170 => 'GPF-T',
109
+ 171 => 'GPF-F',
110
+ 172 => 'Gcom T1/E1',
111
+ 173 => 'Gcom Serial',
112
+ 174 => 'Juniper PIC Peer',
113
+ 175 => 'ERF ETH',
114
+ 176 => 'ERF POS',
115
+ 177 => 'Linux LAPD',
116
+ 178 => 'Juniper Ether',
117
+ 179 => 'Juniper PPP',
118
+ 180 => 'Juniper Frame Relay',
119
+ 181 => 'Juniper CHDLC',
120
+ 182 => 'MFR',
121
+ 182 => 'Juniper VP',
122
+ 185 => 'A653 ICM',
123
+ 186 => 'USB FreeBSD',
124
+ 187 => 'Bluetooth HCI H4',
125
+ 188 => 'IEEE 802.16 MAC CPS',
126
+ 189 => 'USB Linux',
127
+ 190 => 'CAN 2.0B',
128
+ 191 => 'IEEE 802.15.4 Linux',
129
+ 192 => 'PPI',
130
+ 193 => 'IEEE 802.16 MAC CPS Radio',
131
+ 194 => 'Juniper ISM',
132
+ 195 => 'IEEE 802.15.4 with FCS',
133
+ 196 => 'SITA',
134
+ 197 => 'ERF',
135
+ 198 => 'RAIF1',
136
+ 199 => 'IPMB Kontron',
137
+ 200 => 'Juniper ST',
138
+ 201 => 'Bluetooth HCI H4 with PHDR',
139
+ 202 => 'AX.25 KISS',
140
+ 203 => 'LAPD',
141
+ 204 => 'PPP with DIR',
142
+ 205 => 'Cisco HDLC with DIR',
143
+ 206 => 'Frame Relay with DIR',
144
+ 207 => 'LAPB with DIR',
145
+ # 208 reserved
146
+ 209 => 'IPMB Linux',
147
+ 210 => 'FlexRay',
148
+ 211 => 'MOST',
149
+ 212 => 'LIN',
150
+ 213 => 'X2E Serial',
151
+ 214 => 'X2E Xoraya',
152
+ 215 => 'IEEE 802.15.4 Nonask PHY',
153
+ 216 => 'Linux evdev',
154
+ 217 => 'GSMtap Um',
155
+ 218 => 'GSMtap Abis',
156
+ 219 => 'MPLS',
157
+ 220 => 'USB Linux MMapped',
158
+ 221 => 'DECT',
159
+ 222 => 'AOS',
160
+ 223 => 'Wireless HART',
161
+ 224 => 'FC-2',
162
+ 225 => 'FC-2 with Frame Delims',
163
+ 226 => 'IPNET',
164
+ 227 => 'CAN Socketcan',
165
+ 228 => 'IPv4',
166
+ 229 => 'IPv6',
167
+ 230 => 'IEEE 802.15.4 No FCS',
168
+ 231 => 'D-Bus',
169
+ 232 => 'Juniper VS',
170
+ 233 => 'Juniper SRX E2E',
171
+ 234 => 'Juniper Fibre Channel',
172
+ 235 => 'DVB-CI',
173
+ 236 => 'Mux 27.010',
174
+ 237 => 'STANAG 5066 D_PDU',
175
+ 238 => 'Juniper ATM Cemic',
176
+ 239 => 'NFLOG',
177
+ 240 => 'Netanalyzer',
178
+ 241 => 'Netanalyzer Transparent',
179
+ 242 => 'IP-over-InfiniBand',
180
+ 243 => 'MPEG-2 TS',
181
+ 244 => 'NG40',
182
+ 245 => 'NFC LLCP',
183
+ 246 => 'Pfsync',
184
+ 247 => 'InfiniBand',
185
+ 248 => 'SCTP',
186
+ 249 => 'USBPcap',
187
+ 250 => 'RTAC Serial',
188
+ 251 => 'Bluetooth LE LL',
189
+ 252 => 'Wireshark Upper PDU',
190
+ 253 => 'Netlink',
191
+ 254 => 'Bluetooth Linux Monitor',
192
+ 255 => 'Bluetooth BREDR BB',
193
+ 256 => 'Bluetooth LE LL with PHDR',
194
+ 257 => 'PROFIBUS Data Link',
195
+ 258 => 'Apple PKTAP',
196
+ 259 => 'EPON',
197
+ 260 => 'IPMI HPM.2',
198
+ 261 => 'Z-Wave R1/R2',
199
+ 262 => 'Z-Wave R3',
200
+ 263 => 'WattStopper DLM',
201
+ 264 => 'ISO 14443',
202
+ 265 => 'RDS',
203
+ 266 => 'USB Darwin',
204
+ 267 => 'Openflow',
205
+ 268 => 'SDLC',
206
+ 269 => 'TI LLN Sniffer',
207
+ 270 => 'LoRaTap',
208
+ 271 => 'Vsock',
209
+ 272 => 'Nordic BLE',
210
+ 273 => 'DOCSIS 31 XRA31',
211
+ 274 => 'Ethernet MPacket',
212
+ 275 => 'DisplayPort AUX',
213
+ 276 => 'Linux SLL2',
214
+ 277 => 'Sercos Monitor',
215
+ 278 => 'Openvizsla',
216
+ 279 => 'Elektrobit EBHSR',
217
+ 280 => 'VPP Dispatch',
218
+ 281 => 'DSA Tag BRCM',
219
+ 282 => 'DSA Tag BRCM Prepend',
220
+ 283 => 'IEEE 802.15.4 Tap',
221
+ 284 => 'DSA Tag DSA',
222
+ 285 => 'DSA Tag EDSA',
223
+ 286 => 'ELEE',
224
+ 287 => 'Z-Wave Serial',
225
+ 288 => 'USB 2.0',
226
+ 289 => 'ATSC ALP',
227
+ },
228
+ },
229
+ TimeStamp => {
230
+ Groups => { 2 => 'Time' },
231
+ ValueConv => q{
232
+ return $val if $$self{WindowsTS};
233
+ return ConvertUnixTime($val, 1, $$self{TSResol} < 1e-7 ? 9 : 6);
234
+ },
235
+ PrintConv => '$self->ConvertDateTime($val)',
236
+ },
237
+ #
238
+ # "options" tags common to all blocks
239
+ #
240
+ 1 => 'Comment',
241
+ 2988 => {
242
+ Name => 'CustomOption1',
243
+ ValueConv => q{
244
+ return undef unless length($val) > 4;
245
+ my $len = Get16u(\$val, 2);
246
+ return undef unless $len > 4 and $len <= length($val) - 4;
247
+ my $str = 'Type='.Get16u(\$val,0).' PEN='.Get32u(\$val,4). ' Val='.substr($val, 8, $len-4);
248
+ },
249
+ },
250
+ 2989 => {
251
+ Name => 'CustomOption2',
252
+ Binary => 1,
253
+ },
254
+ 19372 => {
255
+ Name => 'CustomOption3',
256
+ ValueConv => q{
257
+ return undef unless length($val) > 4;
258
+ my $len = Get16u(\$val, 2);
259
+ return undef unless $len > 4 and $len <= length($val) - 4;
260
+ my $str = 'Type='.Get16u(\$val,0).' PEN='.Get32u(\$val,4). ' Val='.substr($val, 8, $len-4);
261
+ },
262
+ },
263
+ 19373 => {
264
+ Name => 'CustomOption3',
265
+ Binary => 1,
266
+ },
267
+ #
268
+ # "options" tags in Section Header Block
269
+ #
270
+ 'SHB-2' => 'Hardware',
271
+ 'SHB-3' => 'OperatingSytem',
272
+ 'SHB-4' => 'UserApplication',
273
+ #
274
+ # "options" tags in Interface Description Block
275
+ #
276
+ 'IDB-2' => 'DeviceName',
277
+ 'IDB-3' => 'Description',
278
+ 'IDB-4' => {
279
+ Name => 'IPv4Addr',
280
+ Description => 'IPv4 Addr',
281
+ # IP address and net mask
282
+ ValueConv => '$_=join(".", unpack("C*", $val))); s/(:.*?:.*?:.*?):/$1 /; $_',
283
+ },
284
+ 'IDB-5' => {
285
+ Name => 'IPv6Addr',
286
+ Description => 'IPv6 Addr',
287
+ ValueConv => 'join(":", unpack("(H4)8", $val)) . "/" . unpack("x16C",$val)',
288
+ },
289
+ 'IDB-6' => {
290
+ Name => 'MACAddr',
291
+ ValueConv => 'join("-", unpack("(H2)6", $val))',
292
+ },
293
+ 'IDB-7' => {
294
+ Name => 'EUIAddr',
295
+ ValueConv => 'join(":", unpack("(H4)4", $val))',
296
+ },
297
+ 'IDB-8' => {
298
+ Name => 'Speed',
299
+ Format => 'int64u',
300
+ },
301
+ 'IDB-9' => {
302
+ Name => 'TimeStampResolution',
303
+ Format => 'int8u',
304
+ RawConv => '$$self{TSResol} = ($val & 0x80 ? 2 ** -($val & 0x7f) : 10 ** -$val)',
305
+ },
306
+ 'IDB-10' => 'TimeZone',
307
+ 'IDB-11' => {
308
+ Name => 'Filter',
309
+ ValueConv => 'Get8u(\$val,0) . ": " . substr($val, 1)',
310
+ },
311
+ 'IDB-12' => 'OperatingSytem',
312
+ 'IDB-13' => { Name => 'FCSLen', Format => 'int8u' },
313
+ 'IDB-14' => {
314
+ Name => 'TimeStampOffset',
315
+ Format => 'int64u',
316
+ RawConv => '$$self{TSOff} = $val',
317
+ },
318
+ 'IDB-15' => 'Hardware',
319
+ 'IDB-16' => { Name => 'TXSpeed', Format => 'int64u' },
320
+ 'IDB-17' => { Name => 'RXSpeed', Format => 'int64u' },
321
+ 'IDB-18' => 'TimezoneName',
322
+ );
323
+
324
+ #------------------------------------------------------------------------------
325
+ # Extract metadata from a PCAP file
326
+ # Inputs: 0) ExifTool ref, 1) dirInfo ref
327
+ # Returns: 1 on success, 0 if this wasn't a valid PCAP file
328
+ sub ProcessPCAP($$)
329
+ {
330
+ my ($et, $dirInfo) = @_;
331
+ my $raf = $$dirInfo{RAF};
332
+ my ($buff, $tagTablePtr, $type, $ts, $byteOrder, $verPos);
333
+
334
+ # verify this is a valid PCAP file
335
+ return 0 unless $raf->Read($buff, 24) == 24;
336
+
337
+ if ($buff =~ /^\xa1\xb2(\xc3\xd4|\x3c\x4d)\0.\0.|(\xd4\xc3|\x4d\x3c)\xb2\xa1.\0.\0/s) {
338
+ $type = 'PCAP';
339
+ my $tmp = $1 || $2;
340
+ $$et{TSResol} = ($tmp eq "\xc3\xd4" or $tmp eq "\xd4\xc3") ? 1e-6 : 1e-9;
341
+ $byteOrder = $buff =~ /^\xa1/ ? 'MM' : 'II';
342
+ $verPos = 4;
343
+ } elsif ($buff =~ /^\x0a\x0d\x0d\x0a.{4}(\x1a\x2b\x3c\x4d|\x4d\x3c\x2b\x1a)/s) {
344
+ $type = 'PCAPNG';
345
+ $byteOrder = $1 eq "\x1a\x2b\x3c\x4d" ? 'MM' : 'II';
346
+ $verPos = 12;
347
+ } elsif ($buff =~ /^GMBU\0\x02/) {
348
+ # handle Windows Network Monitor 2.0 CAP files
349
+ # http://web.archive.org/web/20240430011527/https://learn.microsoft.com/en-us/windows/win32/netmon2/capturefile-header-values
350
+ $Image::ExifTool::static_vars{OverrideFileDescription}{CAP} = 'Microsoft Network Monitor Capture';
351
+ $et->SetFileType('CAP', 'application/octet-stream', 'cap');
352
+ $type = 'CAP';
353
+ $tagTablePtr = GetTagTable('Image::ExifTool::PCAP::Main');
354
+ my @a = unpack('x6v*', $buff);
355
+ $$et{WindowsTS} = 1;
356
+ my $val = sprintf('%.4d:%.2d:%.2d %.2d:%.2d:%.2d.%.3d',@a[1,2,4..8]);
357
+ $et->HandleTag($tagTablePtr, LinkType => $a[0]);
358
+ $et->HandleTag($tagTablePtr, TimeStamp => $val);
359
+ if ($raf->Seek(40,0) and $raf->Read($buff,8)==8) {
360
+ my ($off, $len) = unpack('V2', $buff);
361
+ # extract comment
362
+ if ($len < 1024 and $raf->Seek($off,0) and $raf->Read($buff,$len) == $len) {
363
+ $et->HandleTag($tagTablePtr, 1 => $buff); # (NC) null terminated? Unicode?
364
+ }
365
+ }
366
+ return 1;
367
+ } else {
368
+ return 0;
369
+ }
370
+ $et->SetFileType($type);
371
+ SetByteOrder($byteOrder);
372
+ $tagTablePtr = GetTagTable('Image::ExifTool::PCAP::Main');
373
+ my $ver = Get16u(\$buff, $verPos) . '.' . Get16u(\$buff, $verPos+2);
374
+ $et->HandleTag($tagTablePtr, PCAPVersion => "$type $ver");
375
+ $et->HandleTag($tagTablePtr, ByteOrder => $byteOrder);
376
+
377
+ if ($type eq 'PCAP') {
378
+ $et->HandleTag($tagTablePtr, LinkType => Get16u(\$buff, 22));
379
+ $raf->Read($buff, 8) == 8 or $et->Warn('Truncated PCAP file'), return 1;
380
+ $ts = Get32u(\$buff, 0) + Get32u(\$buff, 4) * $$et{TSResol};
381
+ $et->HandleTag($tagTablePtr, TimeStamp => $ts);
382
+ return 1;
383
+ }
384
+ # read through PCAPNG options for the SHB, IDB and get the timestamp from the first EPD
385
+ my $dir = 'SHB'; # Section Header Block
386
+ for (;;) {
387
+ $raf->Read($buff, 4) == 4 or last;
388
+ my $opt = Get16u(\$buff, 0);
389
+ my $len = Get16u(\$buff, 2);
390
+ if ($opt == 0) { # (end of options?)
391
+ last unless $raf->Read($buff, 20) == 20;
392
+ my $dirNum = Get32u(\$buff, 4);
393
+ if ($dirNum == 1) {
394
+ $et->HandleTag($tagTablePtr, LinkType => Get16u(\$buff, 12));
395
+ $dir = 'IDB'; # Interface Description Block
396
+ next; # continue with IDB options
397
+ } elsif ($dirNum == 6) { # EPD (Enhanced Packet Data)
398
+ my $ts = 4294967296 * Get32u(\$buff, 16);
399
+ $raf->Read(\$buff, 4) == 4 or last;
400
+ if ($$et{TSResol}) {
401
+ $ts = ($ts + Get32u(\$buff, 0)) * $$et{TSResol} + ($$et{TSOff} || 0);
402
+ $et->HandleTag($tagTablePtr, TimeStamp => $ts);
403
+ }
404
+ }
405
+ last;
406
+ }
407
+ my $n = ($len + 3) & 0xfffc; # round to an even 4 bytes
408
+ $raf->Read($buff, $n) == $n or $et->Warn("Error reading $dir options"), last;
409
+ my $id = $$tagTablePtr{$opt} ? $opt : "$dir-$opt";
410
+ $et->HandleTag($tagTablePtr, $id, undef,
411
+ DataPt => \$buff,
412
+ DataPos => $raf->Tell() - $n,
413
+ Size => $len,
414
+ MakeTagInfo => 1,
415
+ );
416
+ }
417
+ return 1;
418
+ }
419
+
420
+ 1; # end
421
+
422
+ __END__
423
+
424
+ =head1 NAME
425
+
426
+ Image::ExifTool::PCAP - Read CAP, PCAP and PCAPNG Packet Capture files
427
+
428
+ =head1 SYNOPSIS
429
+
430
+ This module is used by Image::ExifTool
431
+
432
+ =head1 DESCRIPTION
433
+
434
+ This module contains definitions required by Image::ExifTool to read
435
+ metadata from CAP, PCAP and PCAPNG Packet Capture files.
436
+
437
+ =head1 AUTHOR
438
+
439
+ Copyright 2003-2025, Phil Harvey (philharvey66 at gmail.com)
440
+
441
+ This library is free software; you can redistribute it and/or modify it
442
+ under the same terms as Perl itself.
443
+
444
+ =head1 REFERENCES
445
+
446
+ =over 4
447
+
448
+ =item L<https://www.ietf.org/archive/id/draft-gharris-opsawg-pcap-01.html>
449
+
450
+ =item L<https://www.ietf.org/archive/id/draft-ietf-opsawg-pcapng-02.html>
451
+
452
+ =item L<https://formats.kaitai.io/microsoft_network_monitor_v2/>
453
+
454
+ =back
455
+
456
+ =head1 SEE ALSO
457
+
458
+ L<Image::ExifTool::TagNames/PCAP Tags>,
459
+ L<Image::ExifTool(3pm)|Image::ExifTool>
460
+
461
+ =cut
462
+
@@ -21,7 +21,7 @@ use Image::ExifTool qw(:DataAccess :Utils);
21
21
  use Image::ExifTool::XMP;
22
22
  use Image::ExifTool::GPS;
23
23
 
24
- $VERSION = '1.14';
24
+ $VERSION = '1.15';
25
25
 
26
26
  sub ExtractObject($$;$);
27
27
  sub Get24u($$);
@@ -352,7 +352,7 @@ sub ExtractObject($$;$)
352
352
  my $name = $tag;
353
353
  $name =~ s/([^A-Za-z])([a-z])/$1\u$2/g; # capitalize words
354
354
  $name =~ tr/-_a-zA-Z0-9//dc; # remove illegal characters
355
- $name = "Tag$name" if length($name) < 2 or $name =~ /^[-0-9]/;
355
+ $name = 'Tag'.ucfirst($name) if length($name) < 2 or $name =~ /^[-0-9]/;
356
356
  $tagInfo = { Name => ucfirst($name), List => 1 };
357
357
  if ($$plistInfo{DateFormat}) {
358
358
  $$tagInfo{Groups}{2} = 'Time';
@@ -11,7 +11,7 @@ package Image::ExifTool::Plot;
11
11
  use strict;
12
12
  use vars qw($VERSION);
13
13
 
14
- $VERSION = '1.01';
14
+ $VERSION = '1.02';
15
15
 
16
16
  # default plot settings (lower-case settings may be overridden by the user)
17
17
  my %defaults = (
@@ -448,7 +448,8 @@ sub Draw($$)
448
448
  } else {
449
449
  $mark .= ' fill="none"';
450
450
  }
451
- $mark .= ' stroke="context-stroke"/>';
451
+ # (was using 'context-stroke', but Chrome didn't show this properly)
452
+ $mark .= " stroke='$$cols[$i]'/>";
452
453
  # don't re-define mark if it is the same as a previous one
453
454
  $markID{$mark} and $markID{$i} = $markID{$mark}, next;
454
455
  $markID{$mark} = $markID{$i} = "mark$i";
@@ -217,7 +217,9 @@ sub ProcessProtobuf($$$;$)
217
217
  } elsif (length($buff) % 4) {
218
218
  $val = '0x' . unpack('H*', $buff);
219
219
  } else {
220
- $val = '0x' . join(' ', unpack('(H8)*', $buff)); # (group in 4-byte blocks)
220
+ my $n = length($buff) / 4;
221
+ # (do this instead of '(H8)*' because older Perl version didn't support this)
222
+ $val = '0x' . join(' ', unpack("(H8)$n", $buff)); # (group in 4-byte blocks)
221
223
  }
222
224
  $val .= $rat if $rat;
223
225
  }
@@ -12,9 +12,10 @@ use strict;
12
12
  use vars qw($VERSION);
13
13
  use Image::ExifTool qw(:DataAccess :Utils);
14
14
 
15
- $VERSION = '1.01';
15
+ $VERSION = '1.02';
16
16
 
17
17
  sub ProcessQualcomm($$$);
18
+ sub ProcessDualCamera($$$);
18
19
  sub MakeNameAndDesc($$);
19
20
 
20
21
  # Qualcomm format codes (ref PH (NC))
@@ -1224,6 +1225,59 @@ my @qualcommFormat = (
1224
1225
  'HJR_texture_threshold' => { },
1225
1226
  );
1226
1227
 
1228
+ %Image::ExifTool::Qualcomm::DualCamera = (
1229
+ PROCESS_PROC => \&ProcessDualCamera,
1230
+ GROUPS => { 0 => 'MakerNotes', 2 => 'Camera' },
1231
+ VARS => { NO_ID => 1, NO_LOOKUP => 1 }, # too long, too many, and too obscure
1232
+ NOTES => q{
1233
+ Found in JPEG APP4 "Qualcomm Dual Camera Attributes" written by some Nokia
1234
+ phones.
1235
+ },
1236
+ 'Sensor Crop left' => 'SensorCropLeft',
1237
+ 'Sensor Crop top' => 'SensorCropTop',
1238
+ 'Sensor Crop width' => 'SensorCropWidth',
1239
+ 'Sensor Crop height' => 'SensorCropHeight',
1240
+ 'Sensor ROI Map left' => 'SensorROIMapLeft',
1241
+ 'Sensor ROI Map top' => 'SensorROIMapTop',
1242
+ 'Sensor ROI Map width' => 'SensorROIMapWidth',
1243
+ 'Sensor ROI Map height' => 'SensorROIMapHeight',
1244
+ 'CAMIF Crop left' => 'CAMIFCropLeft',
1245
+ 'CAMIF Crop top' => 'CAMIFCropTop',
1246
+ 'CAMIF Crop width' => 'CAMIFCropWidth',
1247
+ 'CAMIF Crop height' => 'CAMIFCropHeight',
1248
+ 'CAMIF ROI Map left' => 'CAMIF_ROIMapLeft',
1249
+ 'CAMIF ROI Map top' => 'CAMIF_ROIMapTop',
1250
+ 'CAMIF ROI Map width' => 'CAMIF_ROIMapWidth',
1251
+ 'CAMIF ROI Map height' => 'CAMIF_ROIMapHeight',
1252
+ 'ISP Crop left' => 'ISPCropLeft',
1253
+ 'ISP Crop top' => 'ISPCropTop',
1254
+ 'ISP Crop width' => 'ISPCropWidth',
1255
+ 'ISP Crop height' => 'ISPCropHeight',
1256
+ 'ISP ROI Map left' => 'ISP_ROIMapLeft',
1257
+ 'ISP ROI Map top' => 'ISP_ROIMapTop',
1258
+ 'ISP ROI Map width' => 'ISP_ROIMapWidth',
1259
+ 'ISP ROI Map height' => 'ISP_ROIMapHeight',
1260
+ 'CPP Crop left' => 'CPPCropLeft',
1261
+ 'CPP Crop top' => 'CPPCropTop',
1262
+ 'CPP Crop width' => 'CPPCropWidth',
1263
+ 'CPP Crop height' => 'CPPCropHeight',
1264
+ 'CPP ROI Map left' => 'CPP_ROIMapLeft',
1265
+ 'CPP ROI Map top' => 'CPP_ROIMapTop',
1266
+ 'CPP ROI Map width' => 'CPP_ROIMapWidth',
1267
+ 'CPP ROI Map height' => 'CPP_ROIMapHeight',
1268
+ 'DDM Crop left' => 'DDMCropLeft',
1269
+ 'DDM Crop top' => 'DDMCropTop',
1270
+ 'DDM Crop width' => 'DDMCropWidth',
1271
+ 'DDM Crop height' => 'DDMCropHeight',
1272
+ 'Focal length Ratio' => 'FocalLengthRatio',
1273
+ 'Current pipeline mirror flip setting' => 'CurrentPipelineMirrorFlipSetting',
1274
+ 'Current pipeline rotation setting' => 'CurrentPipelineRotationSetting',
1275
+ 'AF ROI left' => 'AF_ROILeft',
1276
+ 'AF ROI top' => 'AF_ROITop',
1277
+ 'AF ROI width' => 'AF_ROIWidth',
1278
+ 'AF ROI height' => 'AF_ROIHeight',
1279
+ );
1280
+
1227
1281
  # generate tag names and descriptions
1228
1282
  {
1229
1283
  local $_;
@@ -1258,6 +1312,29 @@ sub MakeNameAndDesc($$)
1258
1312
  return 1;
1259
1313
  }
1260
1314
 
1315
+ #------------------------------------------------------------------------------
1316
+ # Process Qualcomm Dual Camera APP4 metadata (ref PH)
1317
+ # Inputs: 0) ExifTool object ref, 1) dirInfo ref, 2) tag table ref
1318
+ # Returns: 1 on success
1319
+ sub ProcessDualCamera($$$)
1320
+ {
1321
+ my ($et, $dirInfo, $tagTablePtr) = @_;
1322
+ my $dataPt = $$dirInfo{DataPt};
1323
+ my $dataPos = $$dirInfo{DataPos};
1324
+ my $pos = $$dirInfo{DirStart};
1325
+
1326
+ $et->VerboseDir('Qualcomm Dual Camera', undef, $$dirInfo{DirLen});
1327
+ pos($$dataPt) = $pos;
1328
+ while ($$dataPt =~ /\x0a/g) {
1329
+ my $str = substr($$dataPt, $pos, pos($$dataPt) - $pos - 1);
1330
+ next unless $str =~ /^(.*?)\s*=\s*(.*)/;
1331
+ my ($tag, $val) = ($1, $2);
1332
+ $et->HandleTag($tagTablePtr, $tag, $val, MakeTagInfo => 1);
1333
+ $pos = pos($$dataPt);
1334
+ }
1335
+ return 1;
1336
+ }
1337
+
1261
1338
  #------------------------------------------------------------------------------
1262
1339
  # Process Qualcomm APP7 metadata (ref PH)
1263
1340
  # Inputs: 0) ExifTool object ref, 1) dirInfo ref, 2) tag table ref
@@ -48,7 +48,7 @@ use Image::ExifTool qw(:DataAccess :Utils);
48
48
  use Image::ExifTool::Exif;
49
49
  use Image::ExifTool::GPS;
50
50
 
51
- $VERSION = '3.13';
51
+ $VERSION = '3.14';
52
52
 
53
53
  sub ProcessMOV($$;$);
54
54
  sub ProcessKeys($$$);
@@ -492,6 +492,8 @@ my %qtFlags = ( #12
492
492
  %dontInherit = (
493
493
  ispe => 1, # primary item must have an ispe and pixi, so no need to inherit these
494
494
  pixi => 1,
495
+ irot => 1, # (tmap may have a different irot)
496
+ pasp => 1, # (NC)
495
497
  hvcC => 2, # (hvcC is a property of hvc1 referred to by primary grid)
496
498
  colr => 2, # (colr is a property of primary grid or hvc1 referred to by primary)
497
499
  );
@@ -2854,6 +2856,7 @@ my %userDefined = (
2854
2856
  %Image::ExifTool::QuickTime::ItemPropCont = (
2855
2857
  PROCESS_PROC => \&ProcessMOV,
2856
2858
  WRITE_PROC => \&WriteQuickTime,
2859
+ CHECK_PROC => \&CheckQTValue,
2857
2860
  PERMANENT => 1, # (can't be deleted)
2858
2861
  GROUPS => { 2 => 'Image' },
2859
2862
  VARS => { START_INDEX => 1 }, # show verbose indices starting at 1
@@ -2911,6 +2914,7 @@ my %userDefined = (
2911
2914
  Name => 'PixelAspectRatio',
2912
2915
  Format => 'int32u',
2913
2916
  Writable => 'int32u',
2917
+ Count => 2,
2914
2918
  Protected => 1,
2915
2919
  },
2916
2920
  rloc => {
@@ -9174,7 +9178,8 @@ sub HandleItemInfo($)
9174
9178
  $et->VPrint(0, "$$et{INDENT}Item $id) '${type}' ($len bytes$enc)\n");
9175
9179
  }
9176
9180
  # get ExifTool name for this item
9177
- my $name = { Exif => 'EXIF', 'application/rdf+xml' => 'XMP', jpeg => 'PreviewImage' }->{$type} || '';
9181
+ my $name = { Exif => 'EXIF', 'application/rdf+xml' => 'XMP', jpeg => 'PreviewImage',
9182
+ 'uri ' => 'PLIST' }->{$type} || '';
9178
9183
  my ($warn, $extent);
9179
9184
  if ($$item{ContentEncoding}) {
9180
9185
  if ($$item{ContentEncoding} ne 'deflate') {
@@ -9267,7 +9272,6 @@ sub HandleItemInfo($)
9267
9272
  if ($name eq 'EXIF' and length $buff >= 4) {
9268
9273
  if ($buff =~ /^(MM\0\x2a|II\x2a\0)/) {
9269
9274
  $et->Warn('Missing Exif header');
9270
- $start = 0;
9271
9275
  } elsif ($buff =~ /^Exif\0\0/) {
9272
9276
  # (haven't seen this yet, but it is just a matter of time
9273
9277
  # until someone screws it up like this)
@@ -9302,10 +9306,14 @@ sub HandleItemInfo($)
9302
9306
  }
9303
9307
  $et->FoundTag($type => $buff);
9304
9308
  next;
9309
+ } elsif ($name eq 'PLIST') {
9310
+ # extract PLIST information from 'uri ' if available
9311
+ next unless $buff =~ /^bplist00/;
9312
+ $subTable = GetTagTable('Image::ExifTool::PLIST::Main');
9305
9313
  } else {
9306
- $start = 0;
9307
9314
  $subTable = GetTagTable('Image::ExifTool::XMP::Main');
9308
9315
  }
9316
+ $start or $start = 0;
9309
9317
  my %dirInfo = (
9310
9318
  DataPt => \$buff,
9311
9319
  DataLen => length $buff,