- Timestamp:
- Jul 7, 2009, 10:27:37 PM (15 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
extensions/charlies_content/getid3/getid3/module.tag.apetag.php
r3318 r3544 1 1 <?php 2 // +----------------------------------------------------------------------+ 3 // | PHP version 5 | 4 // +----------------------------------------------------------------------+ 5 // | Copyright (c) 2002-2006 James Heinrich, Allan Hansen | 6 // +----------------------------------------------------------------------+ 7 // | This source file is subject to version 2 of the GPL license, | 8 // | that is bundled with this package in the file license.txt and is | 9 // | available through the world-wide-web at the following url: | 10 // | http://www.gnu.org/copyleft/gpl.html | 11 // +----------------------------------------------------------------------+ 12 // | getID3() - http://getid3.sourceforge.net or http://www.getid3.org | 13 // +----------------------------------------------------------------------+ 14 // | Authors: James Heinrich <infoØgetid3*org> | 15 // | Allan Hansen <ahØartemis*dk> | 16 // +----------------------------------------------------------------------+ 17 // | module.tag.apetag.php | 18 // | module for analyzing APE tags | 19 // | dependencies: NONE | 20 // +----------------------------------------------------------------------+ 21 // 22 // $Id$ 23 24 25 26 class getid3_apetag extends getid3_handler 2 ///////////////////////////////////////////////////////////////// 3 /// getID3() by James Heinrich <info@getid3.org> // 4 // available at http://getid3.sourceforge.net // 5 // or http://www.getid3.org // 6 ///////////////////////////////////////////////////////////////// 7 // See readme.txt for more details // 8 ///////////////////////////////////////////////////////////////// 9 // // 10 // module.tag.apetag.php // 11 // module for analyzing APE tags // 12 // dependencies: NONE // 13 // /// 14 ///////////////////////////////////////////////////////////////// 15 16 class getid3_apetag 27 17 { 28 /* 29 ID3v1_TAG_SIZE = 128; 30 APETAG_HEADER_SIZE = 32; 31 LYRICS3_TAG_SIZE = 10; 32 */ 33 34 public $option_override_end_offset = 0; 35 36 37 38 public function Analyze() { 39 40 $getid3 = $this->getid3; 41 42 if ($this->option_override_end_offset == 0) { 43 44 fseek($getid3->fp, 0 - 170, SEEK_END); // 170 = ID3v1_TAG_SIZE + APETAG_HEADER_SIZE + LYRICS3_TAG_SIZE 45 $apetag_footer_id3v1 = fread($getid3->fp, 170); // 170 = ID3v1_TAG_SIZE + APETAG_HEADER_SIZE + LYRICS3_TAG_SIZE 46 47 // APE tag found before ID3v1 48 if (substr($apetag_footer_id3v1, strlen($apetag_footer_id3v1) - 160, 8) == 'APETAGEX') { // 160 = ID3v1_TAG_SIZE + APETAG_HEADER_SIZE 49 $getid3->info['ape']['tag_offset_end'] = filesize($getid3->filename) - 128; // 128 = ID3v1_TAG_SIZE 50 } 51 52 // APE tag found, no ID3v1 53 elseif (substr($apetag_footer_id3v1, strlen($apetag_footer_id3v1) - 32, 8) == 'APETAGEX') { // 32 = APETAG_HEADER_SIZE 54 $getid3->info['ape']['tag_offset_end'] = filesize($getid3->filename); 55 } 56 57 } 58 else { 59 60 fseek($getid3->fp, $this->option_override_end_offset - 32, SEEK_SET); // 32 = APETAG_HEADER_SIZE 61 if (fread($getid3->fp, 8) == 'APETAGEX') { 62 $getid3->info['ape']['tag_offset_end'] = $this->option_override_end_offset; 63 } 64 65 } 66 67 // APE tag not found 68 if (!@$getid3->info['ape']['tag_offset_end']) { 69 return false; 70 } 71 72 // Shortcut 73 $info_ape = &$getid3->info['ape']; 74 75 // Read and parse footer 76 fseek($getid3->fp, $info_ape['tag_offset_end'] - 32, SEEK_SET); // 32 = APETAG_HEADER_SIZE 77 $apetag_footer_data = fread($getid3->fp, 32); 78 if (!($this->ParseAPEHeaderFooter($apetag_footer_data, $info_ape['footer']))) { 79 throw new getid3_exception('Error parsing APE footer at offset '.$info_ape['tag_offset_end']); 80 } 81 82 if (isset($info_ape['footer']['flags']['header']) && $info_ape['footer']['flags']['header']) { 83 fseek($getid3->fp, $info_ape['tag_offset_end'] - $info_ape['footer']['raw']['tagsize'] - 32, SEEK_SET); 84 $info_ape['tag_offset_start'] = ftell($getid3->fp); 85 $apetag_data = fread($getid3->fp, $info_ape['footer']['raw']['tagsize'] + 32); 86 } 87 else { 88 $info_ape['tag_offset_start'] = $info_ape['tag_offset_end'] - $info_ape['footer']['raw']['tagsize']; 89 fseek($getid3->fp, $info_ape['tag_offset_start'], SEEK_SET); 90 $apetag_data = fread($getid3->fp, $info_ape['footer']['raw']['tagsize']); 91 } 92 $getid3->info['avdataend'] = $info_ape['tag_offset_start']; 93 94 if (isset($getid3->info['id3v1']['tag_offset_start']) && ($getid3->info['id3v1']['tag_offset_start'] < $info_ape['tag_offset_end'])) { 95 $getid3->warning('ID3v1 tag information ignored since it appears to be a false synch in APEtag data'); 96 unset($getid3->info['id3v1']); 97 } 98 99 $offset = 0; 100 if (isset($info_ape['footer']['flags']['header']) && $info_ape['footer']['flags']['header']) { 101 if (!$this->ParseAPEHeaderFooter(substr($apetag_data, 0, 32), $info_ape['header'])) { 102 throw new getid3_exception('Error parsing APE header at offset '.$info_ape['tag_offset_start']); 103 } 104 $offset = 32; 105 } 106 107 // Shortcut 108 $getid3->info['replay_gain'] = array (); 109 $info_replaygain = &$getid3->info['replay_gain']; 110 111 for ($i = 0; $i < $info_ape['footer']['raw']['tag_items']; $i++) { 112 $value_size = getid3_lib::LittleEndian2Int(substr($apetag_data, $offset, 4)); 113 $item_flags = getid3_lib::LittleEndian2Int(substr($apetag_data, $offset + 4, 4)); 114 $offset += 8; 115 116 if (strstr(substr($apetag_data, $offset), "\x00") === false) { 117 throw new getid3_exception('Cannot find null-byte (0x00) seperator between ItemKey #'.$i.' and value. ItemKey starts ' . $offset . ' bytes into the APE tag, at file offset '.($info_ape['tag_offset_start'] + $offset)); 118 } 119 120 $item_key_length = strpos($apetag_data, "\x00", $offset) - $offset; 121 $item_key = strtolower(substr($apetag_data, $offset, $item_key_length)); 122 123 // Shortcut 124 $info_ape['items'][$item_key] = array (); 125 $info_ape_items_current = &$info_ape['items'][$item_key]; 126 127 $offset += $item_key_length + 1; // skip 0x00 terminator 128 $info_ape_items_current['data'] = substr($apetag_data, $offset, $value_size); 129 $offset += $value_size; 130 131 132 $info_ape_items_current['flags'] = $this->ParseAPEtagFlags($item_flags); 133 134 switch ($info_ape_items_current['flags']['item_contents_raw']) { 135 case 0: // UTF-8 136 case 3: // Locator (URL, filename, etc), UTF-8 encoded 137 $info_ape_items_current['data'] = explode("\x00", trim($info_ape_items_current['data'])); 138 break; 139 140 default: // binary data 141 break; 142 } 143 144 switch (strtolower($item_key)) { 145 case 'replaygain_track_gain': 146 $info_replaygain['track']['adjustment'] = (float)str_replace(',', '.', $info_ape_items_current['data'][0]); // float casting will see "0,95" as zero! 147 $info_replaygain['track']['originator'] = 'unspecified'; 148 break; 149 150 case 'replaygain_track_peak': 151 $info_replaygain['track']['peak'] = (float)str_replace(',', '.', $info_ape_items_current['data'][0]); // float casting will see "0,95" as zero! 152 $info_replaygain['track']['originator'] = 'unspecified'; 153 if ($info_replaygain['track']['peak'] <= 0) { 154 $getid3->warning('ReplayGain Track peak from APEtag appears invalid: '.$info_replaygain['track']['peak'].' (original value = "'.$info_ape_items_current['data'][0].'")'); 155 } 156 break; 157 158 case 'replaygain_album_gain': 159 $info_replaygain['album']['adjustment'] = (float)str_replace(',', '.', $info_ape_items_current['data'][0]); // float casting will see "0,95" as zero! 160 $info_replaygain['album']['originator'] = 'unspecified'; 161 break; 162 163 case 'replaygain_album_peak': 164 $info_replaygain['album']['peak'] = (float)str_replace(',', '.', $info_ape_items_current['data'][0]); // float casting will see "0,95" as zero! 165 $info_replaygain['album']['originator'] = 'unspecified'; 166 if ($info_replaygain['album']['peak'] <= 0) { 167 $getid3->warning('ReplayGain Album peak from APEtag appears invalid: '.$info_replaygain['album']['peak'].' (original value = "'.$info_ape_items_current['data'][0].'")'); 168 } 169 break; 170 171 case 'mp3gain_undo': 172 list($mp3gain_undo_left, $mp3gain_undo_right, $mp3gain_undo_wrap) = explode(',', $info_ape_items_current['data'][0]); 173 $info_replaygain['mp3gain']['undo_left'] = intval($mp3gain_undo_left); 174 $info_replaygain['mp3gain']['undo_right'] = intval($mp3gain_undo_right); 175 $info_replaygain['mp3gain']['undo_wrap'] = (($mp3gain_undo_wrap == 'Y') ? true : false); 176 break; 177 178 case 'mp3gain_minmax': 179 list($mp3gain_globalgain_min, $mp3gain_globalgain_max) = explode(',', $info_ape_items_current['data'][0]); 180 $info_replaygain['mp3gain']['globalgain_track_min'] = intval($mp3gain_globalgain_min); 181 $info_replaygain['mp3gain']['globalgain_track_max'] = intval($mp3gain_globalgain_max); 182 break; 183 184 case 'mp3gain_album_minmax': 185 list($mp3gain_globalgain_album_min, $mp3gain_globalgain_album_max) = explode(',', $info_ape_items_current['data'][0]); 186 $info_replaygain['mp3gain']['globalgain_album_min'] = intval($mp3gain_globalgain_album_min); 187 $info_replaygain['mp3gain']['globalgain_album_max'] = intval($mp3gain_globalgain_album_max); 188 break; 189 190 case 'tracknumber': 191 foreach ($info_ape_items_current['data'] as $comment) { 192 $info_ape['comments']['track'][] = $comment; 193 } 194 break; 195 196 default: 197 foreach ($info_ape_items_current['data'] as $comment) { 198 $info_ape['comments'][strtolower($item_key)][] = $comment; 199 } 200 break; 201 } 202 203 } 204 if (empty($info_replaygain)) { 205 unset($getid3->info['replay_gain']); 206 } 207 208 return true; 209 } 210 211 212 213 protected function ParseAPEheaderFooter($data, &$target) { 214 215 // http://www.uni-jena.de/~pfk/mpp/sv8/apeheader.html 216 217 if (substr($data, 0, 8) != 'APETAGEX') { 218 return false; 219 } 220 221 // shortcut 222 $target['raw'] = array (); 223 $target_raw = &$target['raw']; 224 225 $target_raw['footer_tag'] = 'APETAGEX'; 226 227 getid3_lib::ReadSequence("LittleEndian2Int", $target_raw, $data, 8, 228 array ( 229 'version' => 4, 230 'tagsize' => 4, 231 'tag_items' => 4, 232 'global_flags' => 4 233 ) 234 ); 235 $target_raw['reserved'] = substr($data, 24, 8); 236 237 $target['tag_version'] = $target_raw['version'] / 1000; 238 if ($target['tag_version'] >= 2) { 239 240 $target['flags'] = $this->ParseAPEtagFlags($target_raw['global_flags']); 241 } 242 243 return true; 244 } 245 246 247 248 protected function ParseAPEtagFlags($raw_flag_int) { 249 250 // "Note: APE Tags 1.0 do not use any of the APE Tag flags. 251 // All are set to zero on creation and ignored on reading." 252 // http://www.uni-jena.de/~pfk/mpp/sv8/apetagflags.html 253 254 $target['header'] = (bool) ($raw_flag_int & 0x80000000); 255 $target['footer'] = (bool) ($raw_flag_int & 0x40000000); 256 $target['this_is_header'] = (bool) ($raw_flag_int & 0x20000000); 257 $target['item_contents_raw'] = ($raw_flag_int & 0x00000006) >> 1; 258 $target['read_only'] = (bool) ($raw_flag_int & 0x00000001); 259 260 $target['item_contents'] = getid3_apetag::APEcontentTypeFlagLookup($target['item_contents_raw']); 261 262 return $target; 263 } 264 265 266 267 public static function APEcontentTypeFlagLookup($content_type_id) { 268 269 static $lookup = array ( 270 0 => 'utf-8', 271 1 => 'binary', 272 2 => 'external', 273 3 => 'reserved' 274 ); 275 return (isset($lookup[$content_type_id]) ? $lookup[$content_type_id] : 'invalid'); 276 } 277 278 279 280 public static function APEtagItemIsUTF8Lookup($item_key) { 281 282 static $lookup = array ( 283 'title', 284 'subtitle', 285 'artist', 286 'album', 287 'debut album', 288 'publisher', 289 'conductor', 290 'track', 291 'composer', 292 'comment', 293 'copyright', 294 'publicationright', 295 'file', 296 'year', 297 'record date', 298 'record location', 299 'genre', 300 'media', 301 'related', 302 'isrc', 303 'abstract', 304 'language', 305 'bibliography' 306 ); 307 return in_array(strtolower($item_key), $lookup); 308 } 18 19 function getid3_apetag(&$fd, &$ThisFileInfo, $overrideendoffset=0) { 20 21 if ($ThisFileInfo['filesize'] >= pow(2, 31)) { 22 $ThisFileInfo['warning'][] = 'Unable to check for APEtags because file is larger than 2GB'; 23 return false; 24 } 25 26 $id3v1tagsize = 128; 27 $apetagheadersize = 32; 28 $lyrics3tagsize = 10; 29 30 if ($overrideendoffset == 0) { 31 32 fseek($fd, 0 - $id3v1tagsize - $apetagheadersize - $lyrics3tagsize, SEEK_END); 33 $APEfooterID3v1 = fread($fd, $id3v1tagsize + $apetagheadersize + $lyrics3tagsize); 34 35 //if (preg_match('/APETAGEX.{24}TAG.{125}$/i', $APEfooterID3v1)) { 36 if (substr($APEfooterID3v1, strlen($APEfooterID3v1) - $id3v1tagsize - $apetagheadersize, 8) == 'APETAGEX') { 37 38 // APE tag found before ID3v1 39 $ThisFileInfo['ape']['tag_offset_end'] = $ThisFileInfo['filesize'] - $id3v1tagsize; 40 41 //} elseif (preg_match('/APETAGEX.{24}$/i', $APEfooterID3v1)) { 42 } elseif (substr($APEfooterID3v1, strlen($APEfooterID3v1) - $apetagheadersize, 8) == 'APETAGEX') { 43 44 // APE tag found, no ID3v1 45 $ThisFileInfo['ape']['tag_offset_end'] = $ThisFileInfo['filesize']; 46 47 } 48 49 } else { 50 51 fseek($fd, $overrideendoffset - $apetagheadersize, SEEK_SET); 52 if (fread($fd, 8) == 'APETAGEX') { 53 $ThisFileInfo['ape']['tag_offset_end'] = $overrideendoffset; 54 } 55 56 } 57 if (!isset($ThisFileInfo['ape']['tag_offset_end'])) { 58 59 // APE tag not found 60 unset($ThisFileInfo['ape']); 61 return false; 62 63 } 64 65 // shortcut 66 $thisfile_ape = &$ThisFileInfo['ape']; 67 68 fseek($fd, $thisfile_ape['tag_offset_end'] - $apetagheadersize, SEEK_SET); 69 $APEfooterData = fread($fd, 32); 70 if (!($thisfile_ape['footer'] = $this->parseAPEheaderFooter($APEfooterData))) { 71 $ThisFileInfo['error'][] = 'Error parsing APE footer at offset '.$thisfile_ape['tag_offset_end']; 72 return false; 73 } 74 75 if (isset($thisfile_ape['footer']['flags']['header']) && $thisfile_ape['footer']['flags']['header']) { 76 fseek($fd, $thisfile_ape['tag_offset_end'] - $thisfile_ape['footer']['raw']['tagsize'] - $apetagheadersize, SEEK_SET); 77 $thisfile_ape['tag_offset_start'] = ftell($fd); 78 $APEtagData = fread($fd, $thisfile_ape['footer']['raw']['tagsize'] + $apetagheadersize); 79 } else { 80 $thisfile_ape['tag_offset_start'] = $thisfile_ape['tag_offset_end'] - $thisfile_ape['footer']['raw']['tagsize']; 81 fseek($fd, $thisfile_ape['tag_offset_start'], SEEK_SET); 82 $APEtagData = fread($fd, $thisfile_ape['footer']['raw']['tagsize']); 83 } 84 $ThisFileInfo['avdataend'] = $thisfile_ape['tag_offset_start']; 85 86 if (isset($ThisFileInfo['id3v1']['tag_offset_start']) && ($ThisFileInfo['id3v1']['tag_offset_start'] < $thisfile_ape['tag_offset_end'])) { 87 $ThisFileInfo['warning'][] = 'ID3v1 tag information ignored since it appears to be a false synch in APEtag data'; 88 unset($ThisFileInfo['id3v1']); 89 foreach ($ThisFileInfo['warning'] as $key => $value) { 90 if ($value == 'Some ID3v1 fields do not use NULL characters for padding') { 91 unset($ThisFileInfo['warning'][$key]); 92 sort($ThisFileInfo['warning']); 93 break; 94 } 95 } 96 } 97 98 $offset = 0; 99 if (isset($thisfile_ape['footer']['flags']['header']) && $thisfile_ape['footer']['flags']['header']) { 100 if ($thisfile_ape['header'] = $this->parseAPEheaderFooter(substr($APEtagData, 0, $apetagheadersize))) { 101 $offset += $apetagheadersize; 102 } else { 103 $ThisFileInfo['error'][] = 'Error parsing APE header at offset '.$thisfile_ape['tag_offset_start']; 104 return false; 105 } 106 } 107 108 // shortcut 109 $ThisFileInfo['replay_gain'] = array(); 110 $thisfile_replaygain = &$ThisFileInfo['replay_gain']; 111 112 for ($i = 0; $i < $thisfile_ape['footer']['raw']['tag_items']; $i++) { 113 $value_size = getid3_lib::LittleEndian2Int(substr($APEtagData, $offset, 4)); 114 $offset += 4; 115 $item_flags = getid3_lib::LittleEndian2Int(substr($APEtagData, $offset, 4)); 116 $offset += 4; 117 if (strstr(substr($APEtagData, $offset), "\x00") === false) { 118 $ThisFileInfo['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); 119 return false; 120 } 121 $ItemKeyLength = strpos($APEtagData, "\x00", $offset) - $offset; 122 $item_key = strtolower(substr($APEtagData, $offset, $ItemKeyLength)); 123 124 // shortcut 125 $thisfile_ape['items'][$item_key] = array(); 126 $thisfile_ape_items_current = &$thisfile_ape['items'][$item_key]; 127 128 $offset += ($ItemKeyLength + 1); // skip 0x00 terminator 129 $thisfile_ape_items_current['data'] = substr($APEtagData, $offset, $value_size); 130 $offset += $value_size; 131 132 $thisfile_ape_items_current['flags'] = $this->parseAPEtagFlags($item_flags); 133 switch ($thisfile_ape_items_current['flags']['item_contents_raw']) { 134 case 0: // UTF-8 135 case 3: // Locator (URL, filename, etc), UTF-8 encoded 136 $thisfile_ape_items_current['data'] = explode("\x00", trim($thisfile_ape_items_current['data'])); 137 break; 138 139 default: // binary data 140 break; 141 } 142 143 switch (strtolower($item_key)) { 144 case 'replaygain_track_gain': 145 $thisfile_replaygain['track']['adjustment'] = (float) str_replace(',', '.', $thisfile_ape_items_current['data'][0]); // float casting will see "0,95" as zero! 146 $thisfile_replaygain['track']['originator'] = 'unspecified'; 147 break; 148 149 case 'replaygain_track_peak': 150 $thisfile_replaygain['track']['peak'] = (float) str_replace(',', '.', $thisfile_ape_items_current['data'][0]); // float casting will see "0,95" as zero! 151 $thisfile_replaygain['track']['originator'] = 'unspecified'; 152 if ($thisfile_replaygain['track']['peak'] <= 0) { 153 $ThisFileInfo['warning'][] = 'ReplayGain Track peak from APEtag appears invalid: '.$thisfile_replaygain['track']['peak'].' (original value = "'.$thisfile_ape_items_current['data'][0].'")'; 154 } 155 break; 156 157 case 'replaygain_album_gain': 158 $thisfile_replaygain['album']['adjustment'] = (float) str_replace(',', '.', $thisfile_ape_items_current['data'][0]); // float casting will see "0,95" as zero! 159 $thisfile_replaygain['album']['originator'] = 'unspecified'; 160 break; 161 162 case 'replaygain_album_peak': 163 $thisfile_replaygain['album']['peak'] = (float) str_replace(',', '.', $thisfile_ape_items_current['data'][0]); // float casting will see "0,95" as zero! 164 $thisfile_replaygain['album']['originator'] = 'unspecified'; 165 if ($thisfile_replaygain['album']['peak'] <= 0) { 166 $ThisFileInfo['warning'][] = 'ReplayGain Album peak from APEtag appears invalid: '.$thisfile_replaygain['album']['peak'].' (original value = "'.$thisfile_ape_items_current['data'][0].'")'; 167 } 168 break; 169 170 case 'mp3gain_undo': 171 list($mp3gain_undo_left, $mp3gain_undo_right, $mp3gain_undo_wrap) = explode(',', $thisfile_ape_items_current['data'][0]); 172 $thisfile_replaygain['mp3gain']['undo_left'] = intval($mp3gain_undo_left); 173 $thisfile_replaygain['mp3gain']['undo_right'] = intval($mp3gain_undo_right); 174 $thisfile_replaygain['mp3gain']['undo_wrap'] = (($mp3gain_undo_wrap == 'Y') ? true : false); 175 break; 176 177 case 'mp3gain_minmax': 178 list($mp3gain_globalgain_min, $mp3gain_globalgain_max) = explode(',', $thisfile_ape_items_current['data'][0]); 179 $thisfile_replaygain['mp3gain']['globalgain_track_min'] = intval($mp3gain_globalgain_min); 180 $thisfile_replaygain['mp3gain']['globalgain_track_max'] = intval($mp3gain_globalgain_max); 181 break; 182 183 case 'mp3gain_album_minmax': 184 list($mp3gain_globalgain_album_min, $mp3gain_globalgain_album_max) = explode(',', $thisfile_ape_items_current['data'][0]); 185 $thisfile_replaygain['mp3gain']['globalgain_album_min'] = intval($mp3gain_globalgain_album_min); 186 $thisfile_replaygain['mp3gain']['globalgain_album_max'] = intval($mp3gain_globalgain_album_max); 187 break; 188 189 case 'tracknumber': 190 foreach ($thisfile_ape_items_current['data'] as $comment) { 191 $thisfile_ape['comments']['track'][] = $comment; 192 } 193 break; 194 195 default: 196 foreach ($thisfile_ape_items_current['data'] as $comment) { 197 $thisfile_ape['comments'][strtolower($item_key)][] = $comment; 198 } 199 break; 200 } 201 202 } 203 if (empty($thisfile_replaygain)) { 204 unset($ThisFileInfo['replay_gain']); 205 } 206 207 return true; 208 } 209 210 function parseAPEheaderFooter($APEheaderFooterData) { 211 // http://www.uni-jena.de/~pfk/mpp/sv8/apeheader.html 212 213 // shortcut 214 $headerfooterinfo['raw'] = array(); 215 $headerfooterinfo_raw = &$headerfooterinfo['raw']; 216 217 $headerfooterinfo_raw['footer_tag'] = substr($APEheaderFooterData, 0, 8); 218 if ($headerfooterinfo_raw['footer_tag'] != 'APETAGEX') { 219 return false; 220 } 221 $headerfooterinfo_raw['version'] = getid3_lib::LittleEndian2Int(substr($APEheaderFooterData, 8, 4)); 222 $headerfooterinfo_raw['tagsize'] = getid3_lib::LittleEndian2Int(substr($APEheaderFooterData, 12, 4)); 223 $headerfooterinfo_raw['tag_items'] = getid3_lib::LittleEndian2Int(substr($APEheaderFooterData, 16, 4)); 224 $headerfooterinfo_raw['global_flags'] = getid3_lib::LittleEndian2Int(substr($APEheaderFooterData, 20, 4)); 225 $headerfooterinfo_raw['reserved'] = substr($APEheaderFooterData, 24, 8); 226 227 $headerfooterinfo['tag_version'] = $headerfooterinfo_raw['version'] / 1000; 228 if ($headerfooterinfo['tag_version'] >= 2) { 229 $headerfooterinfo['flags'] = $this->parseAPEtagFlags($headerfooterinfo_raw['global_flags']); 230 } 231 return $headerfooterinfo; 232 } 233 234 function parseAPEtagFlags($rawflagint) { 235 // "Note: APE Tags 1.0 do not use any of the APE Tag flags. 236 // All are set to zero on creation and ignored on reading." 237 // http://www.uni-jena.de/~pfk/mpp/sv8/apetagflags.html 238 $flags['header'] = (bool) ($rawflagint & 0x80000000); 239 $flags['footer'] = (bool) ($rawflagint & 0x40000000); 240 $flags['this_is_header'] = (bool) ($rawflagint & 0x20000000); 241 $flags['item_contents_raw'] = ($rawflagint & 0x00000006) >> 1; 242 $flags['read_only'] = (bool) ($rawflagint & 0x00000001); 243 244 $flags['item_contents'] = $this->APEcontentTypeFlagLookup($flags['item_contents_raw']); 245 246 return $flags; 247 } 248 249 function APEcontentTypeFlagLookup($contenttypeid) { 250 static $APEcontentTypeFlagLookup = array( 251 0 => 'utf-8', 252 1 => 'binary', 253 2 => 'external', 254 3 => 'reserved' 255 ); 256 return (isset($APEcontentTypeFlagLookup[$contenttypeid]) ? $APEcontentTypeFlagLookup[$contenttypeid] : 'invalid'); 257 } 258 259 function APEtagItemIsUTF8Lookup($itemkey) { 260 static $APEtagItemIsUTF8Lookup = array( 261 'title', 262 'subtitle', 263 'artist', 264 'album', 265 'debut album', 266 'publisher', 267 'conductor', 268 'track', 269 'composer', 270 'comment', 271 'copyright', 272 'publicationright', 273 'file', 274 'year', 275 'record date', 276 'record location', 277 'genre', 278 'media', 279 'related', 280 'isrc', 281 'abstract', 282 'language', 283 'bibliography' 284 ); 285 return in_array(strtolower($itemkey), $APEtagItemIsUTF8Lookup); 286 } 309 287 310 288 }
Note: See TracChangeset
for help on using the changeset viewer.