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 58     58   450 use strict;
  58         145  
  58         2793  
38 58         4747 use Image::ExifTool qw(GetByteOrder SetByteOrder Get32u Get32s Set32u
39 58     58   364 Get16u Get16s Set16u);
  58         142  
40 58     58   387 use vars qw($VERSION);
  58         674  
  58         98380  
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 1142     1142 0 2365 local $_;
54 1142         2164 my $that = shift;
55 1142   50     4920 my $class = ref($that) || $that || 'Image::ExifTool::Fixup';
56 1142         3312 my $self = bless {}, $class;
57              
58             # initialize required members
59 1142         3387 $self->{Start} = 0;
60 1142         2923 $self->{Shift} = 0;
61              
62 1142         5127 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 16 my $self = shift;
72 6         43 my $clone = new Image::ExifTool::Fixup;
73 6         27 $clone->{Start} = $self->{Start};
74 6         27 $clone->{Shift} = $self->{Shift};
75 6         19 my $phash = $self->{Pointers};
76 6 50       28 if ($phash) {
77 6         27 $clone->{Pointers} = { };
78 6         17 my $byteOrder;
79 6         36 foreach $byteOrder (keys %$phash) {
80 7         16 my @pointers = @{$phash->{$byteOrder}};
  7         61  
81 7         33 $clone->{Pointers}->{$byteOrder} = \@pointers;
82             }
83             }
84 6 50       30 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         23 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 3435     3435 0 7687 my ($self, $pointer, $marker, $format) = @_;
105 3435 100       7170 if (ref $pointer) {
106 552 100       2089 $self->{Fixups} or $self->{Fixups} = [ ];
107 552         1016 push @{$self->{Fixups}}, $pointer;
  552         1845  
108             } else {
109 2883         6221 my $byteOrder = GetByteOrder();
110 2883 50       6693 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 2883 100       5830 $byteOrder .= "_$marker" if defined $marker;
118 2883         5596 my $phash = $self->{Pointers};
119 2883 100       6441 $phash or $phash = $self->{Pointers} = { };
120 2883 100       7569 $phash->{$byteOrder} or $phash->{$byteOrder} = [ ];
121 2883         4259 push @{$phash->{$byteOrder}}, $pointer;
  2883         10169  
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 1373     1373 0 2910 my ($self, $dataPt) = @_;
132              
133 1373         2643 my $start = $self->{Start};
134 1373         2442 my $shift = $self->{Shift} + $start; # make shift relative to start
135 1373         2420 my $phash = $self->{Pointers};
136              
137             # fix up pointers in this fixup
138 1373 100 100     4906 if ($phash and ($start or $shift)) {
      100        
139 663         1748 my $saveOrder = GetByteOrder(); # save original byte ordering
140 663         1346 my ($byteOrder, $ptr);
141 663         2064 foreach $byteOrder (keys %$phash) {
142 748         3112 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 748 50       3781 my ($get, $set) = ($byteOrder =~ /^(II2|MM2)/) ?
146             (\&Get16s, \&Set16u) : (\&Get32s, \&Set32u);
147 748         1326 foreach $ptr (@{$phash->{$byteOrder}}) {
  748         1864  
148 7353         10005 $ptr += $start; # update pointer to new start location
149 7353 50       12668 next unless $shift;
150 7353         13593 &$set(&$get($dataPt, $ptr) + $shift, $dataPt, $ptr);
151             }
152             }
153 663         1761 SetByteOrder($saveOrder); # restore original byte ordering
154             }
155             # recurse into contained fixups
156 1373 100       3493 if ($self->{Fixups}) {
157             # create our pointer hash if it doesn't exist
158 322 100       1270 $phash or $phash = $self->{Pointers} = { };
159             # loop through all contained fixups
160 322         626 my $subFixup;
161 322         607 foreach $subFixup (@{$self->{Fixups}}) {
  322         886  
162             # adjust the subfixup start and shift
163 551         1095 $subFixup->{Start} += $start;
164 551         1149 $subFixup->{Shift} += $shift - $start;
165             # recursively apply contained fixups
166 551         1776 ApplyFixup($subFixup, $dataPt);
167 551 100       1588 my $shash = $subFixup->{Pointers} or next;
168             # add all pointers to our collapsed lists
169 495         822 my $byteOrder;
170 495         1278 foreach $byteOrder (keys %$shash) {
171 484 100       1757 $phash->{$byteOrder} or $phash->{$byteOrder} = [ ];
172 484         854 push @{$phash->{$byteOrder}}, @{$shash->{$byteOrder}};
  484         963  
  484         2092  
173 484         1568 delete $shash->{$byteOrder};
174             }
175 495         1512 delete $subFixup->{Pointers};
176             }
177 322         1501 delete $self->{Fixups}; # remove our contained fixups
178             }
179             # reset our Start/Shift for the collapsed fixup
180 1373         4809 $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 40 my $self = shift;
190 13         45 my $phash = $self->{Pointers};
191 13 50       81 if ($phash) {
192 13         36 my $key;
193 13         57 foreach $key (keys %$phash) {
194 12 50       62 next unless ref $$phash{$key} eq 'ARRAY';
195 12 50       27 return 0 if @{$$phash{$key}};
  12         114  
196             }
197             }
198 1         5 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 134     134 0 602 my ($self, $dataPt, $marker, $value, $startOffset) = @_;
226 134   100     888 my $start = $self->{Start} + ($startOffset || 0);
227 134         426 my $phash = $self->{Pointers};
228              
229 134 100       486 if ($phash) {
230 127         442 my $saveOrder = GetByteOrder(); # save original byte ordering
231 127         361 my ($byteOrder, $ptr);
232 127         550 foreach $byteOrder (keys %$phash) {
233 236 100       2956 next unless $byteOrder =~ /^(II|MM)(2?)_$marker$/;
234 10         54 SetByteOrder($1);
235 10 50       73 my $set = $2 ? \&Set16u : \&Set32u;
236 10         29 foreach $ptr (@{$phash->{$byteOrder}}) {
  10         35  
237 11         50 &$set($value, $dataPt, $ptr + $start);
238             }
239             }
240 127         571 SetByteOrder($saveOrder); # restore original byte ordering
241             }
242 134 100       976 if ($self->{Fixups}) {
243 1         3 my $subFixup;
244 1         3 foreach $subFixup (@{$self->{Fixups}}) {
  1         5  
245 1         8 $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 8     8 0 39 my ($self, $dataPt, $marker, $startOffset) = @_;
258 8   100     53 my $start = $self->{Start} + ($startOffset || 0);
259 8         31 my $phash = $self->{Pointers};
260 8         22 my @pointers;
261              
262 8 100       34 if ($phash) {
263 7         42 my $saveOrder = GetByteOrder();
264 7         26 my ($byteOrder, $ptr);
265 7         173 foreach $byteOrder (grep /_$marker$/, keys %$phash) {
266 4         33 SetByteOrder(substr($byteOrder,0,2));
267 4 50       57 my $get = ($byteOrder =~ /^(II2|MM2)/) ? \&Get16u : \&Get32u;
268 4         12 foreach $ptr (@{$phash->{$byteOrder}}) {
  4         15  
269 10         35 push @pointers, &$get($dataPt, $ptr + $start);
270             }
271             }
272 7         49 SetByteOrder($saveOrder); # restore original byte ordering
273             }
274 8 100       58 if ($self->{Fixups}) {
275 1         2 my $subFixup;
276 1         3 foreach $subFixup (@{$self->{Fixups}}) {
  1         4  
277 1         7 push @pointers, $subFixup->GetMarkerPointers($dataPt, $marker, $start);
278             }
279             }
280 8 100       46 return @pointers if wantarray;
281 1         8 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__