<?xml version="1.0" encoding="windows-1252"?>
<node id="534371" title="Updated QuickTime format movie file dumper" created="2006-03-03 16:21:21" updated="2006-03-03 11:21:21">
<type id="1042">
CUFP</type>
<author id="461912">
GrandFather</author>
<data>
<field name="doctext">
&lt;p&gt;This is an update to the code presented in [id://530009] that adds considerable new atom knowledge, removes the dependence on Video::info, generates the output as a string, and presents the output in a tree using Tk&lt;/p&gt;
&lt;p&gt;Update: fixed a bug handling various strings&lt;/p&gt;
&lt;readmore title="the code"&gt;
&lt;c&gt;
package DumpQuicktime;

use strict;
use Video::Info;
use base qw(Video::Info);
#use diagnostics;

our $VERSION = '0.07';
use constant DEBUG =&gt; 0;

use Class::MakeMethods::Emulator::MethodMaker
    get_set =&gt; [qw(acodec tracks indent indentStr lastErr result)],
    list =&gt; [qw(atomStack)],
    ;

sub init {
    my $self = shift;
    my %param = @_;
    $self-&gt;init_attributes(@_);
    $self-&gt;indentStr ('.  ');
    $self-&gt;indent ('');
    $self-&gt;result ('');
    return $self;
}

sub read
{
    my $self = shift;
    my ($len, $offset) = @_;
    my $buf;
    
    seek $self-&gt;handle, $offset, 0 if defined $offset;
    
    my $n = read $self-&gt;handle, $buf, $len;
    $self-&gt;lastErr ('read failed') unless defined $n;
    $self-&gt;lastErr ("short read ($len/$n)") unless $n == $len;
    return $buf;
}

sub dump { # Find top level atoms
    my $self = shift;
    my $pos = 0;
    
    $pos = $self-&gt;describeAtom ($pos) while ! eof ($self-&gt;handle);
    return $self-&gt;result;
}

sub pr {
    my $self = shift;
    $self-&gt;result ($self-&gt;result . $self-&gt;indent . join '', @_);
}

sub describeAtom {
    my $self = shift;
    my $pos = shift;
    my ($len, $key) = unpack ("Na4", $self-&gt;read (8, $pos));
  
    if ($len == 0) {
        $self-&gt;pr ("End entry\n");
        return $pos + 4;
    }
    
    $key = 'x' . unpack ('H8', $key) if $key =~ /[\x00-\x1f]/;
    $key =~ tr/ /_/;
    $key =~ s/([^\w \d_])/sprintf "%02X", ord ($1)/ge;
    
    if (! length $key) {
        return $pos;
    }
    
    my $member = "dump_$key";
    my $header = sprintf "%s @ %d (0x%08x) for %d (0x%08x):",
        $key, $pos, $pos, $len, $len;

    $self-&gt;pr ("$header\n");
    $self-&gt;indent ($self-&gt;indent . $self-&gt;indentStr);
    if ($self-&gt;can($member)) {
        $self-&gt;atomStack_push ([$key, {}]);
        $self-&gt;$member ($pos, $len);
        $self-&gt;atomStack_pop ();
    } else {
        $self-&gt;pr ("   Unhandled: length = $len\n");
        $self-&gt;dumpBlock ($pos + 8, $len &gt; 24 ? 16 : $len - 8) if $len &gt; 8;
    }
    $self-&gt;indent (substr $self-&gt;indent, length ($self-&gt;indentStr));
    return $pos + $len;
}

sub describeAtoms {
    my $self = shift;
    my ($pos, $count) = @_;
    
    $pos = $self-&gt;describeAtom ($pos) while $count--;
    return $pos;
}

sub describeAtomsIn {
    my $self = shift;
    my ($pos, $end) = @_;
    
    $pos = $self-&gt;describeAtom ($pos) while $pos &lt; $end;
}

sub unwrapAtoms {
    my $self = shift;
    my ($pos, $len) = @_;
    
    $self-&gt;describeAtomsIn ($pos + 8, $pos + $len);
}

sub atomList {
    my $self = shift;
    my ($pos, $len) = @_;

    $self-&gt;pr ('Version:  ', unpack ('C', $self-&gt;read (1)), "\n");
    $self-&gt;pr ('Flags:    ', unpack ('B24', $self-&gt;read (3)), "\n");
    $self-&gt;pr ('Entries:   ', NToSigned ($self-&gt;read (4)), "\n");
    $self-&gt;describeAtomsIn ($pos + 16, $pos + $len);
}

sub construct_hash {
    my ( $input ) = @_;
    my %hash;
    
    while (length($input) &gt; 0) {
        my($len)   = NToSigned (substr( $input, 0, 4, ''));
        my($cntnt) = substr( $input, 0, $len-4, '');
        my($type)  = substr( $cntnt, 0, 4, '');

        if ( exists $hash{$type} ) {
            my @a = grep($type,keys %hash);
            $hash{$type.length(@a)} = $cntnt;
        } else {
            $hash{$type} = $cntnt;
        }
    }
    %hash;
}

sub dump_A9nam {
    my $self = shift;
    $self-&gt;showStr (@_);
}

sub dump_A9cpy {
    my $self = shift;
    $self-&gt;showStr (@_);
}

sub dump_A9cmt {
    my $self = shift;
    $self-&gt;showStr (@_);
}

sub dump_A9des {
    my $self = shift;
    $self-&gt;showStr (@_);
}

sub dump_A9inf {
    my $self = shift;
    $self-&gt;showStr (@_);
}

sub dump_alis {
    my $self = shift;
    $self-&gt;dump_code (@_);
}        

sub dump_cmov {
    my $self = shift;
    $self-&gt;unwrapAtoms (@_);
}        

sub dump_code {
    my $self = shift;
    my ($pos, $len) = @_;

    $len -= 8;
    if ($len &gt; 16) {
        $self-&gt;pr ("First 16 bytes of $len\n");
        $len = 16;
    }
    
    $self-&gt;dumpBlock ($pos, $len);
}

sub dump_dflt {
    my $self = shift;
    $self-&gt;atomList (@_);
}

sub dump_dinf {
    my $self = shift;
    $self-&gt;unwrapAtoms (@_);
}

sub dump_dref {
    my $self = shift;
    $self-&gt;atomList (@_);
}

sub dump_evnt {
    my $self = shift;
    my ($pos, $len) = @_;

    $self-&gt;pr ('Event type:     ', $self-&gt;get4Char (), "\n");
    
    $self-&gt;pr ('Actions:  ', NToSigned ($self-&gt;read (4)), "\n");
    $self-&gt;pr ('Reserved: ', NToSigned ($self-&gt;read (4)), "\n");
    $self-&gt;unwrapAtoms ($pos + 12, $len - 12);
}

sub dump_list {
    my $self = shift;
    my ($pos, $len) = @_;

    $self-&gt;pr ('Id:    ', NToSigned ($self-&gt;read (4)), "\n");
    $self-&gt;pr ('Items: ', NToSigned ($self-&gt;read (4)), "\n");
    $self-&gt;unwrapAtoms ($pos + 8, $len - 8);
}

sub dump_oper {
    my $self = shift;
    my ($pos, $len) = @_;

    $self-&gt;pr ('Operation: ', $self-&gt;get4Char (), "\n");
    $self-&gt;pr ('Operands:  ', NToSigned ($self-&gt;read (4)), "\n");
    $self-&gt;pr ('Reserved: ', NToSigned ($self-&gt;read (4)), "\n");
    $self-&gt;unwrapAtoms ($pos + 12, $len - 12);
}

sub dump_oprn {
    my $self = shift;
    my ($pos, $len) = @_;

    $self-&gt;pr ('Unknown 1: ', NToSigned ($self-&gt;read (4)), "\n");
    $self-&gt;pr ('ID:        ', NToSigned ($self-&gt;read (4)), "\n");
    $self-&gt;pr ('Unknown 3: ', NToSigned ($self-&gt;read (4)), "\n");
    $self-&gt;unwrapAtoms ($pos + 12, $len - 12);
}

sub dump_actn {
    my $self = shift;
    my ($pos, $len) = @_;

    $self-&gt;pr ('Action type: ', NToSigned ($self-&gt;read (4)), "\n");
    $self-&gt;pr ('Actions:     ', NToSigned ($self-&gt;read (4)), "\n");
    $self-&gt;pr ('Reserved:    ', NToSigned ($self-&gt;read (4)), "\n");
    $self-&gt;unwrapAtoms ($pos + 12, $len - 12);
}

sub dump_whic {
    my $self = shift;
    my ($pos, $len) = @_;
    my %actions =
        (
	1024 =&gt; 'kActionMovieSetVolume',
	1025 =&gt; 'kActionMovieSetRate',
	1026 =&gt; 'kActionMovieSetLoopingFlags',
	1027 =&gt; 'kActionMovieGoToTime',
	1028 =&gt; 'kActionMovieGoToTimeByName',
	1029 =&gt; 'kActionMovieGoToBeginning',
	1030 =&gt; 'kActionMovieGoToEnd',
	1031 =&gt; 'kActionMovieStepForward',
	1032 =&gt; 'kActionMovieStepBackward',
	1033 =&gt; 'kActionMovieSetSelection',
	1034 =&gt; 'kActionMovieSetSelectionByName',
	1035 =&gt; 'kActionMoviePlaySelection',
	1036 =&gt; 'kActionMovieSetLanguage',
	1037 =&gt; 'kActionMovieChanged',
	1038 =&gt; 'kActionMovieRestartAtTime',
	2048 =&gt; 'kActionTrackSetVolume',
	2049 =&gt; 'kActionTrackSetBalance',
	2050 =&gt; 'kActionTrackSetEnabled',
	2051 =&gt; 'kActionTrackSetMatrix',
	2052 =&gt; 'kActionTrackSetLayer',
	2053 =&gt; 'kActionTrackSetClip',
	2054 =&gt; 'kActionTrackSetCursor',
	2055 =&gt; 'kActionTrackSetGraphicsMode',
	3072 =&gt; 'kActionSpriteSetMatrix',
	3073 =&gt; 'kActionSpriteSetImageIndex',
	3074 =&gt; 'kActionSpriteSetVisible',
	3075 =&gt; 'kActionSpriteSetLayer',
	3076 =&gt; 'kActionSpriteSetGraphicsMode',
	3078 =&gt; 'kActionSpritePassMouseToCodec',
	3079 =&gt; 'kActionSpriteClickOnCodec',
	3080 =&gt; 'kActionSpriteTranslate',
	3081 =&gt; 'kActionSpriteScale',
	3082 =&gt; 'kActionSpriteRotate',
	3083 =&gt; 'kActionSpriteStretch',
	4096 =&gt; 'kActionQTVRSetPanAngle',
	4097 =&gt; 'kActionQTVRSetTiltAngle',
	4098 =&gt; 'kActionQTVRSetFieldOfView',
	4099 =&gt; 'kActionQTVRShowDefaultView',
	4100 =&gt; 'kActionQTVRGoToNodeID',
	5120 =&gt; 'kActionMusicPlayNote',
	5121 =&gt; 'kActionMusicSetController',
	6144 =&gt; 'kActionCase',
	6145 =&gt; 'kActionWhile',
	6146 =&gt; 'kActionGoToURL',
	6147 =&gt; 'kActionSendQTEventToSprite',
	6148 =&gt; 'kActionDebugStr',
	6149 =&gt; 'kActionPushCurrentTime',
	6150 =&gt; 'kActionPushCurrentTimeWithLabel',
	6151 =&gt; 'kActionPopAndGotoTopTime',
	6152 =&gt; 'kActionPopAndGotoLabeledTime',
	6153 =&gt; 'kActionStatusString',
	6154 =&gt; 'kActionSendQTEventToTrackObject',
	6155 =&gt; 'kActionAddChannelSubscription',
	6156 =&gt; 'kActionRemoveChannelSubscription',
	6157 =&gt; 'kActionOpenCustomActionHandler',
	6158 =&gt; 'kActionDoScript',
	7168 =&gt; 'kActionSpriteTrackSetVariable',
	7169 =&gt; 'kActionSpriteTrackNewSprite',
	7170 =&gt; 'kActionSpriteTrackDisposeSprite',
	7171 =&gt; 'kActionSpriteTrackSetVariableToString',
	7172 =&gt; 'kActionSpriteTrackConcatVariables',
	7173 =&gt; 'kActionSpriteTrackSetVariableToMovieURL',
	7174 =&gt; 'kActionSpriteTrackSetVariableToMovieBaseURL',
	8192 =&gt; 'kActionApplicationNumberAndString',
	9216 =&gt; 'kActionQD3DNamedObjectTranslateTo',
	9217 =&gt; 'kActionQD3DNamedObjectScaleTo',
	9218 =&gt; 'kActionQD3DNamedObjectRotateTo',
	10240 =&gt; 'kActionFlashTrackSetPan',
	10241 =&gt; 'kActionFlashTrackSetZoom',
	10242 =&gt; 'kActionFlashTrackSetZoomRect',
	10243 =&gt; 'kActionFlashTrackGotoFrameNumber',
	10244 =&gt; 'kActionFlashTrackGotoFrameLabel',
	11264 =&gt; 'kActionMovieTrackAddChildMovie',
	11265 =&gt; 'kActionMovieTrackLoadChildMovie',
        );
        
    $self-&gt;pr ('Unknown 1: ', NToSigned ($self-&gt;read (4)), "\n");
    $self-&gt;pr ('Unknown 2: ', NToSigned ($self-&gt;read (4)), "\n");
    $self-&gt;pr ('Unknown 3: ', NToSigned ($self-&gt;read (4)), "\n");
    
    my $action = NToSigned ($self-&gt;read (4));
    my $actionStr = $actions{$action};
    $actionStr = "Unknown - $action" if ! defined $actionStr;
    $self-&gt;pr ("Type: $actionStr\n");
}

sub dump_parm {
    my $self = shift;
    my ($pos, $len) = @_;

    $self-&gt;pr ('Unknown 1: ', NToSigned ($self-&gt;read (4)), "\n");
    $self-&gt;pr ('ID:        ', NToSigned ($self-&gt;read (4)), "\n");
    $self-&gt;pr ('Unknown 3: ', NToSigned ($self-&gt;read (4)), "\n");
    $self-&gt;unwrapAtoms ($pos + 12, $len - 12);
}

sub dump_test {
    my $self = shift;
    my ($pos, $len) = @_;

    $self-&gt;pr ('Unknown 1: ', NToSigned ($self-&gt;read (4)), "\n");
    $self-&gt;pr ('ID:        ', NToSigned ($self-&gt;read (4)), "\n");
    $self-&gt;pr ('Unknown 3: ', NToSigned ($self-&gt;read (4)), "\n");
    $self-&gt;unwrapAtoms ($pos + 12, $len - 12);
}

sub dump_expr {
    my $self = shift;
    my ($pos, $len) = @_;

    $self-&gt;pr ('Unknown 1: ', NToSigned ($self-&gt;read (4)), "\n");
    $self-&gt;pr ('ID:        ', NToSigned ($self-&gt;read (4)), "\n");
    $self-&gt;pr ('Unknown 3: ', NToSigned ($self-&gt;read (4)), "\n");
    $self-&gt;unwrapAtoms ($pos + 12, $len - 12);
}

sub dump_ftyp {
    my $self = shift;
    $self-&gt;pr (unpack ("a4", $self-&gt;read (4)), "\n");
}

sub dump_gmhd {
    my $self = shift;
    $self-&gt;unwrapAtoms (@_);
}

sub dump_gmin {
    my $self = shift;

    $self-&gt;pr ('Version:  ', unpack ('C', $self-&gt;read (1)), "\n");
    $self-&gt;pr ('Flags:    ', unpack ('B24', $self-&gt;read (3)), "\n");
    $self-&gt;showGMode ();
    $self-&gt;showRGB ();
    $self-&gt;pr ('Balance:  ', nToSigned ($self-&gt;read (2)), "\n");
    $self-&gt;pr ('Reserved: ', nToSigned ($self-&gt;read (2)), "\n");
}

sub dump_moov {
    my $self = shift;
    $self-&gt;unwrapAtoms (@_);
}        

sub dump_mvhd {
    my $self = shift;
    my ($pos, $len) = @_;
    my $buffer = $self-&gt;read ($len - 8);

    $self-&gt;pr ('Version:   ', unpack( 'C', substr($buffer,0,1,'') ) . "\n");
    $self-&gt;pr ('Flags:     ', unpack ('B24', substr($buffer,0,3,'')) . "\n");
    $self-&gt;pr ('Created:   ', $self-&gt;showDate (substr($buffer,0,4,'')) . "\n");   
    $self-&gt;pr ('Modified:  ', $self-&gt;showDate (substr($buffer,0,4,'')) . "\n");   
    $self-&gt;pr ('Timescale: ', unpack( "N", substr($buffer,0,4,'')) . "\n");  
    $self-&gt;pr ('Duration:  ', unpack( "N", substr($buffer,0,4,'')) . "\n");  
    $self-&gt;pr ('Pref rate: ', NToFixed (substr($buffer,0,4,'')) . "\n");
    $self-&gt;pr ('Pref vol:  ', unpack( "n", substr($buffer,0,2,'')) . "\n");
    $self-&gt;pr ('reserved:  ', unpack( "H20", substr($buffer,0,10,'')) . "\n");
    $self-&gt;pr ('Matrix:    ', $self-&gt;showMatrix (substr($buffer,0,36,'')) . "\n");
    $self-&gt;pr ('Preview start: ', unpack( "N", substr($buffer,0,4,'')) . "\n");  
    $self-&gt;pr ('Preview time:  ', unpack( "N", substr($buffer,0,4,'')) . "\n");  
    $self-&gt;pr ('Poster loc:    ', unpack( "N", substr($buffer,0,4,'')) . "\n");  
    $self-&gt;pr ('Sel start:  ', unpack( "N", substr($buffer,0,4,'')) . "\n");  
    $self-&gt;pr ('Sel time:   ', unpack( "N", substr($buffer,0,4,'')) . "\n");  
    $self-&gt;pr ('Time now:   ', unpack( "N", substr($buffer,0,4,'')) . "\n");
    my $nextTrackId = unpack( "N", substr($buffer,0,4,''));
    $self-&gt;pr ("Next track: $nextTrackId\n");
    $self-&gt;tracks ($nextTrackId - 1);
}

sub dump_udta {
    my $self = shift;
    $self-&gt;unwrapAtoms (@_);
}

sub dump_mdia {
    my $self = shift;
    $self-&gt;unwrapAtoms (@_);
}

sub dump_minf {
    my $self = shift;
    $self-&gt;unwrapAtoms (@_);
}

sub dump_free {
    my $self = shift;
    my ($pos, $len) = @_;

    $self-&gt;pr ("Padding = $len bytes\n");
}

sub dump_wide {
    my $self = shift;
    my ($pos, $len) = @_;

    $self-&gt;pr ("64 bit expansion place holder\n");
}

sub dump_trak {
    my $self = shift;
    $self-&gt;unwrapAtoms (@_);
}

sub dump_stbl {
    my $self = shift;
    $self-&gt;unwrapAtoms (@_);
}

sub dump_stco {
    my $self = shift;
    my ($pos, $len) = @_;
    my $dataRef;
    my $index = -1;
    my $limit = $self-&gt;atomStack_count ();
    
    while (-$index &lt; $limit) {
        $dataRef = \%{$self-&gt;atomStack_index ($index--)-&gt;[1]};
        next if ! exists $dataRef-&gt;{'HdlrSubCmpt'};
        last if $dataRef-&gt;{'HdlrSubCmpt'} ne 'alis';
    } 

    $self-&gt;pr ('Version:  ', unpack ('C', $self-&gt;read (1)), "\n");
    $self-&gt;pr ('Flags:    ', unpack ('B24', $self-&gt;read (3)), "\n");
    
    my $entries = NToSigned ($self-&gt;read (4));
    my $type = (defined $dataRef &amp;&amp; $dataRef-&gt;{'HdlrSubCmpt'}) || '';
    $self-&gt;pr ('Entries:  ', $entries, " ($type)\n");
    
    while ($entries--) {
        my $off = NToSigned ($self-&gt;read (4));
        $self-&gt;pr ('   Offset:  ', sprintf "%d (0x%04x)\n", $off, $off);
        next if $type ne 'sprt';
        $self-&gt;describeAtom ($off + 12);
    }
}

sub dump_sean {
    my $self = shift;
    my ($pos, $len) = @_;
    my $end = $pos + $len;
    
    $pos += 20;
    $self-&gt;describeAtomsIn ($pos, $end);
}

sub dump_sprt {
    my $self = shift;
    $self-&gt;atomList (@_);
}        

sub dump_stsh {
    my $self = shift;
    $self-&gt;dump_stsz (@_);
}

sub dump_stsc {
    my $self = shift;
    $self-&gt;dump_stts (@_);
}

sub dump_stsd {
    my $self = shift;
    $self-&gt;atomList (@_);
}

sub dump_stst {
    my $self = shift;
    $self-&gt;dump_dref (@_);
}

sub dump_stsz {
    my $self = shift;

    $self-&gt;pr ('Version:  ', unpack ('C', $self-&gt;read (1)), "\n");
    $self-&gt;pr ('Flags:    ', unpack ('B24', $self-&gt;read (3)), "\n");
    $self-&gt;pr ('Samp size: ', NToSigned ($self-&gt;read (4)), "\n");
    $self-&gt;pr ('Entries:   ', NToSigned ($self-&gt;read (4)), "\n");
}

sub dump_stts {
    my $self = shift;
    $self-&gt;atomList (@_);
}

sub dump_stss {
    my $self = shift;
    $self-&gt;dump_stts (@_);
}

sub dump_edts {
    my $self = shift;
    $self-&gt;unwrapAtoms (@_);
}

sub dump_tkhd {
    my $self = shift;

    $self-&gt;pr ('Version:  ', unpack ('C', $self-&gt;read (1)), "\n");
    $self-&gt;pr ('Flags:    ', unpack ('B24', $self-&gt;read (3)), "\n");
    $self-&gt;pr ('Creation time:     ', $self-&gt;showDate (), "\n");
    $self-&gt;pr ('Modification time: ', $self-&gt;showDate (), "\n");
    $self-&gt;pr ('Track ID:          ', unpack( "N", $self-&gt;read (4)), "\n");
    $self-&gt;pr ('Reserved:          ', unpack( "N", $self-&gt;read (4)), "\n");
    $self-&gt;pr ('Duration:          ', NToSigned ($self-&gt;read (4)), "\n");
    $self-&gt;pr ('Reserved:          ', unpack( "NN", $self-&gt;read (8)), "\n");
    $self-&gt;pr ('Layer:             ', nToSigned ($self-&gt;read (2)), "\n");
    $self-&gt;pr ('Alternate group:   ', nToSigned ($self-&gt;read (2)), "\n");
    $self-&gt;pr ('Volume:            ', nToUnsigned($self-&gt;read (2)), "\n");
    $self-&gt;pr ('Reserved:          ', unpack( "n", $self-&gt;read (2)), "\n");
    $self-&gt;pr ('Matrix structure:  ', $self-&gt;showMatrix (), "\n");
    $self-&gt;pr ('Track width:       ', NToFixed ($self-&gt;read (4)), "\n");
    $self-&gt;pr ('Track height:      ', NToFixed ($self-&gt;read (4)), "\n");
}

sub dump_mdhd {
    my $self = shift;

    $self-&gt;pr ('Version:  ', unpack ('C', $self-&gt;read (1)), "\n");
    $self-&gt;pr ('Flags:    ', unpack ('B24', $self-&gt;read (3)), "\n");
    $self-&gt;pr ('Creation time:     ', $self-&gt;showDate (), "\n");
    $self-&gt;pr ('Modification time: ', $self-&gt;showDate (), "\n");
    $self-&gt;pr ('Time scale:        ', NToSigned ($self-&gt;read (4)), "\n");
    $self-&gt;pr ('Duration:          ', NToSigned ($self-&gt;read (4)), "\n");
    $self-&gt;pr ('Locale:            ', nToSigned ($self-&gt;read (2)), "\n");
    $self-&gt;pr ('Quality:           ', unpack ('B16', $self-&gt;read (2)), "\n");
}

sub dump_hdlr {
    my $self = shift;
    my $dataRef = \%{$self-&gt;atomStack_index (-2)-&gt;[1]};
    
    $self-&gt;pr ('Version:  ', unpack ('C', $self-&gt;read (1)), "\n");
    $self-&gt;pr ('Flags:    ', unpack ('B24', $self-&gt;read (3)), "\n");
    
    my $cmpt = $self-&gt;get4Char ();
    $self-&gt;pr ('Component type:     ', $cmpt, "\n");
    
    my $subCmpt = $self-&gt;get4Char ();
    $self-&gt;pr ('Component sub type: ', $subCmpt, "\n");
    
    $dataRef-&gt;{'HdlrCmpt'} = $cmpt;
    $dataRef-&gt;{'HdlrSubCmpt'} = $subCmpt;
    
    $self-&gt;pr ('Manufacturer:       ', $self-&gt;get4Char (), "\n");
    $self-&gt;pr ('Flags:              ', unpack ('B32', $self-&gt;read (4)), "\n");
    $self-&gt;pr ('Mask:               ', unpack ('B32', $self-&gt;read (4)), "\n");
    my $strLen = ord ($self-&gt;read (1));
    $self-&gt;pr ('Name:               ', unpack ("a$strLen", $self-&gt;read ($strLen)), "\n");
}

sub dump_elst {
    my $self = shift;

    $self-&gt;pr ('Version:  ', unpack ('C', $self-&gt;read (1)), "\n");
    $self-&gt;pr ('Flags:    ', unpack ('B24', $self-&gt;read (3)), "\n");
    
    my $items = NToSigned ($self-&gt;read (4));
    $self-&gt;pr ("Items:     $items\n");
    for (1..$items) {
        $self-&gt;pr ("  Item $_\n");
        $self-&gt;pr ('    Duration: ', NToSigned ($self-&gt;read (4)), "\n");
        $self-&gt;pr ('    Start:    ', NToSigned ($self-&gt;read (4)), "\n");
        $self-&gt;pr ('    Rate:     ', NToFixed ($self-&gt;read (4)), "\n");
    }
}

sub dump_dcom {
    my $self = shift;
    my ($pos, $len) = @_;
    my $buffer = $self-&gt;read ($len - 8);
}

sub dump_clip {
    my $self = shift;
    $self-&gt;unwrapAtoms (@_);
}

sub dump_MCPS {
    my $self = shift;
    $self-&gt;dumpText (@_);
}

sub dump_name {
    my $self = shift;
    $self-&gt;dumpText (@_);
}

sub dump_vmhd {
    my $self = shift;
    my $parent = $self-&gt;atomStack_index (-2)-&gt;[0];
    
    if ($parent eq 'minf') {
        $self-&gt;pr ('Version:  ', unpack ('C', $self-&gt;read (1)), "\n");
        $self-&gt;pr ('Flags:    ', unpack ('B24', $self-&gt;read (3)), "\n");
        
        $self-&gt;showGraphicsXferMode ();
        $self-&gt;showRGB ();
    } else {
        $self-&gt;pr ("Unhandled context ($parent) for VideoMediaInfo atom\n");
    }
}

sub dump_WLOC {
    my $self = shift;
    my ($pos, $len) = @_;

    $len = 2 * $len - 16;
    $self-&gt;pr (unpack ("H$len\n", $self-&gt;read ($len)), "\n");
}

sub dump_x00000001 {
    my $self = shift;
    my $parentType = $self-&gt;atomStack_index (-2)-&gt;[0];

    if ($parentType eq 'oprn') {
        my ($pos, $len) = @_;
    
        $self-&gt;pr ('Unknown 1: ', NToSigned ($self-&gt;read (4)), "\n");
        $self-&gt;pr ('ID:        ', NToSigned ($self-&gt;read (4)), "\n");
        $self-&gt;pr ('Unknown 3: ', NToSigned ($self-&gt;read (4)), "\n");
        $self-&gt;unwrapAtoms ($pos + 12, $len - 12);
    } else {
        $self-&gt;showBogus ();
        $self-&gt;pr ('Matrix structure:  ', $self-&gt;showMatrix (), "\n");
    }
}

sub dump_x00000002 {
    my $self = shift;
    
    $self-&gt;pr ("Constant\n");
    $self-&gt;pr ('Unknown 1: ', NToSigned ($self-&gt;read (4)), "\n");
    $self-&gt;pr ('ID:        ', NToSigned ($self-&gt;read (4)), "\n");
    $self-&gt;pr ('Unknown 3: ', NToSigned ($self-&gt;read (4)), "\n");
    $self-&gt;pr ('Value:     ', nToSigned ($self-&gt;read (2)), "\n");
}

sub dump_x00000004 {
    my $self = shift;

    $self-&gt;showBogus ();
    $self-&gt;pr ('Visible:  ', nToSigned ($self-&gt;read (2)), "\n");
}

sub dump_x00000005 {
    my $self = shift;
    $self-&gt;showBogus ();
    $self-&gt;pr ('Layer:  ', nToSigned ($self-&gt;read (2)), "\n");
}

sub dump_x00000006 {
    my $self = shift;

    $self-&gt;showPlayMode ();
    $self-&gt;showBogus ();
    $self-&gt;showRGB ();
}

sub dump_x00000015 {
    my $self = shift;
    $self-&gt;pr ("Quicktime version\n");
}

sub dump_x00000064 {
    my $self = shift;
    $self-&gt;showBogus ();
    $self-&gt;pr ('Image index: ', nToSigned ($self-&gt;read (2)), "\n");
}

sub dump_x00000065 {
    my $self = shift;

    $self-&gt;pr ("Background colour:\n");
    $self-&gt;showBogus ();
    $self-&gt;showRGB ();
}

sub dump_x00000066 {
    my $self = shift;
    $self-&gt;showBogus ();
    $self-&gt;pr ('Offscreen bit depth: ', nToSigned ($self-&gt;read (2)), "\n");
}

sub dump_x00000067 {
    my $self = shift;
    $self-&gt;showBogus ();
    $self-&gt;pr ('Sample format: ', nToSigned ($self-&gt;read (2)), "\n");
}

sub dump_x00000c00 {
    my $self = shift;
    $self-&gt;pr ("Sprite bounds left\n");
}

sub dump_x00000c01 {
    my $self = shift;
    $self-&gt;pr ("Sprite bounds top\n");
}

sub dump_x00000c03 {
    my $self = shift;
    $self-&gt;pr ("Sprite bounds bottom\n");
}

sub dump_x00000c04 {
    my $self = shift;
    $self-&gt;pr ("Sprite bounds right\n");
}

sub dump_x00000c05 {
    my $self = shift;
    $self-&gt;pr ("Sprite is visible\n");
}

sub dump_x00000c06 {
    my $self = shift;
    $self-&gt;pr ("Sprite layer\n");
}

sub dump_x00000c07 {
    my $self = shift;
    $self-&gt;pr ("Sprite track variable\n");
}

sub dump_x00001400 {
    my $self = shift;
    $self-&gt;pr ("Mouse local h loc\n");
}

sub dump_x00001401 {
    my $self = shift;
    $self-&gt;pr ("Mouse local v loc\n");
}

sub dump_x00001402 {
    my $self = shift;
    $self-&gt;pr ("Key is down\n");
}

sub dumpBlock {
    my $self = shift;
    my ($pos, $len) = @_;
    
    while ($len) {
        my $chunk = $len &gt; 16 ? 16 : $len;
        my $str = $self-&gt;read ($chunk);
        
        $str =~ s/([\x00-\x1f\x80-\xff])/sprintf "\\x%02x", ord ($1)/ge;
        $self-&gt;pr ("$str\n");
        $len -= $chunk;
    }
}

sub dumpText {
    my $self = shift;
    my ($pos, $len) = @_;

    $len -= 8;
    $self-&gt;pr (unpack ("a$len", $self-&gt;read ($len)), "\n");
}

sub show {
    local $_;
    my $thing = shift;
    if ($thing =~ /^([^\x00]*)\x00\Z/) {
        return $1;
    } elsif ($thing =~ /[\x00-\x1f]/) {
        my $sum = 0;
        my @chars = split '', $thing;
        $sum = $sum * 256 + ord ($_) for @chars;
        return sprintf "0x%0x", $sum;
    }
    
    return $thing;
}

sub showBogus {
    my $self = shift;

    $self-&gt;pr ('Version:  ', unpack ('C', $self-&gt;read (1)), "\n");
    $self-&gt;pr ('Flags:    ', unpack ('B24', $self-&gt;read (3)), "\n");
    $self-&gt;pr ('Reserved: ', NToSigned ($self-&gt;read (4)), "\n");
    $self-&gt;pr ('Reserved: ', NToSigned ($self-&gt;read (4)), "\n");
}

sub showPlayMode {
    my $self = shift;
    my $flagBits = shift;
    my $flags = '';
    
    $flagBits = $self-&gt;read (4) if ! defined $flagBits;
    $flagBits = NToSigned ($flagBits);

    $flags .= 'fullScreenHideCursor ' if $flags &amp; 1;
    $flags .= 'fullScreenAllowEvents ' if $flags &amp; 2;
    $flags .= 'fullScreenDontChangeMenuBar ' if $flags &amp; 4;
    $flags .= 'fullScreenPreflightSize ' if $flags &amp; 8;
    $self-&gt;pr ("Play mode flags: $flags\n")
}

sub showGMode {
    my $self = shift;
    my $gMode = shift;
    $gMode = $self-&gt;read (2) if ! defined $gMode;
    $gMode = NToSigned ($gMode);

    my %modes =
        (
            0x0000 =&gt; 'Copy',
            0x0040 =&gt; 'Dither copy',
            0x0020 =&gt; 'Blend',
            0x0024 =&gt; 'Transparent',
            0x0100 =&gt; 'Straight alpha',
            0x0101 =&gt; 'Premul white alpha',
            0x0102 =&gt; 'Premul black alpha',
            0x0104 =&gt; 'Straight alpha blend',
            0x0103 =&gt; 'Composition (dither copy)',
        );
        
    $self-&gt;pr ("Graphics mode: $modes{$gMode}\n")
}

sub showRGB {
    my $self = shift;
    my ($red, $green, $blue) = @_;
    
    $red = $self-&gt;read (2) if ! defined $red;
    $green = $self-&gt;read (2) if ! defined $green;
    $blue = $self-&gt;read (2) if ! defined $blue;
    $red = nToUnsigned ($red);
    $green = nToUnsigned ($green);
    $blue = nToUnsigned ($blue);
    
    $self-&gt;pr ("Red:   $red\n");
    $self-&gt;pr ("Green: $green\n");
    $self-&gt;pr ("Blue:  $blue\n");
}

sub showGraphicsXferMode {
    my $self = shift;
    my $gMode = shift;

    $gMode = $self-&gt;read (2) if ! defined $gMode;
    $gMode = nToSigned ($gMode);
    
    my %modes =
        (
            0 =&gt; 'srcCopy',
            1 =&gt; 'srcOr',
            2 =&gt; 'srcXor',
            3 =&gt; 'srcBic',
            4 =&gt; 'notSrcCopy',
            5 =&gt; 'notSrcOr',
            6 =&gt; 'notSrcXor',
            7 =&gt; 'notSrcBic',
            8 =&gt; 'patCopy',
            9 =&gt; 'patOr',
            10 =&gt; 'patXor',
            11 =&gt; 'patBic',
            12 =&gt; 'notPatCopy',
            13 =&gt; 'notPatOr',
            14 =&gt; 'notPatXor',
            15 =&gt; 'notPatBic',
            49 =&gt; 'grayishTextOr',
            50 =&gt; 'hilite',
            50 =&gt; 'hilitetransfermode',
            32 =&gt; 'blend',
            33 =&gt; 'addPin',
            34 =&gt; 'addOver',
            35 =&gt; 'subPin',
            37 =&gt; 'addMax',
            37 =&gt; 'adMax',
            38 =&gt; 'subOver',
            39 =&gt; 'adMin',
            64 =&gt; 'ditherCopy',
            36 =&gt; 'transparent',
        );
    
    if (exists $modes{$gMode}) {
        $self-&gt;pr ('Mode:  ', $modes{$gMode}, "\n");
    } else {
        $self-&gt;pr ('Mode:  unknown - ', $gMode, "\n");
    }
}

sub showDate {
    my $self = shift;
    my $stamp = shift;
    
    $stamp = $self-&gt;read (4) if ! defined $stamp;
    $stamp = NToUnsigned ($stamp);
    
    # seconds difference between Mac epoch and Unix/Windows.
    my $mod = ($^O =~ /MSWin32/) ? (2063824538 - 12530100 + 31536000) : (2063824538 - 12530100);
    my $date = ($^O =~ /Mac/) ? localtime($stamp) : localtime($stamp-$mod);
    return $date;
}

sub showMatrix {
    my $self = shift;
    my $matrix = shift;
    
    $matrix = $self-&gt;read (36) if ! defined $matrix;
    
    my $str = '';
    for (1..3) {
        my $sub = substr $matrix, 0, 12, '';
        $str .= NToFixed (substr $sub, 0, 4, '') . ' ';
        $str .= NToFixed (substr $sub, 0, 4, '') . ' ';
        $str .= NToFrac (substr $sub, 0, 4, '') . ' ';
        $str .= ' / ' if $_ != 3;
    }
    
    return $str;
}

sub showStr {
    my $self = shift;
    my $pos = shift;
    my ($len, $key) = unpack ("Na4", $self-&gt;read (8, $pos));

    $len -= 12;
    $self-&gt;pr (unpack ("a$len", $self-&gt;read ($len, $pos + 12)), "\n");
}

sub get4Char {
    my $self = shift;
    return unpack ("a4", $self-&gt;read (4));
}

sub NToFixed {
    my $str = shift;
    return unpack ('l', pack ('l', unpack( "N", $str))) / 0x10000;
}

sub NToFrac {
    my $str = shift;
    my $fract = unpack ('l', pack ('l', unpack( "N", $str)));
    return $fract / 0x40000000;
}

sub NToSigned {
    my $str = shift;
    return unpack ('l', pack ('l', unpack( "N", $str)));
}

sub NToUnsigned {
    my $str = shift;
    return unpack ('L', pack ('L', unpack( "N", $str)));
}

sub nToSigned {
    my $str = shift;
    return unpack ('s', pack ('s', unpack( "n", $str)));
}

sub nToUnsigned {
    my $str = shift;
    return unpack ('S', pack ('S', unpack( "n", $str)));
}


#1;

package main;

use Tk;
use Tk::Tree;

local $| = 1;

my $file = shift;

if (defined $file) {
    $file = DumpQuicktime-&gt;new(-file=&gt;$file);
    my $str = $file-&gt;dump;
    my $main = MainWindow-&gt;new (-title =&gt; "Quicktime dump of $file-&gt;{'filename'}");
    my $tree = $main-&gt;ScrlTree
        (
        -font =&gt; 'FixedSys 8',
        -itemtype =&gt; 'text', -separator  =&gt; '/',
        -scrollbars =&gt; "osoe"
        );
    
    my @pathStack;
    my $lastLine = 0;
    my $savedTail;
    my $catchIndented;
    my $maxLineLenght = 0;
    my $maxNesting = 0;
    my $totalLines = 0;
    my $currIndent = '';
    my $indentStr = $file-&gt;indentStr;
    
    push @pathStack, 0;
    
    for my $line (split "\n", $str) {
        chomp $line;
        next if length ($line) == 0; # Skip blank lines
      
        my ($newIndent, $nodeText) = $line =~ /^((?:\Q$indentStr\E)*)(.*)/;
      
        while (length ($newIndent) &gt; length ($currIndent)) {# new project
            push @pathStack, 0;
            $currIndent .= $indentStr;
        }
      
        while (length ($newIndent) &lt; length ($currIndent)) {# new project
            pop @pathStack;
            substr $currIndent, 0, length $indentStr, '';
        }
      
        $pathStack[-1]++;
        $maxNesting = @pathStack if $maxNesting &lt; @pathStack;
        my $currPath = join "/", @pathStack;
        $tree-&gt;add ($currPath, -text =&gt; $nodeText);

        ++$totalLines;
        $maxLineLenght = length ($nodeText) if length ($nodeText) &gt; $maxLineLenght;
    }
    
    $totalLines = 40 if $totalLines &gt; 40;
    $main-&gt;geometry (($maxLineLenght + $maxNesting * 4) * 5 . 'x' . (40 + $totalLines * 20));
    closeTree ($tree, '');
    
    $tree-&gt;pack(-fill=&gt;'both',-expand =&gt; 1);
    
    MainLoop;
} else {
    print &lt;&lt;HELP;
DumpQuicktime parses a QuickTime movie file and dumps a report of the file's
structure to STDOUT.

Run DumpQuicktime as:

DumpQuicktime filename
HELP
}

sub closeTree {
    my $tree = shift;
    my ($entryPath, $hideChildren) = @_;
    my @children = $tree-&gt;info (children =&gt; $entryPath);

    return if ! @children;

    for (@children) {
        closeTree ($tree, $_, 1);
        $tree-&gt;hide ('entry' =&gt; $_) if $hideChildren;
    }
    $tree-&gt;setmode ($entryPath, 'open') if length $entryPath;
}
&lt;/c&gt;
&lt;/readmore&gt;
&lt;div class="pmsig"&gt;&lt;div class="pmsig-461912"&gt;
&lt;hr&gt;DWIM is Perl's answer to G&amp;ouml;del
&lt;/div&gt;&lt;/div&gt;</field>
</data>
</node>
