00001 <?php
00028 class MacBinary {
00029 function __construct( $filename ) {
00030 $this->open( $filename );
00031 $this->loadHeader();
00032 }
00033
00040 function open( $filename ) {
00041 $this->valid = false;
00042 $this->version = 0;
00043 $this->filename = '';
00044 $this->dataLength = 0;
00045 $this->resourceLength = 0;
00046 $this->handle = fopen( $filename, 'rb' );
00047 }
00048
00053 function isValid() {
00054 return $this->valid;
00055 }
00056
00061 function dataForkLength() {
00062 return $this->dataLength;
00063 }
00064
00070 function extractData( $destination ) {
00071 if( !$this->isValid() ) {
00072 return false;
00073 }
00074
00075
00076 fseek( $this->handle, 128 );
00077 return $this->copyBytesTo( $destination, $this->dataLength );
00078 }
00079
00083 function close() {
00084 fclose( $this->handle );
00085 }
00086
00087
00088
00098 function loadHeader() {
00099 $fname = 'MacBinary::loadHeader';
00100
00101 fseek( $this->handle, 0 );
00102 $head = fread( $this->handle, 128 );
00103 #$this->hexdump( $head );
00104
00105 if( strlen( $head ) < 128 ) {
00106 wfDebug( "$fname: couldn't read full MacBinary header\n" );
00107 return false;
00108 }
00109
00110 if( $head{0} != "\x00" || $head{74} != "\x00" ) {
00111 wfDebug( "$fname: header bytes 0 and 74 not null\n" );
00112 return false;
00113 }
00114
00115 $signature = substr( $head, 102, 4 );
00116 $a = unpack( "ncrc", substr( $head, 124, 2 ) );
00117 $storedCRC = $a['crc'];
00118 $calculatedCRC = $this->calcCRC( substr( $head, 0, 124 ) );
00119 if( $storedCRC == $calculatedCRC ) {
00120 if( $signature == 'mBIN' ) {
00121 $this->version = 3;
00122 } else {
00123 $this->version = 2;
00124 }
00125 } else {
00126 $crc = sprintf( "%x != %x", $storedCRC, $calculatedCRC );
00127 if( $storedCRC == 0 && $head{82} == "\x00" &&
00128 substr( $head, 101, 24 ) == str_repeat( "\x00", 24 ) ) {
00129 wfDebug( "$fname: no CRC, looks like MacBinary I\n" );
00130 $this->version = 1;
00131 } elseif( $signature == 'mBIN' && $storedCRC == 0x185 ) {
00132
00133
00134 wfDebug( "$fname: CRC doesn't match ($crc), looks like Mac IE 5.0\n" );
00135 $this->version = 3;
00136 } else {
00137 wfDebug( "$fname: CRC doesn't match ($crc) and not MacBinary I\n" );
00138 return false;
00139 }
00140 }
00141
00142 $nameLength = ord( $head{1} );
00143 if( $nameLength < 1 || $nameLength > 63 ) {
00144 wfDebug( "$fname: invalid filename size $nameLength\n" );
00145 return false;
00146 }
00147 $this->filename = substr( $head, 2, $nameLength );
00148
00149 $forks = unpack( "Ndata/Nresource", substr( $head, 83, 8 ) );
00150 $this->dataLength = $forks['data'];
00151 $this->resourceLength = $forks['resource'];
00152 $maxForkLength = 0x7fffff;
00153
00154 if( $this->dataLength < 0 || $this->dataLength > $maxForkLength ) {
00155 wfDebug( "$fname: invalid data fork length $this->dataLength\n" );
00156 return false;
00157 }
00158
00159 if( $this->resourceLength < 0 || $this->resourceLength > $maxForkLength ) {
00160 wfDebug( "$fname: invalid resource fork size $this->resourceLength\n" );
00161 return false;
00162 }
00163
00164 wfDebug( "$fname: appears to be MacBinary $this->version, data length $this->dataLength\n" );
00165 $this->valid = true;
00166 return true;
00167 }
00168
00181 function calcCRC( $data, $seed = 0 ) {
00182 # An array useful for CRC calculations that use 0x1021 as the "seed":
00183 $MAGIC = array(
00184 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50a5, 0x60c6, 0x70e7,
00185 0x8108, 0x9129, 0xa14a, 0xb16b, 0xc18c, 0xd1ad, 0xe1ce, 0xf1ef,
00186 0x1231, 0x0210, 0x3273, 0x2252, 0x52b5, 0x4294, 0x72f7, 0x62d6,
00187 0x9339, 0x8318, 0xb37b, 0xa35a, 0xd3bd, 0xc39c, 0xf3ff, 0xe3de,
00188 0x2462, 0x3443, 0x0420, 0x1401, 0x64e6, 0x74c7, 0x44a4, 0x5485,
00189 0xa56a, 0xb54b, 0x8528, 0x9509, 0xe5ee, 0xf5cf, 0xc5ac, 0xd58d,
00190 0x3653, 0x2672, 0x1611, 0x0630, 0x76d7, 0x66f6, 0x5695, 0x46b4,
00191 0xb75b, 0xa77a, 0x9719, 0x8738, 0xf7df, 0xe7fe, 0xd79d, 0xc7bc,
00192 0x48c4, 0x58e5, 0x6886, 0x78a7, 0x0840, 0x1861, 0x2802, 0x3823,
00193 0xc9cc, 0xd9ed, 0xe98e, 0xf9af, 0x8948, 0x9969, 0xa90a, 0xb92b,
00194 0x5af5, 0x4ad4, 0x7ab7, 0x6a96, 0x1a71, 0x0a50, 0x3a33, 0x2a12,
00195 0xdbfd, 0xcbdc, 0xfbbf, 0xeb9e, 0x9b79, 0x8b58, 0xbb3b, 0xab1a,
00196 0x6ca6, 0x7c87, 0x4ce4, 0x5cc5, 0x2c22, 0x3c03, 0x0c60, 0x1c41,
00197 0xedae, 0xfd8f, 0xcdec, 0xddcd, 0xad2a, 0xbd0b, 0x8d68, 0x9d49,
00198 0x7e97, 0x6eb6, 0x5ed5, 0x4ef4, 0x3e13, 0x2e32, 0x1e51, 0x0e70,
00199 0xff9f, 0xefbe, 0xdfdd, 0xcffc, 0xbf1b, 0xaf3a, 0x9f59, 0x8f78,
00200 0x9188, 0x81a9, 0xb1ca, 0xa1eb, 0xd10c, 0xc12d, 0xf14e, 0xe16f,
00201 0x1080, 0x00a1, 0x30c2, 0x20e3, 0x5004, 0x4025, 0x7046, 0x6067,
00202 0x83b9, 0x9398, 0xa3fb, 0xb3da, 0xc33d, 0xd31c, 0xe37f, 0xf35e,
00203 0x02b1, 0x1290, 0x22f3, 0x32d2, 0x4235, 0x5214, 0x6277, 0x7256,
00204 0xb5ea, 0xa5cb, 0x95a8, 0x8589, 0xf56e, 0xe54f, 0xd52c, 0xc50d,
00205 0x34e2, 0x24c3, 0x14a0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405,
00206 0xa7db, 0xb7fa, 0x8799, 0x97b8, 0xe75f, 0xf77e, 0xc71d, 0xd73c,
00207 0x26d3, 0x36f2, 0x0691, 0x16b0, 0x6657, 0x7676, 0x4615, 0x5634,
00208 0xd94c, 0xc96d, 0xf90e, 0xe92f, 0x99c8, 0x89e9, 0xb98a, 0xa9ab,
00209 0x5844, 0x4865, 0x7806, 0x6827, 0x18c0, 0x08e1, 0x3882, 0x28a3,
00210 0xcb7d, 0xdb5c, 0xeb3f, 0xfb1e, 0x8bf9, 0x9bd8, 0xabbb, 0xbb9a,
00211 0x4a75, 0x5a54, 0x6a37, 0x7a16, 0x0af1, 0x1ad0, 0x2ab3, 0x3a92,
00212 0xfd2e, 0xed0f, 0xdd6c, 0xcd4d, 0xbdaa, 0xad8b, 0x9de8, 0x8dc9,
00213 0x7c26, 0x6c07, 0x5c64, 0x4c45, 0x3ca2, 0x2c83, 0x1ce0, 0x0cc1,
00214 0xef1f, 0xff3e, 0xcf5d, 0xdf7c, 0xaf9b, 0xbfba, 0x8fd9, 0x9ff8,
00215 0x6e17, 0x7e36, 0x4e55, 0x5e74, 0x2e93, 0x3eb2, 0x0ed1, 0x1ef0
00216 );
00217 $len = strlen( $data );
00218 $crc = $seed;
00219 for( $i = 0; $i < $len; $i++ ) {
00220 $crc ^= ord( $data{$i} ) << 8;
00221 $crc &= 0xFFFF;
00222 $crc = ($crc << 8) ^ $MAGIC[$crc >> 8];
00223 $crc &= 0xFFFF;
00224 }
00225 return $crc;
00226 }
00227
00234 function copyBytesTo( $destination, $bytesToCopy ) {
00235 $bufferSize = 65536;
00236 for( $remaining = $bytesToCopy; $remaining > 0; $remaining -= $bufferSize ) {
00237 $thisChunkSize = min( $remaining, $bufferSize );
00238 $buffer = fread( $this->handle, $thisChunkSize );
00239 fwrite( $destination, $buffer );
00240 }
00241 }
00242
00247 function hexdump( $data ) {
00248 global $wgDebugLogFile;
00249 if( !$wgDebugLogFile ) return;
00250
00251 $width = 16;
00252 $at = 0;
00253 for( $remaining = strlen( $data ); $remaining > 0; $remaining -= $width ) {
00254 $line = sprintf( "%04x:", $at );
00255 $printable = '';
00256 for( $i = 0; $i < $width && $remaining - $i > 0; $i++ ) {
00257 $byte = ord( $data{$at++} );
00258 $line .= sprintf( " %02x", $byte );
00259 $printable .= ($byte >= 32 && $byte <= 126 )
00260 ? chr( $byte )
00261 : '.';
00262 }
00263 if( $i < $width ) {
00264 $line .= str_repeat( ' ', $width - $i );
00265 }
00266 wfDebug( "MacBinary: $line $printable\n" );
00267 }
00268 }
00269 }