216 lines
6.6 KiB
Perl
216 lines
6.6 KiB
Perl
|
#------------------------------------------------------------------------------
|
||
|
# File: ITC.pm
|
||
|
#
|
||
|
# Description: Read iTunes Cover Flow meta information
|
||
|
#
|
||
|
# Revisions: 01/12/2008 - P. Harvey Created
|
||
|
#
|
||
|
# References: 1) http://www.waldoland.com/dev/Articles/ITCFileFormat.aspx
|
||
|
# 2) http://www.falsecognate.org/2007/01/deciphering_the_itunes_itc_fil/
|
||
|
#------------------------------------------------------------------------------
|
||
|
|
||
|
package Image::ExifTool::ITC;
|
||
|
|
||
|
use strict;
|
||
|
use vars qw($VERSION);
|
||
|
use Image::ExifTool qw(:DataAccess :Utils);
|
||
|
|
||
|
$VERSION = '1.02';
|
||
|
|
||
|
sub ProcessITC($$);
|
||
|
|
||
|
# tags used in ITC files
|
||
|
%Image::ExifTool::ITC::Main = (
|
||
|
NOTES => 'This information is found in iTunes Cover Flow data files.',
|
||
|
itch => { SubDirectory => { TagTable => 'Image::ExifTool::ITC::Header' } },
|
||
|
item => { SubDirectory => { TagTable => 'Image::ExifTool::ITC::Item' } },
|
||
|
data => {
|
||
|
Name => 'ImageData',
|
||
|
Notes => 'embedded JPEG or PNG image, depending on ImageType',
|
||
|
},
|
||
|
);
|
||
|
|
||
|
# ITC header information
|
||
|
%Image::ExifTool::ITC::Header = (
|
||
|
PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
|
||
|
GROUPS => { 2 => 'Image' },
|
||
|
0x10 => {
|
||
|
Name => 'DataType',
|
||
|
Format => 'undef[4]',
|
||
|
PrintConv => { artw => 'Artwork' },
|
||
|
},
|
||
|
);
|
||
|
|
||
|
# ITC item information
|
||
|
%Image::ExifTool::ITC::Item = (
|
||
|
PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
|
||
|
GROUPS => { 2 => 'Image' },
|
||
|
FORMAT => 'int32u',
|
||
|
FIRST_ENTRY => 0,
|
||
|
0 => {
|
||
|
Name => 'LibraryID',
|
||
|
Format => 'undef[8]',
|
||
|
ValueConv => 'uc unpack "H*", $val',
|
||
|
},
|
||
|
2 => {
|
||
|
Name => 'TrackID',
|
||
|
Format => 'undef[8]',
|
||
|
ValueConv => 'uc unpack "H*", $val',
|
||
|
},
|
||
|
4 => {
|
||
|
Name => 'DataLocation',
|
||
|
Format => 'undef[4]',
|
||
|
PrintConv => {
|
||
|
down => 'Downloaded Separately',
|
||
|
locl => 'Local Music File',
|
||
|
},
|
||
|
},
|
||
|
5 => {
|
||
|
Name => 'ImageType',
|
||
|
Format => 'undef[4]',
|
||
|
ValueConv => { # (not PrintConv because the unconverted JPEG value is nasty)
|
||
|
'PNGf' => 'PNG',
|
||
|
"\0\0\0\x0d" => 'JPEG',
|
||
|
},
|
||
|
},
|
||
|
7 => 'ImageWidth',
|
||
|
8 => 'ImageHeight',
|
||
|
);
|
||
|
|
||
|
#------------------------------------------------------------------------------
|
||
|
# Process an iTunes Cover Flow (ITC) file
|
||
|
# Inputs: 0) ExifTool object reference, 1) Directory information reference
|
||
|
# Returns: 1 on success, 0 if this wasn't a valid ITC file
|
||
|
sub ProcessITC($$)
|
||
|
{
|
||
|
my ($et, $dirInfo) = @_;
|
||
|
my $raf = $$dirInfo{RAF};
|
||
|
my $rtnVal = 0;
|
||
|
my ($buff, $err, $pos, $tagTablePtr, %dirInfo);
|
||
|
|
||
|
# loop through all blocks in this image
|
||
|
for (;;) {
|
||
|
# read the block header
|
||
|
my $n = $raf->Read($buff, 8);
|
||
|
unless ($n == 8) {
|
||
|
# no error if we reached the EOF normally
|
||
|
undef $err unless $n;
|
||
|
last;
|
||
|
}
|
||
|
my ($size, $tag) = unpack('Na4', $buff);
|
||
|
if ($rtnVal) {
|
||
|
last unless $size >= 8 and $size < 0x80000000;
|
||
|
} else {
|
||
|
# check to be sure this is a valid ITC image
|
||
|
# (first block must be 'itch')
|
||
|
last unless $tag eq 'itch';
|
||
|
last unless $size >= 0x1c and $size < 0x10000;
|
||
|
$et->SetFileType();
|
||
|
SetByteOrder('MM');
|
||
|
$rtnVal = 1; # this is an ITC file
|
||
|
$err = 1; # format error unless we read to EOF
|
||
|
}
|
||
|
if ($tag eq 'itch') {
|
||
|
$pos = $raf->Tell();
|
||
|
$size -= 8; # size of remaining data in block
|
||
|
$raf->Read($buff,$size) == $size or last;
|
||
|
# extract header information
|
||
|
%dirInfo = (
|
||
|
DirName => 'ITC Header',
|
||
|
DataPt => \$buff,
|
||
|
DataPos => $pos,
|
||
|
);
|
||
|
my $tagTablePtr = GetTagTable('Image::ExifTool::ITC::Header');
|
||
|
$et->ProcessDirectory(\%dirInfo, $tagTablePtr);
|
||
|
} elsif ($tag eq 'item') {
|
||
|
# don't want to read the entire item data (includes image)
|
||
|
$size > 12 or last;
|
||
|
$raf->Read($buff, 4) == 4 or last;
|
||
|
my $len = unpack('N', $buff);
|
||
|
$len >= 0xd0 and $len <= $size or last;
|
||
|
$size -= $len; # size of data after item header
|
||
|
$len -= 12; # length of remaining item header
|
||
|
# read in 4-byte blocks until we find the null terminator
|
||
|
# (this is just a guess about how to parse this variable-length part)
|
||
|
while ($len >= 4) {
|
||
|
$raf->Read($buff, 4) == 4 or last;
|
||
|
$len -= 4;
|
||
|
last if $buff eq "\0\0\0\0";
|
||
|
}
|
||
|
last if $len < 4;
|
||
|
$pos = $raf->Tell();
|
||
|
$raf->Read($buff, $len) == $len or last;
|
||
|
unless ($len >= 0xb4 and substr($buff, 0xb0, 4) eq 'data') {
|
||
|
$et->Warn('Parsing error. Please submit this ITC file for testing');
|
||
|
last;
|
||
|
}
|
||
|
%dirInfo = (
|
||
|
DirName => 'ITC Item',
|
||
|
DataPt => \$buff,
|
||
|
DataPos => $pos,
|
||
|
);
|
||
|
$tagTablePtr = GetTagTable('Image::ExifTool::ITC::Item');
|
||
|
$et->ProcessDirectory(\%dirInfo, $tagTablePtr);
|
||
|
# extract embedded image
|
||
|
$pos += $len;
|
||
|
if ($size > 0) {
|
||
|
$tagTablePtr = GetTagTable('Image::ExifTool::ITC::Main');
|
||
|
my $tagInfo = $et->GetTagInfo($tagTablePtr, 'data');
|
||
|
my $image = $et->ExtractBinary($pos, $size, $$tagInfo{Name});
|
||
|
$et->FoundTag($tagInfo, \$image);
|
||
|
# skip the rest of the block if necessary
|
||
|
$raf->Seek($pos+$size, 0) or last
|
||
|
} elsif ($size < 0) {
|
||
|
last;
|
||
|
}
|
||
|
} else {
|
||
|
$et->VPrint(0, "Unknown $tag block ($size bytes)\n");
|
||
|
$raf->Seek($size-8, 1) or last;
|
||
|
}
|
||
|
}
|
||
|
$err and $et->Warn('ITC file format error');
|
||
|
return $rtnVal;
|
||
|
}
|
||
|
|
||
|
1; # end
|
||
|
|
||
|
__END__
|
||
|
|
||
|
=head1 NAME
|
||
|
|
||
|
Image::ExifTool::ITC - Read iTunes Cover Flow meta information
|
||
|
|
||
|
=head1 SYNOPSIS
|
||
|
|
||
|
This module is used by Image::ExifTool
|
||
|
|
||
|
=head1 DESCRIPTION
|
||
|
|
||
|
This module contains the routines required by Image::ExifTool to read meta
|
||
|
information (including artwork images) from iTunes Cover Flow files.
|
||
|
|
||
|
=head1 AUTHOR
|
||
|
|
||
|
Copyright 2003-2018, Phil Harvey (phil at owl.phy.queensu.ca)
|
||
|
|
||
|
This library is free software; you can redistribute it and/or modify it
|
||
|
under the same terms as Perl itself.
|
||
|
|
||
|
=head1 REFERENCES
|
||
|
|
||
|
=over 4
|
||
|
|
||
|
=item L<http://www.waldoland.com/dev/Articles/ITCFileFormat.aspx>
|
||
|
|
||
|
=item L<http://www.falsecognate.org/2007/01/deciphering_the_itunes_itc_fil/>
|
||
|
|
||
|
=back
|
||
|
|
||
|
=head1 SEE ALSO
|
||
|
|
||
|
L<Image::ExifTool::TagNames/ITC Tags>,
|
||
|
L<Image::ExifTool(3pm)|Image::ExifTool>
|
||
|
|
||
|
=cut
|
||
|
|