Fixup.pm 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366
  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. package Image::ExifTool::Fixup;
  35. use strict;
  36. use Image::ExifTool qw(GetByteOrder SetByteOrder Get32u Get32s Set32u
  37. Get16u Get16s Set16u);
  38. use vars qw($VERSION);
  39. $VERSION = '1.05';
  40. sub AddFixup($$;$$);
  41. sub ApplyFixup($$);
  42. sub Dump($;$);
  43. #------------------------------------------------------------------------------
  44. # New - create new Fixup object
  45. # Inputs: 0) reference to Fixup object or Fixup class name
  46. sub new
  47. {
  48. local $_;
  49. my $that = shift;
  50. my $class = ref($that) || $that || 'Image::ExifTool::Fixup';
  51. my $self = bless {}, $class;
  52. # initialize required members
  53. $self->{Start} = 0;
  54. $self->{Shift} = 0;
  55. return $self;
  56. }
  57. #------------------------------------------------------------------------------
  58. # Clone this object
  59. # Inputs: 0) reference to Fixup object or Fixup class name
  60. # Returns: reference to new Fixup object
  61. sub Clone($)
  62. {
  63. my $self = shift;
  64. my $clone = new Image::ExifTool::Fixup;
  65. $clone->{Start} = $self->{Start};
  66. $clone->{Shift} = $self->{Shift};
  67. my $phash = $self->{Pointers};
  68. if ($phash) {
  69. $clone->{Pointers} = { };
  70. my $byteOrder;
  71. foreach $byteOrder (keys %$phash) {
  72. my @pointers = @{$phash->{$byteOrder}};
  73. $clone->{Pointers}->{$byteOrder} = \@pointers;
  74. }
  75. }
  76. if ($self->{Fixups}) {
  77. $clone->{Fixups} = [ ];
  78. my $subFixup;
  79. foreach $subFixup (@{$self->{Fixups}}) {
  80. push @{$clone->{Fixups}}, $subFixup->Clone();
  81. }
  82. }
  83. return $clone;
  84. }
  85. #------------------------------------------------------------------------------
  86. # Add fixup pointer or another fixup object below this one
  87. # Inputs: 0) Fixup object reference
  88. # 1) Scalar for pointer offset, or reference to Fixup object
  89. # 2) Optional marker name for the pointer
  90. # 3) Optional pointer format ('int16u' or 'int32u', defaults to 'int32u')
  91. # Notes: Byte ordering must be set properly for the pointer being added (must keep
  92. # track of the byte order of each offset since MakerNotes may have different byte order!)
  93. sub AddFixup($$;$$)
  94. {
  95. my ($self, $pointer, $marker, $format) = @_;
  96. if (ref $pointer) {
  97. $self->{Fixups} or $self->{Fixups} = [ ];
  98. push @{$self->{Fixups}}, $pointer;
  99. } else {
  100. my $byteOrder = GetByteOrder();
  101. if (defined $format) {
  102. if ($format eq 'int16u') {
  103. $byteOrder .= '2';
  104. } elsif ($format ne 'int32u') {
  105. warn "Bad Fixup pointer format $format\n";
  106. }
  107. }
  108. $byteOrder .= "_$marker" if defined $marker;
  109. my $phash = $self->{Pointers};
  110. $phash or $phash = $self->{Pointers} = { };
  111. $phash->{$byteOrder} or $phash->{$byteOrder} = [ ];
  112. push @{$phash->{$byteOrder}}, $pointer;
  113. }
  114. }
  115. #------------------------------------------------------------------------------
  116. # fix up pointer offsets
  117. # Inputs: 0) Fixup object reference, 1) data reference
  118. # Outputs: Collapses fixup hierarchy into linear lists of fixup pointers
  119. sub ApplyFixup($$)
  120. {
  121. my ($self, $dataPt) = @_;
  122. my $start = $self->{Start};
  123. my $shift = $self->{Shift} + $start; # make shift relative to start
  124. my $phash = $self->{Pointers};
  125. # fix up pointers in this fixup
  126. if ($phash and ($start or $shift)) {
  127. my $saveOrder = GetByteOrder(); # save original byte ordering
  128. my ($byteOrder, $ptr);
  129. foreach $byteOrder (keys %$phash) {
  130. SetByteOrder(substr($byteOrder,0,2));
  131. # apply the fixup offset shift (must get as signed integer
  132. # to avoid overflow in case it was negative before)
  133. my ($get, $set) = ($byteOrder =~ /^(II2|MM2)/) ?
  134. (\&Get16s, \&Set16u) : (\&Get32s, \&Set32u);
  135. foreach $ptr (@{$phash->{$byteOrder}}) {
  136. $ptr += $start; # update pointer to new start location
  137. next unless $shift;
  138. &$set(&$get($dataPt, $ptr) + $shift, $dataPt, $ptr);
  139. }
  140. }
  141. SetByteOrder($saveOrder); # restore original byte ordering
  142. }
  143. # recurse into contained fixups
  144. if ($self->{Fixups}) {
  145. # create our pointer hash if it doesn't exist
  146. $phash or $phash = $self->{Pointers} = { };
  147. # loop through all contained fixups
  148. my $subFixup;
  149. foreach $subFixup (@{$self->{Fixups}}) {
  150. # adjust the subfixup start and shift
  151. $subFixup->{Start} += $start;
  152. $subFixup->{Shift} += $shift - $start;
  153. # recursively apply contained fixups
  154. ApplyFixup($subFixup, $dataPt);
  155. my $shash = $subFixup->{Pointers} or next;
  156. # add all pointers to our collapsed lists
  157. my $byteOrder;
  158. foreach $byteOrder (keys %$shash) {
  159. $phash->{$byteOrder} or $phash->{$byteOrder} = [ ];
  160. push @{$phash->{$byteOrder}}, @{$shash->{$byteOrder}};
  161. delete $shash->{$byteOrder};
  162. }
  163. delete $subFixup->{Pointers};
  164. }
  165. delete $self->{Fixups}; # remove our contained fixups
  166. }
  167. # reset our Start/Shift for the collapsed fixup
  168. $self->{Start} = $self->{Shift} = 0;
  169. }
  170. #------------------------------------------------------------------------------
  171. # Is this Fixup empty?
  172. # Inputs: 0) Fixup object ref
  173. # Returns: True if there are no offsets to fix
  174. sub IsEmpty($)
  175. {
  176. my $self = shift;
  177. my $phash = $self->{Pointers};
  178. if ($phash) {
  179. my $key;
  180. foreach $key (keys %$phash) {
  181. next unless ref $$phash{$key} eq 'ARRAY';
  182. return 0 if @{$$phash{$key}};
  183. }
  184. }
  185. return 1;
  186. }
  187. #------------------------------------------------------------------------------
  188. # Does specified marker exist?
  189. # Inputs: 0) Fixup object reference, 1) marker name
  190. # Returns: True if fixup contains specified marker name
  191. sub HasMarker($$)
  192. {
  193. my ($self, $marker) = @_;
  194. my $phash = $self->{Pointers};
  195. return 0 unless $phash;
  196. return 1 if grep /_$marker$/, keys %$phash;
  197. return 0 unless $self->{Fixups};
  198. my $subFixup;
  199. foreach $subFixup (@{$self->{Fixups}}) {
  200. return 1 if $subFixup->HasMarker($marker);
  201. }
  202. return 0;
  203. }
  204. #------------------------------------------------------------------------------
  205. # Set all marker pointers to specified value
  206. # Inputs: 0) Fixup object reference, 1) data reference
  207. # 2) marker name, 3) pointer value, 4) offset to start of data
  208. sub SetMarkerPointers($$$$;$)
  209. {
  210. my ($self, $dataPt, $marker, $value, $startOffset) = @_;
  211. my $start = $self->{Start} + ($startOffset || 0);
  212. my $phash = $self->{Pointers};
  213. if ($phash) {
  214. my $saveOrder = GetByteOrder(); # save original byte ordering
  215. my ($byteOrder, $ptr);
  216. foreach $byteOrder (keys %$phash) {
  217. next unless $byteOrder =~ /^(II|MM)(2?)_$marker$/;
  218. SetByteOrder($1);
  219. my $set = $2 ? \&Set16u : \&Set32u;
  220. foreach $ptr (@{$phash->{$byteOrder}}) {
  221. &$set($value, $dataPt, $ptr + $start);
  222. }
  223. }
  224. SetByteOrder($saveOrder); # restore original byte ordering
  225. }
  226. if ($self->{Fixups}) {
  227. my $subFixup;
  228. foreach $subFixup (@{$self->{Fixups}}) {
  229. $subFixup->SetMarkerPointers($dataPt, $marker, $value, $start);
  230. }
  231. }
  232. }
  233. #------------------------------------------------------------------------------
  234. # Get pointer values for specified marker
  235. # Inputs: 0) Fixup object reference, 1) data reference,
  236. # 2) marker name, 3) offset to start of data
  237. # Returns: List of marker pointers in list context, or first marker pointer otherwise
  238. sub GetMarkerPointers($$$;$)
  239. {
  240. my ($self, $dataPt, $marker, $startOffset) = @_;
  241. my $start = $self->{Start} + ($startOffset || 0);
  242. my $phash = $self->{Pointers};
  243. my @pointers;
  244. if ($phash) {
  245. my $saveOrder = GetByteOrder();
  246. my ($byteOrder, $ptr);
  247. foreach $byteOrder (grep /_$marker$/, keys %$phash) {
  248. SetByteOrder(substr($byteOrder,0,2));
  249. my $get = ($byteOrder =~ /^(II2|MM2)/) ? \&Get16u : \&Get32u;
  250. foreach $ptr (@{$phash->{$byteOrder}}) {
  251. push @pointers, &$get($dataPt, $ptr + $start);
  252. }
  253. }
  254. SetByteOrder($saveOrder); # restore original byte ordering
  255. }
  256. if ($self->{Fixups}) {
  257. my $subFixup;
  258. foreach $subFixup (@{$self->{Fixups}}) {
  259. push @pointers, $subFixup->GetMarkerPointers($dataPt, $marker, $start);
  260. }
  261. }
  262. return @pointers if wantarray;
  263. return $pointers[0];
  264. }
  265. #------------------------------------------------------------------------------
  266. # Dump fixup to console for debugging
  267. # Inputs: 0) Fixup object reference, 1) optional initial indent string
  268. sub Dump($;$)
  269. {
  270. my ($self, $indent) = @_;
  271. $indent or $indent = '';
  272. printf "${indent}Fixup start=0x%x shift=0x%x\n", $self->{Start}, $self->{Shift};
  273. my $phash = $self->{Pointers};
  274. if ($phash) {
  275. my $byteOrder;
  276. foreach $byteOrder (sort keys %$phash) {
  277. print "$indent $byteOrder: ", join(' ',@{$phash->{$byteOrder}}),"\n";
  278. }
  279. }
  280. if ($self->{Fixups}) {
  281. my $subFixup;
  282. foreach $subFixup (@{$self->{Fixups}}) {
  283. Dump($subFixup, $indent . ' ');
  284. }
  285. }
  286. }
  287. 1; # end
  288. __END__
  289. =head1 NAME
  290. Image::ExifTool::Fixup - Utility to handle pointer fixups
  291. =head1 SYNOPSIS
  292. use Image::ExifTool::Fixup;
  293. $fixup = new Image::ExifTool::Fixup;
  294. # add a new fixup to a pointer at the specified offset in data
  295. $fixup->AddFixup($offset);
  296. # add a new Fixup object to the tree
  297. $fixup->AddFixup($subFixup);
  298. $fixup->{Start} += $shift1; # shift pointer offsets and values
  299. $fixup->{Shift} += $shift2; # shift pointer values only
  300. # recursively apply fixups to the specified data
  301. $fixup->ApplyFixups(\$data);
  302. $fixup->Dump(); # dump debugging information
  303. $fixup->IsEmpty(); # return true if no offsets to fix
  304. =head1 DESCRIPTION
  305. This module contains the code to keep track of pointers in memory and to
  306. shift these pointers as required. It is used by ExifTool to maintain the
  307. pointers in image file directories (IFD's).
  308. =head1 NOTES
  309. Keeps track of pointers with different byte ordering, and relies on
  310. Image::ExifTool::GetByteOrder() to determine the current byte ordering
  311. when adding new pointers to a fixup.
  312. Maintains a hierarchical list of fixups so that the whole hierarchy can
  313. be shifted by a simple shift at the base. Hierarchy is collapsed to a
  314. linear list when ApplyFixups() is called.
  315. =head1 AUTHOR
  316. Copyright 2003-2016, Phil Harvey (phil at owl.phy.queensu.ca)
  317. This library is free software; you can redistribute it and/or modify it
  318. under the same terms as Perl itself.
  319. =head1 SEE ALSO
  320. L<Image::ExifTool(3pm)|Image::ExifTool>
  321. =cut