public function Analyze() { $info =& $this->getid3->info; if (!Utils::intValueSupported($info['filesize'])) { $info['warning'][] = 'Unable to check for APEtags because file is larger than ' . round(PHP_INT_MAX / 1073741824) . 'GB'; return false; } $id3v1tagsize = 128; $apetagheadersize = 32; $lyrics3tagsize = 10; if ($this->overrideendoffset == 0) { $this->fseek(0 - $id3v1tagsize - $apetagheadersize - $lyrics3tagsize, SEEK_END); $APEfooterID3v1 = $this->fread($id3v1tagsize + $apetagheadersize + $lyrics3tagsize); //if (preg_match('/APETAGEX.{24}TAG.{125}$/i', $APEfooterID3v1)) { if (substr($APEfooterID3v1, strlen($APEfooterID3v1) - $id3v1tagsize - $apetagheadersize, 8) == 'APETAGEX') { // APE tag found before ID3v1 $info['ape']['tag_offset_end'] = $info['filesize'] - $id3v1tagsize; //} elseif (preg_match('/APETAGEX.{24}$/i', $APEfooterID3v1)) { } elseif (substr($APEfooterID3v1, strlen($APEfooterID3v1) - $apetagheadersize, 8) == 'APETAGEX') { // APE tag found, no ID3v1 $info['ape']['tag_offset_end'] = $info['filesize']; } } else { $this->fseek($this->overrideendoffset - $apetagheadersize); if ($this->fread(8) == 'APETAGEX') { $info['ape']['tag_offset_end'] = $this->overrideendoffset; } } if (!isset($info['ape']['tag_offset_end'])) { // APE tag not found unset($info['ape']); return false; } // shortcut $thisfile_ape =& $info['ape']; $this->fseek($thisfile_ape['tag_offset_end'] - $apetagheadersize); $APEfooterData = $this->fread(32); if (!($thisfile_ape['footer'] = $this->parseAPEheaderFooter($APEfooterData))) { $info['error'][] = 'Error parsing APE footer at offset ' . $thisfile_ape['tag_offset_end']; return false; } if (isset($thisfile_ape['footer']['flags']['header']) && $thisfile_ape['footer']['flags']['header']) { $this->fseek($thisfile_ape['tag_offset_end'] - $thisfile_ape['footer']['raw']['tagsize'] - $apetagheadersize); $thisfile_ape['tag_offset_start'] = $this->ftell(); $APEtagData = $this->fread($thisfile_ape['footer']['raw']['tagsize'] + $apetagheadersize); } else { $thisfile_ape['tag_offset_start'] = $thisfile_ape['tag_offset_end'] - $thisfile_ape['footer']['raw']['tagsize']; $this->fseek($thisfile_ape['tag_offset_start']); $APEtagData = $this->fread($thisfile_ape['footer']['raw']['tagsize']); } $info['avdataend'] = $thisfile_ape['tag_offset_start']; if (isset($info['id3v1']['tag_offset_start']) && $info['id3v1']['tag_offset_start'] < $thisfile_ape['tag_offset_end']) { $info['warning'][] = 'ID3v1 tag information ignored since it appears to be a false synch in APEtag data'; unset($info['id3v1']); foreach ($info['warning'] as $key => $value) { if ($value == 'Some ID3v1 fields do not use NULL characters for padding') { unset($info['warning'][$key]); sort($info['warning']); break; } } } $offset = 0; if (isset($thisfile_ape['footer']['flags']['header']) && $thisfile_ape['footer']['flags']['header']) { if ($thisfile_ape['header'] = $this->parseAPEheaderFooter(substr($APEtagData, 0, $apetagheadersize))) { $offset += $apetagheadersize; } else { $info['error'][] = 'Error parsing APE header at offset ' . $thisfile_ape['tag_offset_start']; return false; } } // shortcut $info['replay_gain'] = array(); $thisfile_replaygain =& $info['replay_gain']; for ($i = 0; $i < $thisfile_ape['footer']['raw']['tag_items']; $i++) { $value_size = Utils::LittleEndian2Int(substr($APEtagData, $offset, 4)); $offset += 4; $item_flags = Utils::LittleEndian2Int(substr($APEtagData, $offset, 4)); $offset += 4; if (strstr(substr($APEtagData, $offset), "") === false) { $info['error'][] = 'Cannot find null-byte (0x00) seperator between ItemKey #' . $i . ' and value. ItemKey starts ' . $offset . ' bytes into the APE tag, at file offset ' . ($thisfile_ape['tag_offset_start'] + $offset); return false; } $ItemKeyLength = strpos($APEtagData, "", $offset) - $offset; $item_key = strtolower(substr($APEtagData, $offset, $ItemKeyLength)); // shortcut $thisfile_ape['items'][$item_key] = array(); $thisfile_ape_items_current =& $thisfile_ape['items'][$item_key]; $thisfile_ape_items_current['offset'] = $thisfile_ape['tag_offset_start'] + $offset; $offset += $ItemKeyLength + 1; // skip 0x00 terminator $thisfile_ape_items_current['data'] = substr($APEtagData, $offset, $value_size); $offset += $value_size; $thisfile_ape_items_current['flags'] = $this->parseAPEtagFlags($item_flags); switch ($thisfile_ape_items_current['flags']['item_contents_raw']) { case 0: // UTF-8 // UTF-8 case 2: // Locator (URL, filename, etc), UTF-8 encoded $thisfile_ape_items_current['data'] = explode("", $thisfile_ape_items_current['data']); break; case 1: // binary data // binary data default: break; } switch (strtolower($item_key)) { // http://wiki.hydrogenaud.io/index.php?title=ReplayGain#MP3Gain case 'replaygain_track_gain': if (preg_match('#^[\\-\\+][0-9\\.,]{8}$#', $thisfile_ape_items_current['data'][0])) { $thisfile_replaygain['track']['adjustment'] = (double) str_replace(',', '.', $thisfile_ape_items_current['data'][0]); // float casting will see "0,95" as zero! $thisfile_replaygain['track']['originator'] = 'unspecified'; } else { $info['warning'][] = 'MP3gainTrackGain value in APEtag appears invalid: "' . $thisfile_ape_items_current['data'][0] . '"'; } break; case 'replaygain_track_peak': if (preg_match('#^[0-9\\.,]{8}$#', $thisfile_ape_items_current['data'][0])) { $thisfile_replaygain['track']['peak'] = (double) str_replace(',', '.', $thisfile_ape_items_current['data'][0]); // float casting will see "0,95" as zero! $thisfile_replaygain['track']['originator'] = 'unspecified'; if ($thisfile_replaygain['track']['peak'] <= 0) { $info['warning'][] = 'ReplayGain Track peak from APEtag appears invalid: ' . $thisfile_replaygain['track']['peak'] . ' (original value = "' . $thisfile_ape_items_current['data'][0] . '")'; } } else { $info['warning'][] = 'MP3gainTrackPeak value in APEtag appears invalid: "' . $thisfile_ape_items_current['data'][0] . '"'; } break; case 'replaygain_album_gain': if (preg_match('#^[\\-\\+][0-9\\.,]{8}$#', $thisfile_ape_items_current['data'][0])) { $thisfile_replaygain['album']['adjustment'] = (double) str_replace(',', '.', $thisfile_ape_items_current['data'][0]); // float casting will see "0,95" as zero! $thisfile_replaygain['album']['originator'] = 'unspecified'; } else { $info['warning'][] = 'MP3gainAlbumGain value in APEtag appears invalid: "' . $thisfile_ape_items_current['data'][0] . '"'; } break; case 'replaygain_album_peak': if (preg_match('#^[0-9\\.,]{8}$#', $thisfile_ape_items_current['data'][0])) { $thisfile_replaygain['album']['peak'] = (double) str_replace(',', '.', $thisfile_ape_items_current['data'][0]); // float casting will see "0,95" as zero! $thisfile_replaygain['album']['originator'] = 'unspecified'; if ($thisfile_replaygain['album']['peak'] <= 0) { $info['warning'][] = 'ReplayGain Album peak from APEtag appears invalid: ' . $thisfile_replaygain['album']['peak'] . ' (original value = "' . $thisfile_ape_items_current['data'][0] . '")'; } } else { $info['warning'][] = 'MP3gainAlbumPeak value in APEtag appears invalid: "' . $thisfile_ape_items_current['data'][0] . '"'; } break; case 'mp3gain_undo': if (preg_match('#^[\\-\\+][0-9]{3},[\\-\\+][0-9]{3},[NW]$#', $thisfile_ape_items_current['data'][0])) { list($mp3gain_undo_left, $mp3gain_undo_right, $mp3gain_undo_wrap) = explode(',', $thisfile_ape_items_current['data'][0]); $thisfile_replaygain['mp3gain']['undo_left'] = intval($mp3gain_undo_left); $thisfile_replaygain['mp3gain']['undo_right'] = intval($mp3gain_undo_right); $thisfile_replaygain['mp3gain']['undo_wrap'] = $mp3gain_undo_wrap == 'Y' ? true : false; } else { $info['warning'][] = 'MP3gainUndo value in APEtag appears invalid: "' . $thisfile_ape_items_current['data'][0] . '"'; } break; case 'mp3gain_minmax': if (preg_match('#^[0-9]{3},[0-9]{3}$#', $thisfile_ape_items_current['data'][0])) { list($mp3gain_globalgain_min, $mp3gain_globalgain_max) = explode(',', $thisfile_ape_items_current['data'][0]); $thisfile_replaygain['mp3gain']['globalgain_track_min'] = intval($mp3gain_globalgain_min); $thisfile_replaygain['mp3gain']['globalgain_track_max'] = intval($mp3gain_globalgain_max); } else { $info['warning'][] = 'MP3gainMinMax value in APEtag appears invalid: "' . $thisfile_ape_items_current['data'][0] . '"'; } break; case 'mp3gain_album_minmax': if (preg_match('#^[0-9]{3},[0-9]{3}$#', $thisfile_ape_items_current['data'][0])) { list($mp3gain_globalgain_album_min, $mp3gain_globalgain_album_max) = explode(',', $thisfile_ape_items_current['data'][0]); $thisfile_replaygain['mp3gain']['globalgain_album_min'] = intval($mp3gain_globalgain_album_min); $thisfile_replaygain['mp3gain']['globalgain_album_max'] = intval($mp3gain_globalgain_album_max); } else { $info['warning'][] = 'MP3gainAlbumMinMax value in APEtag appears invalid: "' . $thisfile_ape_items_current['data'][0] . '"'; } break; case 'tracknumber': if (is_array($thisfile_ape_items_current['data'])) { foreach ($thisfile_ape_items_current['data'] as $comment) { $thisfile_ape['comments']['track'][] = $comment; } } break; case 'cover art (artist)': case 'cover art (back)': case 'cover art (band logo)': case 'cover art (band)': case 'cover art (colored fish)': case 'cover art (composer)': case 'cover art (conductor)': case 'cover art (front)': case 'cover art (icon)': case 'cover art (illustration)': case 'cover art (lead)': case 'cover art (leaflet)': case 'cover art (lyricist)': case 'cover art (media)': case 'cover art (movie scene)': case 'cover art (other icon)': case 'cover art (other)': case 'cover art (performance)': case 'cover art (publisher logo)': case 'cover art (recording)': case 'cover art (studio)': // list of possible cover arts from http://taglib-sharp.sourcearchive.com/documentation/2.0.3.0-2/Ape_2Tag_8cs-source.html if (is_array($thisfile_ape_items_current['data'])) { $info['warning'][] = 'APEtag "' . $item_key . '" should be flagged as Binary data, but was incorrectly flagged as UTF-8'; $thisfile_ape_items_current['data'] = implode("", $thisfile_ape_items_current['data']); } list($thisfile_ape_items_current['filename'], $thisfile_ape_items_current['data']) = explode("", $thisfile_ape_items_current['data'], 2); $thisfile_ape_items_current['data_offset'] = $thisfile_ape_items_current['offset'] + strlen($thisfile_ape_items_current['filename'] . ""); $thisfile_ape_items_current['data_length'] = strlen($thisfile_ape_items_current['data']); do { $thisfile_ape_items_current['image_mime'] = ''; $imageinfo = array(); $imagechunkcheck = Utils::GetDataImageSize($thisfile_ape_items_current['data'], $imageinfo); if ($imagechunkcheck === false || !isset($imagechunkcheck[2])) { $info['warning'][] = 'APEtag "' . $item_key . '" contains invalid image data'; break; } $thisfile_ape_items_current['image_mime'] = image_type_to_mime_type($imagechunkcheck[2]); if ($this->inline_attachments === false) { // skip entirely unset($thisfile_ape_items_current['data']); break; } if ($this->inline_attachments === true) { // great } elseif (is_int($this->inline_attachments)) { if ($this->inline_attachments < $thisfile_ape_items_current['data_length']) { // too big, skip $info['warning'][] = 'attachment at ' . $thisfile_ape_items_current['offset'] . ' is too large to process inline (' . number_format($thisfile_ape_items_current['data_length']) . ' bytes)'; unset($thisfile_ape_items_current['data']); break; } } elseif (is_string($this->inline_attachments)) { $this->inline_attachments = rtrim(str_replace(array('/', '\\'), DIRECTORY_SEPARATOR, $this->inline_attachments), DIRECTORY_SEPARATOR); if (!is_dir($this->inline_attachments) || !is_writable($this->inline_attachments)) { // cannot write, skip $info['warning'][] = 'attachment at ' . $thisfile_ape_items_current['offset'] . ' cannot be saved to "' . $this->inline_attachments . '" (not writable)'; unset($thisfile_ape_items_current['data']); break; } } // if we get this far, must be OK if (is_string($this->inline_attachments)) { $destination_filename = $this->inline_attachments . DIRECTORY_SEPARATOR . md5($info['filenamepath']) . '_' . $thisfile_ape_items_current['data_offset']; if (!file_exists($destination_filename) || is_writable($destination_filename)) { file_put_contents($destination_filename, $thisfile_ape_items_current['data']); } else { $info['warning'][] = 'attachment at ' . $thisfile_ape_items_current['offset'] . ' cannot be saved to "' . $destination_filename . '" (not writable)'; } $thisfile_ape_items_current['data_filename'] = $destination_filename; unset($thisfile_ape_items_current['data']); } else { if (!isset($info['ape']['comments']['picture'])) { $info['ape']['comments']['picture'] = array(); } $comments_picture_data = array(); foreach (array('data', 'image_mime', 'image_width', 'image_height', 'imagetype', 'picturetype', 'description', 'datalength') as $picture_key) { if (isset($thisfile_ape_items_current[$picture_key])) { $comments_picture_data[$picture_key] = $thisfile_ape_items_current[$picture_key]; } } $info['ape']['comments']['picture'][] = $comments_picture_data; unset($comments_picture_data); } } while (false); break; default: if (is_array($thisfile_ape_items_current['data'])) { foreach ($thisfile_ape_items_current['data'] as $comment) { $thisfile_ape['comments'][strtolower($item_key)][] = $comment; } } break; } } if (empty($thisfile_replaygain)) { unset($info['replay_gain']); } return true; }
public function ASF_WMpicture(&$data) { //typedef struct _WMPicture{ // LPWSTR pwszMIMEType; // BYTE bPictureType; // LPWSTR pwszDescription; // DWORD dwDataLen; // BYTE* pbData; //} WM_PICTURE; $WMpicture = array(); $offset = 0; $WMpicture['image_type_id'] = Utils::LittleEndian2Int(substr($data, $offset, 1)); $offset += 1; $WMpicture['image_type'] = self::WMpictureTypeLookup($WMpicture['image_type_id']); $WMpicture['image_size'] = Utils::LittleEndian2Int(substr($data, $offset, 4)); $offset += 4; $WMpicture['image_mime'] = ''; do { $next_byte_pair = substr($data, $offset, 2); $offset += 2; $WMpicture['image_mime'] .= $next_byte_pair; } while ($next_byte_pair !== ""); $WMpicture['image_description'] = ''; do { $next_byte_pair = substr($data, $offset, 2); $offset += 2; $WMpicture['image_description'] .= $next_byte_pair; } while ($next_byte_pair !== ""); $WMpicture['dataoffset'] = $offset; $WMpicture['data'] = substr($data, $offset); $imageinfo = array(); $WMpicture['image_mime'] = ''; $imagechunkcheck = Utils::GetDataImageSize($WMpicture['data'], $imageinfo); unset($imageinfo); if (!empty($imagechunkcheck)) { $WMpicture['image_mime'] = image_type_to_mime_type($imagechunkcheck[2]); } if (!isset($this->getid3->info['asf']['comments']['picture'])) { $this->getid3->info['asf']['comments']['picture'] = array(); } $this->getid3->info['asf']['comments']['picture'][] = array('data' => $WMpicture['data'], 'image_mime' => $WMpicture['image_mime']); return $WMpicture; }