#!/usr/bin/perl # # avi-metadata script 0.3 # copyright (c) 2007 nhjm449 # # Reads/writes metadata from AVI files via File::Format::RIFF. # # You may do whatever you want to with this script as long as copyright # information is retained. # $|++; use strict; use Getopt::Long; use File::Format::RIFF; my %options = ( ); sub usage { print << "EOF"; usage: $0 [OPTIONS] [FILE] -h, --help this (help) message -o, --output output file -s, --strip-tags strip tags from file -l, --list-tags lists tags in file -a, --all-tags lists all possible tags -v, --video-info shows video information (-vv = more info) (needs -l, too) -j, --show-junk shows JUNK data (needs -l, too) -c, --clear-junk clears JUNK data (needs -lj, too) -t, --tag set tag value example: $0 input.avi -l $0 input.avi -o output.avi -t IART=foo -t INAM=bar EOF exit; }; Getopt::Long::Configure("bundling"); GetOptions(\%options, "output|o=s", "strip-tags|s", "list-tags|l", "all-tags|a", "video-info|v+", "show-junk|j", "clear-junk|c", "tag|t=s%", "debug|d+") or usage(); $options{"input"} = $ARGV[0]; my %tags = ( 'INAM' => 'Name', 'IART' => 'Artist', 'ICOP' => 'Copyright', 'IPRD' => 'Product', 'ICRD' => 'Creation Date', 'IGNR' => 'Genre', 'ISGN' => 'Secondary Genre (EXT)', 'ISBJ' => 'Subject', 'IKEY' => 'Keywords', 'ICMT' => 'Comments', 'IWRI' => 'Written by (EXT)', 'IPRO' => 'Produced by (EXT)', 'ICNM' => 'Cinematographer (EXT)', 'IPDS' => 'Production Designer (EXT)', 'IEDT' => 'Edited by (EXT)', 'ICDS' => 'Costume Designer (EXT)', 'IMUS' => 'Music by (EXT)', 'ISTD' => 'Production Studio (EXT)', 'IDST' => 'Distributed by (EXT)', 'ICNT' => 'Country (EXT)', 'ILNG' => 'Language (EXT)', 'IRTD' => 'Rating (EXT)', 'ISTR' => 'Starring (EXT)', 'ISFT' => 'Software', 'ITCH' => 'Technician / Encoded by', 'IENG' => 'Engineer / Digitized by', 'IWEB' => 'Website (EXT)', 'IDIT' => 'Digitizing Date', 'ISMP' => 'SMPTE time code', 'ISRF' => 'Source From', 'IMED' => 'Medium', 'ISRC' => 'Source', 'IARL' => 'Archival Location', 'ICMS' => 'Commissioned by', 'IPRT' => 'Part (EXT)', 'IFRM' => 'Total Number of Parts (EXT)', 'ICRP' => 'Cropped', 'ISHP' => 'Sharpness', 'IDIM' => 'Dimensions', 'ILGT' => 'Lightness', 'IDPI' => 'Dots Per Inch', 'IPLT' => 'Palette Setting', 'IAS1' => 'First language (EXT)', 'IAS2' => 'Second language (EXT)', 'IAS3' => 'Third language (EXT)', 'IAS4' => 'Fourth language (EXT)', 'IAS5' => 'Fifth language (EXT)', 'IAS6' => 'Sixth language (EXT)', 'IAS7' => 'Seventh language (EXT)', 'IAS8' => 'Eighth language (EXT)', 'IAS9' => 'Ninth language (EXT)', 'ICAS' => 'Default audio stream (EXT)', 'IBSU' => 'Base URL (EXT)', 'ILGU' => 'Logo URL (EXT)', 'ILIU' => 'Logo Icon URL (EXT)', 'IWMU' => 'Watermark URL (EXT)', 'IMIU' => 'More Info URL (EXT)', 'IMBI' => 'More Info Banner Image (EXT)', 'IMBU' => 'More Info Banner URL (EXT)', 'IMIT' => 'More Info Text (EXT)' ); # From Image::ExifTool::Riff my %audioEncodings = ( #2 0x01 => 'Microsoft PCM', 0x02 => 'Microsoft ADPCM', 0x03 => 'Microsoft IEEE float', 0x04 => 'Compaq VSELP', #4 0x05 => 'IBM CVSD', #4 0x06 => 'Microsoft a-Law', 0x07 => 'Microsoft u-Law', 0x08 => 'Microsoft DTS', #4 0x09 => 'DRM', #4 0x0a => 'WMA 9 Speech', #9 0x10 => 'OKI-ADPCM', 0x11 => 'Intel IMA/DVI-ADPCM', 0x12 => 'Videologic Mediaspace ADPCM', #4 0x13 => 'Sierra ADPCM', #4 0x14 => 'Antex G.723 ADPCM', #4 0x15 => 'DSP Solutions DIGISTD', 0x16 => 'DSP Solutions DIGIFIX', 0x17 => 'Dialoic OKI ADPCM', #6 0x18 => 'Media Vision ADPCM', #6 0x19 => 'HP CU', #7 0x20 => 'Yamaha ADPCM', #6 0x21 => 'SONARC Speech Compression', #6 0x22 => 'DSP Group True Speech', #6 0x23 => 'Echo Speech Corp.', #6 0x24 => 'Virtual Music Audiofile AF36', #6 0x25 => 'Audio Processing Tech.', #6 0x26 => 'Virtual Music Audiofile AF10', #6 0x27 => 'Aculab Prosody 1612', #7 0x28 => 'Merging Tech. LRC', #7 0x30 => 'Dolby AC2', 0x31 => 'Microsoft GSM610', 0x32 => 'MSN Audio', #6 0x33 => 'Antex ADPCME', #6 0x34 => 'Control Resources VQLPC', #6 0x35 => 'DSP Solutions DIGIREAL', #6 0x36 => 'DSP Solutions DIGIADPCM', #6 0x37 => 'Control Resources CR10', #6 0x38 => 'Natural MicroSystems VBX ADPCM', #6 0x39 => 'Crystal Semiconductor IMA ADPCM', #6 0x3a => 'Echo Speech ECHOSC3', #6 0x3b => 'Rockwell ADPCM', 0x3c => 'Rockwell DIGITALK', 0x3d => 'Xebec Multimedia', #6 0x40 => 'Antex G.721 ADPCM', 0x41 => 'Antex G.728 CELP', 0x42 => 'Microsoft MSG723', #7 0x45 => 'ITU-T G.726', #9 0x50 => 'Microsoft MPEG', 0x51 => 'RT23 or PAC', #7 0x52 => 'InSoft RT24', #4 0x53 => 'InSoft PAC', #4 0x55 => 'MP3', 0x59 => 'Cirrus', #7 0x60 => 'Cirrus Logic', #6 0x61 => 'ESS Tech. PCM', #6 0x62 => 'Voxware Inc.', #6 0x63 => 'Canopus ATRAC', #6 0x64 => 'APICOM G.726 ADPCM', 0x65 => 'APICOM G.722 ADPCM', 0x66 => 'Microsoft DSAT', #6 0x67 => 'Micorsoft DSAT DISPLAY', #6 0x69 => 'Voxware Byte Aligned', #7 0x70 => 'Voxware AC8', #7 0x71 => 'Voxware AC10', #7 0x72 => 'Voxware AC16', #7 0x73 => 'Voxware AC20', #7 0x74 => 'Voxware MetaVoice', #7 0x75 => 'Voxware MetaSound', #7 0x76 => 'Voxware RT29HW', #7 0x77 => 'Voxware VR12', #7 0x78 => 'Voxware VR18', #7 0x79 => 'Voxware TQ40', #7 0x80 => 'Soundsoft', #6 0x81 => 'Voxware TQ60', #7 0x82 => 'Microsoft MSRT24', #7 0x83 => 'AT&T G.729A', #7 0x84 => 'Motion Pixels MVI MV12', #7 0x85 => 'DataFusion G.726', #7 0x86 => 'DataFusion GSM610', #7 0x88 => 'Iterated Systems Audio', #7 0x89 => 'Onlive', #7 0x91 => 'Siemens SBC24', #7 0x92 => 'Sonic Foundry Dolby AC3 APDIF', #7 0x93 => 'MediaSonic G.723', #8 0x94 => 'Aculab Prosody 8kbps', #8 0x97 => 'ZyXEL ADPCM', #7, 0x98 => 'Philips LPCBB', #7 0x99 => 'Studer Professional Audio Packed', #7 0xa0 => 'Malden PhonyTalk', #8 0x100 => 'Rhetorex ADPCM', #6 0x101 => 'IBM u-Law', #3 0x102 => 'IBM a-Law', #3 0x103 => 'IBM ADPCM', #3 0x111 => 'Vivo G.723', #7 0x112 => 'Vivo Siren', #7 0x123 => 'Digital G.723', #7 0x125 => 'Sanyo LD ADPCM', #8 0x130 => 'Sipro Lab ACEPLNET', #8 0x131 => 'Sipro Lab ACELP4800', #8 0x132 => 'Sipro Lab ACELP8V3', #8 0x133 => 'Sipro Lab G.729', #8 0x134 => 'Sipro Lab G.729A', #8 0x135 => 'Sipro Lab Kelvin', #8 0x140 => 'Dictaphone G.726 ADPCM', #8 0x150 => 'Qualcomm PureVoice', #8 0x151 => 'Qualcomm HalfRate', #8 0x155 => 'Ring Zero Systems TUBGSM', #8 0x160 => 'Microsoft Audio1', #8 0x200 => 'Creative Labs ADPCM', #6 0x202 => 'Creative Labs FASTSPEECH8', #6 0x203 => 'Creative Labs FASTSPEECH10', #6 0x210 => 'UHER ADPCM', #8 0x220 => 'Quarterdeck Corp.', #6 0x230 => 'I-Link VC', #8 0x240 => 'Aureal Semiconductor Raw Sport', #8 0x250 => 'Interactive Products HSX', #8 0x251 => 'Interactive Products RPELP', #8 0x260 => 'Consistent CS2', #8 0x270 => 'Sony SCX', #8 0x300 => 'Fujitsu FM TOWNS SND', #6 0x400 => 'Brooktree Digital', #6 0x450 => 'QDesign Music', #8 0x680 => 'AT&T VME VMPCM', #7 0x681 => 'AT&T TCP', #8 0x1000 => 'Olivetti GSM', #6 0x1001 => 'Olivetti ADPCM', #6 0x1002 => 'Olivetti CELP', #6 0x1003 => 'Olivetti SBC', #6 0x1004 => 'Olivetti OPR', #6 0x1100 => 'Lernout & Hauspie', #6 0x1400 => 'Norris Comm. Inc.', #6 0x1401 => 'ISIAudio', #7 0x1500 => 'AT&T Soundspace Music Compression', #7 0x2000 => 'FAST Multimedia DVM (Dolby AC3)', #7 0xfffe => 'Extensible', #7 0xffff => 'Development', #4 ); use constant { # AVIMAINHEADER AVIF_HASINDEX => 0x00000010, AVIF_MUSTUSEINDEX => 0x00000020, AVIF_ISINTERLEAVED => 0x00000100, AVIF_TRUSTCKTYPE => 0x00000800, AVIF_WASCAPTUREFILE => 0x00010000, AVIF_COPYRIGHTED => 0x00020000, # AVISTREAMHEADER AVISF_DISABLED => 0x00000001, AVISF_VIDEO_PALCHANGES => 0x00010000 }; if ( $options{"all-tags"} ) { print "\nAll available tags: (EXT=Extended Info)\n\n"; foreach my $tag ( sort keys %tags ) { print " $tag: " . $tags{$tag} . "\n"; } print "\nMore info: http://abcavi.kibi.ru/infotags.htm\n\n"; exit; } usage() if ( !($options{"input"} && $options{"output"}) && !($options{"input"} && $options{"list-tags"}) ); sub { foreach ( keys %options ) { print "$_=" . $options{$_} . "\n"; } } if ($options{"debug"}); my %currentinfo = ( ); my %new = ( ); my %streams = ( ); my %aviinfo = ( ); my $position = 0; my @junktags; my $junk_recurse_has_run = 0; my $junk_clear_recurse_has_run = 0; foreach ( keys %{$options{"tag"}} ) { next if ( $_ eq uc($_) ); $options{"tag"}{uc($_)} = $options{"tag"}{$_}; delete( $options{"tag"}{$_} ); } print "\nReading from " . $options{"input"} . "... "; open( IN, $options{"input"} ) or die "Could not open file: $!\n\n"; my ( $riff1 ) = File::Format::RIFF->read( \*IN ); close( IN ); print "done\n"; sub list_tags { my $tagsfound; my ( $data, $file, $out ) = @_; print (($out ? "\nOutput" : "\nInput") . " tags: ($file)\n\n"); foreach my $chunk ( $data->data ) { if ( $chunk->id eq 'LIST' ) { if ( $chunk->type eq 'INFO' ) { foreach my $currentinfochunk ( $chunk->data ) { my $cleandata = $currentinfochunk->data; $cleandata =~ s/\c@//g; print " " . uc($currentinfochunk->id) . ": ".$tags{uc($currentinfochunk->id)}.(' ' x (30- length($tags{uc($currentinfochunk->id)}))).": ".$cleandata."\n"; $tagsfound = 1; } } if ( $chunk->type eq 'hdrl' ) { my $vidscount = 0; my $audscount = 0; foreach my $hdrlchunk ( $chunk->data ) { if ( $hdrlchunk->id eq 'avih' ) { print "AVIH FOUND!!!\n" if ($options{"debug"}); $aviinfo{"microsecperframe"} = unpack("V", substr($hdrlchunk->data, 0, 4)); $aviinfo{"maxbytespersec"} = unpack("V", substr($hdrlchunk->data, 4, 4)); $aviinfo{"paddinggranularity"} = unpack("V", substr($hdrlchunk->data, 8, 4)); $aviinfo{"flags"} = unpack("V", substr($hdrlchunk->data, 12, 4)); $aviinfo{"flags_hasindex"} = ($aviinfo{"flags"} & AVIF_HASINDEX ? 1 : 0); $aviinfo{"flags_mustuseindex"} = ($aviinfo{"flags"} & AVIF_MUSTUSEINDEX ? 1 : 0); $aviinfo{"flags_isinterleaved"} = ($aviinfo{"flags"} & AVIF_ISINTERLEAVED ? 1 : 0); $aviinfo{"flags_trustcktype"} = ($aviinfo{"flags"} & AVIF_TRUSTCKTYPE ? 1 : 0); $aviinfo{"flags_wascapturefile"} = ($aviinfo{"flags"} & AVIF_WASCAPTUREFILE ? 1 : 0); $aviinfo{"flags_copyrighted"} = ($aviinfo{"flags"} & AVIF_COPYRIGHTED ? 1 : 0); $aviinfo{"flagshex"} = "0x" . sprintf("%x", unpack("V", substr($hdrlchunk->data, 12, 4))); $aviinfo{"totalframes"} = unpack("V", substr($hdrlchunk->data, 16, 4)); $aviinfo{"initialframes"} = unpack("V", substr($hdrlchunk->data, 20, 4)); $aviinfo{"streams"} = unpack("V", substr($hdrlchunk->data, 24, 4)); $aviinfo{"suggestedbuffersize"} = unpack("V", substr($hdrlchunk->data, 28, 4)); $aviinfo{"width"} = unpack("V", substr($hdrlchunk->data, 32, 4)); $aviinfo{"height"} = unpack("V", substr($hdrlchunk->data, 36, 4)); $aviinfo{"reserved"} = unpack("V", substr($hdrlchunk->data, 40, 4)); $aviinfo{"duration"} = $aviinfo{"totalframes"} * $aviinfo{"microsecperframe"} / 1000000; foreach (sort keys %aviinfo) { print " avi/$_: ".$aviinfo{$_}."\n" if ($options{"debug"}); } } if ( $hdrlchunk->id eq 'LIST' and $hdrlchunk->type eq 'strl' ) { my $i; my $streamtype; my %streaminfo = ( ); print "STRL FOUND!!!\ndata:".@{ $hdrlchunk->{data} }[0]->id ."\n" if ($options{"debug"}); foreach my $strlchunk ( $hdrlchunk->data ) { if ( $strlchunk->id eq 'strh' ) { if ( substr($strlchunk->data, 0, 4) eq 'vids' ) { $streamtype = "vids"; $streaminfo{"fourcc"} = substr($strlchunk->data, 4, 4); $streaminfo{"flags"} = unpack("V", substr($strlchunk->data, 8, 4)); $streaminfo{"flags_disabled"} = ($streaminfo{"flags"} & AVISF_DISABLED ? 1 : 0); $streaminfo{"flags_video_palchanges"} = ($streaminfo{"flags"} & AVISF_VIDEO_PALCHANGES ? 1 : 0); $streaminfo{"priority"} = unpack("v", substr($strlchunk->data, 12, 2)); $streaminfo{"language"} = unpack("v", substr($strlchunk->data, 14, 2)); $streaminfo{"initialframes"} = unpack("V", substr($strlchunk->data, 16, 4)); $streaminfo{"scale"} = unpack("V", substr($strlchunk->data, 20, 4)); $streaminfo{"vrate"} = unpack("V", substr($strlchunk->data, 24, 4)); $streaminfo{"start"} = unpack("V", substr($strlchunk->data, 28, 4)); $streaminfo{"fps"} = sprintf("%.3f", ($streaminfo{"vrate"} / $streaminfo{"scale"})); $streaminfo{"length"} = unpack("V", substr($strlchunk->data, 32, 4)); $streaminfo{"duration"} = sprintf("%.3f", ($streaminfo{"length"} / $streaminfo{"fps"})) if ($streaminfo{"fps"}); $streaminfo{"suggestedbuffersize"} = unpack("V", substr($strlchunk->data, 36, 4)); $streaminfo{"quality"} = unpack("i", substr($strlchunk->data, 40, 4)); $streaminfo{"samplesize"} = unpack("V", substr($strlchunk->data, 44, 4)); $streaminfo{"frame_l"} = unpack("v", substr($strlchunk->data, 48, 2)); $streaminfo{"frame_r"} = unpack("v", substr($strlchunk->data, 50, 2)); $streaminfo{"frame_t"} = unpack("v", substr($strlchunk->data, 52, 2)); $streaminfo{"frame_b"} = unpack("v", substr($strlchunk->data, 54, 2)); foreach (sort keys %streaminfo) { print " vids/$_: ".$streaminfo{$_}."\n" if ($options{"debug"}); } $i = $vidscount++; } if ( substr($strlchunk->data, 0, 4) eq 'auds' ) { $streamtype = "auds"; $streaminfo{"fourcc"} = unpack("V",substr($strlchunk->data, 4, 4)); $streaminfo{"fourcchex"} = "0x" . sprintf("%2x", $streaminfo{"fourcc"}); $streaminfo{"flags"} = unpack("V", substr($strlchunk->data, 8, 4)); $streaminfo{"flags_disabled"} = ($streaminfo{"flags"} & AVISF_DISABLED ? 1 : 0); $streaminfo{"flags_video_palchanges"} = ($streaminfo{"flags"} & AVISF_VIDEO_PALCHANGES ? 1 : 0); $streaminfo{"priority"} = unpack("v", substr($strlchunk->data, 12, 2)); $streaminfo{"language"} = unpack("v", substr($strlchunk->data, 14, 2)); $streaminfo{"initialframes"} = unpack("V", substr($strlchunk->data, 16, 4)); $streaminfo{"scale"} = unpack("V", substr($strlchunk->data, 20, 4)); $streaminfo{"vrate"} = unpack("V", substr($strlchunk->data, 24, 4)); $streaminfo{"start"} = unpack("V", substr($strlchunk->data, 28, 4)); $streaminfo{"fps"} = ($streaminfo{"vrate"} / $streaminfo{"scale"}); $streaminfo{"length"} = unpack("V", substr($strlchunk->data, 32, 4)); $streaminfo{"duration"} = sprintf("%.3f", ($streaminfo{"length"} / $streaminfo{"fps"})) if ($streaminfo{"fps"}); $streaminfo{"suggestedbuffersize"} = unpack("V", substr($strlchunk->data, 36, 4)); $streaminfo{"quality"} = unpack("i", substr($strlchunk->data, 40, 4)); $streaminfo{"samplesize"} = unpack("V", substr($strlchunk->data, 44, 4)); $streaminfo{"frame_l"} = unpack("v", substr($strlchunk->data, 48, 2)); $streaminfo{"frame_r"} = unpack("v", substr($strlchunk->data, 50, 2)); $streaminfo{"frame_t"} = unpack("v", substr($strlchunk->data, 52, 2)); $streaminfo{"frame_b"} = unpack("v", substr($strlchunk->data, 54, 2)); foreach (sort keys %streaminfo) { print " auds/$_: ".$streaminfo{$_}."\n" if ($options{"debug"}); } $i = $audscount++; } } if ( $strlchunk->id eq 'strf' ) { if ( $streamtype eq "vids" ) { $streaminfo{"width"} = unpack("V", substr($strlchunk->data, 4, 4)); $streaminfo{"height"} = unpack("V", substr($strlchunk->data, 8, 4)); $streaminfo{"vcodec"} = substr($strlchunk->data, 16, 4); foreach (sort keys %streaminfo) { print " vids/$_: ".$streaminfo{$_}."\n" if ($options{"debug"}); } } if ( $streamtype eq "auds" ) { $streaminfo{"acodecraw"} = unpack("v", substr($strlchunk->data, 0, 2)); $streaminfo{"acodechex"} = "0x" . sprintf("%2x", $streaminfo{"acodecraw"}); $streaminfo{"achans"} = unpack("v", substr($strlchunk->data, 2, 2)); $streaminfo{"afreq"} = unpack("V", substr($strlchunk->data, 4, 4)); $streaminfo{"arate"} = 8 * unpack("V", substr($strlchunk->data, 8, 4)); $streaminfo{"blockalign"} = unpack("v", substr($strlchunk->data, 12, 2)); $streaminfo{"bitspersample"} = unpack("v", substr($strlchunk->data, 14, 2)); $streaminfo{"size"} = unpack("v", substr($strlchunk->data, 16, 2)); if ($audioEncodings{$streaminfo{"acodecraw"}}) { $streaminfo{"acodec"} = $audioEncodings{$streaminfo{"acodecraw"}}; } foreach (sort keys %streaminfo) { print " auds/$_: ".$streaminfo{$_}."\n" if ($options{"debug"}); } } } print "id:".$strlchunk->id ."\n" if ($options{"debug"}); } %{ $streams{$streamtype}{$i} } = %streaminfo; } } } } } print " None found!\n" if (!$tagsfound); if ( $options{"video-info"} ) { print "\nVideo Streams:\n"; foreach my $streamnum ( sort keys %{ $streams{"vids"} } ) { #print "$streamnum ::\n"; #print $streams{"vids"}{$streamnum}{"fourcc"}." <---\n"; print " " . ($streamnum + 1) . ". " . $streams{"vids"}{$streamnum}{"vcodec"} . " : " . $streams{"vids"}{$streamnum}{"width"} . "x" . $streams{"vids"}{$streamnum}{"height"} . ", " . $streams{"vids"}{$streamnum}{"vframes"} . " frames @ " . $streams{"vids"}{$streamnum}{"fps"} . "fps for " . $streams{"vids"}{$streamnum}{"duration"} . " seconds\n"; if ( $options{"video-info"} > 1 ) { foreach my $stream ( sort keys %{ $streams{"vids"}{$streamnum} } ) { print " $stream: ".(' ' x (34 - length($stream))).": ".$streams{"vids"}{$streamnum}{$stream}."\n"; } } } print "\nAudio Streams:\n"; foreach my $streamnum ( sort keys %{ $streams{"auds"} } ) { #print "$streamnum ::\n"; #print $streams{"vids"}{$streamnum}{"fourcc"}." <---\n"; print " " . ($streamnum + 1) . ". " . $streams{"auds"}{$streamnum}{"acodec"} . " (" . $streams{"auds"}{$streamnum}{"acodechex"} . ") : " . $streams{"auds"}{$streamnum}{"achans"} . " channels @ " . $streams{"auds"}{$streamnum}{"afreq"} . " Hz, " . $streams{"auds"}{$streamnum}{"arate"} . " bits/second\n"; if ( $options{"video-info"} > 1 ) { foreach my $stream ( sort keys %{ $streams{"auds"}{$streamnum} } ) { print " $stream: ".(' ' x (34 - length($stream))).": ".$streams{"auds"}{$streamnum}{$stream}."\n"; } } } } if ( $options{"show-junk"} ) { junk_recurse( $riff1 ) if (!$junk_recurse_has_run); print "\n" if ($#junktags > -1); foreach my $junkinfo ( @junktags ) { $junkinfo =~ s/\c@//g; print " JUNK found: : " . $junkinfo . "\n"; } } print "\n"; } sub junk_recurse { $junk_recurse_has_run = 1; my ( $data ) = shift; foreach my $chunk ( $data->data ) { print "recursing...".$chunk->id."\n" if ($options{"debug"} > 1); if ( $chunk->id eq 'LIST' ) { print "> LIST type ".$chunk->type."\n" if ($options{"debug"} > 1); junk_recurse($chunk); next; } if ( uc($chunk->id) eq 'JUNK' ) { print " JUNK: " . $chunk->data . "\n" if ($options{"debug"} > 1); push(@junktags, $chunk->data); } } } sub junk_clear_recurse { $junk_clear_recurse_has_run = 1; my ( $data ) = shift; foreach my $chunk ( $data->data ) { print "clear recursing...".$chunk->id."\n" if ($options{"debug"} > 1); if ( $chunk->id eq 'LIST' ) { print "> LIST type ".$chunk->type."\n" if ($options{"debug"} > 1); junk_clear_recurse($chunk); next; } if ( uc($chunk->id) eq 'JUNK' ) { print " JUNK: " . $chunk->data . "\n" if ($options{"debug"} > 1); $chunk->{data} = (chr(0) x length($chunk->data)) if ( $options{"clear-junk"} ); push(@junktags, $chunk->data); } } } if ( $options{"list-tags"} ) { list_tags( $riff1, $options{"input"} ); $riff1->dump if ($options{"debug"} && !$options{"output"}); exit if ( !$options{"output"} ); } foreach my $chunk ( $riff1->data ) { $position++; if ( $chunk->id eq 'LIST' ) { if ( $chunk->type eq 'INFO' ) { if ( !$options{"strip-tags"} ) { foreach my $currentinfochunk ( $chunk->data ) { $currentinfo{uc($currentinfochunk->id)} = $currentinfochunk->data; print "current: " . uc($currentinfochunk->id) . " = " . $currentinfochunk->data . "\n" if ($options{"debug"}); } } print @{ $riff1->{data} }[$position-1]->type."<-THE ID IS $position\n" if ($options{"debug"}); $riff1->splice( $position-1, 1 ); } } } $position = 0; if ( !$options{"strip-tags"} || $options{"tag"} ) { foreach my $chunk ( $riff1->data ) { $position++; print "($position)".$chunk->id.":"."\n" if ($options{"debug"}); if ( $chunk->id eq 'LIST' ) { if ( $chunk->type eq 'hdrl' ) { my $infochunk = new File::Format::RIFF::List( 'INFO' ); foreach my $newitem ( sort keys %tags ) { next if ( !$options{"tag"}{$newitem} && !$currentinfo{$newitem} ); print "new $newitem:".($options{"tag"}{$newitem} ? "new:".$options{"tag"}{$newitem} : "old:".$currentinfo{$newitem})."\n" if ($options{"debug"}); $infochunk->addChunk( $newitem => ($options{"tag"}{$newitem} ? $options{"tag"}{$newitem} : $currentinfo{$newitem}) ); } $riff1->splice( $position, 0, $infochunk ); } } } } if ( $options{"clear-junk"} ) { undef(@junktags); junk_clear_recurse( $riff1 ); } $riff1->dump if ($options{"debug"}); print "\n" if (!$options{"list-tags"}); print "Outputting to ".$options{"output"}."... "; open( OUT, ">", $options{"output"} ) or die "Could not open file: $!\n\n"; $riff1->write( \*OUT ); close( OUT ); print "done!\n"; if ( $options{"list-tags"} ) { list_tags( $riff1, $options{"output"}, 1 ); } else { print "\n"; } # id: LIST (INFO) size: 70 (82) # id: IART size: 5 (14): [p7]^@ # id: ICMT size: 11 (20): Code Lyoko^@ # id: INAM size: 27 (36): (301) X.A.N.A. Awakens (1)^@