<?php

class BitStream {
          private 
$_data;
          private 
$_byte_offset;
          private 
$_bit_offset;
      function 
__construct($data) {
              
$this->_data $data;
          
$this->_byte_offset 0;
          
$this->_bit_offset  0;
      }
      function 
getBits($bit_width) {
        
$value 0;
        for(
$i=0$i$bit_width$i++) {
          
$value <<= 1;
          
$value |= $this->getBit();
        }
        return 
$value;
      }
      function 
getBit() {
              
$shift_width $this->_bit_offset;
          
$bit = (ord($this->_data{$this->_byte_offset}) >> $shift_width) & 0x1;
          
$this->_bit_offset ++;
          if (
$this->_bit_offset == 8) {
            
$this->_byte_offset ++;
            
$this->_bit_offset 0;
          }
          return 
$bit;
      }
      function 
getBytesLE($bytes) {
              
$this->align();
          
$value 0;
          for (
$i=$i<$bytes $i++) {
            
$value >>= 8;
            
$value |= $this->getByte() << (* ($bytes 1));
          }
          return 
$value;
      }
      function 
getByte() {
              
$this->align();
              
$byte ord($this->_data{$this->_byte_offset});
          
$this->_byte_offset ++;
          return 
$byte;
      }
      function 
align() {
              if (
$this->_bit_offset 0) {
                  
$this->_byte_offset ++;
              
$this->_bit_offset 0;
          }
      }
      function 
getByteOffset() {
              return 
$this->_byte_offset;
      }
      function 
setByteOffset($offset) {
              return 
$this->_byte_offset $offset;
      }

}

class 
SWFEditor {
        private 
$_header = array();
    private 
$_block_list = array();
        function 
__construct($filename) {
                
$fp fopen($filename'rb');
        
$magic fread($fp3);
        
$version ord(fread($fp1));
        
$len_le fread($fp4); // little endian
        
$len_a unpack('V'$len_le);
        
$filelength $len_a[1];
                
$this->_header['magic'] = $magic;
                
$this->_header['version'] = $version;
        
$this->_header['filelength'] = $filelength;
        if (
$magic == 'FWS') {
                
$movie_and_body fread($fp$filelength);
        } elseif (
$magic == 'CWS') {
                
$movie_and_body gzuncompress(fread($fp$filelength));
        } else {
                return ;
        }
        
        list(
$movie_header$movie_header_length) =
                
$this->get_movie_header($movie_and_body);
        
$this->_header['movie_header'] = $movie_header;
        
// 
        
$offset $movie_header_length;
        while(
true) {
                list(
$block$block_length) = $this->get_block($movie_and_body$offset);
            
$this->_block_list [] = $block;
            if (
$block['tag'] == 0) {
                    break;
            }
            
$offset += $block_length;
        }
        
    }
    function 
get_movie_header($movie_and_body) {
      
$movie_header = array();
      
$bs = new BitStream($movie_and_body);
      
$movie_align $bs->getBits(5);
      
$movie_header['align'] = $movie_align;
      
$movie_header['x_min'] = $bs->getBits($movie_align);
      
$movie_header['x_max'] = $bs->getBits($movie_align);
      
$movie_header['y_min'] = $bs->getBits($movie_align);
      
$movie_header['y_max'] = $bs->getBits($movie_align);
      
$bs->align();
      
$movie_header['frame_rate_decimal'] = $bs->getByte();
      
$movie_header['frame_rate_integer'] = $bs->getByte();
      
$movie_header['frame_rate_count'] = $bs->getBytesLE(2);
      
$movie_header_length $bs->getByteOffset();
      return array(
$movie_header$movie_header_length);
    }
    function 
get_block($data$offset) {
      
$first_offset $offset;
      
$bs = new BitStream($data);
      
$bs->setByteOffset($offset);
      
$tag_and_length =$bs->getBytesLE(2); // important
      
$tag $tag_and_length >> 6;
      
$length $tag_and_length 0x3f;
      if (
$length == 0x3f) {
              
$length $bs->getBytesLE(4);
      }
      
$data_offset $bs->getByteOffset();
      
$block = array();
      
$block['tag'] = $tag;
      
$block['length'] = $length;
      
$block['rawdata'] = substr($data$data_offset$length);
      return array(
$block$data_offset $length $first_offset);
    }
    private 
$_tagNameTable = array(=> 'End',
                       
=> 'ShowFrame',
                       
=> 'DefineShape',
                       
=> 'DefineBitsJPEG',
                       
=> 'JPEGTables',
                       
=> 'SetBackgroundColor',
                       
12 =>'DoAction',
                       
26 =>'PlaceObject2'
                       
);

    function 
getTagName($tag) {
            if (isset(
$this->_tagNameTable[$tag])) {
                return 
$this->_tagNameTable[$tag];
        }
        return 
$tag;
    }
    function 
dump() {
                
var_dump($this->_header);
        foreach (
$this->_block_list as $block) {
                print 
'tag=' $this->getTagName($block['tag']) . ' length=' $block['length'] . "\n";
        }
    }
}