source: extensions/AMetaData/JpegMetaData/Readers/NikonReader.class.php @ 4931

Last change on this file since 4931 was 4931, checked in by grum, 14 years ago

Implement Nikon camera's maker note ; add some Xmp tag ; fix some bugs

  • Property svn:executable set to *
File size: 20.8 KB
Line 
1<?php
2/*
3 * --:: JPEG MetaDatas ::-------------------------------------------------------
4 *
5 *  Author    : Grum
6 *   email    : grum at piwigo.org
7 *   website  : http://photos.grum.fr
8 *
9 *   << May the Little SpaceFrog be with you ! >>
10 *
11 *
12 * +-----------------------------------------------------------------------+
13 * | JpegMetaData - a PHP based Jpeg Metadata manager                      |
14 * +-----------------------------------------------------------------------+
15 * | Copyright(C) 2010  Grum - http://www.grum.fr                          |
16 * +-----------------------------------------------------------------------+
17 * | This program is free software; you can redistribute it and/or modify  |
18 * | it under the terms of the GNU General Public License as published by  |
19 * | the Free Software Foundation                                          |
20 * |                                                                       |
21 * | This program is distributed in the hope that it will be useful, but   |
22 * | WITHOUT ANY WARRANTY; without even the implied warranty of            |
23 * | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU      |
24 * | General Public License for more details.                              |
25 * |                                                                       |
26 * | You should have received a copy of the GNU General Public License     |
27 * | along with this program; if not, write to the Free Software           |
28 * | Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, |
29 * | USA.                                                                  |
30 * +-----------------------------------------------------------------------+
31 *
32 *
33 * -----------------------------------------------------------------------------
34 *
35 * The NikonReader class is the dedicated to read the specific Nikon tags
36 *
37 * ====> See MakerNotesReader.class.php to know more about the structure <======
38 *
39 * -----------------------------------------------------------------------------
40 *
41 * .. Notes ..
42 *
43 *
44 * ****             All known tags are not implemented !!                   ****
45 *
46 *
47 * The NikonReader class is derived from the MakerNotesReader class.
48 *
49 * =====> See MakerNotesReader.class.php to know more about common methods <====
50 *
51 * -----------------------------------------------------------------------------
52 */
53
54
55
56  require_once(JPEG_METADATA_DIR."TagDefinitions/NikonTags.class.php");
57  require_once(JPEG_METADATA_DIR."Readers/MakerNotesReader.class.php");
58
59  class NikonReader extends MakerNotesReader
60  {
61    /* these 2 specific metadata are used to decrypt some information, like the
62     * data lens
63     */
64    protected $serialNumber="";
65    protected $shutterCount=-1;
66
67    protected $toDecode=Array();
68
69    /**
70     * The constructor needs, like the ancestor, the datas to be parsed
71     *
72     * Some datas are offset on extra data, and this offset can be (some time)
73     * absolute inside the IFD, or relative. So, the offset of the IFD structure
74     * is needed
75     *
76     * The byte order can be different from the TIFF byte order !
77     *
78     * The constructor need the maker signature (see the MakerNotesSignatures
79     * class for a list of known signatures)
80     *
81     * @param String $data
82     * @param ULong $offset : offset of IFD block in the jpeg file
83     * @param String $byteOrder
84     * @param String $makerSignature :
85     */
86    function __construct($data, $offset, $byteOrder, $makerSignature)
87    {
88      $this->maker = MAKER_NIKON;
89      switch($makerSignature)
90      {
91        case MakerNotesSignatures::Nikon2Header:
92          $this->header = MakerNotesSignatures::Nikon2Header;
93          $this->headerSize = MakerNotesSignatures::Nikon2HeaderSize;
94          break;
95        case MakerNotesSignatures::Nikon3Header:
96          $this->header = MakerNotesSignatures::Nikon3Header;
97          $this->headerSize = MakerNotesSignatures::Nikon3HeaderSize;
98          break;
99      }
100
101      // offset inside Nikon maker note are relative
102      $offset=-10;
103
104      parent::__construct($data, $offset, $byteOrder);
105
106      $this->processEncryptedTags();
107    }
108
109    function __destruct()
110    {
111      parent::__destruct();
112    }
113
114    /**
115     * initialize the definition for Pentax exif tags
116     */
117    protected function initializeTagDef()
118    {
119      $this->tagDef = new NikonTags();
120    }
121
122    /**
123     * skip the IFD header
124     */
125    protected function skipHeader($headerSize=0)
126    {
127      parent::skipHeader($headerSize);
128      if($this->header == MakerNotesSignatures::Nikon3Header)
129      {
130        /*
131         * the nikon3header is made of 7 char, and then 3 next char ?????
132         */
133        $header=$this->data->readASCII(3);
134
135
136        $header=$this->data->readASCII(2);
137        /*
138         * The Nikon3Header is formatted as a TIFF header, but the class is not
139         * derived from TiffReader, because we can think there is no more than
140         * one IFD in the maker notes.
141         * By this way, it's easier to skip the header and start reading the
142         * entries.
143         *
144         * begins wih "II" or "MM" (indicate the byte order)
145         * next value is an USHORT, must equals 0x2a
146         *
147         * all data have to be read with the byte order defined in header
148         */
149        if($header=="II" or $header="MM")
150        {
151          $this->byteOrder=$header;
152          $this->data->setByteOrder($this->byteOrder);
153
154          $header=$this->data->readUShort();
155          if($header==0x002a)
156          {
157            $this->isValid=true;
158            $header=$this->data->readULong();
159          }
160        }
161      }
162    }
163
164    /**
165     * encrypted values are processed in one shot when all the tags are processed
166     * with this method, we are sure the "SerialNumber" (0x001d) and the
167     * "ShutterCount" (0x00a7) tags are initialized
168     */
169    protected function processEncryptedTags()
170    {
171      foreach($this->toDecode as $key=>$val)
172      {
173        $tagIndex=$this->getTagIndexById($key);
174        if($tagIndex>-1)
175        {
176          $this->entries[$tagIndex]->getTag()->setLabel($this->processSpecialTag($key, $val[0], $val[1]));
177          //echo sprintf("%04x * %s => %s<br>",$key,ConvertData::toHexDump($val[0], ByteType::ASCII),$this->processSpecialTag($key, $val[0], $val[1]));
178        }
179      }
180    }
181
182    /**
183     * this function do the interpretation of specials tags
184     *
185     * the function return the interpreted value for the tag
186     *
187     * @param $tagId             : the id of the tag
188     * @param $values            : 'raw' value to be interpreted
189     * @param UByte $type        : if needed (for IFD structure) the type of data
190     * @param ULong $valueOffset : if needed, the offset of data in the jpeg file
191     * @return String or Array or DateTime or Integer or Float...
192     */
193    protected function processSpecialTag($tagId, $values, $type, $valuesOffset=0)
194    {
195      switch($tagId)
196      {
197        case 0x0001: // "MakerNoteVersion"
198          // 0x30323030 => String "0200" => 2.00
199          $major=substr($values,0,2);
200          if(substr($values,0,1)=="0")
201          {
202            $major=substr($values,1,1);
203          }
204          else
205          {
206            $major=substr($values,0,2);
207          }
208          $minor=substr($values,2);
209          $returned=sprintf("%s.%s", $major, $minor);
210          break;
211        case 0x001d: // "SerialNumber"
212          $returned=ConvertData::toStrings($values);
213          $this->serialNumber=$returned;
214          break;
215        case 0x0003: // "ColorMode"
216        case 0x0004: // "Quality"
217        case 0x0005: // "WhiteBalance"
218        case 0x0006: // "Sharpness"
219        case 0x0007: // "FocusMode"
220        case 0x0008: // "FlashSetting"
221        case 0x0009: // "FlashType"
222        case 0x000f: // "ISOSelection"
223        case 0x0080: // "ImageAdjustment"
224        case 0x0081: // "ToneComp"
225        case 0x0082: // "AuxiliaryLens"
226        case 0x008D: // "ColorHue"
227        case 0x008f: // "SceneMode"
228        case 0x0090: // "LightSource"
229        case 0x0095: // "NoiseReduction"
230        case 0x009c: // "SceneAssist"
231        case 0x00a0: // "SerialNumber"
232        case 0x00a9: // "ImageOptimization"
233        case 0x00aa: // "Saturation"
234        case 0x00ab: // "VariProgram"
235        case 0x0e09: // "NikonCaptureVersion"
236          /*
237           * null terminated strings
238           */
239          $returned=ConvertData::toStrings($values);
240          break;
241        case 0x092: // "HueAdjustment"
242        case 0x00a2: // "ImageDataSize"
243          $returned=$values;
244          break;
245        case 0x0a7: // "ShutterCount"
246          $this->shutterCount=$values;
247          $returned=$values;
248          break;
249        case 0x001f: // "VRInfo"
250          $returned=(ord($values{4})==1)?"On":"Off";
251          break;
252        case 0x0083: // "LensType"
253          $tag=$this->tagDef->getTagById(0x0083);
254
255          $returned=Array();
256          if(($values & 0x0001) == 0x0001)
257            $returned[]=$tag['tagValues.specialValues'][0x0001];
258
259          if(($values & 0x0002) == 0x0002)
260            $returned[]=$tag['tagValues.specialValues'][0x0002];
261
262          if(($values & 0x0004) == 0x0004)
263            $returned[]=$tag['tagValues.specialValues'][0x0004];
264
265          if(($values & 0x0008) == 0x0008)
266            $returned[]=$tag['tagValues.specialValues'][0x0008];
267          break;
268        case 0x0084: // "Lens"
269          /* array of 4 rationnal
270           * - short focal
271           * - long focal
272           * - aperture at short focal
273           * - aperture at long focal
274           *
275           * ==> 18-70mm f/3.5-4.5
276           */
277          $result=array();
278          foreach($values as $val)
279          {
280            if($val[1]==0) $val[1]==1;
281            $result[]=$val[0]/$val[1];
282          }
283
284          if($result[0]!=$result[1])
285          {
286            $returned=sprintf("%.0f-%.0fmm ", $result[0], $result[1]);
287          }
288          else
289          {
290            $returned=sprintf("%.0fmm ", $result[0]);
291          }
292
293          if($result[2]!=$result[3])
294          {
295            $returned.=sprintf("f/%.1f-%.1f", $result[2], $result[3]);
296          }
297          else
298          {
299            $returned.=sprintf("f/%.1f", $result[2]);
300          }
301          break;
302        case 0x0086: // "DigitalZoom"
303          if($values[1]==0) $values[1]=1;
304          $returned=sprintf("x%.1f", $values[0]/$values[1]);
305          break;
306        case 0x0088: // "AFInfo"
307          /* - shortInt : AFAreaMode
308           * - shortInt : AFPoint
309           * - longInt  : AFPointsInFocus
310           */
311          $tag=$this->tagDef->getTagById(0x0088);
312
313          $values.="\0\0\0\0";
314
315          $returned=Array();
316
317          $tmp=ord($values{0});
318          $returned['AFAreaMode'] = $tag['tagValues.specialValues'][0][$tmp];
319
320          $tmp=ord($values{1});
321          $returned['AFPoint'] = $tag['tagValues.specialValues'][1][$tmp];
322
323          if($values=="\x00\x07\xff\xff")
324          {
325            $returned['AFPointsInFocus'] = $tag['tagValues.specialValues'][2][$values];
326          }
327          else
328          {
329            $tmp=ord($values{2}) * 256 + ord($values{3});
330            $returned['AFPointsInFocus']=Array();
331            foreach($tag['tagValues.specialValues'][2] as $key => $val)
332            {
333              if(($tmp & $key) == $key)
334                $returned['AFPointsInFocus'][]=$val;
335            }
336          }
337          break;
338        case 0x0089: // "ShootingMode"
339          $returned=Array();
340          $tag=$this->tagDef->getTagById(0x0089);
341
342          if(!($values & 0x87))
343          {
344            $returned[]=$tag['tagValues.specialValues'][0x00];
345          }
346
347          if(preg_match("/.*d70.*/i",MakerNotesSignatures::getExifMaker()))
348          {
349            $bit5=1;
350          }
351          else
352          {
353            $bit5=0;
354          }
355
356          if($values)
357          {
358            foreach($tag['tagValues.specialValues'] as $key => $val)
359            {
360              if((($values & $key) == $key) && ($key!=0x00))
361              {
362                if($key==0x20)
363                {
364                  $returned[]=$val[$bit5];
365                }
366                else
367                {
368                  $returned[]=$val;
369                }
370              }
371            }
372          }
373          break;
374        case 0x0098: // "LensData"
375          if($this->shutterCount==-1 or $this->serialNumber=="")
376          {
377            $this->toDecode[0x0098]=Array($values, $type);
378            $returned="";
379          }
380          else
381          {
382            $returned=$this->readLensData($values);
383          }
384          break;
385        case 0x0099: // "RawImageCenter"
386          $returned=Array(
387            'x' => $values[0],
388            'y' => $values[1],
389          );
390          break;
391        case 0x009a: // "SensorPixelSize"
392          if($values[0][1]==0) $values[0][1]=1;
393          if($values[1][1]==0) $values[1][1]=1;
394
395          $returned=sprintf("%.1f x %.1f µm", ($values[0][0]/$values[0][1]), ($values[1][0]/$values[1][1]));
396          break;
397        default:
398          $returned="Not yet implemented;".ConvertData::toHexDump($tagId, ByteType::USHORT)." => ".ConvertData::toHexDump($values, $type);
399          break;
400      }
401      return($returned);
402    }
403
404    /**
405     * this function is used to decrypt datas from the Nikon "LensData" tag
406     *
407     * @param String $data : data from the exif.Nikon.LensData metadata
408     * @return Array : list of properties + lens name (computed)
409     */
410    private function readLensData($data)
411    {
412      $returned="";
413      $nfo=Array();
414      $dataReader=new Data($data, $this->byteOrder);
415
416      $nfo['LensDataVersion']=$dataReader->readASCII(4);
417
418      if(substr($nfo['LensDataVersion'],0,2)=="02")
419      {
420        // in this case, data are encrypted
421        $decrypted=$nfo['LensDataVersion'].$this->decryptData($this->serialNumberInteger(), $this->shutterCount,$dataReader->readASCII(-1,4));
422        $dataReader->setData($decrypted);
423      }
424
425      switch($nfo['LensDataVersion'])
426      {
427        case '0100' :
428            $nfo['LensIDNumber']=$dataReader->readUByte(6);
429            $nfo['LensFStops']=$dataReader->readUByte(7);
430            $nfo['MinFocalLength']=$dataReader->readUByte(8);
431            $nfo['MaxFocalLength']=$dataReader->readUByte(9);
432            $nfo['MaxApertureAtMinFocal']=$dataReader->readUByte(10);
433            $nfo['MaxApertureAtMaxFocal']=$dataReader->readUByte(11);
434            $nfo['MCUVersion']=$dataReader->readUByte(12);
435          break;
436        case '0101' :
437        case '0201' :
438        case '0202' :
439        case '0203' :
440            $nfo['LensIDNumber']=$dataReader->readUByte(11);
441            $nfo['LensFStops']=$dataReader->readUByte(12);
442            $nfo['MinFocalLength']=$dataReader->readUByte(13);
443            $nfo['MaxFocalLength']=$dataReader->readUByte(14);
444            $nfo['MaxApertureAtMinFocal']=$dataReader->readUByte(15);
445            $nfo['MaxApertureAtMaxFocal']=$dataReader->readUByte(16);
446            $nfo['MCUVersion']=$dataReader->readUByte(17);
447          break;
448        case '0204' :
449            $nfo['LensIDNumber']=$dataReader->readUByte(12);
450            $nfo['LensFStops']=$dataReader->readUByte(13);
451            $nfo['MinFocalLength']=$dataReader->readUByte(14);
452            $nfo['MaxFocalLength']=$dataReader->readUByte(15);
453            $nfo['MaxApertureAtMinFocal']=$dataReader->readUByte(16);
454            $nfo['MaxApertureAtMaxFocal']=$dataReader->readUByte(17);
455            $nfo['MCUVersion']=$dataReader->readUByte(18);
456          break;
457      }
458
459      $keyLens = sprintf("%02X %02X %02X %02X %02X %02X %02X %02X",
460        $nfo['LensIDNumber'],
461        $nfo['LensFStops'],
462        $nfo['MinFocalLength'],
463        $nfo['MaxFocalLength'],
464        $nfo['MaxApertureAtMinFocal'],
465        $nfo['MaxApertureAtMaxFocal'],
466        $nfo['MCUVersion'],
467        $this->getTagById(0x0083)->getValue()
468      );
469
470      $tag=$this->tagDef->getTagById(0x0098);
471      if(array_key_exists($keyLens, $tag['tagValues.lenses']))
472      {
473        $returned = $tag['tagValues.lenses'][$keyLens];
474      }
475      else
476      {
477        $returned = $tag['tagValues.lenses']['unknown']." (".$keyLens." / ".$nfo['LensDataVersion'].")";
478      }
479
480
481      unset($data);
482      unset($nfo);
483      return($returned);
484    }
485
486    /**
487     * Nikon encrypt some data
488     * This function is used to decrypt them. Don't ask anything about "how does
489     * it work" abd "what's it doing", I just translated the C++ & Perl code
490     * from Exiv2, Exiftool & Raw Photo Parser (Copyright 2004-2006 Dave Coffin)
491     *
492     * @param Integer $serialNumber : the camera serial number, in Integer
493     *                                format (from the 0x001d tag, not the 0x00a0)
494     * @param Integer $shutterCount : shutterCount value in the file
495     * @param String $data : data to decrypt
496     * @param Integer $decryptedLength : number of byte to decrypt
497     * @return String : the decrypted data
498     */
499    private function decryptData($serialNumber, $shutterCount, $data, $decryptedLength=0)
500    {
501      $xlat = Array(
502                Array(0xc1,0xbf,0x6d,0x0d,0x59,0xc5,0x13,0x9d,0x83,0x61,0x6b,0x4f,0xc7,0x7f,0x3d,0x3d,
503                      0x53,0x59,0xe3,0xc7,0xe9,0x2f,0x95,0xa7,0x95,0x1f,0xdf,0x7f,0x2b,0x29,0xc7,0x0d,
504                      0xdf,0x07,0xef,0x71,0x89,0x3d,0x13,0x3d,0x3b,0x13,0xfb,0x0d,0x89,0xc1,0x65,0x1f,
505                      0xb3,0x0d,0x6b,0x29,0xe3,0xfb,0xef,0xa3,0x6b,0x47,0x7f,0x95,0x35,0xa7,0x47,0x4f,
506                      0xc7,0xf1,0x59,0x95,0x35,0x11,0x29,0x61,0xf1,0x3d,0xb3,0x2b,0x0d,0x43,0x89,0xc1,
507                      0x9d,0x9d,0x89,0x65,0xf1,0xe9,0xdf,0xbf,0x3d,0x7f,0x53,0x97,0xe5,0xe9,0x95,0x17,
508                      0x1d,0x3d,0x8b,0xfb,0xc7,0xe3,0x67,0xa7,0x07,0xf1,0x71,0xa7,0x53,0xb5,0x29,0x89,
509                      0xe5,0x2b,0xa7,0x17,0x29,0xe9,0x4f,0xc5,0x65,0x6d,0x6b,0xef,0x0d,0x89,0x49,0x2f,
510                      0xb3,0x43,0x53,0x65,0x1d,0x49,0xa3,0x13,0x89,0x59,0xef,0x6b,0xef,0x65,0x1d,0x0b,
511                      0x59,0x13,0xe3,0x4f,0x9d,0xb3,0x29,0x43,0x2b,0x07,0x1d,0x95,0x59,0x59,0x47,0xfb,
512                      0xe5,0xe9,0x61,0x47,0x2f,0x35,0x7f,0x17,0x7f,0xef,0x7f,0x95,0x95,0x71,0xd3,0xa3,
513                      0x0b,0x71,0xa3,0xad,0x0b,0x3b,0xb5,0xfb,0xa3,0xbf,0x4f,0x83,0x1d,0xad,0xe9,0x2f,
514                      0x71,0x65,0xa3,0xe5,0x07,0x35,0x3d,0x0d,0xb5,0xe9,0xe5,0x47,0x3b,0x9d,0xef,0x35,
515                      0xa3,0xbf,0xb3,0xdf,0x53,0xd3,0x97,0x53,0x49,0x71,0x07,0x35,0x61,0x71,0x2f,0x43,
516                      0x2f,0x11,0xdf,0x17,0x97,0xfb,0x95,0x3b,0x7f,0x6b,0xd3,0x25,0xbf,0xad,0xc7,0xc5,
517                      0xc5,0xb5,0x8b,0xef,0x2f,0xd3,0x07,0x6b,0x25,0x49,0x95,0x25,0x49,0x6d,0x71,0xc7),
518                Array(0xa7,0xbc,0xc9,0xad,0x91,0xdf,0x85,0xe5,0xd4,0x78,0xd5,0x17,0x46,0x7c,0x29,0x4c,
519                      0x4d,0x03,0xe9,0x25,0x68,0x11,0x86,0xb3,0xbd,0xf7,0x6f,0x61,0x22,0xa2,0x26,0x34,
520                      0x2a,0xbe,0x1e,0x46,0x14,0x68,0x9d,0x44,0x18,0xc2,0x40,0xf4,0x7e,0x5f,0x1b,0xad,
521                      0x0b,0x94,0xb6,0x67,0xb4,0x0b,0xe1,0xea,0x95,0x9c,0x66,0xdc,0xe7,0x5d,0x6c,0x05,
522                      0xda,0xd5,0xdf,0x7a,0xef,0xf6,0xdb,0x1f,0x82,0x4c,0xc0,0x68,0x47,0xa1,0xbd,0xee,
523                      0x39,0x50,0x56,0x4a,0xdd,0xdf,0xa5,0xf8,0xc6,0xda,0xca,0x90,0xca,0x01,0x42,0x9d,
524                      0x8b,0x0c,0x73,0x43,0x75,0x05,0x94,0xde,0x24,0xb3,0x80,0x34,0xe5,0x2c,0xdc,0x9b,
525                      0x3f,0xca,0x33,0x45,0xd0,0xdb,0x5f,0xf5,0x52,0xc3,0x21,0xda,0xe2,0x22,0x72,0x6b,
526                      0x3e,0xd0,0x5b,0xa8,0x87,0x8c,0x06,0x5d,0x0f,0xdd,0x09,0x19,0x93,0xd0,0xb9,0xfc,
527                      0x8b,0x0f,0x84,0x60,0x33,0x1c,0x9b,0x45,0xf1,0xf0,0xa3,0x94,0x3a,0x12,0x77,0x33,
528                      0x4d,0x44,0x78,0x28,0x3c,0x9e,0xfd,0x65,0x57,0x16,0x94,0x6b,0xfb,0x59,0xd0,0xc8,
529                      0x22,0x36,0xdb,0xd2,0x63,0x98,0x43,0xa1,0x04,0x87,0x86,0xf7,0xa6,0x26,0xbb,0xd6,
530                      0x59,0x4d,0xbf,0x6a,0x2e,0xaa,0x2b,0xef,0xe6,0x78,0xb6,0x4e,0xe0,0x2f,0xdc,0x7c,
531                      0xbe,0x57,0x19,0x32,0x7e,0x2a,0xd0,0xb8,0xba,0x29,0x00,0x3c,0x52,0x7d,0xa8,0x49,
532                      0x3b,0x2d,0xeb,0x25,0x49,0xfa,0xa3,0xaa,0x39,0xa7,0xc5,0xa7,0x50,0x11,0x36,0xfb,
533                      0xc6,0x67,0x4a,0xf5,0xa5,0x12,0x65,0x7e,0xb0,0xdf,0xaf,0x4e,0xb3,0x61,0x7f,0x2f)
534              );
535      $returned="";
536
537      if($decryptedLength==0 or $decryptedLength>strlen($data))
538        $decryptedLength=strlen($data);
539
540      $key = 0;
541      for($i=0; $i < 4; $i++)
542      {
543        $key = $key ^ (($shutterCount >> ($i*8)) & 0xff);
544      }
545      $ci = $xlat[0][$serialNumber & 0xff];
546      $cj = $xlat[1][$key];
547      $ck = 0x60;
548
549      for($i=0;$i<$decryptedLength;++$i)
550      {
551        $cj+=$ci*$ck++;
552        $returned.= chr(ord($data{$i}) ^ $cj);
553      }
554      return($returned);
555    }
556
557    /**
558     * return the serialNumber as an integer
559     */
560    private function serialNumberInteger()
561    {
562      if($this->serialNumber!="")
563      {
564        return(0+$this->serialNumber);
565      }
566      elseif(preg_match("/.*d50.*/i",MakerNotesSignatures::getExifMaker()))
567      {
568        //D50
569        return(0x22);
570      }
571      else
572      {
573        //D200, D40X, ...
574        return(0x60);
575      }
576
577
578
579    }
580  }
581
582
583?>
Note: See TracBrowser for help on using the repository browser.