File Coverage

blib/lib/SWF/Parser.pm
Criterion Covered Total %
statement 86 96 89.5
branch 29 58 50.0
condition 11 25 44.0
subroutine 11 15 73.3
pod 6 7 85.7
total 143 201 71.1


line stmt bran cond sub pod time code
1             package SWF::Parser;
2            
3 1     1   11 use strict;
  1         3  
  1         57  
4 1     1   6 use vars qw($VERSION);
  1         2  
  1         135  
5            
6             $VERSION = '0.11';
7            
8 1     1   6 use SWF::BinStream;
  1         3  
  1         23  
9 1     1   15 use Carp;
  1         2  
  1         606  
10            
11             sub new {
12 1     1 1 143 my $class = shift;
13 1         6 my %param = @_;
14 1         7 my $self = { _tag => {},
15             _version => 5,
16             _aborted => 0,
17             };
18 0     0   0 $self->{_header_callback} =
19             $param{'header-callback'}
20             || $param{'header_callback'}
21 1   50     8 || (sub {0});
22 0     0   0 $self->{_tag_callback} =
23             $param{'tag-callback'}
24             || $param{'tag_callback'}
25 1   50     9 || (sub {0});
26 1 50 33     7 $self->{_header} = {} unless $param{header} and $param{header} =~ /^no(?:ne)?$/;
27 1   33 0   19 $self->{_stream}=$param{'stream'}||(SWF::BinStream::Read->new('', sub{ die "The stream ran short by $_[0] bytes."}));
  0         0  
28            
29            
30 1         7 bless $self, $class;
31             }
32            
33             sub parse {
34 2     2 1 6 my ($self, $data) = @_;
35 2         5 my $stream = $self->{_stream};
36            
37 2 50       8 if ($self->{_aborted}) {
38 0         0 carp 'The SWF parser has been aborted';
39 0         0 return $self;
40             }
41            
42             # unless (defined $data) {
43             # if (my $bytes=$stream->Length) {
44             # carp "Data remains $bytes bytes in the stream.";
45             # }
46             # return $self;
47             # }
48 2         16 $stream->add_stream($data);
49 2         31 eval {{
50 2 100       4 unless (exists $self->{_header}) {
  3         1147  
51 2   66     15 $self->parsetag while !$self->{_aborted} and $stream->Length;
52             } else {
53 1         5 $self->parseheader;
54 1 50 33     10 redo if !$self->{_aborted} and $stream->Length;
55             }
56             }};
57 2 50       15 if ($@) {
58 0 0       0 return $self if ($@=~/^The stream ran short by/);
59 0         0 die $@;
60             }
61 2         20 $self;
62             }
63            
64             sub parse_file {
65 1     1 1 19 my($self, $file) = @_;
66 1     1   7 no strict 'refs'; # so that a symbol ref as $file works
  1         2  
  1         901  
67 1         5 local(*F);
68 1 50 33     10 unless (ref($file) || $file =~ /^\*[\w:]+$/) {
69             # Assume $file is a filename
70 1 50       54 open(F, $file) || die "Can't open $file: $!";
71 1         5 $file = *F;
72             }
73 1         4 binmode($file);
74 1         5 my $chunk = '';
75 1   66     73 while(!$self->{_aborted} and read($file, $chunk, 4096)) {
76 1         7 $self->parse($chunk);
77             }
78 1         18 close($file);
79 1 50       9 $self->eof unless $self->{_aborted};
80             }
81            
82             sub eof
83             {
84 1     1 0 5 shift->parse(undef);
85             }
86            
87             sub parseheader {
88 1     1 1 2 my $self = shift;
89 1         3 my $stream = $self->{_stream};
90 1         3 my $header = $self->{_header};
91            
92 1 50       10 unless (exists $header->{signature}) {
93 1         7 my $h = $header->{signature} = $stream->get_string(3);
94 1 50 33     25 Carp::confess "This is not SWF stream " if ($h ne 'CWS' and $h ne 'FWS');
95             }
96 1 50       9 $stream->Version($header->{version} = $self->{_version} = $stream->get_UI8) unless exists $header->{version};
97 1 50       16 $header->{filelen} = $stream->get_UI32 unless exists $header->{filelen};
98 1 50       25 $stream->add_codec('Zlib') if ($header->{signature} eq 'CWS');
99 1 50       13 $header->{nbits} = $stream->get_bits(5) unless exists $header->{nbits};
100 1         3 my $nbits = $header->{nbits};
101 1 50       17 $header->{xmin} = $stream->get_sbits($nbits) unless exists $header->{xmin};
102 1 50       13 $header->{xmax} = $stream->get_sbits($nbits) unless exists $header->{xmax};
103 1 50       7 $header->{ymin} = $stream->get_sbits($nbits) unless exists $header->{ymin};
104 1 50       12 $header->{ymax} = $stream->get_sbits($nbits) unless exists $header->{ymax};
105 1 50       9 $header->{rate} = $stream->get_UI16 / 256 unless exists $header->{rate};
106 1 50       23 $header->{count} = $stream->get_UI16 unless exists $header->{count};
107            
108 1         16 $self->{_header_callback}->($self, @{$header}{qw(signature version filelen xmin ymin xmax ymax rate count)});
  1         7  
109 1         42 delete $self->{_header};
110             }
111            
112             sub parsetag {
113 6     6 1 46 my $self = shift;
114 6         15 my $tag = $self->{_tag};
115 6         10 my $stream = $self->{_stream};
116 6 50       35 $tag->{header}=$stream->get_UI16 unless exists $tag->{header};
117 6 50       119 unless (exists $tag->{length}) {
118 6         13 my $length = ($tag->{header} & 0x3f);
119 6 100       19 $length=$stream->get_UI32 if ($length == 0x3f);
120 6         24 $tag->{length}=$length;
121             }
122 6 50       25 unless (exists $tag->{data}) {
123 6         20 $stream->_require($tag->{length});
124 6         11 $tag->{data} = $stream;
125 6         21 $tag->{_next_pos} = $stream->tell + $tag->{length};
126             }
127 6         11 eval {
128 6         30 $self->{_tag_callback}->($self, $tag->{header} >> 6, $tag->{length}, $tag->{data});
129             };
130 6 50       231 if ($@) {
131 0 0       0 Carp::confess 'Short!' if ($@=~/^The stream ran short by/);
132 0         0 die $@;
133             }
134 6         23 my $offset = $tag->{_next_pos} - $stream->tell;
135 6 50       17 Carp::confess 'Short!' if $offset < 0;
136 6 50       32 $stream->get_string($offset) if $offset > 0;
137 6         48 $self->{_tag}={};
138             }
139            
140             sub abort {
141 0     0 1   shift->{_aborted} = 1;
142             }
143            
144             1;
145            
146             __END__