exiftool_vendored 13.06.0 → 13.08.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (56) hide show
  1. checksums.yaml +4 -4
  2. data/bin/Changes +29 -3
  3. data/bin/META.json +1 -1
  4. data/bin/META.yml +1 -1
  5. data/bin/README +2 -2
  6. data/bin/exiftool +6 -5
  7. data/bin/lib/Image/ExifTool/AIFF.pm +1 -1
  8. data/bin/lib/Image/ExifTool/APE.pm +1 -1
  9. data/bin/lib/Image/ExifTool/ASF.pm +1 -1
  10. data/bin/lib/Image/ExifTool/BuildTagLookup.pm +4 -3
  11. data/bin/lib/Image/ExifTool/Canon.pm +19 -1
  12. data/bin/lib/Image/ExifTool/DJI.pm +1 -1
  13. data/bin/lib/Image/ExifTool/Exif.pm +2 -2
  14. data/bin/lib/Image/ExifTool/FITS.pm +2 -2
  15. data/bin/lib/Image/ExifTool/FLIF.pm +2 -2
  16. data/bin/lib/Image/ExifTool/FlashPix.pm +11 -11
  17. data/bin/lib/Image/ExifTool/Font.pm +1 -1
  18. data/bin/lib/Image/ExifTool/HP.pm +1 -1
  19. data/bin/lib/Image/ExifTool/ID3.pm +3 -3
  20. data/bin/lib/Image/ExifTool/IPTC.pm +2 -2
  21. data/bin/lib/Image/ExifTool/InDesign.pm +1 -1
  22. data/bin/lib/Image/ExifTool/Jpeg2000.pm +6 -6
  23. data/bin/lib/Image/ExifTool/M2TS.pm +39 -9
  24. data/bin/lib/Image/ExifTool/MXF.pm +2 -2
  25. data/bin/lib/Image/ExifTool/Matroska.pm +1 -1
  26. data/bin/lib/Image/ExifTool/Microsoft.pm +1 -1
  27. data/bin/lib/Image/ExifTool/PDF.pm +15 -15
  28. data/bin/lib/Image/ExifTool/PLIST.pm +1 -1
  29. data/bin/lib/Image/ExifTool/PNG.pm +4 -4
  30. data/bin/lib/Image/ExifTool/Panasonic.pm +1 -1
  31. data/bin/lib/Image/ExifTool/PhaseOne.pm +3 -3
  32. data/bin/lib/Image/ExifTool/Photoshop.pm +63 -3
  33. data/bin/lib/Image/ExifTool/Protobuf.pm +4 -4
  34. data/bin/lib/Image/ExifTool/QuickTime.pm +23 -14
  35. data/bin/lib/Image/ExifTool/QuickTimeStream.pl +336 -91
  36. data/bin/lib/Image/ExifTool/README +4 -1
  37. data/bin/lib/Image/ExifTool/RIFF.pm +3 -3
  38. data/bin/lib/Image/ExifTool/RTF.pm +1 -1
  39. data/bin/lib/Image/ExifTool/Ricoh.pm +3 -3
  40. data/bin/lib/Image/ExifTool/Sony.pm +2 -2
  41. data/bin/lib/Image/ExifTool/TagInfoXML.pm +4 -3
  42. data/bin/lib/Image/ExifTool/TagLookup.pm +6979 -6970
  43. data/bin/lib/Image/ExifTool/TagNames.pod +26 -5
  44. data/bin/lib/Image/ExifTool/VCard.pm +2 -2
  45. data/bin/lib/Image/ExifTool/Validate.pm +3 -3
  46. data/bin/lib/Image/ExifTool/WriteExif.pl +2 -2
  47. data/bin/lib/Image/ExifTool/WriteQuickTime.pl +4 -4
  48. data/bin/lib/Image/ExifTool/WriteXMP.pl +2 -2
  49. data/bin/lib/Image/ExifTool/Writer.pl +16 -16
  50. data/bin/lib/Image/ExifTool/XMP.pm +9 -9
  51. data/bin/lib/Image/ExifTool/ZIP.pm +1 -1
  52. data/bin/lib/Image/ExifTool.pm +58 -57
  53. data/bin/lib/Image/ExifTool.pod +1 -1
  54. data/bin/perl-Image-ExifTool.spec +1 -1
  55. data/lib/exiftool_vendored/version.rb +1 -1
  56. metadata +2 -2
@@ -94,6 +94,7 @@ my %insvDataLen = (
94
94
  # 0xb00 => 10, # ? (Insta360 X3)
95
95
  # 0xd00 => 10, # ? (Insta360 Ace Pro)
96
96
  # 0x1200 ? # ? (Insta360 Ace Pro)
97
+ # 0x1600 ? # ? (?)
97
98
  );
98
99
 
99
100
  # limit the default amount of data we read for some record types
@@ -109,7 +110,7 @@ my %insvLimit = (
109
110
  The tags below are extracted from timed metadata in QuickTime and other
110
111
  formats of video files when the ExtractEmbedded option is used. Although
111
112
  most of these tags are combined into the single table below, ExifTool
112
- currently reads 85 different formats of timed GPS metadata from video files.
113
+ currently reads 96 different types of timed GPS metadata from video files.
113
114
  },
114
115
  VARS => { NO_ID => 1 },
115
116
  GPSLatitude => { PrintConv => 'Image::ExifTool::GPS::ToDMS($self, $val, 1, "N")', RawConv => '$$self{FoundGPSLatitude} = 1; $val' },
@@ -353,6 +354,8 @@ my %insvLimit = (
353
354
  Unknown01 => { Unknown => 1 },
354
355
  Unknown02 => { Unknown => 1 },
355
356
  Unknown03 => { Unknown => 1 },
357
+ M => { Name => 'Unknown_M', Unknown => 1 }, # (from LIGOGPSINFO)
358
+ H => { Name => 'Unknown_H', Unknown => 1 }, # (from LIGOGPSINFO)
356
359
  );
357
360
 
358
361
  # tags found in 'camm' type 0 timed metadata (ref 4)
@@ -911,9 +914,9 @@ sub SetGPSDateTime($$$;$)
911
914
  if ($$et{CreateDateAtEnd}) { # adjust if CreateDate is at end of video
912
915
  return unless $$value{TimeScale} and $$value{Duration};
913
916
  $sampleTime -= $$value{Duration} / $$value{TimeScale};
914
- $et->WarnOnce('Approximating GPSDateTime as CreateDate - Duration + SampleTime', 1);
917
+ $et->Warn('Approximating GPSDateTime as CreateDate - Duration + SampleTime', 1);
915
918
  } else {
916
- $et->WarnOnce('Approximating GPSDateTime as CreateDate + SampleTime', 1);
919
+ $et->Warn('Approximating GPSDateTime as CreateDate + SampleTime', 1);
917
920
  }
918
921
  my $utc = $et->Options('QuickTimeUTC');
919
922
  $utc = $isUTC unless defined $utc; # (allow QuickTimeUTC=0 to override $isUTC default)
@@ -1274,7 +1277,7 @@ sub ProcessSamples($)
1274
1277
  ($startChunk, $samplesPerChunk, $descIdx) = @{shift @$stsc};
1275
1278
  $nextChunk = $$stsc[0][0] if @$stsc;
1276
1279
  }
1277
- @$size < @$start + $samplesPerChunk and $et->WarnOnce('Sample size error'), last;
1280
+ @$size < @$start + $samplesPerChunk and $et->Warn('Sample size error'), last;
1278
1281
  last unless defined $chunkStart and length $chunkStart;
1279
1282
  my $sampleStart = $chunkStart;
1280
1283
  my $chunkSize = 0;
@@ -1302,7 +1305,7 @@ Sample: for ($i=0; ; ) {
1302
1305
  push @chunkSize, $chunkSize;
1303
1306
  ++$iChunk;
1304
1307
  }
1305
- @$start == @$size or $et->WarnOnce('Incorrect sample start/size count'), return;
1308
+ @$start == @$size or $et->Warn('Incorrect sample start/size count'), return;
1306
1309
  # process as chunks if we are only interested in calculating hash
1307
1310
  if ($type eq 'soun' or $type eq 'vide') {
1308
1311
  $start = $stco;
@@ -1332,7 +1335,7 @@ Sample: for ($i=0; ; ) {
1332
1335
  $hdrFmt = ($hdrLen == 4 ? 'N' : $hdrLen == 2 ? 'n' : 'C');
1333
1336
  require Image::ExifTool::H264;
1334
1337
  }
1335
-
1338
+
1336
1339
  # loop through all samples
1337
1340
  for ($i=0; $i<@$start and $i<@$size; ++$i) {
1338
1341
 
@@ -1352,11 +1355,11 @@ Sample: for ($i=0; ; ) {
1352
1355
  }
1353
1356
  }
1354
1357
  # read the sample data
1355
- $raf->Seek($$start[$i], 0) or $et->WarnOnce("Seek error in $type data"), next;
1358
+ $raf->Seek($$start[$i], 0) or $et->Warn("Seek error in $type data"), next;
1356
1359
  my $buff;
1357
1360
  my $n = $raf->Read($buff, $size);
1358
1361
  unless ($n == $size) {
1359
- $et->WarnOnce("Error reading $type data");
1362
+ $et->Warn("Error reading $type data");
1360
1363
  next unless $n;
1361
1364
  $size = $n;
1362
1365
  }
@@ -1560,7 +1563,7 @@ sub ProcessFreeGPS($$$)
1560
1563
  my ($et, $dirInfo, $tagTbl) = @_;
1561
1564
  my $dataPt = $$dirInfo{DataPt};
1562
1565
  my $dirLen = length $$dataPt;
1563
- my ($yr, $mon, $day, $hr, $min, $sec, $stat, $lbl, $ddd);
1566
+ my ($yr, $mon, $day, $hr, $min, $sec, $ss, $stat, $lbl, $ddd, $done);
1564
1567
  my ($lat, $latRef, $lon, $lonRef, $spd, $trk, $alt, @acc, @xtra);
1565
1568
 
1566
1569
  return 0 if $dirLen < 82;
@@ -1699,7 +1702,7 @@ sub ProcessFreeGPS($$$)
1699
1702
  ($sec,$min,$hr,$day,$mon,$yr) = gmtime($time);
1700
1703
  $yr += 1900;
1701
1704
  ++$mon;
1702
- $et->WarnOnce('Converting GPSDateTime to UTC based on local time zone',1);
1705
+ $et->Warn('Converting GPSDateTime to UTC based on local time zone',1);
1703
1706
  }
1704
1707
  $lat = GetFloat($dataPt, 0x2c);
1705
1708
  $lon = GetFloat($dataPt, 0x30);
@@ -1744,36 +1747,69 @@ sub ProcessFreeGPS($$$)
1744
1747
  ($lon = DecryptLucky($ln, $key)) =~ /^\d{1,5}\.\d+$/ or undef($lon), next;
1745
1748
  last;
1746
1749
  }
1747
- $lon or $et->WarnOnce('Unknown encryption for latitude/longitude');
1750
+ $lon or $et->Warn('Unknown encryption for latitude/longitude');
1748
1751
  }
1749
1752
  }
1750
1753
 
1751
- } elsif ($$dataPt =~ /^.{21}\0\0\0A([NS])([EW])/s) {
1754
+ } elsif ($$dataPt =~ /^(.{16}|.{48}|.{80})LIGOGPSINFO\0/s and length($$dataPt) >= length($1) + 0x84) {
1752
1755
 
1753
1756
  $debug and $et->FoundTag(GPSType => 5);
1754
- # also decode 'gpmd' chunk from Kingslim D4 dashcam videos
1755
- # 0000: 0a 00 00 00 0b 00 00 00 07 00 00 00 e5 07 00 00 [................]
1756
- # 0010: 06 00 00 00 03 00 00 00 41 4e 57 31 91 52 83 45 [........ANW1.R.E]
1757
- # 0020: 15 70 fe c5 29 5c c3 41 ae c7 af 42 00 00 d1 be [.p..)\.A...B....]
1758
- # 0030: 00 00 80 3b 00 00 2c 3e 00 00 00 00 00 00 00 00 [...;..,>........]
1759
- # 0040: 00 00 00 00 00 00 00 00 00 00 00 00 26 26 26 26 [............&&&&]
1760
- # 0050: 4c 49 47 4f 47 50 53 49 4e 46 4f 00 00 00 00 05 [LIGOGPSINFO.....]
1761
- # 0060: 01 00 00 00 23 23 23 23 75 00 00 00 c0 22 20 20 [....####u...." ]
1762
- # 0070: 20 f0 12 10 12 21 e5 0e 10 12 2f 90 10 13 01 f2 [ ....!..../.....]
1763
- ($latRef, $lonRef) = ($1, $2);
1764
- ($hr,$min,$sec,$yr,$mon,$day) = unpack("V6", $$dataPt);
1765
- # lat/lon aren't decoded properly, but spd,trk,acc are
1766
- $lat = GetFloat($dataPt, 0x1c);
1767
- $lon = GetFloat($dataPt, 0x20);
1768
- $et->VPrint(0, sprintf("Raw lat/lon = %.9f %.9f\n", $lat, $lon));
1769
- $et->WarnOnce('GPSLatitude/Longitude encryption is not yet known, so these will be wrong');
1770
- $lat = abs $lat;
1771
- $lon = abs $lon;
1772
- $spd = GetFloat($dataPt, 0x24) * $knotsToKph; # (convert knots to km/h)
1773
- $trk = GetFloat($dataPt, 0x28);
1774
- $acc[0] = GetFloat($dataPt, 0x2c);
1775
- $acc[1] = GetFloat($dataPt, 0x30);
1776
- $acc[2] = GetFloat($dataPt, 0x34);
1757
+ my $pos = length $1;
1758
+ # iiway s1 dual dash cam
1759
+ # 0000: 00 00 40 00 66 72 65 65 47 50 53 20 f0 03 00 00 [..@.freeGPS ....]
1760
+ # 0010: 4c 49 47 4f 47 50 53 49 4e 46 4f 00 00 00 00 05 [LIGOGPSINFO.....]
1761
+ # 0020: 0a 00 00 00 23 23 23 23 6a 00 00 00 c0 20 20 20 [....####j.... ]
1762
+ # 0030: 20 f0 12 10 12 22 e1 0e 10 12 2f 90 10 13 02 f2 [ ...."..../.....]
1763
+ # ABASK A8 4K Dashcam (different scaling factor)
1764
+ # 0000: 00 00 40 00 66 72 65 65 47 50 53 20 f0 03 00 00 [..@.freeGPS ....]
1765
+ # 0010: 4c 49 47 4f 47 50 53 49 4e 46 4f 00 00 00 00 05 [LIGOGPSINFO.....]
1766
+ # 0020: 00 00 00 00 23 23 23 23 69 00 00 00 c0 20 20 20 [....####i.... ]
1767
+ # 0030: 20 f0 12 10 12 23 e5 0e 10 12 2f 99 10 11 02 f2 [ ....#..../.....]
1768
+ # XGODY 12" 4K Dashcam
1769
+ # 0000: 00 00 00 a8 66 72 65 65 47 50 53 20 98 00 00 00 [....freeGPS ....]
1770
+ # 0010: 4c 49 47 4f 47 50 53 49 4e 46 4f 00 00 00 00 05 [LIGOGPSINFO.....]
1771
+ # 0020: cd 61 00 00 23 23 23 23 6d 00 00 00 c1 ec 41 20 [.a..####m.....A ]
1772
+ # 0030: 20 f0 12 10 12 24 e5 0e 10 11 2f 92 10 12 00 f6 [ ....$..../.....]
1773
+ # Rexing dashcam V1GW-4K
1774
+ # 0000: 00 00 40 00 66 72 65 65 47 50 53 20 f0 03 00 00 [..@.freeGPS ....]
1775
+ # 0010: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 [................]
1776
+ # 0020: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 [................]
1777
+ # 0030: 4c 49 47 4f 47 50 53 49 4e 46 4f 00 00 00 00 05 [LIGOGPSINFO.....]
1778
+ # 0040: 01 00 00 00 23 23 23 23 73 00 00 00 c0 20 20 20 [....####s.... ]
1779
+ # 0050: 20 f0 12 10 12 23 e5 0e 10 12 2f 95 10 12 01 f3 [ ....#..../.....]
1780
+ # 0060: 16 18 10 26 b4 1a 10 04 f2 6e 18 12 20 f0 0e 11 [...&.....n.. ...]
1781
+ # 0070: 13 22 b3 16 10 01 fb 76 18 10 24 fa 0e 11 10 22 [.".....v..$...."]
1782
+ # Kingslim D4 dashcam
1783
+ # 0000: 0a 00 00 00 0b 00 00 00 07 00 00 00 e5 07 00 00 [................]
1784
+ # 0010: 06 00 00 00 03 00 00 00 41 4e 57 31 91 52 83 45 [........ANW1.R.E]
1785
+ # 0020: 15 70 fe c5 29 5c c3 41 ae c7 af 42 00 00 d1 be [.p..)\.A...B....]
1786
+ # 0030: 00 00 80 3b 00 00 2c 3e 00 00 00 00 00 00 00 00 [...;..,>........]
1787
+ # 0040: 00 00 00 00 00 00 00 00 00 00 00 00 26 26 26 26 [............&&&&]
1788
+ # 0050: 4c 49 47 4f 47 50 53 49 4e 46 4f 00 00 00 00 05 [LIGOGPSINFO.....]
1789
+ # 0060: 01 00 00 00 23 23 23 23 75 00 00 00 c0 22 20 20 [....####u...." ]
1790
+ # 0070: 20 f0 12 10 12 21 e5 0e 10 12 2f 90 10 13 01 f2 [ ....!..../.....]
1791
+ my %dirInfo = ( DataPt => $dataPt, DirStart => $pos, DirName => "LigoGPS_$pos" );
1792
+ # (this is weak, but the only difference I could find between these 2 headers)
1793
+ # (NOTE: ../testpics/gps_video/forum16229.mp4 uses this word for a counter!)
1794
+ $$et{LigoGPSScale} = 3 if $pos == 16 and $$dataPt =~ /^.{12}\xf0\x03\0\0.{16}\0{4}/s;
1795
+ ProcessLigoGPS($et, \%dirInfo, $tagTbl);
1796
+ $done = 1;
1797
+
1798
+ # also... when offset is 0x50 (Kingslim), the GPS also exists in this format:
1799
+ # ($latRef, $lonRef) = ($1, $2);
1800
+ # ($hr,$min,$sec,$yr,$mon,$day) = unpack("V6", $$dataPt);
1801
+ # # lat/lon aren't decoded properly, but spd,trk,acc are
1802
+ # $lat = GetFloat($dataPt, 0x1c);
1803
+ # $lon = GetFloat($dataPt, 0x20);
1804
+ # $et->VPrint(0, sprintf("Raw lat/lon = %.9f %.9f\n", $lat, $lon));
1805
+ # $et->Warn('GPSLatitude/Longitude encryption is not yet known, so these will be wrong');
1806
+ # $lat = abs $lat;
1807
+ # $lon = abs $lon;
1808
+ # $spd = GetFloat($dataPt, 0x24) * $knotsToKph; # (convert knots to km/h)
1809
+ # $trk = GetFloat($dataPt, 0x28);
1810
+ # $acc[0] = GetFloat($dataPt, 0x2c);
1811
+ # $acc[1] = GetFloat($dataPt, 0x30);
1812
+ # $acc[2] = GetFloat($dataPt, 0x34);
1777
1813
 
1778
1814
  } elsif ($$dataPt =~ /^.{60}A\0{3}.{4}([NS])\0{3}.{4}([EW])\0{3}/s) {
1779
1815
 
@@ -1821,14 +1857,14 @@ sub ProcessFreeGPS($$$)
1821
1857
  # 0060: 42 3e 49 49 40 42 45 3c 55 3c 45 47 3e 45 43 41 [B>II@BE<U<EG>ECA]
1822
1858
  # decipher $GPRMC by subtracting 16 from each character value
1823
1859
  $_ = pack 'C*', map { $_>=16 and $_-=16 } unpack('x60C80', $$dataPt);
1824
- unless (/[A-Z]{2}RMC,(\d{2})(\d{2})(\d+(\.\d*)?),A?,(\d*?\d{1,2}\.\d+),([NS]),(\d*?\d{1,2}\.\d+),([EW]),(\d*\.?\d*),(\d*\.?\d*),(\d{2})(\d{2})(\d+)/) {
1825
- SetByteOrder($oldOrder);
1826
- return 0;
1860
+ if (/[A-Z]{2}RMC,(\d{2})(\d{2})(\d+(\.\d*)?),A?,(\d*?\d{1,2}\.\d+),([NS]),(\d*?\d{1,2}\.\d+),([EW]),(\d*\.?\d*),(\d*\.?\d*),(\d{2})(\d{2})(\d+)/) {
1861
+ ($yr,$mon,$day,$hr,$min,$sec,$lat,$latRef,$lon,$lonRef) = ($13,$12,$11,$1,$2,$3,$5,$6,$7,$8);
1862
+ $yr += ($yr >= 70 ? 1900 : 2000);
1863
+ $spd = $9 * $knotsToKph if length $9;
1864
+ $trk = $10 if length $10;
1865
+ } else {
1866
+ $done = 1;
1827
1867
  }
1828
- ($yr,$mon,$day,$hr,$min,$sec,$lat,$latRef,$lon,$lonRef) = ($13,$12,$11,$1,$2,$3,$5,$6,$7,$8);
1829
- $yr += ($yr >= 70 ? 1900 : 2000);
1830
- $spd = $9 * $knotsToKph if length $9;
1831
- $trk = $10 if length $10;
1832
1868
 
1833
1869
  } elsif ($$dataPt =~ /^.{64}[\x01-\x0c]\0{3}[\x01-\x1f]\0{3}A[NS][EW]\0{5}/s) {
1834
1870
 
@@ -1857,7 +1893,7 @@ sub ProcessFreeGPS($$$)
1857
1893
  ($hr,$min,$sec,$yr,$mon,$day,$stat,$latRef,$lonRef) =
1858
1894
  unpack('x48V6a1a1a1x1', $$dataPt);
1859
1895
 
1860
- $et->WarnOnce('GPSLatitude/Longitude encryption is not yet known, so these will be wrong');
1896
+ $et->Warn('GPSLatitude/Longitude encryption is not yet known, so these will be wrong');
1861
1897
  # (see https://exiftool.org/forum/index.php?topic=11320.0)
1862
1898
 
1863
1899
  $spd = GetFloat($dataPt, 0x60);
@@ -1865,7 +1901,7 @@ sub ProcessFreeGPS($$$)
1865
1901
  $lat = GetDouble($dataPt, 0x50); # latitude is here, but encrypted somehow
1866
1902
  $lon = GetDouble($dataPt, 0x58); # longitude is here, but encrypted somehow
1867
1903
  $ddd = 1; # don't convert until we know what the format is
1868
- #my $serialNum = substr($$dataPt, 0x68, 20);
1904
+ #my $serialNum = substr($$dataPt, 0x68, 20); # (confirmed)
1869
1905
 
1870
1906
  } elsif ($$dataPt =~ /^.{12}\xac\0\0\0.{44}(.{72})/s) {
1871
1907
 
@@ -1881,15 +1917,14 @@ sub ProcessFreeGPS($$$)
1881
1917
  # 0070: 43 41 3c 40 42 40 46 42 40 3c 3c 3c 51 3a 47 46 [CA<@B@FB@<<<Q:GF]
1882
1918
  # 0080: 00 2a 36 35 00 00 00 00 00 00 00 00 00 00 00 00 [.*65............]
1883
1919
 
1884
- $et->WarnOnce("Can't yet decrypt EACHPAI timed GPS", 1);
1920
+ $et->Warn("Can't yet decrypt EACHPAI timed GPS", 1);
1885
1921
  # (see https://exiftool.org/forum/index.php?topic=5095.msg61266#msg61266)
1886
- SetByteOrder($oldOrder);
1887
- return 1;
1922
+ $done = 1;
1888
1923
 
1889
- my $time = pack 'C*', map { $_ ^= 0 } unpack 'C*', $1;
1890
- # bytes 7-12 are the timestamp in ASCII HHMMSS after xor-ing with 0x70
1891
- substr($time,7,6) = pack 'C*', map { $_ ^= 0x70 } unpack 'C*', substr($time,7,6);
1892
- # (other values are currently unknown)
1924
+ # my $time = pack 'C*', map { $_ ^= 0 } unpack 'C*', $1;
1925
+ # # bytes 7-12 are the timestamp in ASCII HHMMSS after xor-ing with 0x70
1926
+ # substr($time,7,6) = pack 'C*', map { $_ ^= 0x70 } unpack 'C*', substr($time,7,6);
1927
+ # # (other values are currently unknown)
1893
1928
 
1894
1929
  } elsif ($$dataPt =~ /^.{64}A([NS])([EW])\0/s) {
1895
1930
 
@@ -1905,17 +1940,17 @@ sub ProcessFreeGPS($$$)
1905
1940
  # 0070: 05 00 00 00 7f 00 00 00 07 01 00 00 00 00 00 00 [................]
1906
1941
  ($latRef, $lonRef) = ($1, $2);
1907
1942
  ($yr,$mon,$day,$hr,$min,$sec,@acc) = unpack('x68V6x20V3', $$dataPt);
1908
- unless ($mon>=1 and $mon<=12 and $day>=1 and $day<=31) {
1909
- SetByteOrder($oldOrder);
1910
- return 0;
1943
+ if ($mon>=1 and $mon<=12 and $day>=1 and $day<=31) {
1944
+ # (not sure about acc scaling)
1945
+ @acc = map { SignedInt32 / 1000 } @acc;
1946
+ $lon = GetFloat($dataPt, 0x5c);
1947
+ $lat = GetFloat($dataPt, 0x60);
1948
+ $spd = GetFloat($dataPt, 0x64) * $knotsToKph;
1949
+ $trk = GetFloat($dataPt, 0x68);
1950
+ $alt = GetFloat($dataPt, 0x6c);
1951
+ } else {
1952
+ $done = 1;
1911
1953
  }
1912
- # (not sure about acc scaling)
1913
- @acc = map { SignedInt32 / 1000 } @acc;
1914
- $lon = GetFloat($dataPt, 0x5c);
1915
- $lat = GetFloat($dataPt, 0x60);
1916
- $spd = GetFloat($dataPt, 0x64) * $knotsToKph;
1917
- $trk = GetFloat($dataPt, 0x68);
1918
- $alt = GetFloat($dataPt, 0x6c);
1919
1954
 
1920
1955
  } elsif (substr($$dataPt,0x45,3) eq 'ATC') {
1921
1956
 
@@ -1960,7 +1995,7 @@ ATCRec: for ($recPos = 0x30; $recPos + 52 < $dirLen; $recPos += 52) {
1960
1995
  my $i;
1961
1996
  for ($i=0; $i<@dateMax; ++$i) {
1962
1997
  next if $now[$i] <= $dateMax[$i];
1963
- $et->WarnOnce('Invalid GPS date/time');
1998
+ $et->Warn('Invalid GPS date/time');
1964
1999
  next ATCRec; # ignore this record
1965
2000
  }
1966
2001
  # look for next ATC record in temporal sequence
@@ -2027,8 +2062,7 @@ ATCRec: for ($recPos = 0x30; $recPos + 52 < $dirLen; $recPos += 52) {
2027
2062
  }
2028
2063
  # save position of most recent record (needed when parsing the next freeGPS block)
2029
2064
  $$et{FreeGPS2}{RecentRecPos} = $lastRecPos;
2030
- SetByteOrder($oldOrder);
2031
- return 1;
2065
+ $done = 1;
2032
2066
 
2033
2067
  } elsif ($$dataPt =~ /^.{60}A\0.{10}([NS])\0.{14}([EW])\0/s and $dirLen >= 0x88) {
2034
2068
 
@@ -2065,6 +2099,11 @@ ATCRec: for ($recPos = 0x30; $recPos + 52 < $dirLen; $recPos += 52) {
2065
2099
 
2066
2100
  $debug and $et->FoundTag(GPSType => 13);
2067
2101
  # INNOVV MP4 video (same format as INNOVV TS)
2102
+ # 0000: 00 00 40 00 66 72 65 65 47 50 53 20 f0 03 00 00 [..@.freeGPS ....]
2103
+ # 0010: 41 4e 45 00 e4 56 96 45 86 b1 ca 44 5c 8f e2 40 [ANE..V.E...D\..@]
2104
+ # 0020: 33 33 58 43 c3 00 00 00 30 00 00 00 a0 fe ff ff [33XC....0.......]
2105
+ # 0030: 41 4e 45 00 e3 56 96 45 82 b1 ca 44 5c 8f fa 40 [ANE..V.E...D\..@]
2106
+ # 0040: c3 75 56 43 8c ff ff ff 8c 00 00 00 c3 fd ff ff [.uVC............]
2068
2107
  while ($$dataPt =~ /(A[NS][EW]\0.{28})/sg) {
2069
2108
  my $dat = $1;
2070
2109
  $lat = abs(GetFloat(\$dat, 4)); # (abs just to be safe)
@@ -2080,10 +2119,9 @@ ATCRec: for ($recPos = 0x30; $recPos + 52 < $dirLen; $recPos += 52) {
2080
2119
  $et->HandleTag($tagTbl, GPSTrack => $trk);
2081
2120
  $et->HandleTag($tagTbl, Accelerometer => "@acc");
2082
2121
  }
2083
- SetByteOrder($oldOrder);
2084
- return 1;
2122
+ $done = 1;
2085
2123
 
2086
- } elsif ($$dataPt =~ /^.{24}A([NS])([EW])/s) {
2124
+ } elsif ($$dataPt =~ /^.{20}[\0-\x18][\0-\x3b]{2}[\0-\x09]A([NS])([EW])/s) {
2087
2125
 
2088
2126
  $debug and $et->FoundTag(GPSType => 14);
2089
2127
  # XBHT motorcycle dashcam Model XB702
@@ -2092,12 +2130,11 @@ ATCRec: for ($recPos = 0x30; $recPos + 52 < $dirLen; $recPos += 52) {
2092
2130
  # 0020: 44 3d c5 02 48 6d ff 07 df 03 00 00 6b 00 00 00 [D=..Hm......k...]
2093
2131
  # 0030: 00 00 00 00 00 17 05 11 0d 25 18 01 41 4e 45 64 [.........%..ANEd]
2094
2132
  # 0040: 8b 3f 00 00 30 3d c5 02 50 6d ff 07 df 03 00 00 [.?..0=..Pm......]
2095
- while ($$dataPt =~ /(.{8}A[NS][EW].{25})/sg) {
2133
+ while ($$dataPt =~ /(.{7}[\0-\x09]A[NS][EW].{25})/sg) {
2096
2134
  my $dat = $1;
2097
- my ($yr,$mon,$day,$hr,$min,$sec,$ss,$latRef,$lonRef,$lat,$lon,$spd) =
2135
+ ($yr,$mon,$day,$hr,$min,$sec,$ss,$latRef,$lonRef,$lat,$lon,$spd) =
2098
2136
  unpack('xC7xCCx5VVx4v', $dat);
2099
2137
  $yr += 2000; $lat /= 1e4; $lon /= 1e4;
2100
- $ss = 0 if $ss > 9; # (just in case)
2101
2138
  ConvertLatLon($lat, $lon);
2102
2139
  $$et{DOC_NUM} = ++$$et{DOC_COUNT};
2103
2140
  my $time = sprintf('%.4d:%.2d:%.2d %.2d:%.2d:%.2d.%d',$yr,$mon,$day,$hr,$min,$sec,$ss);
@@ -2106,8 +2143,7 @@ ATCRec: for ($recPos = 0x30; $recPos + 52 < $dirLen; $recPos += 52) {
2106
2143
  $et->HandleTag($tagTbl, GPSLongitude => $lon * ($lonRef eq 'W' ? -1 : 1));
2107
2144
  $et->HandleTag($tagTbl, GPSSpeed => $spd);
2108
2145
  }
2109
- SetByteOrder($oldOrder);
2110
- return 1;
2146
+ $done = 1;
2111
2147
 
2112
2148
  } elsif ($$dataPt =~ /^.{28}A.{11}([NS]).{15}([EW])/s) {
2113
2149
 
@@ -2231,7 +2267,7 @@ ATCRec: for ($recPos = 0x30; $recPos + 52 < $dirLen; $recPos += 52) {
2231
2267
  }
2232
2268
 
2233
2269
  } elsif ($$dataPt =~ m/^.{30}A.{20}VV/) {
2234
-
2270
+
2235
2271
  $debug and $et->FoundTag(GPSType => 19);
2236
2272
  # 70mai A810 dashcam (note: no timestamps in the samples I have)
2237
2273
  # 0000: 00 00 40 00 66 72 65 65 47 50 53 20 ed 01 00 00 [..@.freeGPS ....]
@@ -2295,14 +2331,14 @@ ATCRec: for ($recPos = 0x30; $recPos + 52 < $dirLen; $recPos += 52) {
2295
2331
  $et->HandleTag($tagTbl, GPSTrack => $trk);
2296
2332
  last if $pos += 0x20 > length($$dataPt) - 0x1e;
2297
2333
  }
2298
- SetByteOrder($oldOrder);
2299
- return $$et{DOC_NUM} ? 1 : 0; # return 0 if nothing extracted
2334
+ $done = 1;
2300
2335
  }
2336
+ SetByteOrder($oldOrder);
2337
+ return $$et{DOC_NUM} ? 1 : 0 if $done;
2338
+ return 0 if defined $yr and $mon < 1 or $mon > 12; # quick sanity check
2301
2339
  #
2302
2340
  # save tag values extracted by above code
2303
2341
  #
2304
- SetByteOrder($oldOrder);
2305
- return 0 if defined $yr and $mon < 1 or $mon > 12; # quick sanity check
2306
2342
  FoundSomething($et, $tagTbl, $$dirInfo{SampleTime}, $$dirInfo{SampleDuration});
2307
2343
  $sec = '0' . $sec unless $sec =~ /^\d{2}/; # pad integer part of seconds to 2 digits
2308
2344
  if (defined $yr) {
@@ -2442,8 +2478,8 @@ sub Process_tx3g($$$)
2442
2478
  if ($text =~ /^HOME\(/) {
2443
2479
  # --- sample text from Autel Evo II drone ---
2444
2480
  # HOME(W: 109.318642, N: 40.769371) 2023-09-12 10:28:07
2445
- # GPS(W: 109.339287, N: 40.768574, 2371.76m)
2446
- # HDR ISO:100 SHUTTER:1000 EV:-0.7 F-NUM:1.8
2481
+ # GPS(W: 109.339287, N: 40.768574, 2371.76m)
2482
+ # HDR ISO:100 SHUTTER:1000 EV:-0.7 F-NUM:1.8
2447
2483
  # F.PRY (1.0\xc2\xb0, -3.7\xc2\xb0, -59.0\xc2\xb0), G.PRY (-51.1\xc2\xb0, 0.0\xc2\xb0, -58.9\xc2\xb0)
2448
2484
  my $line;
2449
2485
  foreach $line (split /\x0a/, $text) {
@@ -2520,7 +2556,7 @@ sub Process_mebx($$$)
2520
2556
  Size => $len - 8,
2521
2557
  );
2522
2558
  } else {
2523
- $et->WarnOnce('No key information for mebx ID ' . PrintableTagID($id,1));
2559
+ $et->Warn('No key information for mebx ID ' . PrintableTagID($id,1));
2524
2560
  }
2525
2561
  }
2526
2562
  return 1;
@@ -2643,7 +2679,7 @@ sub Process_gdat($$$)
2643
2679
  {
2644
2680
  my ($et, $dirInfo, $tagTbl) = @_;
2645
2681
  unless ($$et{OPTIONS}{ExtractEmbedded}) {
2646
- $et->WarnOnce('Use the ExtractEmbedded option to extract timed GPSData',3);
2682
+ $et->Warn('Use the ExtractEmbedded option to extract timed GPSData',3);
2647
2683
  return 0;
2648
2684
  }
2649
2685
  my $dataPt = $$dirInfo{DataPt};
@@ -2694,13 +2730,220 @@ sub Process_nbmt($$$)
2694
2730
  delete $$et{NoMoreTextDecoding};
2695
2731
  delete $$et{DOC_NUM};
2696
2732
  } else {
2697
- $et->WarnOnce('Use the ExtractEmbedded option to extract timed GPSData',3);
2733
+ $et->Warn('Use the ExtractEmbedded option to extract timed GPSData',3);
2734
+ }
2735
+ return 1;
2736
+ }
2737
+
2738
+ #------------------------------------------------------------------------------
2739
+ # Un-do LIGOGPS fuzzing
2740
+ # Inputs: 0) fuzzed latitude, 1) fuzzed longitude, 2) scale factor
2741
+ # Returns: 0) latitude, 1) longitude
2742
+ sub UnfuzzLigoGPS($$$)
2743
+ {
2744
+ my ($lat, $lon, $scl) = @_;
2745
+ my $lat2 = int($lat / 10) * 10;
2746
+ my $lon2 = int($lon / 10) * 10;
2747
+ return($lat2 + ($lon - $lon2) * $scl, $lon2 + ($lat - $lat2) * $scl);
2748
+ }
2749
+
2750
+ #------------------------------------------------------------------------------
2751
+ # Decrypt LIGOGPSINFO record (starting with "####")
2752
+ # Inputs: 0) encrypted GPS record incuding 8-byte header
2753
+ # Returns: decrypted record including 4-byte uint32 header, or undef on error
2754
+ sub DecryptLigoGPS($)
2755
+ {
2756
+ my $str = shift;
2757
+ my $num = unpack('x4V',$str);
2758
+ return undef if $num < 4;
2759
+ $num = 0x84 if $num > 0x84; # (be safe)
2760
+ my @in = unpack("x8C$num",$str);
2761
+ my @out;
2762
+ while (@in) {
2763
+ my $b = shift @in;
2764
+ my $steeringBits = $b & 0xe0;
2765
+ if ($steeringBits >= 0xc0) {
2766
+ return undef if @in < 4;
2767
+ push @out, (shift(@in) | $b & 0x01) ^ 0x20,
2768
+ (shift(@in) | $b & 0x02) ^ 0x20,
2769
+ (shift(@in) | $b & 0x0c) ^ 0x20,
2770
+ shift(@in) ^ 0x20 | $b & 0x30;
2771
+ } elsif ($steeringBits >= 0x40) {
2772
+ return undef if @in < 3;
2773
+ if ($steeringBits == 0x40) {
2774
+ push @out, 0x20,
2775
+ (shift(@in) | $b & 0x01) ^ 0x20,
2776
+ (shift(@in) | $b & 0x06) ^ 0x20,
2777
+ (shift(@in) | $b & 0x18) ^ 0x20;
2778
+ } elsif ($steeringBits == 0x60) {
2779
+ push @out, (shift(@in) | $b & 0x03) ^ 0x20,
2780
+ 0x20,
2781
+ (shift(@in) | $b & 0x04) ^ 0x20,
2782
+ (shift(@in) | $b & 0x18) ^ 0x20;
2783
+ } elsif ($steeringBits == 0x80) {
2784
+ push @out, (shift(@in) | $b & 0x03) ^ 0x20,
2785
+ (shift(@in) | $b & 0x0c) ^ 0x20,
2786
+ 0x20,
2787
+ (shift(@in) | $b & 0x10) ^ 0x20;
2788
+ } else {
2789
+ push @out, (shift(@in) | $b & 0x01) ^ 0x20,
2790
+ (shift(@in) | $b & 0x06) ^ 0x20,
2791
+ (shift(@in) | $b & 0x18) ^ 0x20,
2792
+ 0x20;
2793
+ }
2794
+ } elsif ($steeringBits == 0x00) {
2795
+ return undef if @in < 1;
2796
+ push @out, shift(@in) | $b & 0x13;
2797
+ } else {
2798
+ return undef; # (shouldn't happen)
2799
+ }
2800
+ }
2801
+ return pack 'C*', @out;
2802
+ }
2803
+
2804
+ #------------------------------------------------------------------------------
2805
+ # Decipher and parse LIGOGPSINFO record (starting with "####")
2806
+ # Inputs: 0) ExifTool ref, 1) enciphered string, 2) tag table ref
2807
+ # Returns: true if this looked like an enciphered string
2808
+ # Notes: handles contained tags, but may defer handling until full cipher is known
2809
+ sub DecipherLigoGPS($$$)
2810
+ {
2811
+ my ($et, $str, $tagTbl) = @_;
2812
+
2813
+ # (enciphered characters must be in the range 0x30-0x5f ('0' - '_'))
2814
+ $str =~ m[^####.{4}([0-_])[0-_]{3}/[0-_]{2}/[0-_]{2} ..([0-_])..([0-_]).([0-_]) ]s or return undef;
2815
+ return undef unless $2 eq $3; # (colons in time string must be the same)
2816
+
2817
+ my $cipherInfo = $$et{LigoCipher};
2818
+ $cipherInfo or $cipherInfo = $$et{LigoCipher} = { cache => [ ], secs => [ ], two => -1 };
2819
+ my $decipher = $$cipherInfo{decipher};
2820
+ my $cache = $$cipherInfo{cache};
2821
+
2822
+ # determine the cipher code table based on the advancing 1's digit of seconds
2823
+ unless ($decipher) {
2824
+ push @$cache, $str; # cache records until we can decipher them
2825
+ my ($millennium, $colon) = ($1, $2);
2826
+ # determine the Caesar cipher lookup table
2827
+ # (only characters in range 0x30-0x5f are encrypted)
2828
+ my $secs = $$cipherInfo{secs};
2829
+ push @$secs, $4 unless @$secs and $${secs}[-1] eq $4;
2830
+ $$cipherInfo{two} = $#$secs if $4 eq $millennium; # save index of enciphered '2'
2831
+ return 1 if @$secs < 10; # must cache the data until we know all 10 digits
2832
+ my $two = $$cipherInfo{two}; # (index of '2' in the array)
2833
+ my %decipher = ( $colon => ':' ); # (':' is the time separator)
2834
+ foreach (0..9) {
2835
+ my $ch = $$secs[($_ + $two - 2 + 10) % 10];
2836
+ if ($two < 0 or defined $decipher{$ch}) { # (must be a unique code for each digit)
2837
+ @$cipherInfo{'secs','two'} = ([ ], -1); # reset and try again
2838
+ $et->Warn('Hiccup while deciphering LIGOGPSINFO');
2839
+ return 1;
2840
+ }
2841
+ $decipher{$ch} = chr($_ + 0x30);
2842
+ }
2843
+ # also know the lat/lon quadrant from the signs of the coordinates
2844
+ if ($str =~ / ([0-_])$colon(-?).*? ([0-_])$colon(-?)/) {
2845
+ @decipher{$1,$3} = ($2 ? 'S' : 'N', $4 ? 'W' : 'E');
2846
+ }
2847
+ # fill in unknown entries with '?' (only chars 0x30-0x5f are enciphered)
2848
+ defined $decipher{$_} or $decipher{$_} = '?' foreach map(chr, 0x30..0x5f);
2849
+ $decipher = $$cipherInfo{decipher} = \%decipher;
2850
+ $str = shift @$cache; # start deciphering at oldest cache entry
2851
+ }
2852
+
2853
+ # apply reverse Caesar cipher and extract GPS information
2854
+ do {
2855
+ my $pre = substr($str, 4, 4); # save second 4 bytes of header
2856
+ ($str = substr($str,8)) =~ s/\0+$//; # remove 8-byte header and null padding
2857
+ $str =~ s/([0-_])/$$decipher{$1}/g; # decipher
2858
+ if ($$et{OPTIONS}{Verbose} > 1) {
2859
+ $et->VPrint(1, "$$et{INDENT}\(Deciphered: ".unpack('H8',$pre)." $str)\n");
2860
+ }
2861
+ # add back leading 4 bytes (int16u counter plus 2 unknown bytes), and parse
2862
+ # (not fuzzed in my only sample when found in standard 'skip' atom)
2863
+ ParseLigoGPS($et, "$pre$str", $tagTbl, $$et{LigoType} eq 'LigoGPSInfo');
2864
+ } while $str = shift @$cache;
2865
+
2866
+ return 1;
2867
+ }
2868
+
2869
+ #------------------------------------------------------------------------------
2870
+ # Parse decrypted/deciphered (but not defuzzed) LIGOGPSINFO record
2871
+ # (record starts with 4-byte int32u counter followed by date/time, etc)
2872
+ # Inputs: 0) ExifTool ref, 1) GPS string, 2) tag table ref, 3) not fuzzed
2873
+ # Returns: nothing
2874
+ sub ParseLigoGPS($$$;$)
2875
+ {
2876
+ my ($et, $str, $tagTbl, $noFuzz) = @_;
2877
+
2878
+ # example string input
2879
+ # "....2022/09/19 12:45:24 N:31.285065 W:124.759483 46.93 km/h x:-0.000 y:-0.000 z:-0.000"
2880
+ unless ($str=~ /^.{4}(\S+ \S+)\s+([NS?]):(-?)([.\d]+)\s+([EW?]):(-?)([\.\d]+)\s+([.\d]+)/s) {
2881
+ $et->Warn('LIGOGPSINFO format error');
2882
+ return;
2883
+ }
2884
+ my ($time,$latRef,$latNeg,$lat,$lonRef,$lonNeg,$lon,$spd) = ($1,$2,$3,$4,$5,$6,$7,$8);
2885
+ my %gpsScl = ( 1 => 1.524855137, 2 => 1.456027985, 3 => 1.15368 );
2886
+ my $spdScl = $noFuzz ? $knotsToKph : 1.85407333;
2887
+ $$et{DOC_NUM} = ++$$et{DOC_COUNT};
2888
+ $time =~ tr(/)(:);
2889
+ # convert from DDMM.MMMMMM to DD.DDDDDD if necessary
2890
+ # (speed wasn't scaled in my 1 sample with this format)
2891
+ $lat =~ /^\d{3}/ and ConvertLatLon($lat,$lon), $spdScl = 1;
2892
+ unless ($noFuzz) { # unfuzz the coordinates if necessary
2893
+ my $scl = $$et{OPTIONS}{LigoGPSScale} || $$et{LigoGPSScale} || 1;
2894
+ $scl = $gpsScl{$scl} if $gpsScl{$scl};
2895
+ ($lat, $lon) = UnfuzzLigoGPS($lat, $lon, $scl);
2896
+ }
2897
+ # a final sanity check
2898
+ ($lat > 90 or $lon > 180) and $et->Warn('LIGOGPSINFO coordinates out of range'), return;
2899
+ $$et{SET_GROUP1} = 'LIGO';
2900
+ $et->HandleTag($tagTbl, 'GPSDateTime', $time);
2901
+ # (ignore N/S/E/W if coordinate is signed)
2902
+ $et->HandleTag($tagTbl, 'GPSLatitude', $lat * (($latNeg or $latRef eq 'S') ? -1 : 1));
2903
+ $et->HandleTag($tagTbl, 'GPSLongitude', $lon * (($lonNeg or $lonRef eq 'W') ? -1 : 1));
2904
+ $et->HandleTag($tagTbl, 'GPSSpeed', $spd * $spdScl);
2905
+ $et->HandleTag($tagTbl, 'GPSTrack', $1) if $str =~ /\bA:(\S+)/;
2906
+ # (have a sample where tab is used to separate acc components)
2907
+ $et->HandleTag($tagTbl, 'Accelerometer',"$1 $2 $3") if $str =~ /x:(\S+)\sy:(\S+)\sz:(\S+)/;
2908
+ $et->HandleTag($tagTbl, 'M', $1) if $str =~ /\bM:(\S+)/;
2909
+ $et->HandleTag($tagTbl, 'H', $1) if $str =~ /\bH:(\S+)/;
2910
+ delete $$et{SET_GROUP1};
2911
+ }
2912
+
2913
+ #------------------------------------------------------------------------------
2914
+ # Process LIGOGPSINFO data (non-JSON format)
2915
+ # Inputs: 0) ExifTool object ref, 1) dirInfo ref, 2) tag table ref
2916
+ # 3) 1=LIGOGPS lat/lon/spd weren't fuzzed
2917
+ # Returns: 1 on success
2918
+ sub ProcessLigoGPS($$$;$)
2919
+ {
2920
+ my ($et, $dirInfo, $tagTbl, $noFuzz) = @_;
2921
+ my $dataPt = $$dirInfo{DataPt};
2922
+ my $pos = ($$dirInfo{DirStart} || 0) + 0x14;
2923
+ my $cipherInfo = $$et{LigoCipher};
2924
+ return undef if $pos > length $$dataPt;
2925
+ $$et{LigoType} = $$dirInfo{DirName} || 'LigoGPS';
2926
+ push @{$$et{PATH}}, $$et{LigoType} unless $$dirInfo{DirID};
2927
+ # not fuzzed if header is "LIGOGPSINFO\0\0\0\0\x01" (BlueSkySeaDV688)
2928
+ $noFuzz = 1 if substr($$dataPt, $pos-8, 4) eq "\0\0\0\x01";
2929
+ $et->VerboseDir($$et{LigoType});
2930
+ for (; $pos + 0x84 <= length($$dataPt); $pos+=0x84) {
2931
+ my $dat = substr($$dataPt, $pos, 0x84);
2932
+ $dat =~ /^####/ or next; # (have seen blank records filled with zeros, so keep trying)
2933
+ # decipher if we already know the encryption
2934
+ $cipherInfo and $$cipherInfo{decipher} and DecipherLigoGPS($et, $dat, $tagTbl) and next;
2935
+ my $str = DecryptLigoGPS($dat);
2936
+ defined $str or DecipherLigoGPS($et, $dat, $tagTbl), next; # try to decipher
2937
+ $et->VPrint(1, "$$et{INDENT}\(Decrypted: ",unpack('V',$str),' ',substr($str,4),")\n") if $$et{OPTIONS}{Verbose} > 1;
2938
+ ParseLigoGPS($et, $str, $tagTbl, $noFuzz);
2698
2939
  }
2940
+ pop @{$$et{PATH}} unless $$dirInfo{DirID};
2941
+ delete $$et{DOC_NUM};
2699
2942
  return 1;
2700
2943
  }
2701
2944
 
2702
2945
  #------------------------------------------------------------------------------
2703
- # Process LIGOGPS JSON-format GPS from Yada RoadCam Pro 4K BT58189
2946
+ # Process LIGOGPSINFO JSON-format GPS (Yada RoadCam Pro 4K BT58189)
2704
2947
  # Inputs: 0) ExifTool ref, 1) dirInfo ref, 2) tag table ref
2705
2948
  # Returns: 1 on success
2706
2949
  # Sample data (chained 512-byte records starting like this):
@@ -2711,13 +2954,14 @@ sub Process_nbmt($$$)
2711
2954
  # 0040: 22 3a 20 22 32 30 32 33 22 2c 20 22 4d 6f 6e 74 [": "2023", "Mont]
2712
2955
  # 0050: 68 22 3a 20 22 31 32 22 2c 20 22 44 61 79 22 3a [h": "12", "Day":]
2713
2956
  # 0060: 20 22 32 38 22 2c 20 22 73 74 61 74 75 73 22 3a [ "28", "status":]
2714
- sub ProcessLIGO_JSON($$$)
2957
+ sub ProcessLigoJSON($$$)
2715
2958
  {
2716
2959
  my ($et, $dirInfo, $tagTbl) = @_;
2717
2960
  my $dataPt = $$dirInfo{DataPt};
2718
2961
  my $dirLen = $$dirInfo{DirLen};
2719
2962
  require Image::ExifTool::Import;
2720
2963
  $et->VerboseDir('LIGO_JSON', undef, length($$dataPt));
2964
+ $$et{SET_GROUP1} = 'LIGO';
2721
2965
  while ($$dataPt =~ /LIGOGPSINFO (\{.*?\})/g) {
2722
2966
  my $json = $1;
2723
2967
  my %dbase;
@@ -2767,11 +3011,12 @@ sub ProcessLIGO_JSON($$$)
2767
3011
  $et->HandleTag($tagTbl, GPSLongitude2 => $lon);
2768
3012
  }
2769
3013
  unless ($et->Options('ExtractEmbedded')) {
2770
- $et->WarnOnce('Use the ExtractEmbedded option to extract all timed GPS',3);
3014
+ $et->Warn('Use the ExtractEmbedded option to extract all timed GPS',3);
2771
3015
  last;
2772
3016
  }
2773
3017
  }
2774
3018
  delete $$et{DOC_NUM};
3019
+ delete $$et{SET_GROUP1};
2775
3020
  return 1;
2776
3021
  }
2777
3022
 
@@ -2814,7 +3059,7 @@ sub ProcessKenwood($$$)
2814
3059
  }
2815
3060
  $et->HandleTag($tagTbl, Accelerometer => "@acc") if @acc;
2816
3061
  unless ($et->Options('ExtractEmbedded')) {
2817
- $et->WarnOnce('Use the ExtractEmbedded option to extract all timed GPS',3);
3062
+ $et->Warn('Use the ExtractEmbedded option to extract all timed GPS',3);
2818
3063
  last;
2819
3064
  }
2820
3065
  }
@@ -2937,7 +3182,7 @@ sub ProcessKenwoodTrailer($$$)
2937
3182
  $raf->Read($buff, 14) and $buff eq 'CCCCCCCCCCCCCC' or return 0;
2938
3183
  $et->VerboseDir('Kenwood trailer', undef, undef);
2939
3184
  unless ($$et{OPTIONS}{ExtractEmbedded}) {
2940
- $et->WarnOnce('Use the ExtractEmbedded option to extract timed GPSData from Kenwood trailer',3);
3185
+ $et->Warn('Use the ExtractEmbedded option to extract timed GPSData from Kenwood trailer',3);
2941
3186
  return 1;
2942
3187
  }
2943
3188
  while ($raf->Read($buff, 121) and $buff =~ /^GPSDATA--(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})/) {
@@ -3160,7 +3405,7 @@ sub ProcessTTAD($$$)
3160
3405
  $et->HandleTag($tagTbl, "Unknown0$type" => "@a");
3161
3406
  }
3162
3407
  } else {
3163
- $et->WarnOnce("Unknown TTAD record type $type",1);
3408
+ $et->Warn("Unknown TTAD record type $type",1);
3164
3409
  }
3165
3410
  # without -ee, stop after we find types 0,3,5 (ie. bitmask 0x29)
3166
3411
  $eeOpt or ($found & 0x29) != 0x29 or EEWarn($et), last;
@@ -3212,7 +3457,7 @@ sub ProcessInsta360($;$)
3212
3457
  }
3213
3458
  unless ($et->Options('ExtractEmbedded')) {
3214
3459
  # can arrive here when reading Insta360 trailer on JPEG image (INSP file)
3215
- $et->WarnOnce('Use ExtractEmbedded option to extract timed metadata from Insta360 trailer',3);
3460
+ $et->Warn('Use ExtractEmbedded option to extract timed metadata from Insta360 trailer',3);
3216
3461
  return 1;
3217
3462
  }
3218
3463
 
@@ -3409,8 +3654,8 @@ sub ProcessCAMM($$$)
3409
3654
  my $rtnVal = 0;
3410
3655
  while ($pos + 4 < $end) {
3411
3656
  my $type = Get16u($dataPt, $pos + 2);
3412
- my $size = $size{$type} or $et->WarnOnce("Unknown camm record type $type"), last;
3413
- $pos + $size > $end and $et->WarnOnce("Truncated camm record $type"), last;
3657
+ my $size = $size{$type} or $et->Warn("Unknown camm record type $type"), last;
3658
+ $pos + $size > $end and $et->Warn("Truncated camm record $type"), last;
3414
3659
  my $tagTbl = GetTagTable("Image::ExifTool::QuickTime::camm$type");
3415
3660
  $$dirInfo{DirStart} = $pos;
3416
3661
  $$dirInfo{DirLen} = $size;