1<?php 2 3///////////////////////////////////////////////////////////////// 4/// getID3() by James Heinrich <info@getid3.org> // 5// available at https://github.com/JamesHeinrich/getID3 // 6// or https://www.getid3.org // 7// or http://getid3.sourceforge.net // 8// see readme.txt for more details // 9///////////////////////////////////////////////////////////////// 10// // 11// module.audio-video.swf.php // 12// module for analyzing Shockwave Flash files // 13// dependencies: NONE // 14// /// 15///////////////////////////////////////////////////////////////// 16 17if (!defined('GETID3_INCLUDEPATH')) { // prevent path-exposing attacks that access modules directly on public webservers 18 exit; 19} 20 21class getid3_swf extends getid3_handler 22{ 23 public $ReturnAllTagData = false; 24 25 /** 26 * @return bool 27 */ 28 public function Analyze() { 29 $info = &$this->getid3->info; 30 31 $info['fileformat'] = 'swf'; 32 $info['video']['dataformat'] = 'swf'; 33 34 // http://www.openswf.org/spec/SWFfileformat.html 35 36 $this->fseek($info['avdataoffset']); 37 38 $SWFfileData = $this->fread($info['avdataend'] - $info['avdataoffset']); // 8 + 2 + 2 + max(9) bytes NOT including Frame_Size RECT data 39 40 $info['swf']['header']['signature'] = substr($SWFfileData, 0, 3); 41 switch ($info['swf']['header']['signature']) { 42 case 'FWS': 43 $info['swf']['header']['compressed'] = false; 44 break; 45 46 case 'CWS': 47 $info['swf']['header']['compressed'] = true; 48 break; 49 50 default: 51 $this->error('Expecting "FWS" or "CWS" at offset '.$info['avdataoffset'].', found "'.getid3_lib::PrintHexBytes($info['swf']['header']['signature']).'"'); 52 unset($info['swf']); 53 unset($info['fileformat']); 54 return false; 55 } 56 $info['swf']['header']['version'] = getid3_lib::LittleEndian2Int(substr($SWFfileData, 3, 1)); 57 $info['swf']['header']['length'] = getid3_lib::LittleEndian2Int(substr($SWFfileData, 4, 4)); 58 59 if ($info['swf']['header']['compressed']) { 60 $SWFHead = substr($SWFfileData, 0, 8); 61 $SWFfileData = substr($SWFfileData, 8); 62 if ($decompressed = @gzuncompress($SWFfileData)) { 63 $SWFfileData = $SWFHead.$decompressed; 64 } else { 65 $this->error('Error decompressing compressed SWF data ('.strlen($SWFfileData).' bytes compressed, should be '.($info['swf']['header']['length'] - 8).' bytes uncompressed)'); 66 return false; 67 } 68 } 69 70 $FrameSizeBitsPerValue = (ord(substr($SWFfileData, 8, 1)) & 0xF8) >> 3; 71 $FrameSizeDataLength = ceil((5 + (4 * $FrameSizeBitsPerValue)) / 8); 72 $FrameSizeDataString = str_pad(decbin(ord(substr($SWFfileData, 8, 1)) & 0x07), 3, '0', STR_PAD_LEFT); 73 for ($i = 1; $i < $FrameSizeDataLength; $i++) { 74 $FrameSizeDataString .= str_pad(decbin(ord(substr($SWFfileData, 8 + $i, 1))), 8, '0', STR_PAD_LEFT); 75 } 76 list($X1, $X2, $Y1, $Y2) = explode("\n", wordwrap($FrameSizeDataString, $FrameSizeBitsPerValue, "\n", 1)); 77 $info['swf']['header']['frame_width'] = getid3_lib::Bin2Dec($X2); 78 $info['swf']['header']['frame_height'] = getid3_lib::Bin2Dec($Y2); 79 80 // http://www-lehre.informatik.uni-osnabrueck.de/~fbstark/diplom/docs/swf/Flash_Uncovered.htm 81 // Next in the header is the frame rate, which is kind of weird. 82 // It is supposed to be stored as a 16bit integer, but the first byte 83 // (or last depending on how you look at it) is completely ignored. 84 // Example: 0x000C -> 0x0C -> 12 So the frame rate is 12 fps. 85 86 // Byte at (8 + $FrameSizeDataLength) is always zero and ignored 87 $info['swf']['header']['frame_rate'] = getid3_lib::LittleEndian2Int(substr($SWFfileData, 9 + $FrameSizeDataLength, 1)); 88 $info['swf']['header']['frame_count'] = getid3_lib::LittleEndian2Int(substr($SWFfileData, 10 + $FrameSizeDataLength, 2)); 89 90 $info['video']['frame_rate'] = $info['swf']['header']['frame_rate']; 91 $info['video']['resolution_x'] = intval(round($info['swf']['header']['frame_width'] / 20)); 92 $info['video']['resolution_y'] = intval(round($info['swf']['header']['frame_height'] / 20)); 93 $info['video']['pixel_aspect_ratio'] = (float) 1; 94 95 if (($info['swf']['header']['frame_count'] > 0) && ($info['swf']['header']['frame_rate'] > 0)) { 96 $info['playtime_seconds'] = $info['swf']['header']['frame_count'] / $info['swf']['header']['frame_rate']; 97 } 98//echo __LINE__.'='.number_format(microtime(true) - $start_time, 3).'<br>'; 99 100 101 // SWF tags 102 103 $CurrentOffset = 12 + $FrameSizeDataLength; 104 $SWFdataLength = strlen($SWFfileData); 105 106 while ($CurrentOffset < $SWFdataLength) { 107//echo __LINE__.'='.number_format(microtime(true) - $start_time, 3).'<br>'; 108 109 $TagIDTagLength = getid3_lib::LittleEndian2Int(substr($SWFfileData, $CurrentOffset, 2)); 110 $TagID = ($TagIDTagLength & 0xFFFC) >> 6; 111 $TagLength = ($TagIDTagLength & 0x003F); 112 $CurrentOffset += 2; 113 if ($TagLength == 0x3F) { 114 $TagLength = getid3_lib::LittleEndian2Int(substr($SWFfileData, $CurrentOffset, 4)); 115 $CurrentOffset += 4; 116 } 117 118 unset($TagData); 119 $TagData['offset'] = $CurrentOffset; 120 $TagData['size'] = $TagLength; 121 $TagData['id'] = $TagID; 122 $TagData['data'] = substr($SWFfileData, $CurrentOffset, $TagLength); 123 switch ($TagID) { 124 case 0: // end of movie 125 break 2; 126 127 case 9: // Set background color 128 //$info['swf']['tags'][] = $TagData; 129 $info['swf']['bgcolor'] = strtoupper(str_pad(dechex(getid3_lib::BigEndian2Int($TagData['data'])), 6, '0', STR_PAD_LEFT)); 130 break; 131 132 default: 133 if ($this->ReturnAllTagData) { 134 $info['swf']['tags'][] = $TagData; 135 } 136 break; 137 } 138 139 $CurrentOffset += $TagLength; 140 } 141 142 return true; 143 } 144 145} 146