File Coverage

blib/lib/Image/ExifTool/Fixup.pm
Criterion Covered Total %
statement 118 160 73.7
branch 38 70 54.2
condition 9 12 75.0
subroutine 10 12 83.3
pod 0 9 0.0
total 175 263 66.5


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 61     61   456 use strict;
  61         141  
  61         3472  
38 61         6215 use Image::ExifTool qw(GetByteOrder SetByteOrder Get32u Get32s Set32u
39 61     61   369 Get16u Get16s Set16u);
  61         126  
40 61     61   447 use vars qw($VERSION);
  61         135  
  61         126936  
41              
42             $VERSION = '1.06';
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 1168     1168 0 2803 local $_;
54 1168         2535 my $that = shift;
55 1168   50     6584 my $class = ref($that) || $that || 'Image::ExifTool::Fixup';
56 1168         3843 my $self = bless {}, $class;
57              
58             # initialize required members
59 1168         4281 $self->{Start} = 0;
60 1168         2938 $self->{Shift} = 0;
61              
62 1168         6117 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 20 my $self = shift;
72 6         33 my $clone = Image::ExifTool::Fixup->new;
73 6         26 $clone->{Start} = $self->{Start};
74 6         18 $clone->{Shift} = $self->{Shift};
75 6         21 my $phash = $self->{Pointers};
76 6 50       22 if ($phash) {
77 6         21 $clone->{Pointers} = { };
78 6         14 my $byteOrder;
79 6         27 foreach $byteOrder (keys %$phash) {
80 7         16 my @pointers = @{$phash->{$byteOrder}};
  7         53  
81 7         28 $clone->{Pointers}->{$byteOrder} = \@pointers;
82             }
83             }
84 6 50       28 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         14 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 3417     3417 0 8956 my ($self, $pointer, $marker, $format) = @_;
105 3417 100       7470 if (ref $pointer) {
106 559 100       2404 $self->{Fixups} or $self->{Fixups} = [ ];
107 559         1072 push @{$self->{Fixups}}, $pointer;
  559         2127  
108             } else {
109 2858         9661 my $byteOrder = GetByteOrder();
110 2858 50       6813 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 2858 100       6245 $byteOrder .= "_$marker" if defined $marker;
118 2858         6430 my $phash = $self->{Pointers};
119 2858 100       6883 $phash or $phash = $self->{Pointers} = { };
120 2858 100       8782 $phash->{$byteOrder} or $phash->{$byteOrder} = [ ];
121 2858         4613 push @{$phash->{$byteOrder}}, $pointer;
  2858         11659  
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 1402     1402 0 3498 my ($self, $dataPt) = @_;
132              
133 1402         3179 my $start = $self->{Start};
134 1402         2732 my $shift = $self->{Shift} + $start; # make shift relative to start
135 1402         2812 my $phash = $self->{Pointers};
136              
137             # fix up pointers in this fixup
138 1402 100 100     5519 if ($phash and ($start or $shift)) {
      100        
139 652         2021 my $saveOrder = GetByteOrder(); # save original byte ordering
140 652         1273 my ($byteOrder, $ptr);
141 652         2220 foreach $byteOrder (keys %$phash) {
142 729         3266 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 729 50       6694 my ($get, $set) = ($byteOrder =~ /^(II2|MM2)/) ?
146             (\&Get16s, \&Set16u) : (\&Get32s, \&Set32u);
147 729         1465 foreach $ptr (@{$phash->{$byteOrder}}) {
  729         1907  
148 7328         10701 $ptr += $start; # update pointer to new start location
149 7328 50       13215 next unless $shift;
150 7328         13942 &$set(&$get($dataPt, $ptr) + $shift, $dataPt, $ptr);
151             }
152             }
153 652         1963 SetByteOrder($saveOrder); # restore original byte ordering
154             }
155             # recurse into contained fixups
156 1402 100       4210 if ($self->{Fixups}) {
157             # create our pointer hash if it doesn't exist
158 329 100       1277 $phash or $phash = $self->{Pointers} = { };
159             # loop through all contained fixups
160 329         671 my $subFixup;
161 329         668 foreach $subFixup (@{$self->{Fixups}}) {
  329         998  
162             # adjust the subfixup start and shift
163 559         1409 $subFixup->{Start} += $start;
164 559         1455 $subFixup->{Shift} += $shift - $start;
165             # recursively apply contained fixups
166 559         2051 ApplyFixup($subFixup, $dataPt);
167 559 100       1947 my $shash = $subFixup->{Pointers} or next;
168             # add all pointers to our collapsed lists
169 479         933 my $byteOrder;
170 479         1473 foreach $byteOrder (keys %$shash) {
171 467 100       1936 $phash->{$byteOrder} or $phash->{$byteOrder} = [ ];
172 467         886 push @{$phash->{$byteOrder}}, @{$shash->{$byteOrder}};
  467         1026  
  467         2858  
173 467         1691 delete $shash->{$byteOrder};
174             }
175 479         1649 delete $subFixup->{Pointers};
176             }
177 329         1825 delete $self->{Fixups}; # remove our contained fixups
178             }
179             # reset our Start/Shift for the collapsed fixup
180 1402         6961 $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 32 my $self = shift;
190 13         43 my $phash = $self->{Pointers};
191 13 50       50 if ($phash) {
192 13         24 my $key;
193 13         70 foreach $key (keys %$phash) {
194 12 50       88 next unless ref $$phash{$key} eq 'ARRAY';
195 12 50       40 return 0 if @{$$phash{$key}};
  12         121  
196             }
197             }
198 1         7 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 131     131 0 599 my ($self, $dataPt, $marker, $value, $startOffset) = @_;
226 131   50     982 my $start = $self->{Start} + ($startOffset || 0);
227 131         380 my $phash = $self->{Pointers};
228              
229 131 100       527 if ($phash) {
230 125         471 my $saveOrder = GetByteOrder(); # save original byte ordering
231 125         358 my ($byteOrder, $ptr);
232 125         555 foreach $byteOrder (keys %$phash) {
233 222 100       3361 next unless $byteOrder =~ /^(II|MM)(2?)_$marker$/;
234 6         34 SetByteOrder($1);
235 6 50       35 my $set = $2 ? \&Set16u : \&Set32u;
236 6         15 foreach $ptr (@{$phash->{$byteOrder}}) {
  6         25  
237 7         30 &$set($value, $dataPt, $ptr + $start);
238             }
239             }
240 125         478 SetByteOrder($saveOrder); # restore original byte ordering
241             }
242 131 50       811 if ($self->{Fixups}) {
243 0         0 my $subFixup;
244 0         0 foreach $subFixup (@{$self->{Fixups}}) {
  0         0  
245 0         0 $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 22 my ($self, $dataPt, $marker, $startOffset) = @_;
258 6   50     74 my $start = $self->{Start} + ($startOffset || 0);
259 6         17 my $phash = $self->{Pointers};
260 6         14 my @pointers;
261              
262 6 50       24 if ($phash) {
263 6         27 my $saveOrder = GetByteOrder();
264 6         34 my ($byteOrder, $ptr);
265 6         140 foreach $byteOrder (grep /_$marker$/, keys %$phash) {
266 3         28 SetByteOrder(substr($byteOrder,0,2));
267 3 50       28 my $get = ($byteOrder =~ /^(II2|MM2)/) ? \&Get16u : \&Get32u;
268 3         9 foreach $ptr (@{$phash->{$byteOrder}}) {
  3         12  
269 9         31 push @pointers, &$get($dataPt, $ptr + $start);
270             }
271             }
272 6         25 SetByteOrder($saveOrder); # restore original byte ordering
273             }
274 6 50       28 if ($self->{Fixups}) {
275 0         0 my $subFixup;
276 0         0 foreach $subFixup (@{$self->{Fixups}}) {
  0         0  
277 0         0 push @pointers, $subFixup->GetMarkerPointers($dataPt, $marker, $start);
278             }
279             }
280 6 50       40 return @pointers if wantarray;
281 0           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__