195 private function unpack( $block, $offset, $struct ) {
197 foreach ( $struct as $key => $length ) {
199 $data[$key] = substr( $block, $offset, $length );
201 $data[$key] = $this->
bin2dec( $block, $offset, $length );
218 $this->
fseek( $offset );
219 Wikimedia\suppressWarnings();
220 $block = fread( $this->file, $length );
221 Wikimedia\restoreWarnings();
222 if ( $block ===
false ) {
223 $this->
error(
'error reading from file', self::ERROR_READ );
225 if ( strlen( $block ) !== $length ) {
226 $this->
error(
'unable to read the required number of bytes from the file',
227 self::ERROR_READ_PAST_END );
255 $binaryDifat = $this->header[
'difat'];
256 $nextDifatSector = $this->header[
'first_difat_sector'];
257 for ( $i = 0; $i < $this->header[
'num_difat_sectors']; $i++ ) {
258 $block = $this->
readSector( $nextDifatSector );
259 $binaryDifat .= substr( $block, 0, $this->sectorLength - 4 );
260 $nextDifatSector = $this->
bin2dec( $block, $this->sectorLength - 4, 4 );
261 if ( $nextDifatSector == 0xFFFFFFFE ) {
267 for ( $pos = 0; $pos < strlen( $binaryDifat ); $pos += 4 ) {
268 $fatSector = $this->
bin2dec( $binaryDifat, $pos, 4 );
269 if ( $fatSector < 0xFFFFFFFC ) {
270 $this->difat[] = $fatSector;
285 if ( !isset( $this->fat[$fatSectorId] ) ) {
287 if ( !isset( $this->difat[$fatSectorId] ) ) {
288 $this->
error(
'FAT sector requested beyond the end of the DIFAT', self::ERROR_INVALID_FORMAT );
290 $absoluteSectorId = $this->difat[$fatSectorId];
291 $block = $this->
readSector( $absoluteSectorId );
292 for ( $pos = 0; $pos < strlen( $block ); $pos += 4 ) {
295 $this->fat[$fatSectorId] =
$fat;
297 return $this->fat[$fatSectorId];
301 $dirSectorId = $this->header[
'first_dir_sector'];
304 while ( $dirSectorId !== 0xFFFFFFFE ) {
305 if ( isset( $seenSectorIds[$dirSectorId] ) ) {
306 $this->
error(
'FAT loop detected', self::ERROR_INVALID_FORMAT );
308 $seenSectorIds[$dirSectorId] =
true;
310 $binaryDir .= $this->
readSector( $dirSectorId );
324 'create_time_low' => 4,
325 'create_time_high' => 4,
326 'modify_time_low' => 4,
327 'modify_time_high' => 4,
332 $entryLength = array_sum( $struct );
334 for ( $pos = 0; $pos < strlen( $binaryDir ); $pos += $entryLength ) {
335 $entry = $this->
unpack( $binaryDir, $pos, $struct );
339 $entry[
'size_high'] = 0;
341 $type = $entry[
'object_type'];
342 if (
$type == self::TYPE_UNALLOCATED ) {
346 $name = iconv(
'UTF-16LE',
'UTF-8', substr( $entry[
'name_raw'], 0, $entry[
'name_length'] - 2 ) );
349 if (
$type == self::TYPE_ROOT && isset( self::$mimesByClsid[$clsid] ) ) {
350 $this->mimeFromClsid = self::$mimesByClsid[$clsid];
353 if ( $name ===
'Workbook' ) {
354 $this->mime =
'application/vnd.ms-excel';
355 } elseif ( $name ===
'WordDocument' ) {
356 $this->mime =
'application/msword';
357 } elseif ( $name ===
'PowerPoint Document' ) {
358 $this->mime =
'application/vnd.ms-powerpoint';