File Coverage

blib/lib/Image/ExifTool/Ricoh.pm
Criterion Covered Total %
statement 71 168 42.2
branch 28 102 27.4
condition 5 23 21.7
subroutine 6 8 75.0
pod 0 3 0.0
total 110 304 36.1


line stmt bran cond sub pod time code
1             #------------------------------------------------------------------------------
2             # File: Ricoh.pm
3             #
4             # Description: Ricoh EXIF maker notes tags
5             #
6             # Revisions: 03/28/2005 - P. Harvey Created
7             #
8             # References: 1) http://www.ozhiker.com/electronics/pjmt/jpeg_info/ricoh_mn.html
9             # 2) http://homepage3.nifty.com/kamisaka/makernote/makernote_ricoh.htm
10             # 3) Tim Gray private communication (GR)
11             # 4) https://github.com/atotto/ricoh-theta-tools/
12             # 5) https://github.com/ricohapi/theta-api-specs/blob/main/theta-metadata/README.md
13             # IB) Iliah Borg private communication (LibRaw)
14             #------------------------------------------------------------------------------
15              
16             package Image::ExifTool::Ricoh;
17              
18 21     21   4239 use strict;
  21         69  
  21         1073  
19 21     21   2361 use vars qw($VERSION);
  21         50  
  21         1458  
20 21     21   124 use Image::ExifTool qw(:DataAccess :Utils);
  21         45  
  21         6458  
21 21     21   1353 use Image::ExifTool::Exif;
  21         47  
  21         833  
22 21     21   597 use Image::ExifTool::GPS;
  21         50  
  21         92628  
23              
24             $VERSION = '1.38';
25              
26             sub ProcessRicohText($$$);
27             sub ProcessRicohRMETA($$$);
28             sub ProcessRicohRDT($$$);
29              
30             # lens types for Ricoh GXR
31             my %ricohLensIDs = (
32             Notes => q{
33             Lens units available for the GXR, used by the Ricoh Composite LensID tag. Note
34             that unlike lenses for all other makes of cameras, the focal lengths in these
35             model names have already been scaled to include the 35mm crop factor.
36             },
37             # (the exact lens model names used by Ricoh, except for a change in case)
38             'RL1' => 'GR Lens A12 50mm F2.5 Macro',
39             'RL2' => 'Ricoh Lens S10 24-70mm F2.5-4.4 VC',
40             'RL3' => 'Ricoh Lens P10 28-300mm F3.5-5.6 VC',
41             'RL5' => 'GR Lens A12 28mm F2.5',
42             'RL8' => 'Mount A12',
43             'RL6' => 'Ricoh Lens A16 24-85mm F3.5-5.5',
44             );
45              
46             %Image::ExifTool::Ricoh::Main = (
47             GROUPS => { 0 => 'MakerNotes', 2 => 'Camera' },
48             WRITE_PROC => \&Image::ExifTool::Exif::WriteExif,
49             CHECK_PROC => \&Image::ExifTool::Exif::CheckExif,
50             WRITABLE => 1,
51             0x0001 => { Name => 'MakerNoteType', Writable => 'string' },
52             0x0002 => { #PH
53             Name => 'FirmwareVersion',
54             Writable => 'string',
55             # eg. "Rev0113" is firmware version 1.13
56             PrintConv => '$val=~/^Rev(\d+)$/ ? sprintf("%.2f",$1/100) : $val',
57             PrintConvInv => '$val=~/^(\d+)\.(\d+)$/ ? sprintf("Rev%.2d%.2d",$1,$2) : $val',
58             },
59             0x0005 => [ #PH
60             {
61             Condition => '$$valPt =~ /^[-\w ]+$/',
62             Name => 'SerialNumber', # (verified for GXR)
63             Writable => 'undef',
64             Count => 16,
65             Notes => q{
66             the serial number stamped on the camera begins with 2 model-specific letters
67             followed by the last 8 digits of this value. For the GXR, this is the
68             serial number of the lens unit
69             },
70             PrintConv => '$val=~s/^(.*)(.{8})$/($1)$2/; $val',
71             PrintConvInv => '$val=~tr/()//d; $val',
72             },{
73             Name => 'InternalSerialNumber',
74             Writable => 'undef',
75             Count => 16,
76             ValueConv => 'unpack("H*", $val)',
77             ValueConvInv => 'pack("H*", $val)',
78             },
79             ],
80             0x0e00 => {
81             Name => 'PrintIM',
82             Writable => 0,
83             Description => 'Print Image Matching',
84             SubDirectory => { TagTable => 'Image::ExifTool::PrintIM::Main' },
85             },
86             0x1000 => { #3
87             Name => 'RecordingFormat',
88             Writable => 'int16u',
89             PrintConv => {
90             2 => 'JPEG',
91             3 => 'DNG',
92             },
93             },
94             0x1001 => [{
95             Name => 'ImageInfo',
96             Condition => '$format ne "int16u"',
97             SubDirectory => { TagTable => 'Image::ExifTool::Ricoh::ImageInfo' },
98             },{ #3
99             Name => 'ExposureProgram',
100             Writable => 'int16u',
101             Notes => 'GR',
102             PrintConv => {
103             1 => 'Auto',
104             2 => 'Program AE',
105             3 => 'Aperture-priority AE',
106             4 => 'Shutter speed priority AE',
107             5 => 'Shutter/aperture priority AE', # TAv
108             6 => 'Manual',
109             7 => 'Movie', #PH
110             },
111             }],
112             0x1002 => { #3
113             Name => 'DriveMode',
114             Condition => '$format eq "int16u"',
115             Notes => 'valid only for some models',
116             Writable => 'int16u',
117             PrintConv => {
118             0 => 'Single-frame',
119             1 => 'Continuous',
120             8 => 'AF-priority Continuous',
121             },
122             },
123             0x1003 => [{
124             Name => 'Sharpness',
125             Condition => '$format ne "int16u"',
126             Writable => 'int32u',
127             PrintConv => {
128             0 => 'Sharp',
129             1 => 'Normal',
130             2 => 'Soft',
131             },
132             },{ #3
133             Name => 'WhiteBalance',
134             Writable => 'int16u',
135             Notes => 'GR',
136             PrintConv => {
137             0 => 'Auto',
138             1 => 'Multi-P Auto',
139             2 => 'Daylight',
140             3 => 'Cloudy',
141             4 => 'Incandescent 1',
142             5 => 'Incandescent 2',
143             6 => 'Daylight Fluorescent',
144             7 => 'Neutral White Fluorescent',
145             8 => 'Cool White Fluorescent',
146             9 => 'Warm White Fluorescent',
147             10 => 'Manual',
148             11 => 'Kelvin',
149             12 => 'Shade', #IB
150             },
151             }],
152             0x1004 => { #3
153             Name => 'WhiteBalanceFineTune',
154             Condition => '$format eq "int16u"',
155             Format => 'int16s',
156             Writable => 'int16u',
157             Notes => q{
158             2 numbers: amount of adjustment towards Amber and Green. Not valid for all
159             models
160             },
161             },
162             # 0x1005 int16u - 5
163             0x1006 => { #3
164             Name => 'FocusMode',
165             Writable => 'int16u',
166             PrintConv => {
167             1 => 'Manual',
168             2 => 'Multi AF',
169             3 => 'Spot AF',
170             4 => 'Snap',
171             5 => 'Infinity',
172             7 => 'Face Detect', #PH
173             8 => 'Subject Tracking',
174             9 => 'Pinpoint AF',
175             10 => 'Movie', #PH
176             },
177             },
178             0x1007 => { #3
179             Name => 'AutoBracketing',
180             Writable => 'int16u',
181             PrintConv => {
182             0 => 'Off',
183             9 => 'AE',
184             11 => 'WB',
185             16 => 'DR', # (dynamic range)
186             17 => 'Contrast',
187             18 => 'WB2', # (selects two different WB presets besides normal)
188             19 => 'Effect',
189             },
190             },
191             0x1009 => { #3
192             Name => 'MacroMode',
193             Writable => 'int16u',
194             PrintConv => { 0 => 'Off', 1 => 'On' },
195             },
196             0x100a => { #3
197             Name => 'FlashMode',
198             Writable => 'int16u',
199             PrintConv => {
200             0 => 'Off',
201             1 => 'Auto, Fired',
202             2 => 'On',
203             3 => 'Auto, Fired, Red-eye reduction',
204             4 => 'Slow Sync',
205             5 => 'Manual',
206             6 => 'On, Red-eye reduction',
207             7 => 'Synchro, Red-eye reduction',
208             8 => 'Auto, Did not fire',
209             },
210             },
211             0x100b => { #3
212             Name => 'FlashExposureComp',
213             Writable => 'rational64s',
214             PrintConv => '$val ? sprintf("%+.1f",$val) : 0',
215             PrintConvInv => '$val',
216             },
217             0x100c => { #3
218             Name => 'ManualFlashOutput',
219             Writable => 'rational64s',
220             PrintConv => {
221             0 => 'Full',
222             -24 => '1/1.4',
223             -48 => '1/2',
224             -72 => '1/2.8',
225             -96 => '1/4',
226             -120 => '1/5.6',
227             -144 => '1/8',
228             -168 => '1/11',
229             -192 => '1/16',
230             -216 => '1/22',
231             -240 => '1/32',
232             -288 => '1/64',
233             },
234             },
235             0x100d => { #3
236             Name => 'FullPressSnap',
237             Writable => 'int16u',
238             PrintConv => { 0 => 'Off', 1 => 'On' },
239             },
240             0x100e => { #3
241             Name => 'DynamicRangeExpansion',
242             Writable => 'int16u',
243             PrintConv => {
244             0 => 'Off',
245             3 => 'Weak',
246             4 => 'Medium',
247             5 => 'Strong',
248             },
249             },
250             0x100f => { #3
251             Name => 'NoiseReduction',
252             Writable => 'int16u',
253             PrintConv => {
254             0 => 'Off',
255             1 => 'Weak',
256             2 => 'Medium',
257             3 => 'Strong',
258             },
259             },
260             0x1010 => { #3
261             Name => 'ImageEffects',
262             Writable => 'int16u',
263             PrintConv => {
264             0 => 'Standard',
265             1 => 'Vivid',
266             3 => 'Black & White',
267             5 => 'B&W Toning Effect',
268             6 => 'Setting 1',
269             7 => 'Setting 2',
270             9 => 'High-contrast B&W',
271             10 => 'Cross Process',
272             11 => 'Positive Film',
273             12 => 'Bleach Bypass',
274             13 => 'Retro',
275             15 => 'Miniature',
276             17 => 'High Key',
277             },
278             },
279             0x1011 => { #3
280             Name => 'Vignetting',
281             Writable => 'int16u',
282             PrintConv => {
283             0 => 'Off',
284             1 => 'Low',
285             2 => 'Medium',
286             3 => 'High',
287             },
288             },
289             0x1012 => { #PH
290             Name => 'Contrast',
291             Writable => 'int32u',
292             Format => 'int32s', #3 (high-contrast B&W also has -1 and -2 settings)
293             PrintConv => {
294             OTHER => sub { shift },
295             2147483647 => 'MAX', #3 (high-contrast B&W effect MAX setting)
296             },
297             },
298             0x1013 => { Name => 'Saturation', Writable => 'int32u' }, #PH
299             0x1014 => { Name => 'Sharpness', Writable => 'int32u' }, #3
300             0x1015 => { #3
301             Name => 'ToningEffect',
302             Writable => 'int16u',
303             PrintConv => {
304             0 => 'Off',
305             1 => 'Sepia',
306             2 => 'Red',
307             3 => 'Green',
308             4 => 'Blue',
309             5 => 'Purple',
310             6 => 'B&W',
311             7 => 'Color',
312             },
313             },
314             0x1016 => { #3
315             Name => 'HueAdjust',
316             Writable => 'int16u',
317             PrintConv => {
318             0 => 'Off',
319             1 => 'Basic',
320             2 => 'Magenta',
321             3 => 'Yellow',
322             4 => 'Normal',
323             5 => 'Warm',
324             6 => 'Cool',
325             },
326             },
327             0x1017 => { #3
328             Name => 'WideAdapter',
329             Writable => 'int16u',
330             PrintConv => {
331             0 => 'Not Attached',
332             2 => 'Attached', # (21mm)
333             },
334             },
335             0x1018 => { #3
336             Name => 'CropMode',
337             Writable => 'int16u',
338             PrintConv => {
339             0 => 'Off',
340             1 => 'On (35mm)',
341             2 => 'On (47mm)', #IB
342             },
343             },
344             0x1019 => { #3
345             Name => 'NDFilter',
346             Writable => 'int16u',
347             PrintConv => { 0 => 'Off', 1 => 'On' },
348             },
349             0x101a => { Name => 'WBBracketShotNumber', Writable => 'int16u' }, #3
350             # 0x1100 - related to DR correction (ref 3)
351             0x1307 => { Name => 'ColorTempKelvin', Writable => 'int32u' }, #3
352             0x1308 => { Name => 'ColorTemperature', Writable => 'int32u' }, #3
353             0x1500 => { #3
354             Name => 'FocalLength',
355             Writable => 'rational64u',
356             PrintConv => 'sprintf("%.1f mm",$val)',
357             PrintConvInv => '$val=~s/\s*mm$//;$val',
358             },
359             0x1200 => { #3
360             Name => 'AFStatus',
361             Writable => 'int16u',
362             PrintConv => {
363             0 => 'Out of Focus',
364             1 => 'In Focus',
365             },
366             },
367             # 0x1201-0x1204 - related to focus points (ref 3)
368             0x1201 => { #PH (NC)
369             Name => 'AFAreaXPosition1',
370             Writable => 'int32u',
371             Notes => 'manual AF area position in a 1280x864 image',
372             },
373             0x1202 => { Name => 'AFAreaYPosition1', Writable => 'int32u' }, #PH (NC)
374             0x1203 => { #PH (NC)
375             Name => 'AFAreaXPosition',
376             Writable => 'int32u',
377             Notes => 'manual AF area position in the full image',
378             # (coordinates change to correspond with smaller image
379             # when recording reduced-size JPEG)
380             },
381             0x1204 => { Name => 'AFAreaYPosition', Writable => 'int32u' }, #PH (NC)
382             0x1205 => { #3
383             Name => 'AFAreaMode',
384             Writable => 'int16u',
385             PrintConv => {
386             0 => 'Auto',
387             2 => 'Manual',
388             },
389             },
390             0x1601 => { Name => 'SensorWidth', Writable => 'int32u' }, #3
391             0x1602 => { Name => 'SensorHeight', Writable => 'int32u' }, #3
392             0x1603 => { Name => 'CroppedImageWidth', Writable => 'int32u' }, #3
393             0x1604 => { Name => 'CroppedImageHeight', Writable => 'int32u' }, #3
394             # 0x1700 - Composite? (0=normal image, 1=interval composite, 2=multi-exposure composite) (ref 3)
395             # 0x1703 - 0=normal, 1=final composite (ref 3)
396             # 0x1704 - 0=normal, 2=final composite (ref 3)
397             0x2001 => [
398             {
399             Name => 'RicohSubdir',
400             Condition => q{
401             $self->{Model} !~ /^Caplio RR1\b/ and
402             ($format ne 'int32u' or $count != 1)
403             },
404             SubDirectory => {
405             Validate => '$val =~ /^\[Ricoh Camera Info\]/',
406             TagTable => 'Image::ExifTool::Ricoh::Subdir',
407             Start => '$valuePtr + 20',
408             ByteOrder => 'BigEndian',
409             },
410             },
411             {
412             Name => 'RicohSubdirIFD',
413             # the CX6 and GR Digital 4 write an int32u pointer in AVI videos -- doh!
414             Condition => '$self->{Model} !~ /^Caplio RR1\b/',
415             Flags => 'SubIFD',
416             SubDirectory => {
417             TagTable => 'Image::ExifTool::Ricoh::Subdir',
418             Start => '$val + 20', # (skip over "[Ricoh Camera Info]\0" header)
419             ByteOrder => 'BigEndian',
420             },
421             },
422             {
423             Name => 'RicohRR1Subdir',
424             SubDirectory => {
425             Validate => '$val =~ /^\[Ricoh Camera Info\]/',
426             TagTable => 'Image::ExifTool::Ricoh::Subdir',
427             Start => '$valuePtr + 20',
428             ByteOrder => 'BigEndian',
429             # the Caplio RR1 uses a different base address -- doh!
430             Base => '$start-20',
431             },
432             },
433             ],
434             0x4001 => {
435             Name => 'ThetaSubdir',
436             Groups => { 1 => 'MakerNotes' }, # SubIFD needs group 1 set
437             Flags => 'SubIFD',
438             SubDirectory => {
439             TagTable => 'Image::ExifTool::Ricoh::ThetaSubdir',
440             Start => '$val',
441             },
442             },
443             );
444              
445             # Ricoh type 2 maker notes (ref PH)
446             # (similar to Kodak::Type11 and GE::Main)
447             %Image::ExifTool::Ricoh::Type2 = (
448             GROUPS => { 0 => 'MakerNotes', 2 => 'Camera' },
449             NOTES => q{
450             Tags written by models such as the Ricoh HZ15 and the Pentax XG-1. These
451             are not writable due to numerous formatting errors as written by these
452             cameras.
453             },
454             # 0x104 - int32u: 1
455             # 0x200 - int32u[3]: 0 0 0
456             # 0x202 - int16u: 0 (GE Macro?)
457             # 0x203 - int16u: 0,3 (Kodak PictureEffect?)
458             # 0x204 - rational64u: 0/10
459             # 0x205 - rational64u: 150/1
460             # 0x206 - float[6]: (not really float because size should be 2 bytes)
461             0x207 => {
462             Name => 'RicohModel',
463             Writable => 'string',
464             },
465             0x300 => {
466             # brutal. There are lots of errors in the XG-1 maker notes. For the XG-1,
467             # 0x300 has a value of "XG-1Pentax". The "XG-1" part is likely an improperly
468             # stored 0x207 RicohModel, resulting in an erroneous 4-byte offset for this tag
469             Name => 'RicohMake',
470             Writable => 'undef',
471             ValueConv => '$val =~ s/ *$//; $val',
472             },
473             # 0x306 - int16u: 1
474             # 0x500 - int16u: 0,1
475             # 0x501 - int16u: 0
476             # 0x502 - int16u: 0
477             # 0x9c9c - int8u[6]: ?
478             # 0xadad - int8u[20480]: ?
479             );
480              
481             # Ricoh image info (ref 2)
482             %Image::ExifTool::Ricoh::ImageInfo = (
483             GROUPS => { 0 => 'MakerNotes', 2 => 'Image' },
484             PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
485             WRITE_PROC => \&Image::ExifTool::WriteBinaryData,
486             CHECK_PROC => \&Image::ExifTool::CheckBinaryData,
487             WRITABLE => 1,
488             PRIORITY => 0,
489             FORMAT => 'int8u',
490             FIRST_ENTRY => 0,
491             IS_OFFSET => [ 28 ], # tag 28 is 'IsOffset'
492             0 => {
493             Name => 'RicohImageWidth',
494             Format => 'int16u',
495             },
496             2 => {
497             Name => 'RicohImageHeight',
498             Format => 'int16u',
499             },
500             6 => {
501             Name => 'RicohDate',
502             Groups => { 2 => 'Time' },
503             Format => 'int8u[7]',
504             # (what an insane way to encode the date)
505             ValueConv => q{
506             sprintf("%.2x%.2x:%.2x:%.2x %.2x:%.2x:%.2x",
507             split(' ', $val));
508             },
509             ValueConvInv => q{
510             my @vals = ($val =~ /(\d{1,2})/g);
511             push @vals, 0 if @vals < 7;
512             join(' ', map(hex, @vals));
513             },
514             },
515             28 => {
516             Name => 'PreviewImageStart',
517             Format => 'int16u', # ha! (only the lower 16 bits, even if > 0xffff)
518             Flags => 'IsOffset',
519             OffsetPair => 30, # associated byte count tagID
520             DataTag => 'PreviewImage',
521             Protected => 2,
522             WriteGroup => 'MakerNotes',
523             # prevent preview from being written to MakerNotes of DNG images
524             RawConvInv => q{
525             return $val if $$self{FILE_TYPE} eq "JPEG";
526             warn "\n"; # suppress warning
527             return undef;
528             },
529             },
530             30 => {
531             Name => 'PreviewImageLength',
532             Format => 'int16u',
533             OffsetPair => 28, # point to associated offset
534             DataTag => 'PreviewImage',
535             Protected => 2,
536             WriteGroup => 'MakerNotes',
537             RawConvInv => q{
538             return $val if $$self{FILE_TYPE} eq "JPEG";
539             warn "\n"; # suppress warning
540             return undef;
541             },
542             },
543             32 => {
544             Name => 'FlashMode',
545             PrintConv => {
546             0 => 'Off',
547             1 => 'Auto', #PH
548             2 => 'On',
549             },
550             },
551             33 => {
552             Name => 'Macro',
553             PrintConv => { 0 => 'Off', 1 => 'On' },
554             },
555             34 => {
556             Name => 'Sharpness',
557             PrintConv => {
558             0 => 'Sharp',
559             1 => 'Normal',
560             2 => 'Soft',
561             },
562             },
563             38 => {
564             Name => 'WhiteBalance',
565             PrintConv => {
566             0 => 'Auto',
567             1 => 'Daylight',
568             2 => 'Cloudy',
569             3 => 'Tungsten',
570             4 => 'Fluorescent',
571             5 => 'Manual', #PH (GXR)
572             7 => 'Detail',
573             9 => 'Multi-pattern Auto', #PH (GXR)
574             },
575             },
576             39 => {
577             Name => 'ISOSetting',
578             PrintConv => {
579             0 => 'Auto',
580             1 => 64,
581             2 => 100,
582             4 => 200,
583             6 => 400,
584             7 => 800,
585             8 => 1600,
586             9 => 'Auto', #PH (? CX3)
587             10 => 3200, #PH (A16)
588             11 => '100 (Low)', #PH (A16)
589             },
590             },
591             40 => {
592             Name => 'Saturation',
593             PrintConv => {
594             0 => 'High',
595             1 => 'Normal',
596             2 => 'Low',
597             3 => 'B&W',
598             6 => 'Toning Effect', #PH (GXR Sepia,Red,Green,Blue,Purple)
599             9 => 'Vivid', #PH (GXR)
600             10 => 'Natural', #PH (GXR)
601             },
602             },
603             );
604              
605             # Ricoh subdirectory tags (ref PH)
606             # NOTE: this subdir is currently not writable because the offsets would require
607             # special code to handle the funny start location and base offset
608             %Image::ExifTool::Ricoh::Subdir = (
609             GROUPS => { 0 => 'MakerNotes', 2 => 'Camera' },
610             WRITE_PROC => \&Image::ExifTool::Exif::WriteExif,
611             CHECK_PROC => \&Image::ExifTool::Exif::CheckExif,
612             # the significance of the following 2 dates is not known. They are usually
613             # within a month of each other, but I have seen differences of nearly a year.
614             # Sometimes the first is more recent, and sometimes the second.
615             # 0x0003 - int32u[1]
616             0x0004 => { # (NC)
617             Name => 'ManufactureDate1',
618             Groups => { 2 => 'Time' },
619             Writable => 'string',
620             Count => 20,
621             },
622             0x0005 => { # (NC)
623             Name => 'ManufactureDate2',
624             Groups => { 2 => 'Time' },
625             Writable => 'string',
626             Count => 20,
627             },
628             # 0x0006 - undef[16] ?
629             # 0x0007 - int32u[1] ?
630             # 0x000c - int32u[2] 1st number is a counter (file number? shutter count?) - PH
631             # 0x0014 - int8u[338] could contain some data related to face detection? - PH
632             # 0x0015 - int8u[2]: related to noise reduction?
633             0x001a => { #PH
634             Name => 'FaceInfo',
635             SubDirectory => { TagTable => 'Image::ExifTool::Ricoh::FaceInfo' },
636             },
637             0x0029 => {
638             Name => 'FirmwareInfo',
639             SubDirectory => { TagTable => 'Image::ExifTool::Ricoh::FirmwareInfo' },
640             },
641             0x002a => {
642             Name => 'NoiseReduction',
643             # this is the applied value if NR is set to "Auto"
644             Writable => 'int32u',
645             PrintConv => {
646             0 => 'Off',
647             1 => 'Weak',
648             2 => 'Strong',
649             3 => 'Max',
650             },
651             },
652             0x002c => { # (GXR)
653             Name => 'SerialInfo',
654             SubDirectory => { TagTable => 'Image::ExifTool::Ricoh::SerialInfo' },
655             }
656             # 0x000E ProductionNumber? (ref 2) [no. zero for most models - PH]
657             );
658              
659             # Ricoh Theta subdirectory tags - Contains orientation information (ref 4)
660             %Image::ExifTool::Ricoh::ThetaSubdir = (
661             GROUPS => { 0 => 'MakerNotes', 2 => 'Camera' },
662             WRITE_PROC => \&Image::ExifTool::Exif::WriteExif,
663             CHECK_PROC => \&Image::ExifTool::Exif::CheckExif,
664             # 0x0001 - int16u[1] ?
665             # 0x0002 - int16u[1] ?
666             0x0003 => {
667             Name => 'Accelerometer',
668             Writable => 'rational64s',
669             Count => 2,
670             },
671             0x0004 => {
672             Name => 'Compass',
673             Writable => 'rational64u',
674             },
675             # 0x0005 - int16u[1] ?
676             # 0x0006 - int16u[1] ?
677             # 0x0007 - int16u[1] ?
678             # 0x0008 - int16u[1] ?
679             # 0x0009 - int16u[1] ?
680             0x000a => {
681             Name => 'TimeZone',
682             Writable => 'string',
683             },
684             # 0x0101 - int16u[4] ISO (why 4 values?)
685             # 0x0102 - rational64s[2] FNumber (why 2 values?)
686             # 0x0103 - rational64u[2] ExposureTime (why 2 values?)
687             # 0x0104 - string[9] SerialNumber?
688             # 0x0105 - string[9] SerialNumber?
689             );
690              
691             # face detection information (ref PH, CX4)
692             %Image::ExifTool::Ricoh::FaceInfo = (
693             GROUPS => { 0 => 'MakerNotes', 2 => 'Camera' },
694             PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
695             WRITE_PROC => \&Image::ExifTool::WriteBinaryData,
696             CHECK_PROC => \&Image::ExifTool::CheckBinaryData,
697             WRITABLE => 1,
698             FIRST_ENTRY => 0,
699             DATAMEMBER => [ 181 ],
700             0xb5 => { # (should be int16u at 0xb4?)
701             Name => 'FacesDetected',
702             DataMember => 'FacesDetected',
703             RawConv => '$$self{FacesDetected} = $val',
704             },
705             0xb6 => {
706             Name => 'FaceDetectFrameSize',
707             Format => 'int16u[2]',
708             },
709             0xbc => {
710             Name => 'Face1Position',
711             Condition => '$$self{FacesDetected} >= 1',
712             Format => 'int16u[4]',
713             Notes => q{
714             left, top, width and height of detected face in coordinates of
715             FaceDetectFrameSize with increasing Y downwards
716             },
717             },
718             0xc8 => {
719             Name => 'Face2Position',
720             Condition => '$$self{FacesDetected} >= 2',
721             Format => 'int16u[4]',
722             },
723             0xd4 => {
724             Name => 'Face3Position',
725             Condition => '$$self{FacesDetected} >= 3',
726             Format => 'int16u[4]',
727             },
728             0xe0 => {
729             Name => 'Face4Position',
730             Condition => '$$self{FacesDetected} >= 4',
731             Format => 'int16u[4]',
732             },
733             0xec => {
734             Name => 'Face5Position',
735             Condition => '$$self{FacesDetected} >= 5',
736             Format => 'int16u[4]',
737             },
738             0xf8 => {
739             Name => 'Face6Position',
740             Condition => '$$self{FacesDetected} >= 6',
741             Format => 'int16u[4]',
742             },
743             0x104 => {
744             Name => 'Face7Position',
745             Condition => '$$self{FacesDetected} >= 7',
746             Format => 'int16u[4]',
747             },
748             0x110 => {
749             Name => 'Face8Position',
750             Condition => '$$self{FacesDetected} >= 8',
751             Format => 'int16u[4]',
752             },
753             );
754              
755             # firmware version information (ref PH)
756             %Image::ExifTool::Ricoh::FirmwareInfo = (
757             GROUPS => { 0 => 'MakerNotes', 2 => 'Camera' },
758             PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
759             WRITE_PROC => \&Image::ExifTool::WriteBinaryData,
760             CHECK_PROC => \&Image::ExifTool::CheckBinaryData,
761             WRITABLE => 1,
762             0x00 => {
763             Name => 'FirmwareRevision',
764             Format => 'string[12]',
765             },
766             0x0c => {
767             Name => 'FirmwareRevision2',
768             Format => 'string[12]',
769             },
770             );
771              
772             # serial/version number information written by GXR (ref PH)
773             %Image::ExifTool::Ricoh::SerialInfo = (
774             GROUPS => { 0 => 'MakerNotes', 2 => 'Camera' },
775             PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
776             WRITE_PROC => \&Image::ExifTool::WriteBinaryData,
777             CHECK_PROC => \&Image::ExifTool::CheckBinaryData,
778             WRITABLE => 1,
779             NOTES => 'This information is found in images from the GXR.',
780             0 => {
781             Name => 'BodyFirmware', #(NC)
782             Format => 'string[16]',
783             # observed: "RS1 :V00560000" --> FirmwareVersion "Rev0056"
784             # "RS1 :V01020200" --> FirmwareVersion "Rev0102"
785             },
786             16 => {
787             Name => 'BodySerialNumber',
788             Format => 'string[16]',
789             # observed: "SID:00100056" --> "WD00100056" on plate
790             },
791             32 => {
792             Name => 'LensFirmware', #(NC)
793             Format => 'string[16]',
794             # observed: "RL1 :V00560000", "RL1 :V01020200" - A12 50mm F2.5 Macro
795             # "RL2 :V00560000", "RL2 :V01020300" - S10 24-70mm F2.5-4.4 VC
796             # --> used in a Composite tag to determine LensType
797             },
798             48 => {
799             Name => 'LensSerialNumber',
800             Format => 'string[16]',
801             # observed: (S10) "LID:00010024" --> "WF00010024" on plate
802             # (A12) "LID:00010054" --> "WE00010029" on plate??
803             },
804             );
805              
806             # Ricoh text-type maker notes (PH)
807             %Image::ExifTool::Ricoh::Text = (
808             GROUPS => { 0 => 'MakerNotes', 2 => 'Camera' },
809             PROCESS_PROC => \&ProcessRicohText,
810             NOTES => q{
811             Some Ricoh DC and RDC models use a text-based format for their maker notes
812             instead of the IFD format used by the Caplio models. Below is a list of known
813             tags in this information.
814             },
815             Rev => {
816             Name => 'FirmwareVersion',
817             PrintConv => '$val=~/^\d+$/ ? sprintf("%.2f",$val/100) : $val',
818             PrintConvInv => '$val=~/^(\d+)\.(\d+)$/ ? sprintf("%.2d%.2d",$1,$2) : $val',
819             },
820             Rv => {
821             Name => 'FirmwareVersion',
822             PrintConv => '$val=~/^\d+$/ ? sprintf("%.2f",$val/100) : $val',
823             PrintConvInv => '$val=~/^(\d+)\.(\d+)$/ ? sprintf("%.2d%.2d",$1,$2) : $val',
824             },
825             Rg => 'RedGain',
826             Gg => 'GreenGain',
827             Bg => 'BlueGain',
828             );
829              
830             %Image::ExifTool::Ricoh::RMETA = (
831             GROUPS => { 0 => 'APP5', 1 => 'RMETA', 2 => 'Image' },
832             PROCESS_PROC => \&Image::ExifTool::Ricoh::ProcessRicohRMETA,
833             NOTES => q{
834             The Ricoh Caplio Pro G3 has the ability to add custom fields to the APP5
835             "RMETA" segment of JPEG images. While only a few observed tags have been
836             defined below, ExifTool will extract any information found here.
837             },
838             'Sign type' => { Name => 'SignType', PrintConv => {
839             1 => 'Directional',
840             2 => 'Warning',
841             3 => 'Information',
842             } },
843             Location => { PrintConv => {
844             1 => 'Verge',
845             2 => 'Gantry',
846             3 => 'Central reservation',
847             4 => 'Roundabout',
848             } },
849             Lit => { PrintConv => {
850             1 => 'Yes',
851             2 => 'No',
852             } },
853             Condition => { PrintConv => {
854             1 => 'Good',
855             2 => 'Fair',
856             3 => 'Poor',
857             4 => 'Damaged',
858             } },
859             Azimuth => { PrintConv => {
860             1 => 'N',
861             2 => 'NNE',
862             3 => 'NE',
863             4 => 'ENE',
864             5 => 'E',
865             6 => 'ESE',
866             7 => 'SE',
867             8 => 'SSE',
868             9 => 'S',
869             10 => 'SSW',
870             11 => 'SW',
871             12 => 'WSW',
872             13 => 'W',
873             14 => 'WNW',
874             15 => 'NW',
875             16 => 'NNW',
876             } },
877             _audio => {
878             Name => 'SoundFile',
879             Notes => 'audio data recorded in JPEG images by the G700SE',
880             },
881             _barcode => { Name => 'Barcodes', List => 1 },
882             );
883              
884             # information stored in Ricoh AVI images (ref PH)
885             %Image::ExifTool::Ricoh::AVI = (
886             GROUPS => { 0 => 'MakerNotes', 2 => 'Video' },
887             ucmt => {
888             Name => 'Comment',
889             # Ricoh writes a "Unicode" header even when text is ASCII (spaces anyway)
890             ValueConv => '$_=$val; s/^(Unicode\0|ASCII\0\0\0)//; tr/\0//d; s/\s+$//; $_',
891             },
892             mnrt => {
893             Name => 'MakerNoteRicoh',
894             SubDirectory => {
895             TagTable => 'Image::ExifTool::Ricoh::Main',
896             Start => '$valuePtr + 8',
897             ByteOrder => 'BigEndian',
898             Base => '8',
899             },
900             },
901             rdc2 => {
902             Name => 'RicohRDC2',
903             Unknown => 1,
904             ValueConv => 'unpack("H*",$val)',
905             # have seen values like 0a000444 and 00000000 - PH
906             },
907             thum => {
908             Name => 'ThumbnailImage',
909             Groups => { 2 => 'Preview' },
910             Binary => 1,
911             },
912             );
913              
914             # real-time metadata in RDTA atom (ref 5)
915             %Image::ExifTool::Ricoh::RDTA = (
916             PROCESS_PROC => \&ProcessRicohRDT,
917             GROUPS => { 0 => 'MakerNotes', 2 => 'Location' },
918             0 => { Name => 'Accelerometer', Format => 'float[3]' },
919             16 => { Name => 'TimeStamp', Format => 'int64u', ValueConv => '$val * 1e-9' },
920             );
921              
922             # real-time metadata in RDTB atom (ref 5)
923             %Image::ExifTool::Ricoh::RDTB = (
924             PROCESS_PROC => \&ProcessRicohRDT,
925             GROUPS => { 0 => 'MakerNotes', 2 => 'Location' },
926             0 => { Name => 'Gyroscope', Format => 'float[3]', Notes => 'rad/s' },
927             16 => { Name => 'TimeStamp', Format => 'int64u', ValueConv => '$val * 1e-9' },
928             );
929              
930             # real-time metadata in RDTC atom (ref 5)
931             %Image::ExifTool::Ricoh::RDTC = (
932             PROCESS_PROC => \&ProcessRicohRDT,
933             GROUPS => { 0 => 'MakerNotes', 2 => 'Location' },
934             0 => { Name => 'MagneticField', Format => 'float[3]' },
935             16 => { Name => 'TimeStamp', Format => 'int64u', ValueConv => '$val * 1e-9' },
936             );
937              
938             # real-time metadata in RDTG atom (ref 5)
939             %Image::ExifTool::Ricoh::RDTG = (
940             PROCESS_PROC => \&ProcessRicohRDT,
941             GROUPS => { 0 => 'MakerNotes', 2 => 'Video' },
942             0 => { Name => 'TimeStamp', Format => 'int64u', ValueConv => '$val * 1e-9' },
943             100 => { Name => 'FrameNumber', Notes => 'generated internally' },
944             );
945              
946             # real-time metadata in RDTL atom (ref 5)
947             %Image::ExifTool::Ricoh::RDTL = (
948             GROUPS => { 0 => 'MakerNotes', 2 => 'Location' },
949             0 => {
950             Name => 'GPSDateTime',
951             Groups => { 2 => 'Time' },
952             Format => 'double',
953             ValueConv => 'ConvertUnixTime($val*1e-9, 1, 9)', # (NC -- what is the epoch?)
954             PrintConv => '$self->ConvertDateTime($val)',
955             },
956             8 => {
957             Name => 'GPSLatitude',
958             Format => 'double',
959             PrintConv => 'Image::ExifTool::GPS::ToDMS($self, $val, 1)',
960             },
961             16 => {
962             Name => 'GPSLongitude',
963             Format => 'double',
964             PrintConv => 'Image::ExifTool::GPS::ToDMS($self, $val, 1)',
965             },
966             24 => {
967             Name => 'GPSAltitude',
968             Format => 'double',
969             PrintConv => '($val =~ s/^-// ? "$val m Below" : "$val m Above") . " Sea Level"',
970             },
971             );
972              
973             # Ricoh composite tags
974             %Image::ExifTool::Ricoh::Composite = (
975             GROUPS => { 2 => 'Camera' },
976             LensID => {
977             SeparateTable => 'Ricoh LensID',
978             Require => 'Ricoh:LensFirmware',
979             RawConv => '$val[0] ? $val[0] : undef',
980             ValueConv => '$val=~s/\s*:.*//; $val',
981             PrintConv => \%ricohLensIDs,
982             },
983             RicohPitch => {
984             Require => 'Ricoh:Accelerometer',
985             ValueConv => 'my @v = split(" ",$val); $v[1]',
986             },
987             RicohRoll => {
988             Require => 'Ricoh:Accelerometer',
989             ValueConv => 'my @v = split(" ",$val); $v[0] <= 180 ? $v[0] : $v[0] - 360',
990             },
991             );
992              
993             # add our composite tags
994             Image::ExifTool::AddCompositeTags('Image::ExifTool::Ricoh');
995              
996              
997             #------------------------------------------------------------------------------
998             # Process Ricoh RDT* real-time metadata
999             # Inputs: 0) ExifTool ref, 1) dirInfo ref, 2) tag table ref
1000             # Returns: 1 on success, otherwise returns 0 and sets a Warning
1001             sub ProcessRicohRDT($$$)
1002             {
1003 0     0 0 0 my ($et, $dirInfo, $tagTablePtr) = @_;
1004 0         0 my $dataPt = $$dirInfo{DataPt};
1005 0         0 my $dirLen = $$dirInfo{DirLen};
1006 0         0 my $dirName = $$dirInfo{DirName};
1007 0         0 my ($i, $rdtg);
1008 0 0       0 return 0 if $dirLen < 16;
1009 0         0 my $ee = $et->Options('ExtractEmbedded');
1010 0 0       0 unless ($ee) {
1011 0         0 $et->Warn('Use ExtractEmbedded option to read Ricoh real-time metadata',3);
1012 0         0 return 1;
1013             }
1014 0         0 my $endian = substr($$dataPt, 8, 2);
1015 0 0       0 SetByteOrder($endian eq "\x23\x01" ? 'II' : 'MM');
1016 0         0 my $count = Get32u($dataPt, 0);
1017 0         0 my $len = Get16u($dataPt, 6);
1018 0 0       0 if ($dirName eq 'RicohRDTG') {
1019 0 0       0 if ($ee < 2) {
1020 0         0 $et->Warn('Set ExtractEmbedded option to 2 or higher to extract frame timestamps',3);
1021 0         0 return 1;
1022             }
1023 0         0 $rdtg = 0;
1024 0 0       0 $et->Warn('Unexpected RDTG record length') if $len > 8;
1025             }
1026 0 0       0 if ($count * $len + 16 > $dirLen) {
1027 0         0 $et->Warn("Truncated $dirName data");
1028 0         0 $count = int(($dirLen - 16) / $len);
1029             }
1030 0         0 $et->VerboseDir($dirName);
1031 0         0 $$dirInfo{DirStart} = 16;
1032 0         0 $$dirInfo{DirLen} = $len;
1033 0         0 for ($i=0; $i<$count; ++$i) {;
1034 0         0 $$et{DOC_NUM} = ++$$et{DOC_COUNT};
1035 0 0       0 $et->HandleTag($tagTablePtr, 100, $rdtg++) if defined $rdtg;
1036 0         0 $et->ProcessBinaryData($dirInfo, $tagTablePtr);
1037 0         0 $$dirInfo{DirStart} += $len;
1038             }
1039 0         0 delete $$et{DOC_NUM};
1040 0         0 return 1;
1041             }
1042              
1043             #------------------------------------------------------------------------------
1044             # Process Ricoh text-based maker notes
1045             # Inputs: 0) ExifTool object reference
1046             # 1) Reference to directory information hash
1047             # 2) Pointer to tag table for this directory
1048             # Returns: 1 on success, otherwise returns 0 and sets a Warning
1049             sub ProcessRicohText($$$)
1050             {
1051 0     0 0 0 my ($et, $dirInfo, $tagTablePtr) = @_;
1052 0         0 my $dataPt = $$dirInfo{DataPt};
1053 0         0 my $dataLen = $$dirInfo{DataLen};
1054 0   0     0 my $dirStart = $$dirInfo{DirStart} || 0;
1055 0   0     0 my $dirLen = $$dirInfo{DirLen} || $dataLen - $dirStart;
1056 0         0 my $verbose = $et->Options('Verbose');
1057              
1058 0         0 my $data = substr($$dataPt, $dirStart, $dirLen);
1059 0 0       0 return 1 if $data =~ /^\0/; # blank Ricoh maker notes
1060 0         0 $et->VerboseDir('RicohText', undef, $dirLen);
1061             # validate text maker notes
1062 0 0       0 unless ($data =~ /^(Rev|Rv)/) {
1063 0         0 $et->Warn('Bad Ricoh maker notes');
1064 0         0 return 0;
1065             }
1066 0         0 while ($data =~ m/([A-Z][a-z]{1,2})([0-9A-F]+);/sg) {
1067 0         0 my $tag = $1;
1068 0         0 my $val = $2;
1069 0         0 my $tagInfo = $et->GetTagInfo($tagTablePtr, $tag);
1070 0 0       0 if ($verbose) {
1071 0         0 $et->VerboseInfo($tag, $tagInfo,
1072             Table => $tagTablePtr,
1073             Value => $val,
1074             );
1075             }
1076 0 0       0 unless ($tagInfo) {
1077 0 0       0 next unless $$et{OPTIONS}{Unknown};
1078 0         0 $tagInfo = {
1079             Name => "Ricoh_Text_$tag",
1080             Unknown => 1,
1081             PrintConv => \&Image::ExifTool::LimitLongValues,
1082             };
1083             # add tag information to table
1084 0         0 AddTagToTable($tagTablePtr, $tag, $tagInfo);
1085             }
1086 0         0 $et->FoundTag($tagInfo, $val);
1087             }
1088 0         0 return 1;
1089             }
1090              
1091             #------------------------------------------------------------------------------
1092             # Process Ricoh APP5 RMETA information
1093             # Inputs: 0) ExifTool ref, 1) dirInfo ref, 2) tag table ref
1094             # Returns: 1 on success, otherwise returns 0 and sets a Warning
1095             sub ProcessRicohRMETA($$$)
1096             {
1097 20     20 0 83 my ($et, $dirInfo, $tagTablePtr) = @_;
1098 20         57 my $dataPt = $$dirInfo{DataPt};
1099 20         49 my $dirStart = $$dirInfo{DirStart};
1100 20         47 my $dataLen = length($$dataPt);
1101 20         45 my $dirLen = $dataLen - $dirStart;
1102 20         100 my $verbose = $et->Options('Verbose');
1103              
1104 20 50       72 $et->VerboseDir('Ricoh RMETA') if $verbose;
1105 20 50       74 $dirLen < 20 and $et->Warn('Truncated Ricoh RMETA data', 1), return 0;
1106 20         66 my $byteOrder = substr($$dataPt, $dirStart, 2);
1107 20 50       71 $byteOrder = GetByteOrder() if $byteOrder eq "\0\0"; # (same order as container)
1108 20 50       82 SetByteOrder($byteOrder) or $et->Warn('Bad Ricoh RMETA data', 1), return 0;
1109             # get the RMETA segment number
1110 20         81 my $rmetaNum = Get16u($dataPt, $dirStart+4);
1111 20 50       82 if ($rmetaNum != 0) {
1112             # not sure how to recognize audio, so do it by checking for "RIFF" header
1113             # and assume all subsequent RMETA segments are part of the audio data
1114             # (but it looks like the int16u at $dirStart+6 is the next block number
1115             # if the data is continued, or 0 for the last block)
1116 0 0       0 $dirLen < 14 and $et->Warn('Short Ricoh RMETA block', 1), return 0;
1117 0 0       0 if ($$dataPt =~ /^.{20}BARCODE/s) {
    0          
1118 0         0 my $val = substr($$dataPt, 20);
1119 0         0 $val =~ s/\0.*//s;
1120 0         0 $val =~ s/^BARCODE\w+,\d{2},//;
1121 0         0 my @codes;
1122 0         0 for (;;) {
1123 0 0 0     0 $val =~ s/(\d+),// and length $val >= $1 or last;
1124 0         0 push @codes, substr($val, 0, $1);
1125 0 0       0 last unless length $val > $1;
1126 0         0 $val = substr($val, $1+1);
1127             }
1128 0 0       0 $et->HandleTag($tagTablePtr, '_barcode', \@codes) if @codes;
1129 0         0 return 1;
1130             } elsif ($$dataPt =~ /^.{18}ASCII/s) {
1131             # (ignore barcode tag names for now)
1132 0         0 return 1;
1133             }
1134 0         0 my $audioLen = Get16u($dataPt, $dirStart+12);
1135 0 0       0 $audioLen + 14 > $dirLen and $et->Warn('Truncated Ricoh RMETA audio data', 1), return 0;
1136 0         0 my $buff = substr($$dataPt, $dirStart + 14, $audioLen);
1137 0 0 0     0 if ($audioLen >= 4 and substr($buff, 0, 4) eq 'RIFF') {
    0          
1138 0         0 $et->HandleTag($tagTablePtr, '_audio', \$buff);
1139             } elsif ($$et{VALUE}{SoundFile}) {
1140 0         0 ${$$et{VALUE}{SoundFile}} .= $buff;
  0         0  
1141             } else {
1142 0         0 $et->Warn('Unknown Ricoh RMETA type', 1);
1143 0         0 return 0;
1144             }
1145 0         0 return 1;
1146             }
1147             # decode standard RMETA tag directory
1148 20         66 my (@tags, @vals, @nums, $valPos, $numPos);
1149 20         74 my $pos = $dirStart + Get16u($dataPt, $dirStart+8);
1150 20         85 my $numEntries = Get16u($dataPt, $pos);
1151 20 50       78 $numEntries > 100 and $et->Warn('Bad RMETA entry count'), return 0;
1152 20         50 $pos += 10; # start of first RMETA section
1153             # loop through RMETA sections
1154 20         82 while ($pos <= $dataLen - 4) {
1155 80         167 my $type = Get16u($dataPt, $pos);
1156 80         177 my $size = Get16u($dataPt, $pos + 2);
1157 80 50       179 last unless $size;
1158 80         128 $pos += 4;
1159 80         116 $size -= 2;
1160 80 50 33     301 if ($size < 0 or $pos + $size > $dataLen) {
1161 0         0 $et->Warn('Corrupted Ricoh RMETA data', 1);
1162 0         0 last;
1163             }
1164 80         195 my $dat = substr($$dataPt, $pos, $size);
1165 80 50       166 if ($verbose) {
1166 0         0 $et->VPrint(2, "$$et{INDENT}RMETA section type=$type size=$size\n");
1167 0         0 $et->VerboseDump(\$dat, Addr => $$dirInfo{DataPos} + $pos);
1168             }
1169 80 100 66     402 if ($type == 1) { # section 1: tag names
    100          
    100          
    50          
1170             # save the tag names
1171 20         156 @tags = split /\0/, $dat, $numEntries+1;
1172             } elsif ($type == 2 || $type == 18) { # section 2/18: string values (G800 uses type 18)
1173             # save the tag values (assume "ASCII\0" encoding since others never seen)
1174 20         97 @vals = split /\0/, $dat, $numEntries+1;
1175 20         42 $valPos = $pos; # save position of first string value
1176             } elsif ($type == 3) { # section 3: numerical values
1177 20 50       74 if ($size < $numEntries * 2) {
1178 0         0 $et->Warn('Truncated RMETA section 3');
1179             } else {
1180             # save the numerical tag values
1181             # (0=empty, 0xffff=text input, otherwise menu item number)
1182 20 50       145 @nums = unpack(($byteOrder eq 'MM' ? 'n' : 'v').$numEntries, $dat);
1183 20         138 $numPos = $pos; # save position of numerical values
1184             }
1185             } elsif ($type != 16) {
1186 0         0 $et->Warn("Unrecognized RMETA section (type $type, len $size)");
1187             }
1188 80         225 $pos += $size;
1189             }
1190 20 50 33     81 return 1 unless @tags or @vals;
1191 20 50       61 $valPos or $valPos = 0; # (just in case there was no value section)
1192             # find next tag in null-delimited list
1193             # unpack numerical values from block of int16u values
1194 20         47 my ($i, $name);
1195 20         77 for ($i=0; $i<$numEntries; ++$i) {
1196 100         205 my $tag = $tags[$i];
1197 100         169 my $val = $vals[$i];
1198 100 50       235 $val = '' unless defined $val;
1199 100 50 33     398 unless (defined $tag and length $tag) {
1200 0 0       0 length $val or ++$valPos, next; # (skip empty entries)
1201 0         0 $tag = '';
1202             }
1203 100         548 ($name = $tag) =~ s/\b([a-z])/\U$1/gs; # capitalize all words
1204 100         271 $name =~ s/ (\w)/\U$1/g; # remove special characters
1205 100 50       242 $name = 'RMETA_Unknown' unless length($name);
1206 100         213 my $num = $nums[$i];
1207 100         304 my $tagInfo = $et->GetTagInfo($tagTablePtr, $tag);
1208 100 50       221 if ($tagInfo) {
1209             # make sure print conversion is defined
1210 100 50       299 $$tagInfo{PrintConv} = { } unless ref $$tagInfo{PrintConv} eq 'HASH';
1211             } else {
1212             # create tagInfo hash
1213 0         0 $tagInfo = { Name => $name, PrintConv => { } };
1214 0         0 AddTagToTable($tagTablePtr, $tag, $tagInfo);
1215             }
1216             # use string value directly if no numerical value
1217 100 50       223 $num = $val unless defined $num;
1218             # add conversion for this value (replacing any existing entry)
1219 100 50       380 $tagInfo->{PrintConv}->{$num} = length $val ? $val : $num;
1220 100 50       254 if ($verbose) {
1221 0         0 my %datParms;
1222 0 0       0 if (length $val) {
    0          
1223 0         0 %datParms = ( Start => $valPos, Size => length($val), Format => 'string' );
1224             } elsif ($numPos) {
1225 0         0 %datParms = ( Start => $numPos + $i * 2, Size => 2, Format => 'int16u' );
1226             }
1227 0 0       0 %datParms and $datParms{DataPt} = $dataPt, $datParms{DataPos} = $$dirInfo{DataPos};
1228 0         0 $et->VerboseInfo($tag, $tagInfo, Table=>$tagTablePtr, Value=>$num, %datParms);
1229             }
1230 100         383 $et->FoundTag($tagInfo, $num);
1231 100         308 $valPos += length($val) + 1;
1232             }
1233 20         135 return 1;
1234             }
1235              
1236             1; # end
1237              
1238             __END__