File Coverage

blib/lib/Image/ExifTool/PhaseOne.pm
Criterion Covered Total %
statement 155 213 72.7
branch 66 112 58.9
condition 39 83 46.9
subroutine 6 7 85.7
pod 0 3 0.0
total 266 418 63.6


line stmt bran cond sub pod time code
1             #------------------------------------------------------------------------------
2             # File: PhaseOne.pm
3             #
4             # Description: Phase One maker notes tags
5             #
6             # Revisions: 2013-02-17 - P. Harvey Created
7             #
8             # References: 1) http://www.cybercom.net/~dcoffin/dcraw/
9             #------------------------------------------------------------------------------
10              
11             package Image::ExifTool::PhaseOne;
12              
13 18     18   3572 use strict;
  18         36  
  18         548  
14 18     18   91 use vars qw($VERSION);
  18         36  
  18         661  
15 18     18   112 use Image::ExifTool qw(:DataAccess :Utils);
  18         40  
  18         3144  
16 18     18   1141 use Image::ExifTool::Exif;
  18         83  
  18         44400  
17              
18             $VERSION = '1.07';
19              
20             sub WritePhaseOne($$$);
21             sub ProcessPhaseOne($$$);
22              
23             # default formats based on PhaseOne format size
24             my @formatName = ( undef, 'string', 'int16s', undef, 'int32s' );
25              
26             # Phase One maker notes (ref PH)
27             %Image::ExifTool::PhaseOne::Main = (
28             PROCESS_PROC => \&ProcessPhaseOne,
29             WRITE_PROC => \&WritePhaseOne,
30             CHECK_PROC => \&Image::ExifTool::Exif::CheckExif,
31             WRITABLE => '1',
32             FORMAT => 'int32s',
33             GROUPS => { 0 => 'MakerNotes', 2 => 'Camera' },
34             VARS => { ENTRY_SIZE => 16 }, # (entries contain a format field)
35             NOTES => 'These tags are extracted from the maker notes of Phase One images.',
36             0x0100 => { #1
37             Name => 'CameraOrientation',
38             ValueConv => '$val & 0x03', # ignore other bits for now
39             PrintConv => {
40             0 => 'Horizontal (normal)',
41             1 => 'Rotate 90 CW',
42             2 => 'Rotate 270 CW',
43             3 => 'Rotate 180',
44             },
45             },
46             # 0x0101 - int32u: 96,160,192,256,544 (same as 0x0213)
47             0x0102 => { Name => 'SerialNumber', Format => 'string' },
48             # 0x0103 - int32u: 19,20,59769034
49             # 0x0104 - int32u: 50,200
50             0x0105 => 'ISO',
51             0x0106 => {
52             Name => 'ColorMatrix1',
53             Format => 'float',
54             Count => 9,
55             PrintConv => q{
56             my @a = map { sprintf('%.3f', $_) } split ' ', $val;
57             return "@a";
58             },
59             PrintConvInv => '$val',
60             },
61             0x0107 => { Name => 'WB_RGBLevels', Format => 'float', Count => 3 },
62             0x0108 => 'SensorWidth',
63             0x0109 => 'SensorHeight',
64             0x010a => 'SensorLeftMargin', #1
65             0x010b => 'SensorTopMargin', #1
66             0x010c => 'ImageWidth',
67             0x010d => 'ImageHeight',
68             0x010e => { #1
69             Name => 'RawFormat',
70             # 1 = raw bit mask 0x5555 (>1 mask 0x1354)
71             # >2 = compressed
72             # 5 = non-linear
73             PrintConv => { #PH
74             1 => 'RAW 1', #? (encrypted)
75             2 => 'RAW 2', #? (encrypted)
76             3 => 'IIQ L', # (now "L14", ref IB)
77             # 4?
78             5 => 'IIQ S',
79             6 => 'IIQ Sv2', # (now "S14" for "IIQ 14 Smart" and "IIQ 14 Sensor+", ref IB)
80             8 => 'IIQ L16', #IB ("IIQ 16 Extended" and "IIQ 16 Large")
81             },
82             },
83             0x010f => {
84             Name => 'RawData',
85             Format => 'undef', # (actually 2-byte integers, but don't convert)
86             Binary => 1,
87             PutFirst => 1,
88             Writable => 0,
89             Drop => 1, # don't copy to other file types
90             },
91             0x0110 => { #1
92             Name => 'SensorCalibration',
93             SubDirectory => { TagTable => 'Image::ExifTool::PhaseOne::SensorCalibration' },
94             },
95             0x0112 => {
96             Name => 'DateTimeOriginal',
97             Description => 'Date/Time Original',
98             Format => 'int32u',
99             Writable => 0, # (don't write because this is an encryption key for RawFormat 1 and 2)
100             Priority => 0,
101             Shift => 'Time',
102             Groups => { 2 => 'Time' },
103             Notes => 'may be used as a key to encrypt the raw data', #1
104             ValueConv => 'ConvertUnixTime($val)',
105             ValueConvInv => 'GetUnixTime($val)',
106             PrintConv => '$self->ConvertDateTime($val)',
107             PrintConvInv => '$self->InverseDateTime($val)',
108             },
109             0x0113 => 'ImageNumber', # (NC)
110             0x0203 => { Name => 'Software', Format => 'string' },
111             0x0204 => { Name => 'System', Format => 'string' },
112             # 0x020b - int32u: 0,1
113             # 0x020c - int32u: 1,2
114             # 0x020e - int32u: 1,3
115             0x0210 => { # (NC) (used in linearization formula - ref 1)
116             Name => 'SensorTemperature',
117             Format => 'float',
118             PrintConv => 'sprintf("%.2f C",$val)',
119             PrintConvInv => '$val=~s/ ?C//; $val',
120             },
121             0x0211 => { # (NC)
122             Name => 'SensorTemperature2',
123             Format => 'float',
124             PrintConv => 'sprintf("%.2f C",$val)',
125             PrintConvInv => '$val=~s/ ?C//; $val',
126             },
127             0x0212 => {
128             Name => 'UnknownDate',
129             Format => 'int32u',
130             Groups => { 2 => 'Time' },
131             # (this time is within about 10 minutes before or after 0x0112)
132             Unknown => 1,
133             Shift => 'Time',
134             ValueConv => 'ConvertUnixTime($val)',
135             ValueConvInv => 'GetUnixTime($val)',
136             PrintConv => '$self->ConvertDateTime($val)',
137             PrintConvInv => '$self->InverseDateTime($val)',
138             },
139             # 0x0213 - int32u: 96,160,192,256,544 (same as 0x0101)
140             # 0x0215 - int32u: 4,5
141             # 0x021a - used by dcraw
142             0x021c => { Name => 'StripOffsets', Binary => 1, Writable => 0 },
143             0x021d => 'BlackLevel', #1
144             # 0x021e - int32u: 1
145             # 0x0220 - int32u: 32
146             # 0x0221 - float: 0-271
147             0x0222 => 'SplitColumn', #1
148             0x0223 => { Name => 'BlackLevelData', Format => 'int16u', Count => -1, Binary => 1 }, #1
149             # 0x0224 - int32u: 1688,2748,3372
150             0x0225 => {
151             Name => 'PhaseOne_0x0225',
152             Format => 'int16s',
153             Count => -1,
154             Flags => ['Unknown','Hidden'],
155             PrintConv => 'length($val) > 60 ? substr($val,0,55) . "[...]" : $val',
156             },
157             0x0226 => {
158             Name => 'ColorMatrix2',
159             Format => 'float',
160             Count => 9,
161             PrintConv => q{
162             my @a = map { sprintf('%.3f', $_) } split ' ', $val;
163             return "@a";
164             },
165             PrintConvInv => '$val',
166             },
167             # 0x0227 - int32u: 0,1
168             # 0x0228 - int32u: 1,2
169             # 0x0229 - int32s: -2,0
170             0x0267 => { #PH
171             Name => 'AFAdjustment',
172             Format => 'float',
173             },
174             0x022b => { #PH
175             Name => 'PhaseOne_0x022b',
176             Format => 'float',
177             Flags => ['Unknown','Hidden'],
178             },
179             # 0x0242 - int32u: 55
180             # 0x0244 - int32u: 102
181             # 0x0245 - float: 1.2
182             0x0258 => { #PH
183             Name => 'PhaseOne_0x0258',
184             Format => 'int16s',
185             Flags => ['Unknown','Hidden'],
186             PrintConv => 'length($val) > 60 ? substr($val,0,55) . "[...]" : $val',
187             },
188             0x025a => { #PH
189             Name => 'PhaseOne_0x025a',
190             Format => 'int16s',
191             Flags => ['Unknown','Hidden'],
192             PrintConv => 'length($val) > 60 ? substr($val,0,55) . "[...]" : $val',
193             },
194             # 0x0300 - int32u: 100,101,102
195             0x0301 => { Name => 'FirmwareVersions', Format => 'string' },
196             # 0x0304 - int32u: 8,3073,3076
197             0x0400 => {
198             Name => 'ShutterSpeedValue',
199             Format => 'float',
200             ValueConv => 'abs($val)<100 ? 2**(-$val) : 0',
201             ValueConvInv => '$val>0 ? -log($val)/log(2) : -100',
202             PrintConv => 'Image::ExifTool::Exif::PrintExposureTime($val)',
203             PrintConvInv => 'Image::ExifTool::Exif::ConvertFraction($val)',
204             },
205             0x0401 => {
206             Name => 'ApertureValue',
207             Format => 'float',
208             ValueConv => '2 ** ($val / 2)',
209             ValueConvInv => '$val>0 ? 2*log($val)/log(2) : 0',
210             PrintConv => 'sprintf("%.1f",$val)',
211             PrintConvInv => '$val',
212             },
213             0x0402 => {
214             Name => 'ExposureCompensation',
215             Format => 'float',
216             PrintConv => 'sprintf("%.3f",$val)',
217             PrintConvInv => '$val',
218             },
219             0x0403 => {
220             Name => 'FocalLength',
221             Format => 'float',
222             PrintConv => 'sprintf("%.1f mm",$val)',
223             PrintConvInv => '$val=~s/\s*mm$//;$val',
224             },
225             # 0x0404 - int32u: 0,3
226             # 0x0405 - int32u? (big numbers)
227             # 0x0406 - int32u: 1
228             # 0x0407 - float: -0.333 (exposure compensation again?)
229             # 0x0408-0x0409 - int32u: 1
230             0x0410 => { Name => 'CameraModel', Format => 'string' },
231             # 0x0411 - int32u: 33556736
232             0x0412 => { Name => 'LensModel', Format => 'string' },
233             0x0414 => {
234             Name => 'MaxApertureValue',
235             Format => 'float',
236             ValueConv => '2 ** ($val / 2)',
237             ValueConvInv => '$val>0 ? 2*log($val)/log(2) : 0',
238             PrintConv => 'sprintf("%.1f",$val)',
239             PrintConvInv => '$val',
240             },
241             0x0415 => {
242             Name => 'MinApertureValue',
243             Format => 'float',
244             ValueConv => '2 ** ($val / 2)',
245             ValueConvInv => '$val>0 ? 2*log($val)/log(2) : 0',
246             PrintConv => 'sprintf("%.1f",$val)',
247             PrintConvInv => '$val',
248             },
249             # 0x0416 - float: (min focal length? ref LR, Credo50) (but looks more like an int32u date for the 645DF - PH)
250             # 0x0417 - float: 80 (max focal length? ref LR)
251             0x0455 => { #PH
252             Name => 'Viewfinder',
253             Format => 'string',
254             },
255             );
256              
257             # Phase One metadata (ref 1)
258             %Image::ExifTool::PhaseOne::SensorCalibration = (
259             PROCESS_PROC => \&ProcessPhaseOne,
260             WRITE_PROC => \&WritePhaseOne,
261             CHECK_PROC => \&Image::ExifTool::Exif::CheckExif,
262             GROUPS => { 0 => 'MakerNotes', 2 => 'Camera' },
263             TAG_PREFIX => 'SensorCalibration',
264             WRITE_GROUP => 'PhaseOne',
265             VARS => { ENTRY_SIZE => 12 }, # (entries do not contain a format field)
266             0x0400 => {
267             Name => 'SensorDefects',
268             # list of defects. each defect is 4 x int16u values:
269             # 0=column, 1=row, 2=type (129=bad pixel, 131=bad column), 3=?
270             # (but it isn't really worth the time decoding this -- it can be a few hundred kB)
271             Format => 'undef',
272             Binary => 1,
273             },
274             0x0401 => {
275             Name => 'AllColorFlatField1',
276             Format => 'undef',
277             Flags => ['Unknown','Binary'],
278             },
279             0x0404 => { #PH
280             Name => 'SensorCalibration_0x0404',
281             Format => 'string',
282             Flags => ['Unknown','Hidden'],
283             },
284             0x0405 => { #PH
285             Name => 'SensorCalibration_0x0405',
286             Format => 'string',
287             Flags => ['Unknown','Hidden'],
288             },
289             0x0406 => { #PH
290             Name => 'SensorCalibration_0x0406',
291             Format => 'string',
292             Flags => ['Unknown','Hidden'],
293             },
294             0x0407 => { #PH
295             Name => 'SerialNumber',
296             Format => 'string',
297             Writable => 1,
298             },
299             0x0408 => { #PH
300             Name => 'SensorCalibration_0x0408',
301             Format => 'float',
302             Flags => ['Unknown','Hidden'],
303             },
304             0x040b => {
305             Name => 'RedBlueFlatField',
306             Format => 'undef',
307             Flags => ['Unknown','Binary'],
308             },
309             0x040f => { #PH
310             Name => 'SensorCalibration_0x040f',
311             Format => 'undef',
312             Flags => ['Unknown','Hidden'],
313             },
314             0x0410 => {
315             Name => 'AllColorFlatField2',
316             Format => 'undef',
317             Flags => ['Unknown','Binary'],
318             },
319             # 0x0412 - used by dcraw
320             0x0413 => { #PH
321             Name => 'SensorCalibration_0x0413',
322             Format => 'double',
323             Flags => ['Unknown','Hidden'],
324             },
325             0x0414 => { #PH
326             Name => 'SensorCalibration_0x0414',
327             Format => 'undef',
328             Flags => ['Unknown','Hidden'],
329             ValueConv => q{
330             my $order = GetByteOrder();
331             if (length $val >= 8 and SetByteOrder(substr($val,0,2))) {
332             $val = ReadValue(\$val, 2, 'int16u', 1, length($val)-2) . ' ' .
333             ReadValue(\$val, 4, 'float', undef, length($val)-4);
334             SetByteOrder($order);
335             }
336             return $val;
337             },
338             },
339             0x0416 => {
340             Name => 'AllColorFlatField3',
341             Format => 'undef',
342             Flags => ['Unknown','Binary'],
343             },
344             0x0418 => { #PH
345             Name => 'SensorCalibration_0x0418',
346             Format => 'undef',
347             Flags => ['Unknown','Hidden'],
348             },
349             0x0419 => {
350             Name => 'LinearizationCoefficients1',
351             Format => 'float',
352             PrintConv => 'my @a=split " ",$val;join " ", map { sprintf("%.5g",$_) } @a',
353             },
354             0x041a => {
355             Name => 'LinearizationCoefficients2',
356             Format => 'float',
357             PrintConv => 'my @a=split " ",$val;join " ", map { sprintf("%.5g",$_) } @a',
358             },
359             0x041c => { #PH
360             Name => 'SensorCalibration_0x041c',
361             Format => 'float',
362             Flags => ['Unknown','Hidden'],
363             },
364             0x041e => { #PH
365             Name => 'SensorCalibration_0x041e',
366             Format => 'undef',
367             Flags => ['Unknown','Hidden'],
368             ValueConv => q{
369             my $order = GetByteOrder();
370             if (length $val >= 8 and SetByteOrder(substr($val,0,2))) {
371             $val = ReadValue(\$val, 2, 'int16u', 1, length($val)-2) . ' ' .
372             ReadValue(\$val, 4, 'float', undef, length($val)-4);
373             SetByteOrder($order);
374             }
375             return $val;
376             },
377             },
378             );
379              
380             #------------------------------------------------------------------------------
381             # Do HTML dump of an IFD entry
382             # Inputs: 0) ExifTool ref, 1) tag table ref, 3) tag ID, 4) tag value,
383             # 5) IFD entry offset, 6) IFD entry size, 7) parameter hash
384             sub HtmlDump($$$$$$%)
385             {
386 0     0 0 0 my ($et, $tagTablePtr, $tagID, $value, $entry, $entryLen, %parms) = @_;
387             my ($dirName, $index, $formatStr, $dataPos, $base, $size, $valuePtr) =
388 0         0 @parms{qw(DirName Index Format DataPos Base Size Start)};
389 0         0 my $tagInfo = $et->GetTagInfo($tagTablePtr, $tagID);
390 0         0 my ($tagName, $colName, $subdir);
391 0   0     0 my $count = $parms{Count} || $size;
392 0 0       0 $base = 0 unless defined $base;
393 0 0       0 if ($tagInfo) {
394 0         0 $tagName = $$tagInfo{Name};
395 0         0 $subdir = $$tagInfo{SubDirectory};
396 0 0       0 if ($$tagInfo{Format}) {
397 0         0 $formatStr = $$tagInfo{Format};
398 0         0 $count = $size / Image::ExifTool::FormatSize($formatStr);
399             }
400             } else {
401 0         0 $tagName = sprintf("Tag 0x%.4x", $tagID);
402             }
403 0         0 my $dname = sprintf("${dirName}-%.2d", $index);
404             # build our tool tip
405 0         0 my $fstr = "$formatStr\[$count]";
406 0         0 my $tip = sprintf("Tag ID: 0x%.4x\n", $tagID) .
407             "Format: $fstr\nSize: $size bytes\n";
408 0 0       0 if ($size > 4) {
409 0         0 $tip .= sprintf("Value offset: 0x%.4x\n", $valuePtr - $base);
410 0         0 $tip .= sprintf("Actual offset: 0x%.4x\n", $valuePtr + $dataPos);
411 0         0 $tip .= sprintf("Offset base: 0x%.4x\n", $dataPos + $base);
412 0         0 $colName = "$tagName";
413             } else {
414 0         0 $colName = $tagName;
415             }
416 0 0       0 unless (ref $value) {
417 0 0       0 my $tval = length($value) > 32 ? substr($value,0,28) . '[...]' : $value;
418 0         0 $tval =~ tr/\x00-\x1f\x7f-\xff/./;
419 0         0 $tip .= "Value: $tval";
420             }
421 0         0 $et->HDump($entry+$dataPos, $entryLen, "$dname $colName", $tip, 1);
422 0 0       0 if ($size > 4) {
423 0         0 my $dumpPos = $valuePtr + $dataPos;
424             # add value data block
425 0 0       0 $et->HDump($dumpPos,$size,"$tagName value",'SAME', $subdir ? 0x04 : 0);
426             }
427             }
428              
429             #------------------------------------------------------------------------------
430             # Write PhaseOne maker notes (both types of PhaseOne IFD)
431             # Inputs: 0) ExifTool object ref, 1) source dirInfo ref, 2) tag table ref
432             # Returns: data block or undef on error
433             sub WritePhaseOne($$$)
434             {
435 199     199 0 462 my ($et, $dirInfo, $tagTablePtr) = @_;
436 199 100       786 $et or return 1; # allow dummy access to autoload this package
437              
438             # nothing to do if we aren't changing any PhaseOne tags
439 2         10 my $newTags = $et->GetNewTagInfoHash($tagTablePtr);
440 2 0 33     7 return undef unless %$newTags or $$et{DropTags} or $$et{EDIT_DIRS}{PhaseOne};
      0        
441              
442 2         3 my $dataPt = $$dirInfo{DataPt};
443 2   100     8 my $dataPos = $$dirInfo{DataPos} || 0;
444 2   50     8 my $dirStart = $$dirInfo{DirStart} || 0;
445 2   66     8 my $dirLen = $$dirInfo{DirLen} || $$dirInfo{DataLen} - $dirStart;
446 2         5 my $dirName = $$dirInfo{DirName};
447 2         7 my $verbose = $et->Options('Verbose');
448              
449 2 50       13 return undef if $dirLen < 12;
450 2 50 33     15 unless ($$tagTablePtr{VARS} and $$tagTablePtr{VARS}{ENTRY_SIZE}) {
451 0         0 $et->WarnOnce("No ENTRY_SIZE for $$tagTablePtr{TABLE_NAME}");
452 0         0 return undef;
453             }
454 2         5 my $entrySize = $$tagTablePtr{VARS}{ENTRY_SIZE};
455 2   50     13 my $ifdType = $$tagTablePtr{TAG_PREFIX} || 'PhaseOne';
456 2         8 my $hdr = substr($$dataPt, $dirStart, 12);
457 2 100       13 if ($entrySize == 16) {
    50          
458 1 50       9 return undef unless $hdr =~ /^(IIII.waR|MMMMRaw.)/s;
459             } elsif ($hdr !~ /^(IIII\x01\0\0\0|MMMM\0\0\0\x01)/s) {
460 0         0 $et->Warn("Unrecognized $ifdType directory version");
461 0         0 return undef;
462             }
463 2         10 SetByteOrder(substr($hdr, 0, 2));
464             # get offset to start of PhaseOne directory
465 2         8 my $ifdStart = Get32u(\$hdr, 8);
466 2 50       7 return undef if $ifdStart + 8 > $dirLen;
467             # initialize output directory buffer with (fixed) number of entries plus 4-byte padding
468 2         7 my $dirBuff = substr($$dataPt, $dirStart + $ifdStart, 8);
469             # get number of entries in PhaseOne directory
470 2         4 my $numEntries = Get32u(\$dirBuff, 0);
471 2         5 my $ifdEnd = $ifdStart + 8 + $entrySize * $numEntries;
472 2 50 33     13 return undef if $numEntries < 2 or $numEntries > 300 or $ifdEnd > $dirLen;
      33        
473 2         4 my $hdrBuff = $hdr;
474 2         4 my $valBuff = ''; # buffer for value data
475 2         12 my $fixup = new Image::ExifTool::Fixup;
476 2         11 my $index;
477 2         9 for ($index=0; $index<$numEntries; ++$index) {
478 102         138 my $entry = $dirStart + $ifdStart + 8 + $entrySize * $index;
479 102         170 my $tagID = Get32u($dataPt, $entry);
480 102         178 my $size = Get32u($dataPt, $entry+$entrySize-8);
481 102         142 my ($formatSize, $formatStr);
482 102 100       148 if ($entrySize == 16) {
483 58         95 $formatSize = Get32u($dataPt, $entry+4);
484 58         87 $formatStr = $formatName[$formatSize];
485 58 50       90 unless ($formatStr) {
486 0         0 $et->Warn("Possibly invalid $ifdType IFD entry $index",1);
487 0         0 delete $$newTags{$tagID}; # make sure we don't try to change this one
488             }
489             } else {
490             # (no format code for SensorCalibration IFD entries)
491 44         49 $formatSize = 1;
492 44         54 $formatStr = 'undef';
493             }
494 102         119 my $valuePtr = $entry + $entrySize - 4;
495 102 100       151 if ($size > 4) {
496 56 50       78 if ($size > 0x7fffffff) {
497 0         0 $et->Error("Invalid size for $ifdType IFD entry $index",1);
498 0         0 return undef;
499             }
500 56         82 $valuePtr = Get32u($dataPt, $valuePtr);
501 56 50       97 if ($valuePtr + $size > $dirLen) {
502 0         0 $et->Error(sprintf("Invalid offset 0x%.4x for $ifdType IFD entry $index",$valuePtr),1);
503 0         0 return undef;
504             }
505 56         59 $valuePtr += $dirStart;
506             }
507 102         154 my $value = substr($$dataPt, $valuePtr, $size);
508 102   100     262 my $tagInfo = $$newTags{$tagID} || $$tagTablePtr{$tagID};
509 102 50 66     290 $tagInfo = $et->GetTagInfo($tagTablePtr, $tagID) if $tagInfo and ref($tagInfo) ne 'HASH';
510 102 100 66     297 if ($$newTags{$tagID}) {
    100 0        
    50 33        
511 2 50       7 $formatStr = $$tagInfo{Format} if $$tagInfo{Format};
512 2         8 my $count = int($size / Image::ExifTool::FormatSize($formatStr));
513 2         8 my $val = ReadValue(\$value, 0, $formatStr, $count, $size);
514 2         12 my $nvHash = $et->GetNewValueHash($tagInfo);
515 2 50       8 if ($et->IsOverwriting($nvHash, $val)) {
516 2         9 my $newVal = $et->GetNewValue($nvHash);
517             # allow count to change for string and undef types only
518 2 50 33     9 undef $count if $formatStr eq 'string' or $formatStr eq 'undef';
519 2         7 my $newValue = WriteValue($newVal, $formatStr, $count);
520 2 50       6 if (defined $newValue) {
521 2         2 $value = $newValue;
522 2         4 $size = length $newValue;
523 2         13 $et->VerboseValue("- $dirName:$$tagInfo{Name}", $val);
524 2         10 $et->VerboseValue("+ $dirName:$$tagInfo{Name}", $newVal);
525 2         5 ++$$et{CHANGED};
526             }
527             }
528             } elsif ($tagInfo and $$tagInfo{SubDirectory}) {
529 1         7 my $subTable = GetTagTable($$tagInfo{SubDirectory}{TagTable});
530             my %subdirInfo = (
531             DirName => $$tagInfo{Name},
532 1         7 DataPt => \$value,
533             DataLen => length $value,
534             );
535 1         21 my $newValue = $et->WriteDirectory(\%subdirInfo, $subTable);
536 1 50 33     5 if (defined $newValue and length($newValue)) {
537 1         3 $value = $newValue;
538 1         4 $size = length $newValue;
539             }
540             } elsif ($$et{DropTags} and (($tagInfo and $$tagInfo{Drop}) or $size > 8192)) {
541             # decrease the number of entries in the directory
542 0         0 Set32u(Get32u(\$dirBuff, 0) - 1, \$dirBuff, 0);
543 0         0 next; # drop this tag
544             }
545             # add the tagID, possibly format size, and size to this directory entry
546 102         208 $dirBuff .= substr($$dataPt, $entry, $entrySize - 8) . Set32u($size);
547              
548             # pad value to an even 4-byte boundary just in case
549 102 100 100     288 $value .= ("\0" x (4 - ($size & 0x03))) if $size & 0x03 or not $size;
550 102 100 66     193 if ($size <= 4) {
    100          
551             # store value in place of the IFD value pointer (already padded to 4 bytes)
552 46         86 $dirBuff .= $value;
553             } elsif ($tagInfo and $$tagInfo{PutFirst}) {
554             # store value immediately after header
555 1         4 $dirBuff .= Set32u(length $hdrBuff);
556 1         4 $hdrBuff .= $value;
557             } else {
558             # store value at end of value buffer
559 55         135 $fixup->AddFixup(length $dirBuff);
560 55         104 $dirBuff .= Set32u(length $valBuff);
561 55         161 $valBuff .= $value;
562             }
563             }
564             # apply necessary fixup to offsets in PhaseOne directory
565 2         6 $$fixup{Shift} = length $hdrBuff;
566 2         9 $fixup->ApplyFixup(\$dirBuff);
567             # set pointer to PhaseOneIFD in header
568 2         10 Set32u(length($hdrBuff) + length($valBuff), \$hdrBuff, 8);
569 2         28 return $hdrBuff . $valBuff . $dirBuff;
570             }
571              
572             #------------------------------------------------------------------------------
573             # Read Phase One maker notes
574             # Inputs: 0) ExifTool object ref, 1) dirInfo ref, 2) tag table ref
575             # Returns: 1 on success
576             # Notes: This routine processes both the main PhaseOne IFD type (with 16 bytes
577             # per entry), and the SensorCalibration IFD type (12 bytes per entry)
578             sub ProcessPhaseOne($$$)
579             {
580 5     5 0 11 my ($et, $dirInfo, $tagTablePtr) = @_;
581 5         11 my $dataPt = $$dirInfo{DataPt};
582 5   50     26 my $dataPos = ($$dirInfo{DataPos} || 0) + ($$dirInfo{Base} || 0);
      50        
583 5   100     27 my $dirStart = $$dirInfo{DirStart} || 0;
584 5   33     19 my $dirLen = $$dirInfo{DirLen} || $$dirInfo{DataLen} - $dirStart;
585 5         16 my $binary = $et->Options('Binary');
586 5         14 my $verbose = $et->Options('Verbose');
587 5         12 my $htmlDump = $$et{HTML_DUMP};
588              
589 5 50       14 return 0 if $dirLen < 12;
590 5 50 33     29 unless ($$tagTablePtr{VARS} and $$tagTablePtr{VARS}{ENTRY_SIZE}) {
591 0         0 $et->WarnOnce("No ENTRY_SIZE for $$tagTablePtr{TABLE_NAME}");
592 0         0 return undef;
593             }
594 5         10 my $entrySize = $$tagTablePtr{VARS}{ENTRY_SIZE};
595 5   50     13 my $ifdType = $$tagTablePtr{TAG_PREFIX} || 'PhaseOne';
596              
597 5         12 my $hdr = substr($$dataPt, $dirStart, 12);
598 5 100       23 if ($entrySize == 16) {
    50          
599 3 50       17 return 0 unless $hdr =~ /^(IIII.waR|MMMMRaw.)/s;
600             } elsif ($hdr !~ /^(IIII\x01\0\0\0|MMMM\0\0\0\x01)/s) {
601 0         0 $et->Warn("Unrecognized $ifdType directory version");
602 0         0 return 0;
603             }
604 5         21 SetByteOrder(substr($hdr, 0, 2));
605             # get offset to start of PhaseOne directory
606 5         18 my $ifdStart = Get32u(\$hdr, 8);
607 5 100       16 return 0 if $ifdStart + 8 > $dirLen;
608             # get number of entries in PhaseOne directory
609 4         19 my $numEntries = Get32u($dataPt, $dirStart + $ifdStart);
610 4         12 my $ifdEnd = $ifdStart + 8 + $entrySize * $numEntries;
611 4 50 33     25 return 0 if $numEntries < 2 or $numEntries > 300 or $ifdEnd > $dirLen;
      33        
612 4         16 $et->VerboseDir($ifdType, $numEntries);
613 4 50       10 if ($htmlDump) {
614 0         0 $et->HDump($dirStart + $dataPos, 8, "$ifdType header");
615 0         0 $et->HDump($dirStart + $dataPos + 8, 4, "$ifdType IFD offset");
616 0         0 $et->HDump($dirStart + $dataPos + $ifdStart, 4, "$ifdType entries",
617             "Entry count: $numEntries");
618 0         0 $et->HDump($dirStart + $dataPos + $ifdStart + 4, 4, '[unused]');
619             }
620 4         5 my $index;
621 4         14 for ($index=0; $index<$numEntries; ++$index) {
622 204         328 my $entry = $dirStart + $ifdStart + 8 + $entrySize * $index;
623 204         362 my $tagID = Get32u($dataPt, $entry);
624 204         378 my $size = Get32u($dataPt, $entry+$entrySize-8);
625 204         306 my $valuePtr = $entry + $entrySize - 4;
626 204         248 my ($formatSize, $formatStr, $value);
627 204 100       328 if ($entrySize == 16) {
    100          
628             # (format code only for the 16-byte IFD entry)
629 116         201 $formatSize = Get32u($dataPt, $entry+4);
630 116         217 $formatStr = $formatName[$formatSize];
631 116 50       167 unless ($formatStr) {
632 0         0 $et->WarnOnce("Unrecognized $ifdType format size $formatSize",1);
633 0         0 $formatSize = 1;
634 0         0 $formatStr = 'undef';
635             }
636             } elsif ($size %4) {
637 8         9 $formatSize = 1;
638 8         10 $formatStr = 'undef';
639             } else {
640 80         96 $formatSize = 4;
641 80         94 $formatStr = 'int32s';
642             }
643 204 100       329 if ($size > 4) {
644 112 50       163 if ($size > 0x7fffffff) {
645 0         0 $et->Warn("Invalid size for $ifdType IFD entry $index");
646 0         0 return 0;
647             }
648 112         213 $valuePtr = Get32u($dataPt, $valuePtr);
649 112 50       180 if ($valuePtr + $size > $dirLen) {
650 0         0 $et->Warn(sprintf("Invalid offset 0x%.4x for $ifdType IFD entry $index",$valuePtr));
651 0         0 return 0;
652             }
653 112         146 $valuePtr += $dirStart;
654             }
655 204         364 my $tagInfo = $et->GetTagInfo($tagTablePtr, $tagID);
656 204 100       311 if ($tagInfo) {
657 134 100       284 $formatStr = $$tagInfo{Format} if $$tagInfo{Format};
658             } else {
659 70 50 33     191 next unless $verbose or $htmlDump;
660             }
661 134         254 my $count = int($size / Image::ExifTool::FormatSize($formatStr));
662 134 50 33     259 if ($count > 100000 and not $binary) {
663 0         0 $value = \ "Binary data $size bytes";
664             } else {
665 134         243 $value = ReadValue($dataPt,$valuePtr,$formatStr,$count,$size);
666             # try to distinguish between the various format types
667 134 100       238 if ($formatStr eq 'int32s') {
668 67         156 my ($val) = split ' ', $value;
669 67 100       110 if (defined $val) {
670             # get floating point exponent (has bias of 127)
671 66         131 my $exp = ($val & 0x7f800000) >> 23;
672 66 100 100     162 if ($exp > 120 and $exp < 140) {
673 1         3 $formatStr = 'float';
674 1         4 $value = ReadValue($dataPt,$valuePtr,$formatStr,$count,$size);
675             }
676             }
677             }
678             }
679 134         506 my %parms = (
680             DirName => $ifdType,
681             Index => $index,
682             DataPt => $dataPt,
683             DataPos => $dataPos,
684             Size => $size,
685             Start => $valuePtr,
686             Format => $formatStr,
687             Count => $count
688             );
689 134 50       544 $htmlDump and HtmlDump($et, $tagTablePtr, $tagID, $value, $entry, $entrySize,
690             %parms, Base => $dirStart);
691 134         463 $et->HandleTag($tagTablePtr, $tagID, $value, %parms);
692             }
693 4         12 return 1;
694             }
695              
696             1; # end
697              
698             __END__