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