File Coverage

blib/lib/Image/ExifTool/WriteCanonRaw.pl
Criterion Covered Total %
statement 280 308 90.9
branch 138 216 63.8
condition 43 59 72.8
subroutine 10 10 100.0
pod 0 7 0.0
total 471 600 78.5


line stmt bran cond sub pod time code
1             #------------------------------------------------------------------------------
2             # File: WriteCanonRaw.pl
3             #
4             # Description: Write Canon RAW (CRW and CR2) meta information
5             #
6             # Revisions: 01/25/2005 - P. Harvey Created
7             # 09/16/2010 - PH Added ability to write XMP in CRW images
8             #------------------------------------------------------------------------------
9             package Image::ExifTool::CanonRaw;
10              
11 11     11   104 use strict;
  11         49  
  11         434  
12 11     11   82 use vars qw($VERSION $AUTOLOAD %crwTagFormat);
  11         36  
  11         617  
13 11     11   97 use Image::ExifTool::Fixup;
  11         30  
  11         38684  
14              
15             # map for adding directories to CRW
16             my %crwMap = (
17             XMP => 'CanonVRD',
18             CanonVRD => 'Trailer',
19             );
20              
21             # mappings to from RAW tagID to MakerNotes tagID
22             # (Note: upper two bits of RawTagID are zero)
23             my %mapRawTag = (
24             # RawTagID => Canon TagID
25             0x080b => 0x07, # CanonFirmwareVersion
26             0x0810 => 0x09, # OwnerName
27             0x0815 => 0x06, # CanonImageType
28             0x1028 => 0x03, # (unknown if no tag name specified)
29             0x1029 => 0x02, # FocalLength
30             0x102a => 0x04, # CanonShotInfo
31             0x102d => 0x01, # CanonCameraSettings
32             0x1033 => 0x0f, # CanonCustomFunctions (only verified for 10D)
33             0x1038 => 0x12, # CanonAFInfo
34             0x1039 => 0x13,
35             0x1093 => 0x93,
36             0x10a8 => 0xa8,
37             0x10a9 => 0xa9, # WhiteBalanceTable
38             0x10aa => 0xaa,
39             0x10ae => 0xae, # ColorTemperature
40             0x10b4 => 0xb4, # ColorSpace
41             0x10b5 => 0xb5,
42             0x10c0 => 0xc0,
43             0x10c1 => 0xc1,
44             0x180b => 0x0c, # SerialNumber
45             0x1817 => 0x08, # FileNumber
46             0x1834 => 0x10,
47             0x183b => 0x15,
48             );
49             # translation from Rotation to Orientation values
50             my %mapRotation = (
51             0 => 1,
52             90 => 6,
53             180 => 3,
54             270 => 8,
55             );
56              
57              
58             #------------------------------------------------------------------------------
59             # Initialize buffers for building MakerNotes from RAW data
60             # Inputs: 0) ExifTool object reference
61             sub InitMakerNotes($)
62             {
63 6     6 0 19 my $et = shift;
64             $$et{MAKER_NOTE_INFO} = {
65 6         60 Entries => { }, # directory entries keyed by tagID
66             ValBuff => "\0\0\0\0", # value data buffer (start with zero nextIFD pointer)
67             FixupTags => { }, # flags for tags with data in value buffer
68             };
69             }
70              
71             #------------------------------------------------------------------------------
72             # Build maker notes from CanonRaw information
73             # Inputs: 0) ExifTool object reference, 1) raw tag ID, 2) reference to tagInfo
74             # 3) reference to value, 4) format name, 5) count
75             # Notes: This will build the directory in the order the tags are found in the CRW
76             # file, which isn't sequential (but Canon's version isn't sequential either...)
77             sub BuildMakerNotes($$$$$$)
78             {
79 188     188 0 450 my ($et, $rawTag, $tagInfo, $valuePt, $formName, $count) = @_;
80              
81 188   100     696 my $tagID = $mapRawTag{$rawTag} || return;
82 57 50       161 $formName or warn(sprintf "No format for tag 0x%x!\n",$rawTag), return;
83             # special case: ignore user comment because it gets saved in EXIF
84             # (and has the same raw tagID as CanonFileDescription)
85 57 50 66     257 return if $tagInfo and $$tagInfo{Name} eq 'UserComment';
86 57         116 my $format = $Image::ExifTool::Exif::formatNumber{$formName};
87 57         118 my $fsiz = $Image::ExifTool::Exif::formatSize[$format];
88 57         103 my $size = length($$valuePt);
89 57         89 my $value;
90 57 100 66     237 if ($count and $size != $count * $fsiz) {
91 19 50       55 if ($size < $count * $fsiz) {
92 0         0 warn sprintf("Value too short for raw tag 0x%x\n",$rawTag);
93 0         0 return;
94             }
95             # shorten value appropriately
96 19         36 $size = $count * $fsiz;
97 19         47 $value = substr($$valuePt, 0, $size);
98             } else {
99 38         76 $count = $size / $fsiz;
100 38         87 $value = $$valuePt;
101             }
102 57         93 my $offsetVal;
103 57         111 my $makerInfo = $$et{MAKER_NOTE_INFO};
104 57 100       125 if ($size > 4) {
105 35         87 my $len = length $makerInfo->{ValBuff};
106 35         100 $offsetVal = Set32u($len);
107 35         118 $makerInfo->{ValBuff} .= $value;
108             # pad to an even number of bytes
109 35 50       95 $size & 0x01 and $makerInfo->{ValBuff} .= "\0";
110             # set flag indicating that this tag needs a fixup
111 35         127 $makerInfo->{FixupTags}->{$tagID} = 1;
112             } else {
113 22         41 $offsetVal = $value;
114 22 100       97 $size < 4 and $offsetVal .= "\0" x (4 - $size);
115             }
116 57         170 $makerInfo->{Entries}->{$tagID} = Set16u($tagID) . Set16u($format) .
117             Set32u($count) . $offsetVal;
118             }
119              
120             #------------------------------------------------------------------------------
121             # Finish building and save MakerNotes
122             # Inputs: 0) ExifTool object reference
123             sub SaveMakerNotes($)
124             {
125 6     6 0 20 my $et = shift;
126             # save maker notes
127 6         21 my $makerInfo = $$et{MAKER_NOTE_INFO};
128 6         20 delete $$et{MAKER_NOTE_INFO};
129 6         18 my $dirEntries = $makerInfo->{Entries};
130 6         23 my $numEntries = scalar(keys %$dirEntries);
131 6         68 my $fixup = new Image::ExifTool::Fixup;
132 6 50       27 return unless $numEntries;
133             # build the MakerNotes directory
134 6         21 my $makerNotes = Set16u($numEntries);
135 6         20 my $tagID;
136             # write the entries in proper tag order (even though Canon doesn't do this...)
137 6         57 foreach $tagID (sort { $a <=> $b } keys %$dirEntries) {
  138         227  
138 57         154 $makerNotes .= $$dirEntries{$tagID};
139 57 100       143 next unless $makerInfo->{FixupTags}->{$tagID};
140             # add fixup for this pointer
141 35         112 $fixup->AddFixup(length($makerNotes) - 4);
142             }
143             # save position of maker notes for pointer fixups
144 6         45 $fixup->{Shift} += length($makerNotes);
145 6         25 $$et{MAKER_NOTE_FIXUP} = $fixup;
146 6         25 $$et{MAKER_NOTE_BYTE_ORDER} = GetByteOrder();
147             # add value data
148 6         30 $makerNotes .= $makerInfo->{ValBuff};
149             # get MakerNotes tag info
150 6         27 my $tagTablePtr = Image::ExifTool::GetTagTable('Image::ExifTool::Exif::Main');
151 6         37 my $tagInfo = $et->GetTagInfo($tagTablePtr, 0x927c, \$makerNotes);
152             # save the MakerNotes
153 6         53 $et->FoundTag($tagInfo, $makerNotes);
154             # save the garbage collection some work later
155 6         47 delete $makerInfo->{Entries};
156 6         25 delete $makerInfo->{ValBuff};
157 6         33 delete $makerInfo->{FixupTags};
158             # also generate Orientation tag since Rotation isn't transferred from RAW info
159 6         38 my $rotation = $et->GetValue('Rotation', 'ValueConv');
160 6 50 33     60 if (defined $rotation and defined $mapRotation{$rotation}) {
161 6         27 $tagInfo = $et->GetTagInfo($tagTablePtr, 0x112);
162 6         65 $et->FoundTag($tagInfo, $mapRotation{$rotation});
163             }
164             }
165              
166             #------------------------------------------------------------------------------
167             # Check CanonRaw information
168             # Inputs: 0) ExifTool object reference, 1) tagInfo hash reference,
169             # 2) raw value reference
170             # Returns: error string or undef (and may change value) on success
171             sub CheckCanonRaw($$$)
172             {
173 150     150 0 551 my ($et, $tagInfo, $valPtr) = @_;
174 150         406 my $tagName = $$tagInfo{Name};
175 150 100 100     771 if ($tagName eq 'JpgFromRaw' or $tagName eq 'ThumbnailImage') {
176 5 100 100     61 unless ($$valPtr =~ /^\xff\xd8/ or $et->Options('IgnoreMinorErrors')) {
177 1         6 return '[Minor] Not a valid image';
178             }
179             } else {
180 145         388 my $format = $$tagInfo{Format};
181 145         427 my $count = $$tagInfo{Count};
182 145 100       494 unless ($format) {
183 130         612 my $tagType = ($$tagInfo{TagID} >> 8) & 0x38;
184 130         520 $format = $crwTagFormat{$tagType};
185             }
186 145 50       707 $format and return Image::ExifTool::CheckValue($valPtr, $format, $count);
187             }
188 4         17 return undef;
189             }
190              
191             #------------------------------------------------------------------------------
192             # Write CR2 file
193             # Inputs: 0) ExifTool ref, 1) dirInfo reference (must have read first 16 bytes)
194             # 2) tag table reference
195             # Returns: true on success
196             sub WriteCR2($$$)
197             {
198 3     3 0 15 my ($et, $dirInfo, $tagTablePtr) = @_;
199 3 50       18 my $dataPt = $$dirInfo{DataPt} or return 0;
200 3 50       30 my $outfile = $$dirInfo{OutFile} or return 0;
201 3 50       15 $$dirInfo{RAF} or return 0;
202              
203             # check CR2 signature
204 3 50       27 if ($$dataPt !~ /^.{8}CR\x02\0/s) {
205 0         0 my ($msg, $minor);
206 0 0       0 if ($$dataPt =~ /^.{8}CR/s) {
    0          
207 0         0 $msg = 'Unsupported Canon RAW file. May cause problems if rewritten';
208 0         0 $minor = 1;
209             } elsif ($$dataPt =~ /^.{8}\xba\xb0\xac\xbb/s) {
210 0         0 $msg = 'Can not currently write Canon 1D RAW images';
211             } else {
212 0         0 $msg = 'Unrecognized Canon RAW file';
213             }
214 0 0       0 return 0 if $et->Error($msg, $minor);
215             }
216              
217             # CR2 has a 16-byte header
218 3         12 $$dirInfo{NewDataPos} = 16;
219 3         38 my $newData = $et->WriteDirectory($dirInfo, $tagTablePtr);
220 3 50       17 return 0 unless defined $newData;
221 3 50       16 unless ($$dirInfo{LastIFD}) {
222 0         0 $et->Error("CR2 image IFD may not be deleted");
223 0         0 return 0;
224             }
225              
226 3 50       15 if (length($newData)) {
227             # build 16 byte header for Canon RAW file
228 3         13 my $header = substr($$dataPt, 0, 16);
229             # set IFD0 pointer (may not be 16 if edited by PhotoMechanic)
230 3         15 Set32u(16, \$header, 4);
231             # last 4 bytes of header is pointer to last IFD
232 3         17 Set32u($$dirInfo{LastIFD}, \$header, 12);
233 3 50       31 Write($outfile, $header, $newData) or return 0;
234 3         15 undef $newData; # free memory
235              
236             # copy over image data now if necessary
237 3 50       20 if (ref $$dirInfo{ImageData}) {
238 3 50       28 $et->CopyImageData($$dirInfo{ImageData}, $outfile) or return 0;
239 3         15 delete $$dirInfo{ImageData};
240             }
241             }
242 3         24 return 1;
243             }
244              
245             #------------------------------------------------------------------------------
246             # Write CanonRaw (CRW) information
247             # Inputs: 0) ExifTool object reference, 1) source dirInfo reference,
248             # 2) tag table reference
249             # Returns: true on success
250             # Notes: Increments ExifTool CHANGED flag for each tag changed This routine is
251             # different from all of the other write routines because Canon RAW files are
252             # designed well! So it isn't necessary to buffer the data in memory before
253             # writing it out. Therefore this routine doesn't return the directory data as
254             # the rest of the Write routines do. Instead, it writes to the dirInfo
255             # OutFile on the fly --> much faster, efficient, and less demanding on memory!
256             sub WriteCanonRaw($$$)
257             {
258 223     223 0 664 my ($et, $dirInfo, $tagTablePtr) = @_;
259 223 100       1045 $et or return 1; # allow dummy access to autoload this package
260 49         107 my $blockStart = $$dirInfo{DirStart};
261 49         103 my $blockSize = $$dirInfo{DirLen};
262 49 50       151 my $raf = $$dirInfo{RAF} or return 0;
263 49 50       141 my $outfile = $$dirInfo{OutFile} or return 0;
264 49 50       133 my $outPos = $$dirInfo{OutPos} or return 0;
265 49         72 my $outBase = $outPos;
266 49         164 my $verbose = $et->Options('Verbose');
267 49         144 my $out = $et->Options('TextOut');
268 49         106 my ($buff, $tagInfo);
269              
270             # 4 bytes at end of block give directory position within block
271 49 50       218 $raf->Seek($blockStart+$blockSize-4, 0) or return 0;
272 49 50       175 $raf->Read($buff, 4) == 4 or return 0;
273 49         165 my $dirOffset = Get32u(\$buff,0) + $blockStart;
274             # avoid infinite recursion
275 49 100       192 $$et{ProcessedCanonRaw} or $$et{ProcessedCanonRaw} = { };
276 49 50       163 if ($$et{ProcessedCanonRaw}{$dirOffset}) {
277 0         0 $et->Error("Double-referenced $$dirInfo{DirName} directory");
278 0         0 return 0;
279             }
280 49         180 $$et{ProcessedCanonRaw}{$dirOffset} = 1;
281 49 50       134 $raf->Seek($dirOffset, 0) or return 0;
282 49 50       170 $raf->Read($buff, 2) == 2 or return 0;
283 49         174 my $entries = Get16u(\$buff,0); # get number of entries in directory
284             # read the directory (10 bytes per entry)
285 49 50       182 $raf->Read($buff, 10 * $entries) == 10 * $entries or return 0;
286 49         153 my $newDir = '';
287              
288             # get hash of new information keyed by tagID
289 49         195 my $newTags = $et->GetNewTagInfoHash($tagTablePtr);
290              
291             # generate list of tags to add or delete (currently, we only allow JpgFromRaw
292             # and ThumbnailImage, to be added or deleted from the root CanonRaw directory)
293 49         94 my (@addTags, %delTag);
294 49 100       159 if ($$dirInfo{Nesting} == 0) {
295 7         22 my $tagID;
296 7         37 foreach $tagID (keys %$newTags) {
297 15         33 my $permanent = $newTags->{$tagID}->{Permanent};
298 15 100 66     128 push(@addTags, $tagID) if defined($permanent) and not $permanent;
299             }
300             }
301              
302 49         97 my $index;
303 49         95 for ($index=0; ; ++$index) {
304 336         596 my ($pt, $tag, $size, $valuePtr, $ptr, $value);
305 336 100       740 if ($index<$entries) {
306 287         466 $pt = 10 * $index;
307 287         714 $tag = Get16u(\$buff, $pt);
308 287         781 $size = Get32u(\$buff, $pt+2);
309 287         763 $valuePtr = Get32u(\$buff, $pt+6);
310 287         552 $ptr = $valuePtr + $blockStart; # all pointers relative to block start
311             }
312             # add any required new tags
313             # NOTE: can't currently add tags where value is stored in directory
314 336 100 66     763 if (@addTags and (not defined($tag) or $tag >= $addTags[0])) {
      100        
315 1         6 my $addTag = shift @addTags;
316 1         3 $tagInfo = $$newTags{$addTag};
317 1         9 my $newVal = $et->GetNewValue($tagInfo);
318 1 50       6 if (defined $newVal) {
319             # pad value to an even length (Canon ImageBrowser and ZoomBrowser
320             # version 6.1.1 have problems with odd-sized embedded JPEG images
321             # even if the value is padded to maintain alignment, so do this
322             # before calculating the size for the directory entry)
323 1 50       7 $newVal .= "\0" if length($newVal) & 0x01;
324             # add new directory entry
325 1         6 $newDir .= Set16u($addTag) . Set32u(length($newVal)) .
326             Set32u($outPos - $outBase);
327             # write new value data
328 1 50       12 Write($outfile, $newVal) or return 0;
329 1         8 $outPos += length($newVal); # update current position
330 1 50       6 $verbose > 1 and print $out " + CanonRaw:$$tagInfo{Name}\n";
331 1         4 ++$$et{CHANGED};
332             }
333             # set flag to delete this tag if found later
334 1         5 $delTag{$addTag} = 1;
335             }
336 336 100       746 last unless defined $tag; # all done if no more directory entries
337 287 50       615 return 0 if $tag & 0x8000; # top bit should not be set
338 287         500 my $tagID = $tag & 0x3fff; # get tag ID
339 287         479 my $tagType = ($tag >> 8) & 0x38; # get tag type
340 287         434 my $valueInDir = ($tag & 0x4000); # flag for value in directory
341              
342 287         909 my $tagInfo = $et->GetTagInfo($tagTablePtr,$tagID);
343 287         675 my $format = $crwTagFormat{$tagType};
344 287         487 my ($count, $subdir);
345 287 100       650 if ($tagInfo) {
346 233         540 $subdir = $$tagInfo{SubDirectory};
347 233 100       542 $format = $$tagInfo{Format} if $$tagInfo{Format};
348 233         413 $count = $$tagInfo{Count};
349             }
350 287 100       547 if ($valueInDir) {
351 97         152 $size = 8;
352 97         246 $value = substr($buff, $pt+2, $size);
353             # set count to 1 by default for normal values in directory
354 97 100 33     695 $count = 1 if not defined $count and $format and
      66        
      100        
355             $format ne 'string' and not $subdir;
356             } else {
357 190 100 100     723 if ($tagType==0x28 or $tagType==0x30) {
358             # this type of tag specifies a raw subdirectory
359 42         106 my $name;
360 42 50       188 $tagInfo and $name = $$tagInfo{Name};
361 42 50       98 $name or $name = sprintf("CanonRaw_0x%.4x", $tagID);
362             my %subdirInfo = (
363             DirName => $name,
364             DataLen => 0,
365             DirStart => $ptr,
366             DirLen => $size,
367             Nesting => $$dirInfo{Nesting} + 1,
368             RAF => $raf,
369             Parent => $$dirInfo{DirName},
370 42         373 OutFile => $outfile,
371             OutPos => $outPos,
372             );
373 42         181 my $result = $et->WriteDirectory(\%subdirInfo, $tagTablePtr);
374 42 50       116 return 0 unless $result;
375             # set size and pointer for this new directory
376 42         95 $size = $subdirInfo{OutPos} - $outPos;
377 42         83 $valuePtr = $outPos - $outBase;
378 42         141 $outPos = $subdirInfo{OutPos};
379             } else {
380             # verify that the value data is within this block
381 148 50       354 $valuePtr + $size <= $blockSize or return 0;
382             # read value from file
383 148 50       498 $raf->Seek($ptr, 0) or return 0;
384 148 50       492 $raf->Read($value, $size) == $size or return 0;
385             }
386             }
387             # set count from tagInfo count if necessary
388 287 100 100     1088 if ($format and not $count) {
389             # set count according to format and size
390 167         401 my $fnum = $Image::ExifTool::Exif::formatNumber{$format};
391 167         352 my $fsiz = $Image::ExifTool::Exif::formatSize[$fnum];
392 167         403 $count = int($size / $fsiz);
393             }
394             # edit subdirectory if necessary
395 287 100       627 if ($tagInfo) {
396 233 100 100     1015 if ($subdir and $$subdir{TagTable}) {
    100          
397 67         189 my $name = $$tagInfo{Name};
398 67         219 my $newTagTable = Image::ExifTool::GetTagTable($$subdir{TagTable});
399 67 50       184 return 0 unless $newTagTable;
400 67         112 my $subdirStart = 0;
401             #### eval Start ()
402 67 50       188 $subdirStart = eval $$subdir{Start} if $$subdir{Start};
403 67         119 my $dirData = \$value;
404             my %subdirInfo = (
405             Name => $name,
406             DataPt => $dirData,
407             DataLen => $size,
408             DirStart => $subdirStart,
409             DirLen => $size - $subdirStart,
410             Nesting => $$dirInfo{Nesting} + 1,
411             RAF => $raf,
412             Parent => $$dirInfo{DirName},
413 67         556 );
414             #### eval Validate ($dirData, $subdirStart, $size)
415 67 50 66     714 if (defined $$subdir{Validate} and not eval $$subdir{Validate}) {
416 0         0 $et->Warn("Invalid $name data");
417             } else {
418 67         370 $subdir = $et->WriteDirectory(\%subdirInfo, $newTagTable);
419 67 100 66     324 if (defined $subdir and length $subdir) {
420 61 50       131 if ($subdirStart) {
421             # add header before data directory
422 0         0 $value = substr($value, 0, $subdirStart) . $subdir;
423             } else {
424 61         255 $value = $subdir;
425             }
426             }
427             }
428             } elsif ($$newTags{$tagID}) {
429 16 100       71 if ($delTag{$tagID}) {
430 1 50       24 $verbose > 1 and print $out " - CanonRaw:$$tagInfo{Name}\n";
431 1         5 ++$$et{CHANGED};
432 1         4 next; # next since we already added this tag
433             }
434 15         24 my $oldVal;
435 15 50       33 if ($format) {
436 15         56 $oldVal = ReadValue(\$value, 0, $format, $count, $size);
437             } else {
438 0         0 $oldVal = $value;
439             }
440 15         74 my $nvHash = $et->GetNewValueHash($tagInfo);
441 15 100       79 if ($et->IsOverwriting($nvHash, $oldVal)) {
442 14         44 my $newVal = $et->GetNewValue($nvHash);
443 14         24 my $verboseVal;
444 14 50       36 $verboseVal = $newVal if $verbose > 1;
445             # convert to specified format if necessary
446 14 50 33     62 if (defined $newVal and $format) {
447 14         45 $newVal = WriteValue($newVal, $format, $count);
448             }
449 14 50       47 if (defined $newVal) {
450 14         27 $value = $newVal;
451 14         32 ++$$et{CHANGED};
452 14         81 $et->VerboseValue("- CanonRaw:$$tagInfo{Name}", $oldVal);
453 14         46 $et->VerboseValue("+ CanonRaw:$$tagInfo{Name}", $verboseVal);
454             }
455             }
456             }
457             }
458 286 100       650 if ($valueInDir) {
459 97         178 my $len = length $value;
460 97 100       312 if ($len < 8) {
    50          
461             # pad with original garbage in case it contained something useful
462 5         18 $value .= substr($buff, $pt+2+8-$len, 8-$len);
463             } elsif ($len > 8) { # this shouldn't happen
464 0         0 warn "Value too long! -- truncated\n";
465 0         0 $value = substr($value, 0, 8);
466             }
467             # create new directory entry
468 97         259 $newDir .= Set16u($tag) . $value;
469 97         296 next; # all done this entry
470             }
471 189 100       407 if (defined $value) {
472             # don't allow value to change length unless Writable is 'resize'
473 147         365 my $writable = $$tagInfo{Writable};
474 147         250 my $diff = length($value) - $size;
475 147 50       304 if ($diff) {
476 0 0 0     0 if ($writable and $writable eq 'resize') {
    0          
477 0         0 $size += $diff; # allow size to change
478             } elsif ($diff > 0) {
479 0         0 $value .= ("\0" x $diff);
480             } else {
481 0         0 $value = substr($value, 0, $size);
482             }
483             }
484             # pad value if necessary to align on even-byte boundary (as per CIFF spec)
485 147 100       321 $value .= "\0" if $size & 0x01;
486 147         236 $valuePtr = $outPos - $outBase;
487             # write out value data
488 147 50       457 Write($outfile, $value) or return 0;
489 147         318 $outPos += length($value); # update current position in outfile
490             }
491             # create new directory entry
492 189         576 $newDir .= Set16u($tag) . Set32u($size) . Set32u($valuePtr);
493             }
494             # add the directory counts and offset to the directory start,
495 49         138 $entries = length($newDir) / 10;
496 49         118 $newDir = Set16u($entries) . $newDir . Set32u($outPos - $outBase);
497             # write directory data
498 49 50       176 Write($outfile, $newDir) or return 0;
499              
500             # update current output file position in dirInfo
501 49         155 $$dirInfo{OutPos} = $outPos + length($newDir);
502             # save outfile directory start (needed for rewriting VRD trailer)
503 49         111 $$dirInfo{OutDirStart} = $outPos - $outBase;
504              
505 49         217 return 1;
506             }
507              
508             #------------------------------------------------------------------------------
509             # write Canon RAW (CRW) file
510             # Inputs: 0) ExifTool object reference, 1) dirInfo reference
511             # Returns: 1 on success, 0 if this wasn't a valid CRW file,
512             # or -1 if a write error occurred
513             sub WriteCRW($$)
514             {
515 7     7 0 34 my ($et, $dirInfo) = @_;
516 7         58 my $outfile = $$dirInfo{OutFile};
517 7         22 my $raf = $$dirInfo{RAF};
518 7         18 my $rtnVal = 0;
519 7         25 my ($buff, $err, $sig);
520              
521 7 50       220 $raf->Read($buff,2) == 2 or return 0;
522 7 50       40 SetByteOrder($buff) or return 0;
523 7 50       41 $raf->Read($buff,4) == 4 or return 0;
524 7 50       33 $raf->Read($sig,8) == 8 or return 0; # get file signature
525 7 50       69 $sig =~ /^HEAP(CCDR|JPGM)/ or return 0; # validate signature
526 7         31 my $type = $1;
527 7         34 my $hlen = Get32u(\$buff, 0); # get header length
528              
529 7 50       55 if ($$et{DEL_GROUP}{MakerNotes}) {
530 0 0       0 if ($type eq 'CCDR') {
531 0         0 $et->Error("Can't delete MakerNotes from CRW");
532 0         0 return 0;
533             } else {
534 0         0 ++$$et{CHANGED};
535 0         0 return 1;
536             }
537             }
538             # make XMP the preferred group for CRW files
539 7 100       35 if ($$et{FILE_TYPE} eq 'CRW') {
540 3         16 $et->InitWriteDirs(\%crwMap, 'XMP');
541             }
542              
543             # write header
544 7 50       91 $raf->Seek(0, 0) or return 0;
545 7 50       37 $raf->Read($buff, $hlen) == $hlen or return 0;
546 7 50       57 Write($outfile, $buff) or $err = 1;
547              
548 7 50       46 $raf->Seek(0, 2) or return 0; # seek to end of file
549 7 50       42 my $filesize = $raf->Tell() or return 0;
550              
551             # build directory information for main raw directory
552 7         105 my %dirInfo = (
553             DataLen => 0,
554             DirStart => $hlen,
555             DirLen => $filesize - $hlen,
556             Nesting => 0,
557             RAF => $raf,
558             Parent => 'CRW',
559             OutFile => $outfile,
560             OutPos => $hlen,
561             );
562             # process the raw directory
563 7         63 my $tagTablePtr = Image::ExifTool::GetTagTable('Image::ExifTool::CanonRaw::Main');
564 7         65 my $success = $et->WriteDirectory(\%dirInfo, $tagTablePtr);
565              
566 7         28 my $trailPt;
567 7         39 while ($success) {
568             # check to see if trailer(s) exist(s)
569 7 100       45 my $trailInfo = Image::ExifTool::IdentifyTrailer($raf) or last;
570             # rewrite the trailer(s)
571 3         14 $buff = '';
572 3         12 $$trailInfo{OutFile} = \$buff;
573 3 50       16 $success = $et->ProcessTrailers($trailInfo) or last;
574 3         11 $trailPt = $$trailInfo{OutFile};
575             # nothing to write if trailers were deleted
576 3 100       15 undef $trailPt if length($$trailPt) < 4;
577 3         16 last;
578             }
579 7 50       30 if ($success) {
580             # add CanonVRD trailer if writing as a block
581 7         51 $trailPt = $et->AddNewTrailers($trailPt,'CanonVRD');
582 7 100 100     51 if (not $trailPt and $$et{ADD_DIRS}{CanonVRD}) {
583             # create CanonVRD from scratch if necessary
584 1         4 my $outbuff = '';
585 1         5 my $saveOrder = GetByteOrder();
586 1         11 require Image::ExifTool::CanonVRD;
587 1 50       11 if (Image::ExifTool::CanonVRD::ProcessCanonVRD($et, { OutFile => \$outbuff }) > 0) {
588 1         3 $trailPt = \$outbuff;
589             }
590 1         5 SetByteOrder($saveOrder);
591             }
592             # write trailer
593 7 100       37 if ($trailPt) {
594             # must append DirStart pointer to end of trailer
595 5         25 my $newDirStart = Set32u($dirInfo{OutDirStart});
596 5         26 my $len = length $$trailPt;
597 5 100       26 my $pad = ($len & 0x01) ? ' ' : ''; # add pad byte if necessary
598 5 50       39 Write($outfile, $pad, substr($$trailPt,0,$len-4), $newDirStart) or $err = 1;
599             }
600 7 50       44 $rtnVal = $err ? -1 : 1;
601             } else {
602 0         0 $et->Error('Error rewriting CRW file');
603             }
604 7         61 return $rtnVal;
605             }
606              
607             1; # end
608              
609             __END__