The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
package IO::SWF;

use strict;
use warnings;

use base 'Class::Accessor::Fast';
use IO::SWF::Bit;
use IO::SWF::Type::RECT;
use IO::SWF::Tag;
use Compress::Zlib;

__PACKAGE__->mk_accessors( qw(
    _headers
    _header_size
    _tags
    _swfdata
    shape_adjust_mode
));

sub new {
    my ($class, $args) = @_;
    my $self;
    if(ref $args eq 'HASH') {
        return $class->SUPER::new($args);
    }else{
        return $class->SUPER::new;
    }
}

sub _set_headers {
    my ($self, %args) = @_;
    my $header = $self->_headers ? $self->_headers : {};
    foreach my $key (keys %args) {
        $header->{$key} = $args{$key};
    }
    $self->_headers($header);
}

sub _set_tag {
    my ($self, $tag) = @_;
    my @tags = $self->_tags ? @{$self->_tags} : ();
    push @tags, $tag;
    $self->_tags(\@tags);
}

sub parse {
    my ($self, $swfdata) = @_;
    my $reader = IO::SWF::Bit->new();
    $reader->input($swfdata);
    $self->_swfdata($swfdata);
    ## SWF Header ##
    $self->_set_headers(
        'Signature'  => $reader->getData(3),
        'Version'    => $reader->getUI8(),
        'FileLength' => $reader->getUI32LE(),
    );

    if (substr($self->_headers->{'Signature'}, 0, 1) eq 'C') {
        # swf binary compressed by zlib
        my $uncompressed_data = Compress::Zlib::memGunzip(substr($swfdata, 8));
        if (!defined $uncompressed_data) {
            return 0;
        }
        my ($byte_offset, $dummy) = $reader->getOffset();
        $reader->setOffset(0, 0);
        $swfdata = $reader->getData($byte_offset) . $uncompressed_data;
        $reader = IO::SWF::Bit->new();
        $reader->input($swfdata);
        $self->_swfdata($swfdata);
        $reader->setOffset($byte_offset, 0);
    }
    ## SWF Movie Header ##
    my $ret = IO::SWF::Type::RECT::parse($reader);
    $self->_set_headers('FrameSize' => $ret);
    $reader->byteAlign();
    $self->_set_headers(
        'FrameRate'  => $reader->getUI16LE(),
        'FrameCount' => $reader->getUI16LE(),
    );

    my ($header_size, $dummy) = $reader->getOffset();
    $self->_header_size($header_size);

    ## SWF Tags ##
    while (1) {
        my %swfInfo = ('Version' => $self->_headers->{'Version'});
        my $tag = IO::SWF::Tag->new(\%swfInfo);
        $tag->parse($reader);
        $self->_set_tag($tag);
        if ($tag->code == 0) { # END Tag
            last;
        }
    }
    return 1;
}

sub build {
    my $self = shift;
    my $writer_head = IO::SWF::Bit->new();
    my $writer = IO::SWF::Bit->new();

    ## SWF Header ##
    $writer_head->putData($self->_headers->{'Signature'});
    $writer_head->putUI8($self->_headers->{'Version'});
    $writer_head->putUI32LE($self->_headers->{'FileLength'});

    ## SWF Movie Header ##
    IO::SWF::Type::RECT::build($writer, $self->_headers->{'FrameSize'});
    $writer->byteAlign();
    $writer->putUI16LE($self->_headers->{'FrameRate'});
    $writer->putUI16LE($self->_headers->{'FrameCount'});

    ## SWF Tags ##
    my $idx = 0;
    foreach my $tag (@{$self->_tags}) {
        my $tagData = $tag->build();
        if ($tagData) {
            $writer->putData($tagData);
        }
        else {
            die "tag build failed (tag idx=$idx)";
        }
        $idx++;
    }
    my ($fileLength, $bit_offset_dummy) = $writer->getOffset();
    $fileLength += 8; # swf header
    $self->_set_headers('FileLength' => $fileLength);
    $writer_head->setUI32LE($fileLength, 4);
    if (substr($self->_headers->{'Signature'}, 0, 1) eq 'C') {
        return $writer_head->output() . Compress::Zlib::memGzip($writer->output());
    }
    return $writer_head->output().$writer->output();
}

sub dump {
    my ($self, $opts_href) = @_;
    my %opts = ref($opts_href) ? %{$opts_href} : ();
    my $bitio;
    if (defined $opts{'hexdump'}) {
        $bitio = IO::SWF::Bit->new();
        $bitio->input($self->_swfdata);
    }
    ## SWF Header ##
    print 'Signature: '.$self->_headers->{'Signature'}."\n";
    print 'Version: '.$self->_headers->{'Version'}."\n";
    print 'FileLength: '.$self->_headers->{'FileLength'}."\n";
    print 'FrameSize: '. IO::SWF::Type::RECT::string($self->_headers->{'FrameSize'})."\n";
    print 'FrameRate: '.($self->_headers->{'FrameRate'} / 0x100)."\n";
    print 'FrameCount: '.$self->_headers->{'FrameCount'}."\n";

    if ($opts{'hexdump'}) {
        $bitio->hexdump(0, $self->_header_size);
        $opts{'bitio'} = $bitio; # for tag
    }

    ## SWF Tags ##
    
    print 'Tags:'."\n";
    foreach my $tag (@{$self->_tags}) {
        $tag->dump(\%opts);
    }
}

1;