File Coverage

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