<?php
// $Id$

function _mapi_video_swf($filename) {
  // Open file
  if (!($handle = @fopen($filename, 'rb'))) {
    return;
  }
  
  // Read SWF header block
  if (($block = @fread($handle, 8)) === FALSE) {
    fclose($handle);
    return;
  }
  
  // process the header block
  $block = unpack('a3signature/Cversion/Vlength', $block);

  // check the signature to verify it is a SWF or compressed SWF
  if ($block['signature'] != 'FWS' && $block['signature'] != 'CWS') {
    fclose($handle);
    return;
  }

  // get the general details since they are uncompressed
  if ($block['signature'] == 'FWS') {
    if (($block = @fread($handle, 13)) === FALSE) {
      fclose($handle);
      return;
    }

    // unpack these next important bytes
    $block = unpack('a9frame/Crate_major/Crate_minor/vcount', $block);

    $dimensions['version'] = $block['version'];

    fclose($handle);
  }
  //
  else {
    // Some kind of compressed method
    if (($byte = @fread($handle, 2)) === FALSE) {
      fclose($handle);
      return;
    }

    // invalid check for this header
    if ($byte % 31 != 0) {
      fclose($handle);
      return;
    }

    // read the details for the compression
    extract(unpack('Ccm/Cflg', $byte));

    // we can only handle standard zlib encoding
    if (($cm & 0xF) != 8) {
      fclose($handle);
      return;
    }

    $wsize = (1 << (($cm >> 4) + 8));

    // cannot handle dictionary compression yet.
    if (($flg & 0x20) == 0x20) {
      fclose($handle);
      return;
    }

    $dimensions['version'] = $block['version'];

    // Read window size, since everything happens in this
    if (($block = @fread($handle, $wsize)) === FALSE) {
      fclose($handle);
      return;
    }

    // don't need to read anymore
    fclose($handle);

    // now we have an index of window size, starting from 1 - $wsize
    $block = unpack('C*', $block);

    $index = 1;
    $bit = 0;
    $state = 0; // state determines what we're going to do

    // where we are going to store the uncompressed data

    $details = array();

    // so now begin decompression routine
    $lens = array(3,4,5,6,7,8,9,10,11,13,15,17,19,23,27,31,35,43,51,59,67,83,99,115,131,163,195,227,258);
    $lext = array(0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0);
    $dists = array(1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193,257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577);
    $dext = array(0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13);

    while ($index <= $wsize) {
      switch ($state) {
        case 0: // read header
          $info = _mapi_video_get_bits($block, $index, $bit, 3);
          $table = array();
          switch ($info >> 1) {
            // uncompressed
            case 0:
              $state = 1;
              $index += 1;
              $bit = 0;
              break;
            // fixed huffman
            case 1:
              // create the table to be used for decoding
              $literals = array_pad(array(), 288, 8);
              $distances = array_pad(array(), 32, 5);

              for ($i = 144; $i <= 255; $i++) {
                $literals[$i] = 9;
              }
              for ($i = 256; $i <= 279; $i++) {
                $literals[$i] = 7;
              }

              $literals = _mapi_video_huffman_construct($literals, 15);
              $distances = _mapi_video_huffman_construct($distances, 15);
              $state = 2;
              break;
            // dynamic huffman
            case 2:
              // decode table
              $lit_count  = _mapi_video_get_bits($block, $index, $bit, 5) + 257;
              $dist_count = _mapi_video_get_bits($block, $index, $bit, 5) + 1;
              $codes      = _mapi_video_get_bits($block, $index, $bit, 4) + 4;

              // the table following is constructed of compressed index codes
              $border = array(16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12 ,3, 13, 2, 14, 1, 15);

              // create frequency table
              $decode = array();
              for ($i = 0; $i < 19; $i++) {
                $decode[$border[$i]] = ($codes > $i ? _mapi_video_get_bits($block, $index, $bit, 3) : 0);
              }

              // create the huffman table
              $decode = _mapi_video_huffman_construct($decode, 7);

              // now construct tables somewhat
              for ($i = 0; $i < $lit_count;) {
                if (($code = _mapi_video_huffman_decode($decode, $block, $index, $bit, 7)) === FALSE) {
                  return;
                }
                if ($code <= 15) {
                  $literals[$i++] = $code;
                }
                elseif ($code <= 18) {
                  $bits = ($code == 16 ? 2 : ($code == 17 ? 3 : 7));
                  $base = ($code < 18 ? 3 : 11);
                  $base += _mapi_video_get_bits($block, $index, $bit, $bits);

                  for ($j = 0; $j < $base; $j++) {
                    $literals[$i] = $literals[$i - 1];
                    $i++;
                  }
                }
              }

              for ($i = 0; $i < $dist_count;) {
                if (($code = _mapi_video_huffman_decode($decode, $block, $index, $bit, 7)) === FALSE) {
                  return;
                }
                if ($code <= 15) {
                  $distances[$i++] = $code;
                }
                elseif ($code <= 18) {
                  $bits = ($code == 16 ? 2 : ($code == 17 ? 3 : 7));
                  $base = ($code < 18 ? 3 : 11);
                  $base += _mapi_video_get_bits($block, $index, $bit, $bits);

                  for ($j = 0; $j < $base; $j++) {
                    $distances[$i] = $distances[$i - 1];
                    $i++;
                  }
                }
              }

              $literals = _mapi_video_huffman_construct($literals, 15);
              $distances = _mapi_video_huffman_construct($distances, 15);
              $state = 2;
              break;
            // error
            case 3:
              return;
          }
          break;
        case 1: // uncompressed
          $length     = _mapi_video_get_bits($block, $index, $bit, 16);
          $compliment = _mapi_video_get_bits($block, $index, $bit, 16);
          for ($i = 0; $i < $length; $i++) {
            $details[] = _mapi_video_get_bits($block, $index, $bit, 8);
          }
          break;
        case 2: // compressed
          if (($code = _mapi_video_huffman_decode($literals, $block, $index, $bit, 15)) === FALSE) {
            return;
          }

          // end of block decompression
          if ($code == 256) {
            break 2;
          }
          elseif ($code <= 255) {
            $details[] = $code;
          }
          elseif ($code >= 257 && $code <= 285) {
            $code -= 257;
            if ($code > 29) {
              return;
            }
            $len = $lens[$code] + _mapi_video_get_bits($block, $index, $bit, $lext[$code]);

            if (($code = _mapi_video_huffman_decode($distances, $block, $index, $bit, 5)) === false) {
              return;
            }
            $dist = $dists[$code] + _mapi_video_get_bits($block, $index, $bits, $dext[$code]);
            $current = count($details) - 1;
            for ($i = 0; $i < $len; $i++) {
              $details[] = $details[count($details) - $dist];
            }
          }
          break;
      }

      if (count($details) > 20) {
        break;
      }
    }

    $block = array(
      'frame' => '',
      'rate_major' => $details[9],
      'rate_minor' => $details[10],
      'count' => $details[11] | ($details[12] << 8),
    );

    for ($i = 0; $i < 9; $i++) {
      $block['frame'] .= chr($details[$i]);
    }
  }

  // get the frame details
  $frame = preg_split('//', $block['frame']);

  // how many bits per index
  $byte = ord($frame[1]);

  // bits per part
  $bpp = ($byte >> 3) & 0x1F;

  $values = array();

  // get the values as a series of bits
  $byte = ($byte << 5) & 0xFF;
  $value = 0;
  $left = 3;
  $index = 1;
  for ($i = 0; $i < 4; $i++) {
    $nbl = $bpp;
    while ($nbl > 0) {
      $bit = ($byte & 0x80) >> 7;
      $byte = ($byte << 1) & 0xFF;
      $left -= 1;

      if ($left <= 0) {
        $index = $index + 1;
        $byte = ord($frame[$index]);
        $left = 8;
      }
      $value = ($value << 1) | $bit;
      $nbl--;
    }
    // convert automatically to pixels (not twips)
    $values[] = $value;
    $value = 0;
  }

  $dimensions['width'] = $values[1] / 20;
  $dimensions['height'] = $values[3] / 20;

  // calculate duration from frame rate, and frame count
  $frame_rate = ($block['rate_major'] + ($block['rate_minor'] / 100));

  $dimensions['duration'] = ($frame_rate ? ($block['count'] / $frame_rate) : 0);

  return array('dimensions' => $dimensions, 'metadata' => array());
}