function getAPEtagFilepointer(&$fd, &$ThisFileInfo) { $id3v1tagsize = 128; $apetagheadersize = 32; fseek($fd, 0 - $id3v1tagsize - $apetagheadersize, SEEK_END); $APEfooterID3v1 = fread($fd, $id3v1tagsize + $apetagheadersize); if (substr($APEfooterID3v1, 0, strlen('APETAGEX')) == 'APETAGEX' && substr($APEfooterID3v1, $apetagheadersize, strlen('TAG')) == 'TAG') { // APE tag found before ID3v1 $APEfooterData = substr($APEfooterID3v1, 0, $apetagheadersize); $APEfooterOffset = 0 - $apetagheadersize - $id3v1tagsize; } elseif (substr($APEfooterID3v1, $id3v1tagsize, strlen('APETAGEX')) == 'APETAGEX') { // APE tag found, no ID3v1 $APEfooterData = substr($APEfooterID3v1, $id3v1tagsize, $apetagheadersize); $APEfooterOffset = 0 - $apetagheadersize; } else { // APE tag not found return false; } $ThisFileInfo['ape']['tag_offset_end'] = $ThisFileInfo['filesize'] - ($APEfooterOffset + $apetagheadersize); if (empty($ThisFileInfo['fileformat'])) { $ThisFileInfo['fileformat'] = 'ape'; } if (!($ThisFileInfo['ape']['footer'] = parseAPEheaderFooter($APEfooterData))) { $ThisFileInfo['error'] .= "\n" . 'Error parsing APE footer at offset ' . $ThisFileInfo['ape']['tag_offset_end']; return false; } if (isset($ThisFileInfo['ape']['footer']['flags']['header']) && $ThisFileInfo['ape']['footer']['flags']['header']) { fseek($fd, $APEfooterOffset - $ThisFileInfo['ape']['footer']['raw']['tagsize'] + $apetagheadersize - $apetagheadersize, SEEK_END); $ThisFileInfo['ape']['tag_offset_start'] = ftell($fd); $APEtagData = fread($fd, $ThisFileInfo['ape']['footer']['raw']['tagsize'] + $apetagheadersize); } else { fseek($fd, $APEfooterOffset - $ThisFileInfo['ape']['footer']['raw']['tagsize'] + $apetagheadersize, SEEK_END); $ThisFileInfo['ape']['tag_offset_start'] = ftell($fd); $APEtagData = fread($fd, $ThisFileInfo['ape']['footer']['raw']['tagsize']); } $offset = 0; if (isset($ThisFileInfo['ape']['footer']['flags']['header']) && $ThisFileInfo['ape']['footer']['flags']['header']) { if ($ThisFileInfo['ape']['header'] = parseAPEheaderFooter(substr($APEtagData, 0, $apetagheadersize))) { $offset += $apetagheadersize; } else { $ThisFileInfo['error'] .= "\n" . 'Error parsing APE header at offset ' . $ThisFileInfo['ape']['tag_offset_start']; return false; } } for ($i = 0; $i < $ThisFileInfo['ape']['footer']['raw']['tag_items']; $i++) { $value_size = LittleEndian2Int(substr($APEtagData, $offset, 4)); $offset += 4; $item_flags = LittleEndian2Int(substr($APEtagData, $offset, 4)); $offset += 4; if (strstr(substr($APEtagData, $offset), chr(0)) === false) { $ThisFileInfo['error'] .= "\n" . 'Cannot find null-byte (0x00) seperator between ItemKey #' . $i . ' and value. ItemKey starts ' . $offset . ' bytes into the APE tag, at file offset ' . ($ThisFileInfo['ape']['tag_offset_start'] + $offset); return false; } $ItemKeyLength = strpos($APEtagData, chr(0), $offset) - $offset; $item_key = strtolower(substr($APEtagData, $offset, $ItemKeyLength)); $offset += $ItemKeyLength + 1; // skip 0x00 terminator $ThisFileInfo['ape']['items']["{$item_key}"]['data'] = substr($APEtagData, $offset, $value_size); $offset += $value_size; if ($ThisFileInfo['ape']['footer']['tag_version'] >= 2) { $ThisFileInfo['ape']['items']["{$item_key}"]['flags'] = parseAPEtagFlags($item_flags); } switch ($ThisFileInfo['ape']['items']["{$item_key}"]['flags']['item_contents_raw']) { case 0: // UTF-8 // UTF-8 case 3: // Locator (URL, filename, etc), UTF-8 encoded $ThisFileInfo['ape']['items']["{$item_key}"]['data'] = explode(chr(0), $ThisFileInfo['ape']['items']["{$item_key}"]['data']); foreach ($ThisFileInfo['ape']['items']["{$item_key}"]['data'] as $key => $value) { $ThisFileInfo['ape']['items']["{$item_key}"]['data_ascii'][$key] = RoughTranslateUnicodeToASCII($value, 3); } break; default: // binary data //$ThisFileInfo['ape']['items']["$item_key"]['data_ascii'] = null; break; } switch ($item_key) { case 'replaygain_track_gain': $ThisFileInfo['replay_gain']['radio']['adjustment'] = (double) $ThisFileInfo['ape']['items']["{$item_key}"]['data_ascii'][0]; $ThisFileInfo['replay_gain']['radio']['originator'] = 'unspecified'; break; case 'replaygain_track_peak': $ThisFileInfo['replay_gain']['radio']['peak'] = (double) $ThisFileInfo['ape']['items']["{$item_key}"]['data_ascii'][0]; $ThisFileInfo['replay_gain']['radio']['originator'] = 'unspecified'; break; case 'replaygain_album_gain': $ThisFileInfo['replay_gain']['audiophile']['adjustment'] = (double) $ThisFileInfo['ape']['items']["{$item_key}"]['data_ascii'][0]; $ThisFileInfo['replay_gain']['audiophile']['originator'] = 'unspecified'; break; case 'replaygain_album_peak': $ThisFileInfo['replay_gain']['audiophile']['peak'] = (double) $ThisFileInfo['ape']['items']["{$item_key}"]['data_ascii'][0]; $ThisFileInfo['replay_gain']['audiophile']['originator'] = 'unspecified'; break; default: foreach ($ThisFileInfo['ape']['items']["{$item_key}"]['data_ascii'] as $comment) { $ThisFileInfo['ape']['comments'][strtolower($item_key)][] = $comment; } break; } } if (isset($ThisFileInfo['ape']['comments'])) { CopyFormatCommentsToRootComments($ThisFileInfo['ape']['comments'], $ThisFileInfo, true, true, true); } return true; }
function getAPEtagFilepointer(&$fd, &$ThisFileInfo) { $id3v1tagsize = 128; $apetagheadersize = 32; fseek($fd, 0 - $id3v1tagsize - $apetagheadersize, SEEK_END); $APEfooterID3v1 = fread($fd, $id3v1tagsize + $apetagheadersize); if (substr($APEfooterID3v1, 0, strlen('APETAGEX')) == 'APETAGEX') { // APE tag found before ID3v1 $APEfooterData = substr($APEfooterID3v1, 0, $apetagheadersize); $APEfooterOffset = 0 - $apetagheadersize - $id3v1tagsize; } elseif (substr($APEfooterID3v1, $id3v1tagsize, strlen('APETAGEX')) == 'APETAGEX') { // APE tag found, no ID3v1 $APEfooterData = substr($APEfooterID3v1, $id3v1tagsize, $apetagheadersize); $APEfooterOffset = 0 - $apetagheadersize; } else { // APE tag not found return false; } if (empty($ThisFileInfo['fileformat'])) { $ThisFileInfo['fileformat'] = 'ape'; } $ThisFileInfo['ape']['footer'] = parseAPEheaderFooter($APEfooterData); if (isset($ThisFileInfo['ape']['footer']['flags']['header']) && $ThisFileInfo['ape']['footer']['flags']['header']) { fseek($fd, $APEfooterOffset - $ThisFileInfo['ape']['footer']['raw']['tagsize'] + $apetagheadersize - $apetagheadersize, SEEK_END); $APEtagData = fread($fd, $ThisFileInfo['ape']['footer']['raw']['tagsize'] + $apetagheadersize); } else { fseek($fd, $APEfooterOffset - $ThisFileInfo['ape']['footer']['raw']['tagsize'] + $apetagheadersize, SEEK_END); $APEtagData = fread($fd, $ThisFileInfo['ape']['footer']['raw']['tagsize']); } $offset = 0; if (isset($ThisFileInfo['ape']['footer']['flags']['header']) && $ThisFileInfo['ape']['footer']['flags']['header']) { $ThisFileInfo['ape']['header'] = parseAPEheaderFooter(substr($APEtagData, 0, $apetagheadersize)); $offset += $apetagheadersize; } for ($i = 0; $i < $ThisFileInfo['ape']['footer']['raw']['tag_items']; $i++) { $value_size = LittleEndian2Int(substr($APEtagData, $offset, 4)); $offset += 4; $item_flags = LittleEndian2Int(substr($APEtagData, $offset, 4)); $offset += 4; $ItemKeyLength = strpos($APEtagData, chr(0), $offset) - $offset; $item_key = strtolower(substr($APEtagData, $offset, $ItemKeyLength)); $offset += $ItemKeyLength + 1; // skip 0x00 terminator $data = substr($APEtagData, $offset, $value_size); $offset += $value_size; $ThisFileInfo['ape']['items']["{$item_key}"]['raw']['value_size'] = $value_size; $ThisFileInfo['ape']['items']["{$item_key}"]['raw']['item_flags'] = $item_flags; if ($ThisFileInfo['ape']['footer']['tag_version'] >= 2) { $ThisFileInfo['ape']['items']["{$item_key}"]['flags'] = parseAPEtagFlags($item_flags); } $ThisFileInfo['ape']['items']["{$item_key}"]['data'] = $data; $ThisFileInfo['ape']['items']["{$item_key}"]['data_ascii'] = $data; if (APEtagItemIsUTF8Lookup($item_key)) { $ThisFileInfo['ape']['items']["{$item_key}"]['data_ascii'] = RoughTranslateUnicodeToASCII($ThisFileInfo['ape']['items']["{$item_key}"]['data'], 3); } switch ($item_key) { case 'replaygain_track_gain': $ThisFileInfo['replay_gain']['radio']['adjustment'] = (double) $ThisFileInfo['ape']['items']["{$item_key}"]['data_ascii']; $ThisFileInfo['replay_gain']['radio']['originator'] = 'unspecified'; break; case 'replaygain_track_peak': $ThisFileInfo['replay_gain']['radio']['peak'] = (double) $ThisFileInfo['ape']['items']["{$item_key}"]['data_ascii']; $ThisFileInfo['replay_gain']['radio']['originator'] = 'unspecified'; break; case 'replaygain_album_gain': $ThisFileInfo['replay_gain']['audiophile']['adjustment'] = (double) $ThisFileInfo['ape']['items']["{$item_key}"]['data_ascii']; $ThisFileInfo['replay_gain']['audiophile']['originator'] = 'unspecified'; break; case 'replaygain_album_peak': $ThisFileInfo['replay_gain']['audiophile']['peak'] = (double) $ThisFileInfo['ape']['items']["{$item_key}"]['data_ascii']; $ThisFileInfo['replay_gain']['audiophile']['originator'] = 'unspecified'; break; case 'title': case 'artist': case 'album': case 'track': case 'genre': case 'comment': case 'year': $ThisFileInfo['ape']['comments']["{$item_key}"][] = $ThisFileInfo['ape']['items']["{$item_key}"]['data_ascii']; break; } } if (isset($ThisFileInfo['ape']['comments'])) { CopyFormatCommentsToRootComments($ThisFileInfo['ape']['comments'], $ThisFileInfo, true, true, true); } return true; }