#line 1 "Image/ExifTool/CanonRaw.pm"
#------------------------------------------------------------------------------
# File:         CanonRaw.pm
#
# Description:  Read Canon RAW (CRW) meta information
#
# Revisions:    11/25/2003 - P. Harvey Created
#               12/02/2003 - P. Harvey Completely reworked and figured out many
#                            more tags
#
# References:   1) http://www.cybercom.net/~dcoffin/dcraw/
#               2) http://www.wonderland.org/crw/
#               3) http://xyrion.org/ciff/CIFFspecV1R04.pdf
#               4) Dave Nicholson private communication (PowerShot S30)
#------------------------------------------------------------------------------

package Image::ExifTool::CanonRaw;

use strict;
use vars qw($VERSION $AUTOLOAD %crwTagFormat);
use Image::ExifTool qw(:DataAccess :Utils);
use Image::ExifTool::Exif;
use Image::ExifTool::Canon;

$VERSION = '1.58';

sub WriteCRW($$);
sub ProcessCanonRaw($$$);
sub WriteCanonRaw($$$);
sub CheckCanonRaw($$$);
sub InitMakerNotes($);
sub SaveMakerNotes($);
sub BuildMakerNotes($$$$$$);

# formats for CRW tag types (($tag >> 8) & 0x38)
# Note: don't define format for undefined types
%crwTagFormat = (
    0x00 => 'int8u',
    0x08 => 'string',
    0x10 => 'int16u',
    0x18 => 'int32u',
  # 0x20 => 'undef',
  # 0x28 => 'undef',
  # 0x30 => 'undef',
);

# Canon raw file tag table
# Note: Tag ID's have upper 2 bits set to zero, since these 2 bits
# just specify the location of the information
%Image::ExifTool::CanonRaw::Main = (
    GROUPS => { 0 => 'MakerNotes', 2 => 'Camera' },
    PROCESS_PROC => \&ProcessCanonRaw,
    WRITE_PROC => \&WriteCanonRaw,
    CHECK_PROC => \&CheckCanonRaw,
    WRITABLE => 1,
    0x0000 => { Name => 'NullRecord', Writable => 'undef' }, #3
    0x0001 => { #3
        Name => 'FreeBytes',
        Format => 'undef',
        Binary => 1,
    },
    0x0032 => { Name => 'CanonColorInfo1', Writable => 0 },
    0x0805 => [
        # this tag is found in more than one directory...
        {
            Condition => '$self->{DIR_NAME} eq "ImageDescription"',
            Name => 'CanonFileDescription',
            Writable => 'string[32]',
        },
        {
            Name => 'UserComment',
            Writable => 'string[256]',
        },
    ],
    0x080a => {
        Name => 'CanonRawMakeModel',
        Writable => 0,
        SubDirectory => { TagTable => 'Image::ExifTool::CanonRaw::MakeModel' },
    },
    0x080b => { Name => 'CanonFirmwareVersion', Writable => 'string[32]' },
    0x080c => { Name => 'ComponentVersion',     Writable => 'string'     }, #3
    0x080d => { Name => 'ROMOperationMode',     Writable => 'string[8]'  }, #3
    0x0810 => { Name => 'OwnerName',            Writable => 'string[32]' },
    0x0815 => { Name => 'CanonImageType',       Writable => 'string[32]' },
    0x0816 => { Name => 'OriginalFileName',     Writable => 'string[32]' },
    0x0817 => { Name => 'ThumbnailFileName',    Writable => 'string[32]' },
    0x100a => { #3
        Name => 'TargetImageType',
        Writable => 'int16u',
        PrintConv => {
            0 => 'Real-world Subject',
            1 => 'Written Document',
        },
    },
    0x1010 => { #3
        Name => 'ShutterReleaseMethod',
        Writable => 'int16u',
        PrintConv => {
            0 => 'Single Shot',
            2 => 'Continuous Shooting',
        },
    },
    0x1011 => { #3
        Name => 'ShutterReleaseTiming',
        Writable => 'int16u',
        PrintConv => {
            0 => 'Priority on shutter',
            1 => 'Priority on focus',
        },
    },
    0x1016 => { Name => 'ReleaseSetting',       Writable => 'int16u' }, #3
    0x101c => { Name => 'BaseISO',              Writable => 'int16u' }, #3
    0x1028=> { #PH
        Name => 'CanonFlashInfo',
        Writable => 'int16u',
        Count => 4,
        Unknown => 1,
    },
    0x1029 => {
        Name => 'CanonFocalLength',
        Writable => 0,
        SubDirectory => { TagTable => 'Image::ExifTool::Canon::FocalLength' },
    },
    0x102a => {
        Name => 'CanonShotInfo',
        Writable => 0,
        SubDirectory => { TagTable => 'Image::ExifTool::Canon::ShotInfo' },
    },
    0x102c => {
        Name => 'CanonColorInfo2',
        Writable => 0,
        # for the S30, the following information has been decoded: (ref 4)
        # offset 66: int32u - shutter half press time in ms
        # offset 70: int32u - image capture time in ms
        # offset 74: int16u - custom white balance flag (0=Off, 512=On)
    },
    0x102d => {
        Name => 'CanonCameraSettings',
        Writable => 0,
        SubDirectory => { TagTable => 'Image::ExifTool::Canon::CameraSettings' },
    },
    0x1030 => { #4
        Name => 'WhiteSample',
        Writable => 0,
        SubDirectory => {
            Validate => 'Image::ExifTool::Canon::Validate($dirData,$subdirStart,$size)',
            TagTable => 'Image::ExifTool::CanonRaw::WhiteSample',
        },
    },
    0x1031 => {
        Name => 'SensorInfo',
        Writable => 0,
        SubDirectory => { TagTable => 'Image::ExifTool::Canon::SensorInfo' },
    },
    # this tag has only be verified for the 10D in CRW files, but the D30 and D60
    # also produce CRW images and have CustomFunction information in their JPEG's
    0x1033 => [
        {
            Name => 'CustomFunctions10D',
            Condition => '$self->{Model} =~ /EOS 10D/',
            SubDirectory => {
                Validate => 'Image::ExifTool::Canon::Validate($dirData,$subdirStart,$size)',
                TagTable => 'Image::ExifTool::CanonCustom::Functions10D',
            },
        },
        {
            Name => 'CustomFunctionsD30',
            Condition => '$self->{Model} =~ /EOS D30\b/',
            SubDirectory => {
                Validate => 'Image::ExifTool::Canon::Validate($dirData,$subdirStart,$size)',
                TagTable => 'Image::ExifTool::CanonCustom::FunctionsD30',
            },
        },
        {
            Name => 'CustomFunctionsD60',
            Condition => '$self->{Model} =~ /EOS D60\b/',
            SubDirectory => {
                # the stored size in the D60 apparently doesn't include the size word:
                Validate => 'Image::ExifTool::Canon::Validate($dirData,$subdirStart,$size-2,$size)',
                # (D60 custom functions are basically the same as D30)
                TagTable => 'Image::ExifTool::CanonCustom::FunctionsD30',
            },
        },
        {
            Name => 'CustomFunctionsUnknown',
            SubDirectory => {
                Validate => 'Image::ExifTool::Canon::Validate($dirData,$subdirStart,$size)',
                TagTable => 'Image::ExifTool::CanonCustom::FuncsUnknown',
            },
        },
    ],
    0x1038 => {
        Name => 'CanonAFInfo',
        Writable => 0,
        SubDirectory => { TagTable => 'Image::ExifTool::Canon::AFInfo' },
    },
    0x1093 => {
        Name => 'CanonFileInfo',
        SubDirectory => {
            Validate => 'Image::ExifTool::Canon::Validate($dirData,$subdirStart,$size)',
            TagTable => 'Image::ExifTool::Canon::FileInfo',
        },
    },
    0x10a9 => {
        Name => 'ColorBalance',
        Writable => 0,
        SubDirectory => { TagTable => 'Image::ExifTool::Canon::ColorBalance' },
    },
    0x10b5 => { #PH
        Name => 'RawJpgInfo',
        SubDirectory => {
            Validate => 'Image::ExifTool::Canon::Validate($dirData,$subdirStart,$size)',
            TagTable => 'Image::ExifTool::CanonRaw::RawJpgInfo',
        },
    },
    0x10ae => {
        Name => 'ColorTemperature',
        Writable => 'int16u',
    },
    0x10b4 => {
        Name => 'ColorSpace',
        Writable => 'int16u',
        PrintConv => {
            1 => 'sRGB',
            2 => 'Adobe RGB',
            0xffff => 'Uncalibrated',
        },
    },
    0x1803 => { #3
        Name => 'ImageFormat',
        Writable => 0,
        SubDirectory => { TagTable => 'Image::ExifTool::CanonRaw::ImageFormat' },
    },
    0x1804 => { Name => 'RecordID', Writable => 'int32u' }, #3
    0x1806 => { #3
        Name => 'SelfTimerTime',
        Writable => 'int32u',
        ValueConv => '$val / 1000',
        ValueConvInv => '$val * 1000',
        PrintConv => '"$val s"',
        PrintConvInv => '$val=~s/\s*s.*//;$val',
    },
    0x1807 => {
        Name => 'TargetDistanceSetting',
        Format => 'float',
        PrintConv => '"$val mm"',
        PrintConvInv => '$val=~s/\s*mm$//;$val',
    },
    0x180b => [
        {
            # D30
            Name => 'SerialNumber',
            Condition => '$$self{Model} =~ /EOS D30\b/',
            Writable => 'int32u',
            PrintConv => 'sprintf("%x-%.5d",$val>>16,$val&0xffff)',
            PrintConvInv => '$val=~/(.*)-(\d+)/ ? (hex($1)<<16)+$2 : undef',
        },
        {
            # all EOS models (D30, 10D, 300D)
            Name => 'SerialNumber',
            Condition => '$$self{Model} =~ /EOS/',
            Writable => 'int32u',
            PrintConv => 'sprintf("%.10d",$val)',
            PrintConvInv => '$val',
        },
        {
            # this is not SerialNumber for PowerShot models (but what is it?) - PH
            Name => 'UnknownNumber',
            Unknown => 1,
        },
    ],
    0x180e => {
        Name => 'TimeStamp',
        Writable => 0,
        SubDirectory => {
            TagTable => 'Image::ExifTool::CanonRaw::TimeStamp',
        },
    },
    0x1810 => {
        Name => 'ImageInfo',
        Writable => 0,
        SubDirectory => {
            TagTable => 'Image::ExifTool::CanonRaw::ImageInfo',
        },
    },
    0x1813 => { #3
        Name => 'FlashInfo',
        Writable => 0,
        SubDirectory => {
            TagTable => 'Image::ExifTool::CanonRaw::FlashInfo',
        },
    },
    0x1814 => { #3
        Name => 'MeasuredEV',
        Notes => q{
            this is the Canon name for what could better be called MeasuredLV, and
            should be close to the calculated LightValue for a proper exposure with most
            models
        },
        Format => 'float',
        ValueConv => '$val + 5',
        ValueConvInv => '$val - 5',
    },
    0x1817 => {
        Name => 'FileNumber',
        Writable => 'int32u',
        Groups => { 2 => 'Image' },
        PrintConv => '$_=$val;s/(\d+)(\d{4})/$1-$2/;$_',
        PrintConvInv => '$_=$val;s/-//;$_',
    },
    0x1818 => { #3
        Name => 'ExposureInfo',
        Groups => { 1 => 'CIFF' }, # (only so CIFF shows up in group lists)
        Writable => 0,
        SubDirectory => { TagTable => 'Image::ExifTool::CanonRaw::ExposureInfo' },
    },
    0x1834 => { #PH
        Name => 'CanonModelID',
        Writable => 'int32u',
        PrintHex => 1,
        Notes => q{
            this is the complete list of model ID numbers, but note that many of these
            models do not produce CRW images
        },
        SeparateTable => 'Canon CanonModelID',
        PrintConv => \%Image::ExifTool::Canon::canonModelID,
    },
    0x1835 => {
        Name => 'DecoderTable',
        Writable => 0,
        SubDirectory => { TagTable => 'Image::ExifTool::CanonRaw::DecoderTable' },
    },
    0x183b => { #PH
        # display format for serial number
        Name => 'SerialNumberFormat',
        Writable => 'int32u',
        PrintHex => 1,
        PrintConv => {
            0x90000000 => 'Format 1',
            0xa0000000 => 'Format 2',
        },
    },
    0x2005 => {
        Name => 'RawData',
        Writable => 0,
        Binary => 1,
    },
    0x2007 => {
        Name => 'JpgFromRaw',
        Groups => { 2 => 'Preview' },
        Writable => 'resize',  # 'resize' allows this value to change size
        Permanent => 0,
        RawConv => '$self->ValidateImage(\$val,$tag)',
    },
    0x2008 => {
        Name => 'ThumbnailImage',
        Groups => { 2 => 'Preview' },
        Writable => 'resize',  # 'resize' allows this value to change size
        WriteCheck => '$self->CheckImage(\$val)',
        Permanent => 0,
        RawConv => '$self->ValidateImage(\$val,$tag)',
    },
    # the following entries are subdirectories
    # (any 0x28 and 0x30 tag types are handled automatically by the decoding logic)
    0x2804 => {
        Name => 'ImageDescription',
        SubDirectory => { },
        Writable => 0,
    },
    0x2807 => { #3
        Name => 'CameraObject',
        SubDirectory => { },
        Writable => 0,
    },
    0x3002 => { #3
        Name => 'ShootingRecord',
        SubDirectory => { },
        Writable => 0,
    },
    0x3003 => { #3
        Name => 'MeasuredInfo',
        SubDirectory => { },
        Writable => 0,
    },
    0x3004 => { #3
        Name => 'CameraSpecification',
        SubDirectory => { },
        Writable => 0,
    },
    0x300a => { #3
        Name => 'ImageProps',
        SubDirectory => { },
        Writable => 0,
    },
    0x300b => {
        Name => 'ExifInformation',
        SubDirectory => { },
        Writable => 0,
    },
);

# Canon binary data blocks
%Image::ExifTool::CanonRaw::MakeModel = (
    PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
    WRITE_PROC => \&Image::ExifTool::WriteBinaryData,
    CHECK_PROC => \&Image::ExifTool::CheckBinaryData,
    DATAMEMBER => [ 0, 6 ], # indices of data members to extract when writing
    WRITABLE => 1,
    FORMAT => 'string',
    GROUPS => { 0 => 'MakerNotes', 2 => 'Camera' },
    # (can't specify a first entry because this isn't
    # a simple binary table with fixed offsets)
    0 => {
        Name => 'Make',
        Format => 'string[6]',  # "Canon\0"
        DataMember => 'Make',
        RawConv => '$self->{Make} = $val',
    },
    6 => {
        Name => 'Model',
        Format => 'string', # no size = to the end of the data
        Description => 'Camera Model Name',
        DataMember => 'Model',
        RawConv => '$self->{Model} = $val',
    },
);

%Image::ExifTool::CanonRaw::TimeStamp = (
    PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
    WRITE_PROC => \&Image::ExifTool::WriteBinaryData,
    CHECK_PROC => \&Image::ExifTool::CheckBinaryData,
    WRITABLE => 1,
    FORMAT => 'int32u',
    FIRST_ENTRY => 0,
    GROUPS => { 0 => 'MakerNotes', 2 => 'Time' },
    0 => {
        Name => 'DateTimeOriginal',
        Description => 'Date/Time Original',
        Shift => 'Time',
        ValueConv => 'ConvertUnixTime($val)',
        ValueConvInv => 'GetUnixTime($val)',
        PrintConv => '$self->ConvertDateTime($val)',
        PrintConvInv => '$self->InverseDateTime($val)',
    },
    1 => { #3
        Name => 'TimeZoneCode',
        Format => 'int32s',
        ValueConv => '$val / 3600',
        ValueConvInv => '$val * 3600',
    },
    2 => { #3
        Name => 'TimeZoneInfo',
        Notes => 'set to 0x80000000 if TimeZoneCode is valid',
    },
);

%Image::ExifTool::CanonRaw::ImageFormat = (
    PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
    WRITE_PROC => \&Image::ExifTool::WriteBinaryData,
    CHECK_PROC => \&Image::ExifTool::CheckBinaryData,
    WRITABLE => 1,
    FORMAT => 'int32u',
    FIRST_ENTRY => 0,
    GROUPS => { 0 => 'MakerNotes', 2 => 'Image' },
    0 => {
        Name => 'FileFormat',
        Flags => 'PrintHex',
        PrintConv => {
            0x00010000 => 'JPEG (lossy)',
            0x00010002 => 'JPEG (non-quantization)',
            0x00010003 => 'JPEG (lossy/non-quantization toggled)',
            0x00020001 => 'CRW',
        },
    },
    1 => {
        Name => 'TargetCompressionRatio',
        Format => 'float',
    },
);

%Image::ExifTool::CanonRaw::RawJpgInfo = (
    PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
    WRITE_PROC => \&Image::ExifTool::WriteBinaryData,
    CHECK_PROC => \&Image::ExifTool::CheckBinaryData,
    WRITABLE => 1,
    FORMAT => 'int16u',
    FIRST_ENTRY => 1,
    GROUPS => { 0 => 'MakerNotes', 2 => 'Image' },
#    0 => 'RawJpgInfoSize',
    1 => { #PH
        Name => 'RawJpgQuality',
        PrintConv => {
            1 => 'Economy',
            2 => 'Normal',
            3 => 'Fine',
            5 => 'Superfine',
        },
    },
    2 => { #PH
        Name => 'RawJpgSize',
        PrintConv => {
            0 => 'Large',
            1 => 'Medium',
            2 => 'Small',
        },
    },
    3 => 'RawJpgWidth', #PH
    4 => 'RawJpgHeight', #PH
);

%Image::ExifTool::CanonRaw::FlashInfo = (
    PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
    WRITE_PROC => \&Image::ExifTool::WriteBinaryData,
    CHECK_PROC => \&Image::ExifTool::CheckBinaryData,
    WRITABLE => 1,
    FORMAT => 'float',
    FIRST_ENTRY => 0,
    GROUPS => { 0 => 'MakerNotes', 2 => 'Image' },
    0 => 'FlashGuideNumber',
    1 => 'FlashThreshold',
);

%Image::ExifTool::CanonRaw::ExposureInfo = (
    PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
    WRITE_PROC => \&Image::ExifTool::WriteBinaryData,
    CHECK_PROC => \&Image::ExifTool::CheckBinaryData,
    WRITABLE => 1,
    FORMAT => 'float',
    FIRST_ENTRY => 0,
    GROUPS => { 0 => 'MakerNotes', 2 => 'Image' },
    0 => 'ExposureCompensation',
    1 => {
        Name => 'ShutterSpeedValue',
        ValueConv => 'abs($val)<100 ? 1/(2**$val) : 0',
        ValueConvInv => '$val>0 ? -log($val)/log(2) : -100',
        PrintConv => 'Image::ExifTool::Exif::PrintExposureTime($val)',
        PrintConvInv => 'Image::ExifTool::Exif::ConvertFraction($val)',
    },
    2 => {
        Name => 'ApertureValue',
        ValueConv => '2 ** ($val / 2)',
        ValueConvInv => '$val>0 ? 2*log($val)/log(2) : 0',
        PrintConv => 'sprintf("%.1f",$val)',
        PrintConvInv => '$val',
    },
);

%Image::ExifTool::CanonRaw::ImageInfo = (
    PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
    WRITE_PROC => \&Image::ExifTool::WriteBinaryData,
    CHECK_PROC => \&Image::ExifTool::CheckBinaryData,
    FORMAT => 'int32u',
    FIRST_ENTRY => 0,
    GROUPS => { 0 => 'MakerNotes', 2 => 'Image' },
    # Note: Don't make these writable (except rotation) because it confuses
    # Canon decoding software if the are changed
    0 => 'ImageWidth', #3
    1 => 'ImageHeight', #3
    2 => { #3
        Name => 'PixelAspectRatio',
        Format => 'float',
    },
    3 => {
        Name => 'Rotation',
        Format => 'int32s',
        Writable => 'int32s',
    },
    4 => 'ComponentBitDepth', #3
    5 => 'ColorBitDepth', #3
    6 => 'ColorBW', #3
);

# ref 4
%Image::ExifTool::CanonRaw::DecoderTable = (
    PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
    WRITE_PROC => \&Image::ExifTool::WriteBinaryData,
    CHECK_PROC => \&Image::ExifTool::CheckBinaryData,
    GROUPS => { 0 => 'MakerNotes', 2 => 'Camera' },
    FORMAT => 'int32u',
    FIRST_ENTRY => 0,
    0 => 'DecoderTableNumber',
    2 => 'CompressedDataOffset',
    3 => 'CompressedDataLength',
);

# ref 1/4
%Image::ExifTool::CanonRaw::WhiteSample = (
    PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
    WRITE_PROC => \&Image::ExifTool::WriteBinaryData,
    CHECK_PROC => \&Image::ExifTool::CheckBinaryData,
    GROUPS => { 0 => 'MakerNotes', 2 => 'Camera' },
    FORMAT => 'int16u',
    FIRST_ENTRY => 1,
    1 => 'WhiteSampleWidth',
    2 => 'WhiteSampleHeight',
    3 => 'WhiteSampleLeftBorder',
    4 => 'WhiteSampleTopBorder',
    5 => 'WhiteSampleBits',
    # this is followed by the encrypted white sample values (ref 1)
);

#------------------------------------------------------------------------------
# AutoLoad our writer routines when necessary
#
sub AUTOLOAD
{
    return Image::ExifTool::DoAutoLoad($AUTOLOAD, @_);
}

#------------------------------------------------------------------------------
# Process Raw file directory
# Inputs: 0) ExifTool object reference
#         1) directory information reference, 2) tag table reference
# Returns: 1 on success
sub ProcessCanonRaw($$$)
{
    my ($et, $dirInfo, $rawTagTable) = @_;
    my $blockStart = $$dirInfo{DirStart};
    my $blockSize = $$dirInfo{DirLen};
    my $raf = $$dirInfo{RAF} or return 0;
    my $buff;
    my $verbose = $et->Options('Verbose');
    my $buildMakerNotes = $et->Options('MakerNotes');

    # 4 bytes at end of block give directory position within block
    $raf->Seek($blockStart+$blockSize-4, 0) or return 0;
    $raf->Read($buff, 4) == 4 or return 0;
    my $dirOffset = Get32u(\$buff,0) + $blockStart;
    $raf->Seek($dirOffset, 0) or return 0;
    $raf->Read($buff, 2) == 2 or return 0;
    my $entries = Get16u(\$buff,0);         # get number of entries in directory
    # read the directory (10 bytes per entry)
    $raf->Read($buff, 10 * $entries) == 10 * $entries or return 0;

    $verbose and $et->VerboseDir('CIFF', $entries);
    my $index;
    for ($index=0; $index<$entries; ++$index) {
        my $pt = 10 * $index;
        my $tag = Get16u(\$buff, $pt);
        my $size = Get32u(\$buff, $pt+2);
        my $valuePtr = Get32u(\$buff, $pt+6);
        my $ptr = $valuePtr + $blockStart;  # all pointers relative to block start
        if ($tag & 0x8000) {
            $et->Warn('Bad CRW directory entry');
            return 1;
        }
        my $tagID = $tag & 0x3fff;          # get tag ID
        my $tagType = ($tag >> 8) & 0x38;   # get tag type
        my $valueInDir = ($tag & 0x4000);   # flag for value in directory
        my $tagInfo = $et->GetTagInfo($rawTagTable, $tagID);
        if (($tagType==0x28 or $tagType==0x30) and not $valueInDir) {
            # this type of tag specifies a raw subdirectory
            my $name;
            $tagInfo and $name = $$tagInfo{Name};
            $name or $name = sprintf("CanonRaw_0x%.4x", $tag);
            my %subdirInfo = (
                DirName  => $name,
                DataLen  => 0,
                DirStart => $ptr,
                DirLen   => $size,
                Nesting  => $$dirInfo{Nesting} + 1,
                RAF      => $raf,
                Parent   => $$dirInfo{DirName},
            );
            if ($verbose) {
                my $fakeInfo = { Name => $name, SubDirectory => { } };
                $et->VerboseInfo($tagID, $fakeInfo,
                    'Index'  => $index,
                    'Size'   => $size,
                    'Start'  => $ptr,
                );
            }
            $et->ProcessDirectory(\%subdirInfo, $rawTagTable);
            next;
        }
        my ($valueDataPos, $count, $subdir);
        my $format = $crwTagFormat{$tagType};
        if ($tagInfo) {
            $subdir = $$tagInfo{SubDirectory};
            $format = $$tagInfo{Format} if $$tagInfo{Format};
            $count = $$tagInfo{Count};
        }
        # get value data
        my ($value, $delRawConv);
        if ($valueInDir) {  # is the value data in the directory?
            # this type of tag stores the value in the 'size' and 'ptr' fields
            $valueDataPos = $dirOffset + $pt + 4;
            $size = 8;
            $value = substr($buff, $pt+2, $size);
            # set count to 1 by default for normal values in directory
            $count = 1 if not defined $count and $format and
                          $format ne 'string' and not $subdir;
        } else {
            $valueDataPos = $ptr;
            if ($size <= 512 or ($verbose > 2 and $size <= 65536)
                or ($tagInfo and ($$tagInfo{SubDirectory}
                or grep(/^$$tagInfo{Name}$/i, $et->GetRequestedTags()) )))
            {
                # read value if size is small or specifically requested
                # or if this is a SubDirectory
                unless ($raf->Seek($ptr, 0) and $raf->Read($value, $size) == $size) {
                    $et->Warn(sprintf("Error reading %d bytes from 0x%x",$size,$ptr));
                    next;
                }
            } else {
                $value = "Binary data $size bytes";
                if ($tagInfo) {
                    if ($et->Options('Binary') or $verbose) {
                        # read the value anyway
                        unless ($raf->Seek($ptr, 0) and $raf->Read($value, $size) == $size) {
                            $et->Warn(sprintf("Error reading %d bytes from 0x%x",$size,$ptr));
                            next;
                        }
                    }
                    # force this to be a binary (scalar reference)
                    $$tagInfo{RawConv} = '\$val';
                    $delRawConv = 1;
                }
                $size = length $value;
                undef $format;
            }
        }
        # set count from tagInfo count if necessary
        if ($format and not $count) {
            # set count according to format and size
            my $fnum = $Image::ExifTool::Exif::formatNumber{$format};
            my $fsiz = $Image::ExifTool::Exif::formatSize[$fnum];
            $count = int($size / $fsiz);
        }
        if ($verbose) {
            my $val = $value;
            $format and $val = ReadValue(\$val, 0, $format, $count, $size);
            $et->VerboseInfo($tagID, $tagInfo,
                Table   => $rawTagTable,
                Index   => $index,
                Value   => $val,
                DataPt  => \$value,
                DataPos => $valueDataPos,
                Size    => $size,
                Format  => $format,
                Count   => $count,
            );
        }
        if ($buildMakerNotes) {
            # build maker notes information if requested
            BuildMakerNotes($et, $tagID, $tagInfo, \$value, $format, $count);
        }
        next unless defined $tagInfo;

        if ($subdir) {
            my $name = $$tagInfo{Name};
            my $newTagTable;
            if ($$subdir{TagTable}) {
                $newTagTable = GetTagTable($$subdir{TagTable});
                unless ($newTagTable) {
                    warn "Unknown tag table $$subdir{TagTable}\n";
                    next;
                }
            } else {
                warn "Must specify TagTable for SubDirectory $name\n";
                next;
            }
            my $subdirStart = 0;
            #### eval Start ()
            $subdirStart = eval $$subdir{Start} if $$subdir{Start};
            my $dirData = \$value;
            my %subdirInfo = (
                Name     => $name,
                DataPt   => $dirData,
                DataLen  => $size,
                DataPos  => $valueDataPos,
                DirStart => $subdirStart,
                DirLen   => $size - $subdirStart,
                Nesting  => $$dirInfo{Nesting} + 1,
                RAF      => $raf,
                DirName  => $name,
                Parent   => $$dirInfo{DirName},
            );
            #### eval Validate ($dirData, $subdirStart, $size)
            if (defined $$subdir{Validate} and not eval $$subdir{Validate}) {
                $et->Warn("Invalid $name data");
            } else {
                $et->ProcessDirectory(\%subdirInfo, $newTagTable, $$subdir{ProcessProc});
            }
        } else {
            # convert to specified format if necessary
            $format and $value = ReadValue(\$value, 0, $format, $count, $size);
            # save the information
            $et->FoundTag($tagInfo, $value);
            delete $$tagInfo{RawConv} if $delRawConv;
        }
    }
    return 1;
}

#------------------------------------------------------------------------------
# get information from raw file
# Inputs: 0) ExifTool object reference, 1) dirInfo reference
# Returns: 1 if this was a valid Canon RAW file
sub ProcessCRW($$)
{
    my ($et, $dirInfo) = @_;
    my ($buff, $sig);
    my $raf = $$dirInfo{RAF};
    my $buildMakerNotes = $et->Options('MakerNotes');

    $raf->Read($buff,2) == 2      or return 0;
    SetByteOrder($buff)           or return 0;
    $raf->Read($buff,4) == 4      or return 0;
    $raf->Read($sig,8) == 8       or return 0;  # get file signature
    $sig =~ /^HEAP(CCDR|JPGM)/    or return 0;  # validate signature
    my $hlen = Get32u(\$buff, 0);

    $raf->Seek(0, 2)              or return 0;  # seek to end of file
    my $filesize = $raf->Tell()   or return 0;

    # initialize maker note data if building maker notes
    $buildMakerNotes and InitMakerNotes($et);

    # set the FileType tag unless already done (eg. APP0 CIFF record in JPEG image)
    $et->SetFileType();

    # build directory information for main raw directory
    my %dirInfo = (
        DataLen  => 0,
        DirStart => $hlen,
        DirLen   => $filesize - $hlen,
        Nesting  => 0,
        RAF      => $raf,
        Parent   => 'CRW',
    );

    # process the raw directory
    my $rawTagTable = GetTagTable('Image::ExifTool::CanonRaw::Main');
    my $oldIndent = $$et{INDENT};
    $$et{INDENT} .= '| ';
    unless (ProcessCanonRaw($et, \%dirInfo, $rawTagTable)) {
        $et->Warn('CRW file format error');
    }
    $$et{INDENT} = $oldIndent;

    # finish building maker notes if necessary
    $buildMakerNotes and SaveMakerNotes($et);

    # process trailers if they exist in CRW file (not in CIFF information!)
    if ($$et{FILE_TYPE} eq 'CRW') {
        my $trailInfo = Image::ExifTool::IdentifyTrailer($raf);
        $et->ProcessTrailers($trailInfo) if $trailInfo;
    }

    return 1;
}

1;  # end

__END__

#line 910

