File Coverage

blib/lib/Image/ExifTool/Fixup.pm
Criterion Covered Total %
statement 127 160 79.3
branch 42 70 60.0
condition 11 12 91.6
subroutine 10 12 83.3
pod 0 9 0.0
total 190 263 72.2


line stmt bran cond sub pod time code
1             #------------------------------------------------------------------------------
2             # File: Fixup.pm
3             #
4             # Description: Utility to handle pointer fixups
5             #
6             # Revisions: 01/19/2005 - P. Harvey Created
7             # 04/11/2005 - P. Harvey Allow fixups to be tagged with a marker,
8             # and add new marker-related routines
9             # 06/21/2006 - P. Harvey Patch to work with negative offsets
10             # 07/07/2006 - P. Harvey Added support for 16-bit pointers
11             # 02/19/2013 - P. Harvey Added IsEmpty()
12             #
13             # Data Members:
14             #
15             # Start - Position in data where a zero pointer points to.
16             # Shift - Amount to shift offsets (relative to Start).
17             # Fixups - List of Fixup object references to to shift relative to this Fixup.
18             # Pointers - Hash of references to fixup pointer arrays, keyed by ByteOrder
19             # string (with "2" added if pointer is 16-bit [default is 32-bit],
20             # plus "_$marker" suffix if tagged with a marker name).
21             #
22             # Procedure:
23             #
24             # 1. Create a Fixup object for each data block containing pointers
25             # 2. Call AddFixup with the offset of each pointer in the block
26             # - pointer is assumed int32u with the current byte order
27             # - may also be called with a fixup reference for contained blocks
28             # 3. Add the necessary pointer offset to $$fixup{Shift}
29             # 4. Add data size to $$fixup{Start} if data is added before the block
30             # - automatically also shifts pointers by this amount
31             # 5. Call ApplyFixup to apply the fixup to all pointers
32             # - resets Shift and Start to 0 after applying fixup
33             #------------------------------------------------------------------------------
34              
35             package Image::ExifTool::Fixup;
36              
37 57     57   360 use strict;
  57         111  
  57         2204  
38 57         3814 use Image::ExifTool qw(GetByteOrder SetByteOrder Get32u Get32s Set32u
39 57     57   277 Get16u Get16s Set16u);
  57         117  
40 57     57   304 use vars qw($VERSION);
  57         106  
  57         78264  
41              
42             $VERSION = '1.05';
43              
44             sub AddFixup($$;$$);
45             sub ApplyFixup($$);
46             sub Dump($;$);
47              
48             #------------------------------------------------------------------------------
49             # New - create new Fixup object
50             # Inputs: 0) reference to Fixup object or Fixup class name
51             sub new
52             {
53 1134     1134 0 2145 local $_;
54 1134         1915 my $that = shift;
55 1134   50     4465 my $class = ref($that) || $that || 'Image::ExifTool::Fixup';
56 1134         2597 my $self = bless {}, $class;
57              
58             # initialize required members
59 1134         2924 $self->{Start} = 0;
60 1134         2315 $self->{Shift} = 0;
61              
62 1134         3988 return $self;
63             }
64              
65             #------------------------------------------------------------------------------
66             # Clone this object
67             # Inputs: 0) reference to Fixup object or Fixup class name
68             # Returns: reference to new Fixup object
69             sub Clone($)
70             {
71 6     6 0 17 my $self = shift;
72 6         22 my $clone = new Image::ExifTool::Fixup;
73 6         21 $clone->{Start} = $self->{Start};
74 6         15 $clone->{Shift} = $self->{Shift};
75 6         15 my $phash = $self->{Pointers};
76 6 50       23 if ($phash) {
77 6         19 $clone->{Pointers} = { };
78 6         15 my $byteOrder;
79 6         27 foreach $byteOrder (keys %$phash) {
80 7         18 my @pointers = @{$phash->{$byteOrder}};
  7         47  
81 7         29 $clone->{Pointers}->{$byteOrder} = \@pointers;
82             }
83             }
84 6 50       24 if ($self->{Fixups}) {
85 0         0 $clone->{Fixups} = [ ];
86 0         0 my $subFixup;
87 0         0 foreach $subFixup (@{$self->{Fixups}}) {
  0         0  
88 0         0 push @{$clone->{Fixups}}, $subFixup->Clone();
  0         0  
89             }
90             }
91 6         22 return $clone;
92             }
93              
94             #------------------------------------------------------------------------------
95             # Add fixup pointer or another fixup object below this one
96             # Inputs: 0) Fixup object reference
97             # 1) Scalar for pointer offset, or reference to Fixup object
98             # 2) Optional marker name for the pointer
99             # 3) Optional pointer format ('int16u' or 'int32u', defaults to 'int32u')
100             # Notes: Byte ordering must be set properly for the pointer being added (must keep
101             # track of the byte order of each offset since MakerNotes may have different byte order!)
102             sub AddFixup($$;$$)
103             {
104 3421     3421 0 6519 my ($self, $pointer, $marker, $format) = @_;
105 3421 100       5921 if (ref $pointer) {
106 546 100       1639 $self->{Fixups} or $self->{Fixups} = [ ];
107 546         776 push @{$self->{Fixups}}, $pointer;
  546         1526  
108             } else {
109 2875         5208 my $byteOrder = GetByteOrder();
110 2875 50       5544 if (defined $format) {
111 0 0       0 if ($format eq 'int16u') {
    0          
112 0         0 $byteOrder .= '2';
113             } elsif ($format ne 'int32u') {
114 0         0 warn "Bad Fixup pointer format $format\n";
115             }
116             }
117 2875 100       4781 $byteOrder .= "_$marker" if defined $marker;
118 2875         4828 my $phash = $self->{Pointers};
119 2875 100       5364 $phash or $phash = $self->{Pointers} = { };
120 2875 100       6079 $phash->{$byteOrder} or $phash->{$byteOrder} = [ ];
121 2875         3844 push @{$phash->{$byteOrder}}, $pointer;
  2875         7874  
122             }
123             }
124              
125             #------------------------------------------------------------------------------
126             # fix up pointer offsets
127             # Inputs: 0) Fixup object reference, 1) data reference
128             # Outputs: Collapses fixup hierarchy into linear lists of fixup pointers
129             sub ApplyFixup($$)
130             {
131 1361     1361 0 2485 my ($self, $dataPt) = @_;
132              
133 1361         2080 my $start = $self->{Start};
134 1361         2007 my $shift = $self->{Shift} + $start; # make shift relative to start
135 1361         2290 my $phash = $self->{Pointers};
136              
137             # fix up pointers in this fixup
138 1361 100 100     4020 if ($phash and ($start or $shift)) {
      100        
139 655         1425 my $saveOrder = GetByteOrder(); # save original byte ordering
140 655         1026 my ($byteOrder, $ptr);
141 655         1718 foreach $byteOrder (keys %$phash) {
142 740         2354 SetByteOrder(substr($byteOrder,0,2));
143             # apply the fixup offset shift (must get as signed integer
144             # to avoid overflow in case it was negative before)
145 740 50       2891 my ($get, $set) = ($byteOrder =~ /^(II2|MM2)/) ?
146             (\&Get16s, \&Set16u) : (\&Get32s, \&Set32u);
147 740         1141 foreach $ptr (@{$phash->{$byteOrder}}) {
  740         1498  
148 7337         8157 $ptr += $start; # update pointer to new start location
149 7337 50       10275 next unless $shift;
150 7337         11088 &$set(&$get($dataPt, $ptr) + $shift, $dataPt, $ptr);
151             }
152             }
153 655         1437 SetByteOrder($saveOrder); # restore original byte ordering
154             }
155             # recurse into contained fixups
156 1361 100       2910 if ($self->{Fixups}) {
157             # create our pointer hash if it doesn't exist
158 318 100       1107 $phash or $phash = $self->{Pointers} = { };
159             # loop through all contained fixups
160 318         518 my $subFixup;
161 318         493 foreach $subFixup (@{$self->{Fixups}}) {
  318         821  
162             # adjust the subfixup start and shift
163 545         950 $subFixup->{Start} += $start;
164 545         868 $subFixup->{Shift} += $shift - $start;
165             # recursively apply contained fixups
166 545         1428 ApplyFixup($subFixup, $dataPt);
167 545 100       1286 my $shash = $subFixup->{Pointers} or next;
168             # add all pointers to our collapsed lists
169 489         657 my $byteOrder;
170 489         1048 foreach $byteOrder (keys %$shash) {
171 478 100       1334 $phash->{$byteOrder} or $phash->{$byteOrder} = [ ];
172 478         673 push @{$phash->{$byteOrder}}, @{$shash->{$byteOrder}};
  478         807  
  478         1620  
173 478         1195 delete $shash->{$byteOrder};
174             }
175 489         1167 delete $subFixup->{Pointers};
176             }
177 318         1178 delete $self->{Fixups}; # remove our contained fixups
178             }
179             # reset our Start/Shift for the collapsed fixup
180 1361         4294 $self->{Start} = $self->{Shift} = 0;
181             }
182              
183             #------------------------------------------------------------------------------
184             # Is this Fixup empty?
185             # Inputs: 0) Fixup object ref
186             # Returns: True if there are no offsets to fix
187             sub IsEmpty($)
188             {
189 13     13 0 34 my $self = shift;
190 13         34 my $phash = $self->{Pointers};
191 13 50       43 if ($phash) {
192 13         28 my $key;
193 13         60 foreach $key (keys %$phash) {
194 12 50       60 next unless ref $$phash{$key} eq 'ARRAY';
195 12 50       26 return 0 if @{$$phash{$key}};
  12         84  
196             }
197             }
198 1         4 return 1;
199             }
200              
201             #------------------------------------------------------------------------------
202             # Does specified marker exist?
203             # Inputs: 0) Fixup object reference, 1) marker name
204             # Returns: True if fixup contains specified marker name
205             sub HasMarker($$)
206             {
207 0     0 0 0 my ($self, $marker) = @_;
208 0         0 my $phash = $self->{Pointers};
209 0 0       0 return 0 unless $phash;
210 0 0       0 return 1 if grep /_$marker$/, keys %$phash;
211 0 0       0 return 0 unless $self->{Fixups};
212 0         0 my $subFixup;
213 0         0 foreach $subFixup (@{$self->{Fixups}}) {
  0         0  
214 0 0       0 return 1 if $subFixup->HasMarker($marker);
215             }
216 0         0 return 0;
217             }
218              
219             #------------------------------------------------------------------------------
220             # Set all marker pointers to specified value
221             # Inputs: 0) Fixup object reference, 1) data reference
222             # 2) marker name, 3) pointer value, 4) offset to start of data
223             sub SetMarkerPointers($$$$;$)
224             {
225 132     132 0 495 my ($self, $dataPt, $marker, $value, $startOffset) = @_;
226 132   100     661 my $start = $self->{Start} + ($startOffset || 0);
227 132         332 my $phash = $self->{Pointers};
228              
229 132 100       393 if ($phash) {
230 125         361 my $saveOrder = GetByteOrder(); # save original byte ordering
231 125         273 my ($byteOrder, $ptr);
232 125         426 foreach $byteOrder (keys %$phash) {
233 234 100       2474 next unless $byteOrder =~ /^(II|MM)(2?)_$marker$/;
234 10         43 SetByteOrder($1);
235 10 50       46 my $set = $2 ? \&Set16u : \&Set32u;
236 10         22 foreach $ptr (@{$phash->{$byteOrder}}) {
  10         52  
237 11         42 &$set($value, $dataPt, $ptr + $start);
238             }
239             }
240 125         460 SetByteOrder($saveOrder); # restore original byte ordering
241             }
242 132 100       680 if ($self->{Fixups}) {
243 1         3 my $subFixup;
244 1         3 foreach $subFixup (@{$self->{Fixups}}) {
  1         3  
245 1         6 $subFixup->SetMarkerPointers($dataPt, $marker, $value, $start);
246             }
247             }
248             }
249              
250             #------------------------------------------------------------------------------
251             # Get pointer values for specified marker
252             # Inputs: 0) Fixup object reference, 1) data reference,
253             # 2) marker name, 3) offset to start of data
254             # Returns: List of marker pointers in list context, or first marker pointer otherwise
255             sub GetMarkerPointers($$$;$)
256             {
257 6     6 0 21 my ($self, $dataPt, $marker, $startOffset) = @_;
258 6   100     32 my $start = $self->{Start} + ($startOffset || 0);
259 6         15 my $phash = $self->{Pointers};
260 6         13 my @pointers;
261              
262 6 100       20 if ($phash) {
263 5         16 my $saveOrder = GetByteOrder();
264 5         11 my ($byteOrder, $ptr);
265 5         84 foreach $byteOrder (grep /_$marker$/, keys %$phash) {
266 4         20 SetByteOrder(substr($byteOrder,0,2));
267 4 50       57 my $get = ($byteOrder =~ /^(II2|MM2)/) ? \&Get16u : \&Get32u;
268 4         11 foreach $ptr (@{$phash->{$byteOrder}}) {
  4         12  
269 10         24 push @pointers, &$get($dataPt, $ptr + $start);
270             }
271             }
272 5         17 SetByteOrder($saveOrder); # restore original byte ordering
273             }
274 6 100       35 if ($self->{Fixups}) {
275 1         2 my $subFixup;
276 1         2 foreach $subFixup (@{$self->{Fixups}}) {
  1         4  
277 1         6 push @pointers, $subFixup->GetMarkerPointers($dataPt, $marker, $start);
278             }
279             }
280 6 100       29 return @pointers if wantarray;
281 1         5 return $pointers[0];
282             }
283              
284             #------------------------------------------------------------------------------
285             # Dump fixup to console for debugging
286             # Inputs: 0) Fixup object reference, 1) optional initial indent string
287             sub Dump($;$)
288             {
289 0     0 0   my ($self, $indent) = @_;
290 0 0         $indent or $indent = '';
291 0           printf "${indent}Fixup start=0x%x shift=0x%x\n", $self->{Start}, $self->{Shift};
292 0           my $phash = $self->{Pointers};
293 0 0         if ($phash) {
294 0           my $byteOrder;
295 0           foreach $byteOrder (sort keys %$phash) {
296 0           print "$indent $byteOrder: ", join(' ',@{$phash->{$byteOrder}}),"\n";
  0            
297             }
298             }
299 0 0         if ($self->{Fixups}) {
300 0           my $subFixup;
301 0           foreach $subFixup (@{$self->{Fixups}}) {
  0            
302 0           Dump($subFixup, $indent . ' ');
303             }
304             }
305             }
306              
307              
308             1; # end
309              
310             __END__