1package SWF::BinStream;
2
3use strict;
4use vars qw($VERSION);
5
6$VERSION="0.11";
7
8##
9
10package SWF::BinStream::Read;
11
12use Carp;
13use Data::TemporaryBag;
14
15
16sub new {
17    my ($class, $initialdata, $shortsub, $version) = @_;
18    my $self = bless {
19	    '_bits'  => '',
20	    '_stream'   =>Data::TemporaryBag->new,
21	    '_shortsub' =>$shortsub||sub{0},
22	    '_pos'      => 0,
23	    '_codec' => [],
24	    '_version' => $version||5,
25	    '_lock_version' => 0,
26	  }, $class;
27    $self->add_stream($initialdata) if $initialdata ne '';
28    $self;
29}
30
31sub Version {
32    my ($self, $ver) = @_;
33
34    if (defined $ver) {
35	croak "Can't change SWF version " if $self->{_lock_version};
36	$self->{_version} = $ver;
37    }
38    $self->{_version};
39}
40
41sub _lock_version {
42    shift->{_lock_version} = 1;
43}
44
45sub add_stream {
46    my ($self, $data) = @_;
47
48    for my $codec ( @{$self->{'_codec'}} ) {
49	$data = $codec->decode($data);
50    }
51    $self->{'_stream'}->add($data);
52}
53
54sub _require {
55    my ($self, $bytes) = @_;
56    {
57	my $len=$self->{'_stream'}->length;
58
59	if ($len < $bytes) {
60	    $self->{'_shortsub'}->($self, $bytes-$len) and redo;
61	    croak "Stream ran short ";
62	}
63    }
64
65}
66
67sub Length {
68    return $_[0]->{'_stream'}->length;
69}
70
71sub tell {$_[0]->{'_pos'}};
72
73sub get_string {
74    my ($self, $bytes, $fNoFlush) = @_;
75
76    flush_bits($self) unless $fNoFlush;
77    _require($self, $bytes);
78    $self->{'_pos'}+=$bytes;
79    $self->{'_stream'}->substr(0, $bytes, '');
80}
81
82sub lookahead_string {
83    my ($self, $offset, $bytes) = @_;
84
85    _require($self, $offset);
86    $self->{'_stream'}->substr($offset, $bytes);
87}
88
89sub get_UI8 {
90    unpack 'C', get_string(shift, 1);
91}
92
93sub lookahead_UI8 {
94    unpack 'C', lookahead_string(@_[0, 1], 1);
95}
96
97sub get_SI8 {
98    unpack 'c', get_string(shift, 1);
99}
100
101sub lookahead_SI8 {
102    unpack 'c', lookahead_string(@_[0, 1], 1);
103}
104
105sub get_UI16 {
106    unpack 'v', get_string(shift, 2);
107}
108
109sub lookahead_UI16 {
110    unpack 'v', lookahead_string(@_[0, 1], 2);
111}
112
113sub get_SI16 {
114    my $w = &get_UI16;
115    $w -= (1<<16) if $w>=(1<<15);
116    $w;
117}
118
119sub lookahead_SI16 {
120    my $w = &lookahead_UI16;
121    $w -= (1<<16) if $w>=(1<<15);
122    $w;
123}
124
125sub get_UI32 {
126    unpack 'V', get_string(shift, 4);
127}
128
129sub lookahead_UI32 {
130    unpack 'V', lookahead_string(@_[0, 1], 4);
131}
132
133sub get_SI32 {
134    my $ww = &get_UI32;
135    $ww -= (2**32) if $ww>=(2**31);
136    $ww;
137}
138
139sub lookahead_SI32 {
140    my $ww = &lookahead_UI32;
141    $ww -= (2**32) if $ww>=(2**31);
142    $ww;
143}
144
145sub flush_bits {
146    $_[0]->{'_bits'}='';
147}
148
149sub get_bits {
150    my ($self, $bits) = @_;
151    my $len = length($self->{'_bits'});
152
153    if ( $len < $bits) {
154	my $slen = (($bits - $len - 1) >>3) + 1;
155	$self->{'_bits'}.=join '', unpack('B8' x $slen, $self->get_string($slen, 'NoFlush'));
156    }
157    unpack('N', pack('B32', '0' x (32-$bits).substr($self->{'_bits'}, 0, $bits, '')));
158}
159
160sub get_sbits {
161    my ($self, $bits) = @_;
162
163    my $b = &get_bits;
164    $b -= (2**$bits) if $b>=(2**($bits-1));
165    $b;
166}
167
168sub close {
169    my $self = shift;
170
171    for my $codec ( @{$self->{'_codec'}} ) {
172	$codec->close;
173    }
174    $self->{'_stream'}->clear;
175}
176
177
178sub add_codec {
179    my ($self, $codec) = @_;
180
181    require "SWF/BinStream/Codec/${codec}.pm" or croak "Can't find codec '$codec'";
182
183    my $m = "SWF::BinStream::Codec::${codec}::Read"->new or croak "Can't find codec '$codec' ";
184
185    push @{$self->{'_codec'}}, $m;
186
187    if (( my $old_stream = $self->{'_stream'})->length > 0) {
188	my $new_stream = Data::TemporaryBag->new;
189
190	while ($old_stream->length > 0) {
191	    $new_stream->add($m->decode($old_stream->substr(0, 1024, '')));
192	}
193	$self->{'_stream'} = $new_stream;
194    }
195}
196
1971;
198
199package SWF::BinStream::Write;
200
201use Carp;
202use Data::TemporaryBag;
203
204sub new {
205    my ($class, $version) = @_;
206    bless { '_bits'    => '',
207	    '_stream'  => Data::TemporaryBag->new,
208	    '_pos' => 0,
209	    '_flushsize' => 0,
210	    '_mark' => {},
211	    '_codec' => [],
212	    '_version' => $version || 5,
213	    '_lock_version' => 0,
214	    '_framecount' => 0,
215	  }, $class;
216}
217
218sub Version {
219    my ($self, $ver) = @_;
220
221    if (defined $ver) {
222	croak "Can't change SWF version " if $self->{_lock_version};
223	$self->{_version} = $ver;
224    }
225    $self->{_version};
226}
227
228sub _lock_version {
229    shift->{_lock_version} = 1;
230}
231
232sub autoflush {
233    my ($self, $size, $flushsub)=@_;
234
235    $self->{'_flushsize'}=$size;
236    $self->{'_flushsub'}=$flushsub;
237}
238
239sub _write_stream {
240    my ($self, $data) = @_;
241
242    for my $codec ( @{$self->{'_codec'}} ) {
243	$data = $codec->encode($data);
244    }
245    return if $data eq '';
246
247    $self->{'_stream'}->add($data);
248
249    if ($self->{'_flushsize'}>0 and $self->{'_stream'}->length >= $self->{'_flushsize'}) {
250	$self->flush_stream($self->{'_flushsize'});
251    }
252}
253
254sub flush_stream {
255    my ($self, $size)=@_;
256    my $str;
257
258    if ( !$size or $size>$self->Length ) {
259	$self->flush_bits;
260    }
261
262    if ($size) {
263	$str = $self->{'_stream'}->substr( 0, $size, '');
264	$self->{'_pos'} += length($str);
265    } else {
266	$str=$self->{'_stream'}->value;
267	$self->{'_pos'}+=length($str);
268	$self->{'_stream'}=Data::TemporaryBag->new;
269    }
270
271    $self->{'_flushsub'}->($self, $str) if defined $self->{'_flushsub'};
272
273    $str;
274}
275
276sub flush_bits {
277    my $self = $_[0];
278    my $bits = $self->{'_bits'};
279    my $len  = length($bits);
280
281    return if $len<=0;
282    $self->{'_bits'}='';
283    $self->_write_stream(pack('B8', $bits.('0'x(8-$len))));
284}
285
286sub Length {
287    return $_[0]->{'_stream'}->length;
288}
289
290sub tell {
291    my $self=shift;
292    my $pos= $self->{'_pos'} + $self->Length;
293    $pos++ if length($self->{'_bits'})>0;
294    $pos;
295}
296
297sub mark {
298    my ($self, $key, $obj)=@_;
299
300    if (not defined $key) {
301	return %{$self->{_mark}};
302    } elsif (not defined $obj) {
303	return wantarray ? $self->{_mark}{$key}[0] : @{$self->{_mark}{$key}};
304    } else {
305	push @{$self->{_mark}{$key}}, $self->tell, $obj;
306    }
307}
308
309sub sub_stream {
310    my $self=shift;
311    my $sub_stream=SWF::BinStream::Write->new($self->Version);
312    $sub_stream->{_parent}=$self;
313    bless $sub_stream, 'SWF::BinStream::Write::SubStream';
314}
315
316sub set_string {
317    my ($self, $str) = @_;
318
319    $self->flush_bits;
320    $self->_write_stream($str);
321}
322
323sub _round {
324    my $a=shift;
325
326    return 0 unless $a;
327    return int($a+0.5*($a<=>0));
328}
329
330sub set_UI8 {
331    $_[0]->set_string(pack('C', _round($_[1])));
332}
333
334sub set_SI8 {
335    $_[0]->set_string(pack('c', _round($_[1])));
336}
337
338sub set_UI16 {
339    $_[0]->set_string(pack('v', _round($_[1])));
340}
341
342*set_SI16 = \&set_UI16;
343
344#sub set_SI16 {
345#    my ($self, $num) = @_;
346#    $num += (1<<16) if $num<0;
347#    $self->set_UI16($num);
348#}
349
350sub set_UI32 {
351    $_[0]->set_string(pack('V', _round($_[1])));
352}
353
354*set_SI32 = \&set_UI32;
355
356#sub set_SI32 {
357#    my ($self, $num) = @_;
358#    $num += (2**32) if $num<0;
359#    $self->set_UI32($num);
360#}
361
362sub set_bits {
363    my ($self, $num, $nbits) = @_;
364    return unless $nbits;
365    $self->{'_bits'} .= substr(unpack('B*',pack('N', _round($num))), -$nbits);
366    my $s = '';
367    while (length($self->{'_bits'})>=8) {
368	$s .= pack('B8', substr($self->{'_bits'}, 0,8, ''));
369    }
370    $self->{'_stream'}->add($s) if $s ne '';
371}
372
373sub set_sbits {
374    my ($self, $num, $nbits) = @_;
375    $num=_round($num);
376    $num += (2**$nbits) if $num<0;
377    $self->set_bits($num, $nbits);
378}
379
380sub set_bits_list {
381    my ($self, $nbitsbit, @param) = @_;
382    my $nbits=get_maxbits_of_bits_list(@param);
383    my $i;
384
385    $self->set_bits($nbits, $nbitsbit);
386    foreach $i (@param) {
387	$self->set_bits($i, $nbits);
388    }
389}
390
391sub set_sbits_list {
392    my ($self, $nbitsbit, @param) = @_;
393    my $nbits=get_maxbits_of_sbits_list(@param);
394    my $i;
395
396    $self->set_bits($nbits, $nbitsbit);
397    foreach $i (@param) {
398	$self->set_sbits($i, $nbits);
399    }
400}
401
402sub get_maxbits_of_bits_list {
403    my (@param)=@_;
404    my $max=shift;
405    my $i;
406
407    foreach $i(@param) {
408	$max=$i if $max<$i;
409    }
410    $i = 0;
411    $i++ while ($max >= 2**$i);
412    return $i;
413}
414
415sub get_maxbits_of_sbits_list {
416    my $z = 0;
417    return (get_maxbits_of_bits_list(map{my $r=_round($_);$z ||= ($r!=0);($r<0)?(~$r):$r} @_)+$z);
418}
419
420sub close {
421    my $self = shift;
422
423    my $data = $self->flush_stream;
424    my $rest = '';
425    for my $codec ( @{$self->{'_codec'}} ) {
426	$rest = $codec->close($rest);
427    }
428    $self->{'_flushsub'}->($self, $rest) if defined $self->{'_flushsub'};
429
430    $data .= $rest;
431    $data;
432}
433
434sub add_codec {
435    my ($self, $codec) = @_;
436
437    require "SWF/BinStream/Codec/${codec}.pm" or croak "Can't find codec '$codec'";
438
439    my $m = "SWF::BinStream::Codec::${codec}::Write"->new or croak "Can't find codec '$codec'";
440
441    push @{$self->{'_codec'}}, $m;
442}
443
444package SWF::BinStream::Write::SubStream;
445
446use vars qw(@ISA);
447
448@ISA=('SWF::BinStream::Write');
449
450sub flush_stream {
451    my $self = shift;
452    my $p_tell = $self->{_parent}->tell;
453
454    while ((my $data = $self->SUPER::flush_stream(1024)) ne '') {
455	$self->{_parent}->set_string($data);
456    }
457
458    my @marks=$self->mark;
459    while (@marks) {
460	my $key = shift @marks;
461	my $mark = shift @marks;
462	$mark->[$_*2] += $p_tell for (0..@$mark/2-1);
463	push @{$self->{_parent}->{_mark}{$key}}, @$mark;
464    }
465    undef $self;
466}
467
468sub autoflush {} # Ignore autoflush.
469sub add_codec {warn "Can't add codec to the sub stream"}
470*SWF::BinStream::Write::SubStream::close = \&flush_stream;
471
4721;
473
474__END__
475
476=head1 NAME
477
478SWF::BinStream - Read and write binary stream.
479
480=head1 SYNOPSIS
481
482  use SWF::BinStream;
483
484  $read_stream = SWF::BinStream::Read->new($binary_data, \&adddata);
485  $byte = $read_stream->get_UI8;
486  $signedbyte = $read_stream->get_SI8;
487  $string = $read_stream->get_string($length);
488  $bits = $read_stream->get_bits($bitlength);
489  ....
490
491  sub adddata {
492      if ($nextdata) {
493	  shift->add_stream($nextdata);
494      } else {
495	  die "The stream ran short ";
496      }
497  }
498
499  $write_stream = SWF::BinStream::Write->new;
500  $write_stream->set_UI8($byte);
501  $write_stream->set_SI8($signedbyte);
502  $write_stream->set_string($string);
503  $write_stream->set_bits($bits, $bitlength);
504  $binary_data=$write_stream->flush_stream;
505  ....
506
507=head1 DESCRIPTION
508
509I<SWF::BinStream> module provides a binary byte and bit data stream.
510It can handle bit-compressed data such as SWF file.
511
512=head2 SWF::BinStream::Read
513
514Provides a read stream. Add the binary data to the stream, and you
515get byte and bit data. The stream calls a user subroutine when the
516stream data runs short.
517I<get_UI16>, I<get_SI16>, I<get_UI32>, and I<get_SI32> get a number
518in VAX byte order from the stream.
519I<get_bits> and I<get_sbits> get the bits from MSB to LSB.
520I<get_UI*>, I<get_SI*>, and I<get_string> skip the remaining bits in
521the current byte and read data from the next byte.
522If you want to skip remaining bits manually, use I<flush_bits>.
523
524=head2 METHODS
525
526=over 4
527
528=item SWF::BinStream::Read->new( [ $initialdata, \&callback_in_short, $version ] )
529
530Creates a read stream. It takes three optional arguments. The first arg
531is a binary string to set as initial data of the stream. The second is
532a reference of a subroutine which is called when the stream data runs
533short.  The subroutine is called with two ARGS, the first is I<$stream>
534itself, and the second is how many bytes wanted.
535The third arg is SWF version number.  Default is 5.  It is necessary to
536set proper version because some SWF tags change their structure by the
537version number.
538
539=item $stream->Version
540
541returns SWF version number of the stream.
542
543=item $stream->add_codec( $codec_name )
544
545Adds stream decoder.
546Decoder 'Zlib' is only available now.
547
548=item $stream->add_stream( $binary_data )
549
550Adds binary data to the stream.
551
552=item $stream->Length
553
554Returns how many bytes remain in the stream.
555
556=item $stream->tell
557
558Returns how many bytes have been read from the stream.
559
560=item $stream->get_string( $num )
561
562Returns $num bytes as a string.
563
564=item $stream->get_UI8
565
566Returns an unsigned byte number.
567
568=item $stream->get_SI8
569
570Returns a signed byte number.
571
572=item $stream->get_UI16
573
574Returns an unsigned word (2 bytes) number.
575
576=item $stream->get_SI16
577
578Returns a signed word (2 bytes) number.
579
580=item $stream->get_UI32
581
582Returns an unsigned double word (4 bytes) number.
583
584=item $stream->get_SI32
585
586Returns a signed double word (4 bytes) number.
587
588=item $stream->get_bits( $num )
589
590Returns the $num bit unsigned number.
591
592=item $stream->get_sbits( $num )
593
594Returns the $num bit signed number.
595
596=item $stream->lookahead_string( $offset, $num )
597
598=item $stream->lookahead_UI8( $offset )
599
600=item $stream->lookahead_SI8( $offset )
601
602=item $stream->lookahead_UI16( $offset )
603
604=item $stream->lookahead_SI16( $offset )
605
606=item $stream->lookahead_UI32( $offset )
607
608=item $stream->lookahead_SI32( $offset )
609
610Returns the stream data $offset bytes ahead of the current read point.
611The read pointer does not move.
612
613=item $stream->flush_bits
614
615Skips the rest bits in the byte and aligned read pointer to the next byte.
616It does not anything when the read pointer already byte-aligned.
617
618=back
619
620=head2 SWF::BinStream::Write
621
622Provides a write stream. Write byte and bit data, then get the stream
623data as binary string using I<flush_stream>. I<autoflush> requests to
624the stream to automatically flush the stream and call a user subroutine.
625I<set_UI16>, I<set_SI16>, I<set_UI32>, and I<set_SI32> write a number in
626VAX byte order to the stream.
627I<set_bits> and I<set_sbits> write the bits from MSB to LSB.
628I<set_UI*>, I<set_SI*>, and I<set_string> set the rest bits in the last
629byte to 0 and write data to the next byte boundary.
630If you want to write bit data and align the write pointer to byte boundary,
631use I<flush_bits>.
632
633=head2 METHODS
634
635=over 4
636
637=item SWF::BinStream::Write->new( [$version] )
638
639Creates a write stream.
640One optional argument is SWF version number.  Default is 5.
641It is necessary to set proper version because some SWF tags change
642their structure by the version number.
643
644=item $stream->Version( [$version] )
645
646returns SWF version number of the stream.
647You can change the version before you write data to the stream.
648
649=item $stream->add_codec( $codec_name )
650
651Adds stream encoder.
652Encoder 'Zlib' is only available now.
653
654=item $stream->autoflush( $size, \&callback_when_flush )
655
656Requests to the stream to automatically flush the stream and call sub
657with the stream data when the stream size becomes larger than I<$size> bytes.
658
659=item $stream->flush_stream( [$size] )
660
661Flushes the stream and returns the stream data. Call with I<$size>,
662it returns I<$size> bytes from the stream. When call without arg or
663with larger I<$size> than the stream data size, it returns all data
664including the last bit data ( by calling I<flush_bits> internally).
665
666=item $stream->flush_bits
667
668Sets the rest bits in the last byte to 0, and aligns write pointer
669to the next byte boundary.
670
671=item $stream->Length
672
673Returns how many bytes remain in the stream.
674
675=item $stream->tell
676
677Returns how many bytes have written.
678
679=item $stream->mark( [$key, [$obj]] )
680
681Keeps current I<tell> number with $key and $obj.
682When called without $obj, it returns I<tell> number associated
683with $key and a list of I<tell> number and object in scalar and
684list context, respectively.
685When called without any parameter, it returns mark list
686( KEY1, [ TELL_NUMBER1, OBJ1 ], KEY2, [...).
687
688=item $stream->sub_stream
689
690Creates temporaly sub stream. When I<flush_stream> the sub stream,
691it's data and marks are written to the parent stream and the sub
692stream is freed.
693
694Ex. write various length of data following it's length.
695
696  $sub_stream=$parent_stream->sub_stream;
697  write_data($sub_stream);
698  $parent_stream->set_UI32($sub_stream->Length);
699  $sub_stream->flush_stream;
700
701=item $stream->set_string( $str )
702
703Writes string to the stream.
704
705=item $stream->set_UI8( $num )
706
707Writes I<$num> as an unsigned byte.
708
709=item $stream->set_SI8( $num )
710
711Writes I<$num> as a signed byte.
712
713=item $stream->set_UI16( $num )
714
715Writes I<$num> as an unsigned word.
716
717=item $stream->set_SI16( $num )
718
719Writes I<$num> as a signed word.
720
721=item $stream->set_UI32( $num )
722
723Writes I<$num> as an unsigned double word.
724
725=item $stream->set_SI32( $num )
726
727Writes I<$num> as an unsigned double word.
728
729=item $stream->set_bits( $num, $nbits )
730
731Write I<$num> as I<$nbits> length unsigned bit data.
732
733=item $stream->set_sbits( $num, $nbits )
734
735Write I<$num> as I<$nbits> length signed bit data.
736
737=item $stream->set_bits_list( $nbitsbit, @list )
738
739Makes I<@list> as unsigned bit data list.
740It writes the maximal bit length of each I<@list> (I<nbits>) as
741I<$nbitsbit> length unsigned bit data, and then writes each I<@list>
742number as I<nbits> length unsigned bit data.
743
744=item $stream->set_sbits_list( $nbitsbit, @list )
745
746Makes I<@list> as signed bit data list.
747It writes the maximal bit length of each I<@list> (I<nbits>) as
748I<$nbitsbit> length unsigned bit data, and then writes each I<@list>
749number as I<nbits>-length signed bit data.
750
751=back
752
753=head2 UTILITY FUNCTIONS
754
755=over 4
756
757=item &SWF::BinStream::Write::get_maxbits_of_bits_list( @list )
758
759=item &SWF::BinStream::Write::get_maxbits_of_sbits_list( @list )
760
761Gets the necessary and sufficient bit length to represent the values of
762I<@list>.  -_bits_list is for unsigned values, and -_sbits_list is for signed.
763
764=back
765
766=head1 COPYRIGHT
767
768Copyright 2000 Yasuhiro Sasama (ySas), <ysas@nmt.ne.jp>
769
770This library is free software; you can redistribute it
771and/or modify it under the same terms as Perl itself.
772
773=cut
774
775
776
777