[ Index ] |
PHP Cross Reference of WordPress |
[Summary view] [Print] [Text view]
1 <?php 2 3 ///////////////////////////////////////////////////////////////// 4 /// getID3() by James Heinrich <info@getid3.org> // 5 // available at https://github.com/JamesHeinrich/getID3 // 6 // or https://www.getid3.org // 7 // or http://getid3.sourceforge.net // 8 // see readme.txt for more details // 9 ///////////////////////////////////////////////////////////////// 10 /// // 11 // module.tag.id3v2.php // 12 // module for analyzing ID3v2 tags // 13 // dependencies: module.tag.id3v1.php // 14 // /// 15 ///////////////////////////////////////////////////////////////// 16 17 if (!defined('GETID3_INCLUDEPATH')) { // prevent path-exposing attacks that access modules directly on public webservers 18 exit; 19 } 20 getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.tag.id3v1.php', __FILE__, true); 21 22 class getid3_id3v2 extends getid3_handler 23 { 24 public $StartingOffset = 0; 25 26 /** 27 * @return bool 28 */ 29 public function Analyze() { 30 $info = &$this->getid3->info; 31 32 // Overall tag structure: 33 // +-----------------------------+ 34 // | Header (10 bytes) | 35 // +-----------------------------+ 36 // | Extended Header | 37 // | (variable length, OPTIONAL) | 38 // +-----------------------------+ 39 // | Frames (variable length) | 40 // +-----------------------------+ 41 // | Padding | 42 // | (variable length, OPTIONAL) | 43 // +-----------------------------+ 44 // | Footer (10 bytes, OPTIONAL) | 45 // +-----------------------------+ 46 47 // Header 48 // ID3v2/file identifier "ID3" 49 // ID3v2 version $04 00 50 // ID3v2 flags (%ab000000 in v2.2, %abc00000 in v2.3, %abcd0000 in v2.4.x) 51 // ID3v2 size 4 * %0xxxxxxx 52 53 54 // shortcuts 55 $info['id3v2']['header'] = true; 56 $thisfile_id3v2 = &$info['id3v2']; 57 $thisfile_id3v2['flags'] = array(); 58 $thisfile_id3v2_flags = &$thisfile_id3v2['flags']; 59 60 61 $this->fseek($this->StartingOffset); 62 $header = $this->fread(10); 63 if (substr($header, 0, 3) == 'ID3' && strlen($header) == 10) { 64 65 $thisfile_id3v2['majorversion'] = ord($header[3]); 66 $thisfile_id3v2['minorversion'] = ord($header[4]); 67 68 // shortcut 69 $id3v2_majorversion = &$thisfile_id3v2['majorversion']; 70 71 } else { 72 73 unset($info['id3v2']); 74 return false; 75 76 } 77 78 if ($id3v2_majorversion > 4) { // this script probably won't correctly parse ID3v2.5.x and above (if it ever exists) 79 80 $this->error('this script only parses up to ID3v2.4.x - this tag is ID3v2.'.$id3v2_majorversion.'.'.$thisfile_id3v2['minorversion']); 81 return false; 82 83 } 84 85 $id3_flags = ord($header[5]); 86 switch ($id3v2_majorversion) { 87 case 2: 88 // %ab000000 in v2.2 89 $thisfile_id3v2_flags['unsynch'] = (bool) ($id3_flags & 0x80); // a - Unsynchronisation 90 $thisfile_id3v2_flags['compression'] = (bool) ($id3_flags & 0x40); // b - Compression 91 break; 92 93 case 3: 94 // %abc00000 in v2.3 95 $thisfile_id3v2_flags['unsynch'] = (bool) ($id3_flags & 0x80); // a - Unsynchronisation 96 $thisfile_id3v2_flags['exthead'] = (bool) ($id3_flags & 0x40); // b - Extended header 97 $thisfile_id3v2_flags['experim'] = (bool) ($id3_flags & 0x20); // c - Experimental indicator 98 break; 99 100 case 4: 101 // %abcd0000 in v2.4 102 $thisfile_id3v2_flags['unsynch'] = (bool) ($id3_flags & 0x80); // a - Unsynchronisation 103 $thisfile_id3v2_flags['exthead'] = (bool) ($id3_flags & 0x40); // b - Extended header 104 $thisfile_id3v2_flags['experim'] = (bool) ($id3_flags & 0x20); // c - Experimental indicator 105 $thisfile_id3v2_flags['isfooter'] = (bool) ($id3_flags & 0x10); // d - Footer present 106 break; 107 } 108 109 $thisfile_id3v2['headerlength'] = getid3_lib::BigEndian2Int(substr($header, 6, 4), 1) + 10; // length of ID3v2 tag in 10-byte header doesn't include 10-byte header length 110 111 $thisfile_id3v2['tag_offset_start'] = $this->StartingOffset; 112 $thisfile_id3v2['tag_offset_end'] = $thisfile_id3v2['tag_offset_start'] + $thisfile_id3v2['headerlength']; 113 114 115 116 // create 'encoding' key - used by getid3::HandleAllTags() 117 // in ID3v2 every field can have it's own encoding type 118 // so force everything to UTF-8 so it can be handled consistantly 119 $thisfile_id3v2['encoding'] = 'UTF-8'; 120 121 122 // Frames 123 124 // All ID3v2 frames consists of one frame header followed by one or more 125 // fields containing the actual information. The header is always 10 126 // bytes and laid out as follows: 127 // 128 // Frame ID $xx xx xx xx (four characters) 129 // Size 4 * %0xxxxxxx 130 // Flags $xx xx 131 132 $sizeofframes = $thisfile_id3v2['headerlength'] - 10; // not including 10-byte initial header 133 if (!empty($thisfile_id3v2['exthead']['length'])) { 134 $sizeofframes -= ($thisfile_id3v2['exthead']['length'] + 4); 135 } 136 if (!empty($thisfile_id3v2_flags['isfooter'])) { 137 $sizeofframes -= 10; // footer takes last 10 bytes of ID3v2 header, after frame data, before audio 138 } 139 if ($sizeofframes > 0) { 140 141 $framedata = $this->fread($sizeofframes); // read all frames from file into $framedata variable 142 143 // if entire frame data is unsynched, de-unsynch it now (ID3v2.3.x) 144 if (!empty($thisfile_id3v2_flags['unsynch']) && ($id3v2_majorversion <= 3)) { 145 $framedata = $this->DeUnsynchronise($framedata); 146 } 147 // [in ID3v2.4.0] Unsynchronisation [S:6.1] is done on frame level, instead 148 // of on tag level, making it easier to skip frames, increasing the streamability 149 // of the tag. The unsynchronisation flag in the header [S:3.1] indicates that 150 // there exists an unsynchronised frame, while the new unsynchronisation flag in 151 // the frame header [S:4.1.2] indicates unsynchronisation. 152 153 154 //$framedataoffset = 10 + ($thisfile_id3v2['exthead']['length'] ? $thisfile_id3v2['exthead']['length'] + 4 : 0); // how many bytes into the stream - start from after the 10-byte header (and extended header length+4, if present) 155 $framedataoffset = 10; // how many bytes into the stream - start from after the 10-byte header 156 157 158 // Extended Header 159 if (!empty($thisfile_id3v2_flags['exthead'])) { 160 $extended_header_offset = 0; 161 162 if ($id3v2_majorversion == 3) { 163 164 // v2.3 definition: 165 //Extended header size $xx xx xx xx // 32-bit integer 166 //Extended Flags $xx xx 167 // %x0000000 %00000000 // v2.3 168 // x - CRC data present 169 //Size of padding $xx xx xx xx 170 171 $thisfile_id3v2['exthead']['length'] = getid3_lib::BigEndian2Int(substr($framedata, $extended_header_offset, 4), 0); 172 $extended_header_offset += 4; 173 174 $thisfile_id3v2['exthead']['flag_bytes'] = 2; 175 $thisfile_id3v2['exthead']['flag_raw'] = getid3_lib::BigEndian2Int(substr($framedata, $extended_header_offset, $thisfile_id3v2['exthead']['flag_bytes'])); 176 $extended_header_offset += $thisfile_id3v2['exthead']['flag_bytes']; 177 178 $thisfile_id3v2['exthead']['flags']['crc'] = (bool) ($thisfile_id3v2['exthead']['flag_raw'] & 0x8000); 179 180 $thisfile_id3v2['exthead']['padding_size'] = getid3_lib::BigEndian2Int(substr($framedata, $extended_header_offset, 4)); 181 $extended_header_offset += 4; 182 183 if ($thisfile_id3v2['exthead']['flags']['crc']) { 184 $thisfile_id3v2['exthead']['flag_data']['crc'] = getid3_lib::BigEndian2Int(substr($framedata, $extended_header_offset, 4)); 185 $extended_header_offset += 4; 186 } 187 $extended_header_offset += $thisfile_id3v2['exthead']['padding_size']; 188 189 } elseif ($id3v2_majorversion == 4) { 190 191 // v2.4 definition: 192 //Extended header size 4 * %0xxxxxxx // 28-bit synchsafe integer 193 //Number of flag bytes $01 194 //Extended Flags $xx 195 // %0bcd0000 // v2.4 196 // b - Tag is an update 197 // Flag data length $00 198 // c - CRC data present 199 // Flag data length $05 200 // Total frame CRC 5 * %0xxxxxxx 201 // d - Tag restrictions 202 // Flag data length $01 203 204 $thisfile_id3v2['exthead']['length'] = getid3_lib::BigEndian2Int(substr($framedata, $extended_header_offset, 4), true); 205 $extended_header_offset += 4; 206 207 $thisfile_id3v2['exthead']['flag_bytes'] = getid3_lib::BigEndian2Int(substr($framedata, $extended_header_offset, 1)); // should always be 1 208 $extended_header_offset += 1; 209 210 $thisfile_id3v2['exthead']['flag_raw'] = getid3_lib::BigEndian2Int(substr($framedata, $extended_header_offset, $thisfile_id3v2['exthead']['flag_bytes'])); 211 $extended_header_offset += $thisfile_id3v2['exthead']['flag_bytes']; 212 213 $thisfile_id3v2['exthead']['flags']['update'] = (bool) ($thisfile_id3v2['exthead']['flag_raw'] & 0x40); 214 $thisfile_id3v2['exthead']['flags']['crc'] = (bool) ($thisfile_id3v2['exthead']['flag_raw'] & 0x20); 215 $thisfile_id3v2['exthead']['flags']['restrictions'] = (bool) ($thisfile_id3v2['exthead']['flag_raw'] & 0x10); 216 217 if ($thisfile_id3v2['exthead']['flags']['update']) { 218 $ext_header_chunk_length = getid3_lib::BigEndian2Int(substr($framedata, $extended_header_offset, 1)); // should be 0 219 $extended_header_offset += 1; 220 } 221 222 if ($thisfile_id3v2['exthead']['flags']['crc']) { 223 $ext_header_chunk_length = getid3_lib::BigEndian2Int(substr($framedata, $extended_header_offset, 1)); // should be 5 224 $extended_header_offset += 1; 225 $thisfile_id3v2['exthead']['flag_data']['crc'] = getid3_lib::BigEndian2Int(substr($framedata, $extended_header_offset, $ext_header_chunk_length), true, false); 226 $extended_header_offset += $ext_header_chunk_length; 227 } 228 229 if ($thisfile_id3v2['exthead']['flags']['restrictions']) { 230 $ext_header_chunk_length = getid3_lib::BigEndian2Int(substr($framedata, $extended_header_offset, 1)); // should be 1 231 $extended_header_offset += 1; 232 233 // %ppqrrstt 234 $restrictions_raw = getid3_lib::BigEndian2Int(substr($framedata, $extended_header_offset, 1)); 235 $extended_header_offset += 1; 236 $thisfile_id3v2['exthead']['flags']['restrictions']['tagsize'] = ($restrictions_raw & 0xC0) >> 6; // p - Tag size restrictions 237 $thisfile_id3v2['exthead']['flags']['restrictions']['textenc'] = ($restrictions_raw & 0x20) >> 5; // q - Text encoding restrictions 238 $thisfile_id3v2['exthead']['flags']['restrictions']['textsize'] = ($restrictions_raw & 0x18) >> 3; // r - Text fields size restrictions 239 $thisfile_id3v2['exthead']['flags']['restrictions']['imgenc'] = ($restrictions_raw & 0x04) >> 2; // s - Image encoding restrictions 240 $thisfile_id3v2['exthead']['flags']['restrictions']['imgsize'] = ($restrictions_raw & 0x03) >> 0; // t - Image size restrictions 241 242 $thisfile_id3v2['exthead']['flags']['restrictions_text']['tagsize'] = $this->LookupExtendedHeaderRestrictionsTagSizeLimits($thisfile_id3v2['exthead']['flags']['restrictions']['tagsize']); 243 $thisfile_id3v2['exthead']['flags']['restrictions_text']['textenc'] = $this->LookupExtendedHeaderRestrictionsTextEncodings($thisfile_id3v2['exthead']['flags']['restrictions']['textenc']); 244 $thisfile_id3v2['exthead']['flags']['restrictions_text']['textsize'] = $this->LookupExtendedHeaderRestrictionsTextFieldSize($thisfile_id3v2['exthead']['flags']['restrictions']['textsize']); 245 $thisfile_id3v2['exthead']['flags']['restrictions_text']['imgenc'] = $this->LookupExtendedHeaderRestrictionsImageEncoding($thisfile_id3v2['exthead']['flags']['restrictions']['imgenc']); 246 $thisfile_id3v2['exthead']['flags']['restrictions_text']['imgsize'] = $this->LookupExtendedHeaderRestrictionsImageSizeSize($thisfile_id3v2['exthead']['flags']['restrictions']['imgsize']); 247 } 248 249 if ($thisfile_id3v2['exthead']['length'] != $extended_header_offset) { 250 $this->warning('ID3v2.4 extended header length mismatch (expecting '.intval($thisfile_id3v2['exthead']['length']).', found '.intval($extended_header_offset).')'); 251 } 252 } 253 254 $framedataoffset += $extended_header_offset; 255 $framedata = substr($framedata, $extended_header_offset); 256 } // end extended header 257 258 259 while (isset($framedata) && (strlen($framedata) > 0)) { // cycle through until no more frame data is left to parse 260 if (strlen($framedata) <= $this->ID3v2HeaderLength($id3v2_majorversion)) { 261 // insufficient room left in ID3v2 header for actual data - must be padding 262 $thisfile_id3v2['padding']['start'] = $framedataoffset; 263 $thisfile_id3v2['padding']['length'] = strlen($framedata); 264 $thisfile_id3v2['padding']['valid'] = true; 265 for ($i = 0; $i < $thisfile_id3v2['padding']['length']; $i++) { 266 if ($framedata[$i] != "\x00") { 267 $thisfile_id3v2['padding']['valid'] = false; 268 $thisfile_id3v2['padding']['errorpos'] = $thisfile_id3v2['padding']['start'] + $i; 269 $this->warning('Invalid ID3v2 padding found at offset '.$thisfile_id3v2['padding']['errorpos'].' (the remaining '.($thisfile_id3v2['padding']['length'] - $i).' bytes are considered invalid)'); 270 break; 271 } 272 } 273 break; // skip rest of ID3v2 header 274 } 275 $frame_header = null; 276 $frame_name = null; 277 $frame_size = null; 278 $frame_flags = null; 279 if ($id3v2_majorversion == 2) { 280 // Frame ID $xx xx xx (three characters) 281 // Size $xx xx xx (24-bit integer) 282 // Flags $xx xx 283 284 $frame_header = substr($framedata, 0, 6); // take next 6 bytes for header 285 $framedata = substr($framedata, 6); // and leave the rest in $framedata 286 $frame_name = substr($frame_header, 0, 3); 287 $frame_size = getid3_lib::BigEndian2Int(substr($frame_header, 3, 3), 0); 288 $frame_flags = 0; // not used for anything in ID3v2.2, just set to avoid E_NOTICEs 289 290 } elseif ($id3v2_majorversion > 2) { 291 292 // Frame ID $xx xx xx xx (four characters) 293 // Size $xx xx xx xx (32-bit integer in v2.3, 28-bit synchsafe in v2.4+) 294 // Flags $xx xx 295 296 $frame_header = substr($framedata, 0, 10); // take next 10 bytes for header 297 $framedata = substr($framedata, 10); // and leave the rest in $framedata 298 299 $frame_name = substr($frame_header, 0, 4); 300 if ($id3v2_majorversion == 3) { 301 $frame_size = getid3_lib::BigEndian2Int(substr($frame_header, 4, 4), 0); // 32-bit integer 302 } else { // ID3v2.4+ 303 $frame_size = getid3_lib::BigEndian2Int(substr($frame_header, 4, 4), 1); // 32-bit synchsafe integer (28-bit value) 304 } 305 306 if ($frame_size < (strlen($framedata) + 4)) { 307 $nextFrameID = substr($framedata, $frame_size, 4); 308 if ($this->IsValidID3v2FrameName($nextFrameID, $id3v2_majorversion)) { 309 // next frame is OK 310 } elseif (($frame_name == "\x00".'MP3') || ($frame_name == "\x00\x00".'MP') || ($frame_name == ' MP3') || ($frame_name == 'MP3e')) { 311 // MP3ext known broken frames - "ok" for the purposes of this test 312 } elseif (($id3v2_majorversion == 4) && ($this->IsValidID3v2FrameName(substr($framedata, getid3_lib::BigEndian2Int(substr($frame_header, 4, 4), 0), 4), 3))) { 313 $this->warning('ID3v2 tag written as ID3v2.4, but with non-synchsafe integers (ID3v2.3 style). Older versions of (Helium2; iTunes) are known culprits of this. Tag has been parsed as ID3v2.3'); 314 $id3v2_majorversion = 3; 315 $frame_size = getid3_lib::BigEndian2Int(substr($frame_header, 4, 4), 0); // 32-bit integer 316 } 317 } 318 319 320 $frame_flags = getid3_lib::BigEndian2Int(substr($frame_header, 8, 2)); 321 } 322 323 if ((($id3v2_majorversion == 2) && ($frame_name == "\x00\x00\x00")) || ($frame_name == "\x00\x00\x00\x00")) { 324 // padding encountered 325 326 $thisfile_id3v2['padding']['start'] = $framedataoffset; 327 $thisfile_id3v2['padding']['length'] = strlen($frame_header) + strlen($framedata); 328 $thisfile_id3v2['padding']['valid'] = true; 329 330 $len = strlen($framedata); 331 for ($i = 0; $i < $len; $i++) { 332 if ($framedata[$i] != "\x00") { 333 $thisfile_id3v2['padding']['valid'] = false; 334 $thisfile_id3v2['padding']['errorpos'] = $thisfile_id3v2['padding']['start'] + $i; 335 $this->warning('Invalid ID3v2 padding found at offset '.$thisfile_id3v2['padding']['errorpos'].' (the remaining '.($thisfile_id3v2['padding']['length'] - $i).' bytes are considered invalid)'); 336 break; 337 } 338 } 339 break; // skip rest of ID3v2 header 340 } 341 342 if ($iTunesBrokenFrameNameFixed = self::ID3v22iTunesBrokenFrameName($frame_name)) { 343 $this->warning('error parsing "'.$frame_name.'" ('.$framedataoffset.' bytes into the ID3v2.'.$id3v2_majorversion.' tag). (ERROR: IsValidID3v2FrameName("'.str_replace("\x00", ' ', $frame_name).'", '.$id3v2_majorversion.'))). [Note: this particular error has been known to happen with tags edited by iTunes (versions "X v2.0.3", "v3.0.1", "v7.0.0.70" are known-guilty, probably others too)]. Translated frame name from "'.str_replace("\x00", ' ', $frame_name).'" to "'.$iTunesBrokenFrameNameFixed.'" for parsing.'); 344 $frame_name = $iTunesBrokenFrameNameFixed; 345 } 346 if (($frame_size <= strlen($framedata)) && ($this->IsValidID3v2FrameName($frame_name, $id3v2_majorversion))) { 347 348 $parsedFrame = array(); 349 $parsedFrame['frame_name'] = $frame_name; 350 $parsedFrame['frame_flags_raw'] = $frame_flags; 351 $parsedFrame['data'] = substr($framedata, 0, $frame_size); 352 $parsedFrame['datalength'] = getid3_lib::CastAsInt($frame_size); 353 $parsedFrame['dataoffset'] = $framedataoffset; 354 355 $this->ParseID3v2Frame($parsedFrame); 356 $thisfile_id3v2[$frame_name][] = $parsedFrame; 357 358 $framedata = substr($framedata, $frame_size); 359 360 } else { // invalid frame length or FrameID 361 362 if ($frame_size <= strlen($framedata)) { 363 364 if ($this->IsValidID3v2FrameName(substr($framedata, $frame_size, 4), $id3v2_majorversion)) { 365 366 // next frame is valid, just skip the current frame 367 $framedata = substr($framedata, $frame_size); 368 $this->warning('Next ID3v2 frame is valid, skipping current frame.'); 369 370 } else { 371 372 // next frame is invalid too, abort processing 373 //unset($framedata); 374 $framedata = null; 375 $this->error('Next ID3v2 frame is also invalid, aborting processing.'); 376 377 } 378 379 } elseif ($frame_size == strlen($framedata)) { 380 381 // this is the last frame, just skip 382 $this->warning('This was the last ID3v2 frame.'); 383 384 } else { 385 386 // next frame is invalid too, abort processing 387 //unset($framedata); 388 $framedata = null; 389 $this->warning('Invalid ID3v2 frame size, aborting.'); 390 391 } 392 if (!$this->IsValidID3v2FrameName($frame_name, $id3v2_majorversion)) { 393 394 switch ($frame_name) { 395 case "\x00\x00".'MP': 396 case "\x00".'MP3': 397 case ' MP3': 398 case 'MP3e': 399 case "\x00".'MP': 400 case ' MP': 401 case 'MP3': 402 $this->warning('error parsing "'.$frame_name.'" ('.$framedataoffset.' bytes into the ID3v2.'.$id3v2_majorversion.' tag). (ERROR: !IsValidID3v2FrameName("'.str_replace("\x00", ' ', $frame_name).'", '.$id3v2_majorversion.'))). [Note: this particular error has been known to happen with tags edited by "MP3ext (www.mutschler.de/mp3ext/)"]'); 403 break; 404 405 default: 406 $this->warning('error parsing "'.$frame_name.'" ('.$framedataoffset.' bytes into the ID3v2.'.$id3v2_majorversion.' tag). (ERROR: !IsValidID3v2FrameName("'.str_replace("\x00", ' ', $frame_name).'", '.$id3v2_majorversion.'))).'); 407 break; 408 } 409 410 } elseif (!isset($framedata) || ($frame_size > strlen($framedata))) { 411 412 $this->error('error parsing "'.$frame_name.'" ('.$framedataoffset.' bytes into the ID3v2.'.$id3v2_majorversion.' tag). (ERROR: $frame_size ('.$frame_size.') > strlen($framedata) ('.(isset($framedata) ? strlen($framedata) : 'null').')).'); 413 414 } else { 415 416 $this->error('error parsing "'.$frame_name.'" ('.$framedataoffset.' bytes into the ID3v2.'.$id3v2_majorversion.' tag).'); 417 418 } 419 420 } 421 $framedataoffset += ($frame_size + $this->ID3v2HeaderLength($id3v2_majorversion)); 422 423 } 424 425 } 426 427 428 // Footer 429 430 // The footer is a copy of the header, but with a different identifier. 431 // ID3v2 identifier "3DI" 432 // ID3v2 version $04 00 433 // ID3v2 flags %abcd0000 434 // ID3v2 size 4 * %0xxxxxxx 435 436 if (isset($thisfile_id3v2_flags['isfooter']) && $thisfile_id3v2_flags['isfooter']) { 437 $footer = $this->fread(10); 438 if (substr($footer, 0, 3) == '3DI') { 439 $thisfile_id3v2['footer'] = true; 440 $thisfile_id3v2['majorversion_footer'] = ord($footer[3]); 441 $thisfile_id3v2['minorversion_footer'] = ord($footer[4]); 442 } 443 if ($thisfile_id3v2['majorversion_footer'] <= 4) { 444 $id3_flags = ord($footer[5]); 445 $thisfile_id3v2_flags['unsynch_footer'] = (bool) ($id3_flags & 0x80); 446 $thisfile_id3v2_flags['extfoot_footer'] = (bool) ($id3_flags & 0x40); 447 $thisfile_id3v2_flags['experim_footer'] = (bool) ($id3_flags & 0x20); 448 $thisfile_id3v2_flags['isfooter_footer'] = (bool) ($id3_flags & 0x10); 449 450 $thisfile_id3v2['footerlength'] = getid3_lib::BigEndian2Int(substr($footer, 6, 4), 1); 451 } 452 } // end footer 453 454 if (isset($thisfile_id3v2['comments']['genre'])) { 455 $genres = array(); 456 foreach ($thisfile_id3v2['comments']['genre'] as $key => $value) { 457 foreach ($this->ParseID3v2GenreString($value) as $genre) { 458 $genres[] = $genre; 459 } 460 } 461 $thisfile_id3v2['comments']['genre'] = array_unique($genres); 462 unset($key, $value, $genres, $genre); 463 } 464 465 if (isset($thisfile_id3v2['comments']['track_number'])) { 466 foreach ($thisfile_id3v2['comments']['track_number'] as $key => $value) { 467 if (strstr($value, '/')) { 468 list($thisfile_id3v2['comments']['track_number'][$key], $thisfile_id3v2['comments']['totaltracks'][$key]) = explode('/', $thisfile_id3v2['comments']['track_number'][$key]); 469 } 470 } 471 } 472 473 if (!isset($thisfile_id3v2['comments']['year']) && !empty($thisfile_id3v2['comments']['recording_time'][0]) && preg_match('#^([0-9]{4})#', trim($thisfile_id3v2['comments']['recording_time'][0]), $matches)) { 474 $thisfile_id3v2['comments']['year'] = array($matches[1]); 475 } 476 477 478 if (!empty($thisfile_id3v2['TXXX'])) { 479 // MediaMonkey does this, maybe others: write a blank RGAD frame, but put replay-gain adjustment values in TXXX frames 480 foreach ($thisfile_id3v2['TXXX'] as $txxx_array) { 481 switch ($txxx_array['description']) { 482 case 'replaygain_track_gain': 483 if (empty($info['replay_gain']['track']['adjustment']) && !empty($txxx_array['data'])) { 484 $info['replay_gain']['track']['adjustment'] = floatval(trim(str_replace('dB', '', $txxx_array['data']))); 485 } 486 break; 487 case 'replaygain_track_peak': 488 if (empty($info['replay_gain']['track']['peak']) && !empty($txxx_array['data'])) { 489 $info['replay_gain']['track']['peak'] = floatval($txxx_array['data']); 490 } 491 break; 492 case 'replaygain_album_gain': 493 if (empty($info['replay_gain']['album']['adjustment']) && !empty($txxx_array['data'])) { 494 $info['replay_gain']['album']['adjustment'] = floatval(trim(str_replace('dB', '', $txxx_array['data']))); 495 } 496 break; 497 } 498 } 499 } 500 501 502 // Set avdataoffset 503 $info['avdataoffset'] = $thisfile_id3v2['headerlength']; 504 if (isset($thisfile_id3v2['footer'])) { 505 $info['avdataoffset'] += 10; 506 } 507 508 return true; 509 } 510 511 /** 512 * @param string $genrestring 513 * 514 * @return array 515 */ 516 public function ParseID3v2GenreString($genrestring) { 517 // Parse genres into arrays of genreName and genreID 518 // ID3v2.2.x, ID3v2.3.x: '(21)' or '(4)Eurodisco' or '(51)(39)' or '(55)((I think...)' 519 // ID3v2.4.x: '21' $00 'Eurodisco' $00 520 $clean_genres = array(); 521 522 // hack-fixes for some badly-written ID3v2.3 taggers, while trying not to break correctly-written tags 523 if (($this->getid3->info['id3v2']['majorversion'] == 3) && !preg_match('#[\x00]#', $genrestring)) { 524 // note: MusicBrainz Picard incorrectly stores plaintext genres separated by "/" when writing in ID3v2.3 mode, hack-fix here: 525 // replace / with NULL, then replace back the two ID3v1 genres that legitimately have "/" as part of the single genre name 526 if (strpos($genrestring, '/') !== false) { 527 $LegitimateSlashedGenreList = array( // https://github.com/JamesHeinrich/getID3/issues/223 528 'Pop/Funk', // ID3v1 genre #62 - https://en.wikipedia.org/wiki/ID3#standard 529 'Cut-up/DJ', // Discogs - https://www.discogs.com/style/cut-up/dj 530 'RnB/Swing', // Discogs - https://www.discogs.com/style/rnb/swing 531 'Funk / Soul', // Discogs (note spaces) - https://www.discogs.com/genre/funk+%2F+soul 532 ); 533 $genrestring = str_replace('/', "\x00", $genrestring); 534 foreach ($LegitimateSlashedGenreList as $SlashedGenre) { 535 $genrestring = str_ireplace(str_replace('/', "\x00", $SlashedGenre), $SlashedGenre, $genrestring); 536 } 537 } 538 539 // some other taggers separate multiple genres with semicolon, e.g. "Heavy Metal;Thrash Metal;Metal" 540 if (strpos($genrestring, ';') !== false) { 541 $genrestring = str_replace(';', "\x00", $genrestring); 542 } 543 } 544 545 546 if (strpos($genrestring, "\x00") === false) { 547 $genrestring = preg_replace('#\(([0-9]{1,3})\)#', '$1'."\x00", $genrestring); 548 } 549 550 $genre_elements = explode("\x00", $genrestring); 551 foreach ($genre_elements as $element) { 552 $element = trim($element); 553 if ($element) { 554 if (preg_match('#^[0-9]{1,3}$#', $element)) { 555 $clean_genres[] = getid3_id3v1::LookupGenreName($element); 556 } else { 557 $clean_genres[] = str_replace('((', '(', $element); 558 } 559 } 560 } 561 return $clean_genres; 562 } 563 564 /** 565 * @param array $parsedFrame 566 * 567 * @return bool 568 */ 569 public function ParseID3v2Frame(&$parsedFrame) { 570 571 // shortcuts 572 $info = &$this->getid3->info; 573 $id3v2_majorversion = $info['id3v2']['majorversion']; 574 575 $parsedFrame['framenamelong'] = $this->FrameNameLongLookup($parsedFrame['frame_name']); 576 if (empty($parsedFrame['framenamelong'])) { 577 unset($parsedFrame['framenamelong']); 578 } 579 $parsedFrame['framenameshort'] = $this->FrameNameShortLookup($parsedFrame['frame_name']); 580 if (empty($parsedFrame['framenameshort'])) { 581 unset($parsedFrame['framenameshort']); 582 } 583 584 if ($id3v2_majorversion >= 3) { // frame flags are not part of the ID3v2.2 standard 585 if ($id3v2_majorversion == 3) { 586 // Frame Header Flags 587 // %abc00000 %ijk00000 588 $parsedFrame['flags']['TagAlterPreservation'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x8000); // a - Tag alter preservation 589 $parsedFrame['flags']['FileAlterPreservation'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x4000); // b - File alter preservation 590 $parsedFrame['flags']['ReadOnly'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x2000); // c - Read only 591 $parsedFrame['flags']['compression'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x0080); // i - Compression 592 $parsedFrame['flags']['Encryption'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x0040); // j - Encryption 593 $parsedFrame['flags']['GroupingIdentity'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x0020); // k - Grouping identity 594 595 } elseif ($id3v2_majorversion == 4) { 596 // Frame Header Flags 597 // %0abc0000 %0h00kmnp 598 $parsedFrame['flags']['TagAlterPreservation'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x4000); // a - Tag alter preservation 599 $parsedFrame['flags']['FileAlterPreservation'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x2000); // b - File alter preservation 600 $parsedFrame['flags']['ReadOnly'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x1000); // c - Read only 601 $parsedFrame['flags']['GroupingIdentity'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x0040); // h - Grouping identity 602 $parsedFrame['flags']['compression'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x0008); // k - Compression 603 $parsedFrame['flags']['Encryption'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x0004); // m - Encryption 604 $parsedFrame['flags']['Unsynchronisation'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x0002); // n - Unsynchronisation 605 $parsedFrame['flags']['DataLengthIndicator'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x0001); // p - Data length indicator 606 607 // Frame-level de-unsynchronisation - ID3v2.4 608 if ($parsedFrame['flags']['Unsynchronisation']) { 609 $parsedFrame['data'] = $this->DeUnsynchronise($parsedFrame['data']); 610 } 611 612 if ($parsedFrame['flags']['DataLengthIndicator']) { 613 $parsedFrame['data_length_indicator'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], 0, 4), 1); 614 $parsedFrame['data'] = substr($parsedFrame['data'], 4); 615 } 616 } 617 618 // Frame-level de-compression 619 if ($parsedFrame['flags']['compression']) { 620 $parsedFrame['decompressed_size'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], 0, 4)); 621 if (!function_exists('gzuncompress')) { 622 $this->warning('gzuncompress() support required to decompress ID3v2 frame "'.$parsedFrame['frame_name'].'"'); 623 } else { 624 if ($decompresseddata = @gzuncompress(substr($parsedFrame['data'], 4))) { 625 //if ($decompresseddata = @gzuncompress($parsedFrame['data'])) { 626 $parsedFrame['data'] = $decompresseddata; 627 unset($decompresseddata); 628 } else { 629 $this->warning('gzuncompress() failed on compressed contents of ID3v2 frame "'.$parsedFrame['frame_name'].'"'); 630 } 631 } 632 } 633 } 634 635 if (!empty($parsedFrame['flags']['DataLengthIndicator'])) { 636 if ($parsedFrame['data_length_indicator'] != strlen($parsedFrame['data'])) { 637 $this->warning('ID3v2 frame "'.$parsedFrame['frame_name'].'" should be '.$parsedFrame['data_length_indicator'].' bytes long according to DataLengthIndicator, but found '.strlen($parsedFrame['data']).' bytes of data'); 638 } 639 } 640 641 if (isset($parsedFrame['datalength']) && ($parsedFrame['datalength'] == 0)) { 642 643 $warning = 'Frame "'.$parsedFrame['frame_name'].'" at offset '.$parsedFrame['dataoffset'].' has no data portion'; 644 switch ($parsedFrame['frame_name']) { 645 case 'WCOM': 646 $warning .= ' (this is known to happen with files tagged by RioPort)'; 647 break; 648 649 default: 650 break; 651 } 652 $this->warning($warning); 653 654 } elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'UFID')) || // 4.1 UFID Unique file identifier 655 (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'UFI'))) { // 4.1 UFI Unique file identifier 656 // There may be more than one 'UFID' frame in a tag, 657 // but only one with the same 'Owner identifier'. 658 // <Header for 'Unique file identifier', ID: 'UFID'> 659 // Owner identifier <text string> $00 660 // Identifier <up to 64 bytes binary data> 661 $exploded = explode("\x00", $parsedFrame['data'], 2); 662 $parsedFrame['ownerid'] = (isset($exploded[0]) ? $exploded[0] : ''); 663 $parsedFrame['data'] = (isset($exploded[1]) ? $exploded[1] : ''); 664 665 } elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'TXXX')) || // 4.2.2 TXXX User defined text information frame 666 (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'TXX'))) { // 4.2.2 TXX User defined text information frame 667 // There may be more than one 'TXXX' frame in each tag, 668 // but only one with the same description. 669 // <Header for 'User defined text information frame', ID: 'TXXX'> 670 // Text encoding $xx 671 // Description <text string according to encoding> $00 (00) 672 // Value <text string according to encoding> 673 674 $frame_offset = 0; 675 $frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1)); 676 $frame_textencoding_terminator = $this->TextEncodingTerminatorLookup($frame_textencoding); 677 if ((($id3v2_majorversion <= 3) && ($frame_textencoding > 1)) || (($id3v2_majorversion == 4) && ($frame_textencoding > 3))) { 678 $this->warning('Invalid text encoding byte ('.$frame_textencoding.') in frame "'.$parsedFrame['frame_name'].'" - defaulting to ISO-8859-1 encoding'); 679 $frame_textencoding_terminator = "\x00"; 680 } 681 $frame_terminatorpos = strpos($parsedFrame['data'], $frame_textencoding_terminator, $frame_offset); 682 if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($frame_textencoding_terminator), 1)) === 0) { 683 $frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00 684 } 685 $parsedFrame['description'] = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); 686 $parsedFrame['description'] = $this->MakeUTF16emptyStringEmpty($parsedFrame['description']); 687 $parsedFrame['encodingid'] = $frame_textencoding; 688 $parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding); 689 690 $parsedFrame['description'] = trim(getid3_lib::iconv_fallback($parsedFrame['encoding'], $info['id3v2']['encoding'], $parsedFrame['description'])); 691 $parsedFrame['data'] = substr($parsedFrame['data'], $frame_terminatorpos + strlen($frame_textencoding_terminator)); 692 $parsedFrame['data'] = $this->RemoveStringTerminator($parsedFrame['data'], $frame_textencoding_terminator); 693 if (!empty($parsedFrame['framenameshort']) && !empty($parsedFrame['data'])) { 694 $commentkey = ($parsedFrame['description'] ? $parsedFrame['description'] : (isset($info['id3v2']['comments'][$parsedFrame['framenameshort']]) ? count($info['id3v2']['comments'][$parsedFrame['framenameshort']]) : 0)); 695 if (!isset($info['id3v2']['comments'][$parsedFrame['framenameshort']]) || !array_key_exists($commentkey, $info['id3v2']['comments'][$parsedFrame['framenameshort']])) { 696 $info['id3v2']['comments'][$parsedFrame['framenameshort']][$commentkey] = trim(getid3_lib::iconv_fallback($parsedFrame['encoding'], $info['id3v2']['encoding'], $parsedFrame['data'])); 697 } else { 698 $info['id3v2']['comments'][$parsedFrame['framenameshort']][] = trim(getid3_lib::iconv_fallback($parsedFrame['encoding'], $info['id3v2']['encoding'], $parsedFrame['data'])); 699 } 700 } 701 //unset($parsedFrame['data']); do not unset, may be needed elsewhere, e.g. for replaygain 702 703 704 } elseif ($parsedFrame['frame_name'][0] == 'T') { // 4.2. T??[?] Text information frame 705 // There may only be one text information frame of its kind in an tag. 706 // <Header for 'Text information frame', ID: 'T000' - 'TZZZ', 707 // excluding 'TXXX' described in 4.2.6.> 708 // Text encoding $xx 709 // Information <text string(s) according to encoding> 710 711 $frame_offset = 0; 712 $frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1)); 713 if ((($id3v2_majorversion <= 3) && ($frame_textencoding > 1)) || (($id3v2_majorversion == 4) && ($frame_textencoding > 3))) { 714 $this->warning('Invalid text encoding byte ('.$frame_textencoding.') in frame "'.$parsedFrame['frame_name'].'" - defaulting to ISO-8859-1 encoding'); 715 } 716 717 $parsedFrame['data'] = (string) substr($parsedFrame['data'], $frame_offset); 718 $parsedFrame['data'] = $this->RemoveStringTerminator($parsedFrame['data'], $this->TextEncodingTerminatorLookup($frame_textencoding)); 719 720 $parsedFrame['encodingid'] = $frame_textencoding; 721 $parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding); 722 if (!empty($parsedFrame['framenameshort']) && !empty($parsedFrame['data'])) { 723 // ID3v2.3 specs say that TPE1 (and others) can contain multiple artist values separated with / 724 // This of course breaks when an artist name contains slash character, e.g. "AC/DC" 725 // MP3tag (maybe others) implement alternative system where multiple artists are null-separated, which makes more sense 726 // getID3 will split null-separated artists into multiple artists and leave slash-separated ones to the user 727 switch ($parsedFrame['encoding']) { 728 case 'UTF-16': 729 case 'UTF-16BE': 730 case 'UTF-16LE': 731 $wordsize = 2; 732 break; 733 case 'ISO-8859-1': 734 case 'UTF-8': 735 default: 736 $wordsize = 1; 737 break; 738 } 739 $Txxx_elements = array(); 740 $Txxx_elements_start_offset = 0; 741 for ($i = 0; $i < strlen($parsedFrame['data']); $i += $wordsize) { 742 if (substr($parsedFrame['data'], $i, $wordsize) == str_repeat("\x00", $wordsize)) { 743 $Txxx_elements[] = substr($parsedFrame['data'], $Txxx_elements_start_offset, $i - $Txxx_elements_start_offset); 744 $Txxx_elements_start_offset = $i + $wordsize; 745 } 746 } 747 $Txxx_elements[] = substr($parsedFrame['data'], $Txxx_elements_start_offset, $i - $Txxx_elements_start_offset); 748 foreach ($Txxx_elements as $Txxx_element) { 749 $string = getid3_lib::iconv_fallback($parsedFrame['encoding'], $info['id3v2']['encoding'], $Txxx_element); 750 if (!empty($string)) { 751 $info['id3v2']['comments'][$parsedFrame['framenameshort']][] = $string; 752 } 753 } 754 unset($string, $wordsize, $i, $Txxx_elements, $Txxx_element, $Txxx_elements_start_offset); 755 } 756 757 } elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'WXXX')) || // 4.3.2 WXXX User defined URL link frame 758 (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'WXX'))) { // 4.3.2 WXX User defined URL link frame 759 // There may be more than one 'WXXX' frame in each tag, 760 // but only one with the same description 761 // <Header for 'User defined URL link frame', ID: 'WXXX'> 762 // Text encoding $xx 763 // Description <text string according to encoding> $00 (00) 764 // URL <text string> 765 766 $frame_offset = 0; 767 $frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1)); 768 $frame_textencoding_terminator = $this->TextEncodingTerminatorLookup($frame_textencoding); 769 if ((($id3v2_majorversion <= 3) && ($frame_textencoding > 1)) || (($id3v2_majorversion == 4) && ($frame_textencoding > 3))) { 770 $this->warning('Invalid text encoding byte ('.$frame_textencoding.') in frame "'.$parsedFrame['frame_name'].'" - defaulting to ISO-8859-1 encoding'); 771 $frame_textencoding_terminator = "\x00"; 772 } 773 $frame_terminatorpos = strpos($parsedFrame['data'], $frame_textencoding_terminator, $frame_offset); 774 if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($frame_textencoding_terminator), 1)) === 0) { 775 $frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00 776 } 777 $parsedFrame['encodingid'] = $frame_textencoding; 778 $parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding); 779 $parsedFrame['description'] = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); // according to the frame text encoding 780 $parsedFrame['url'] = substr($parsedFrame['data'], $frame_terminatorpos + strlen($frame_textencoding_terminator)); // always ISO-8859-1 781 $parsedFrame['description'] = $this->RemoveStringTerminator($parsedFrame['description'], $frame_textencoding_terminator); 782 $parsedFrame['description'] = $this->MakeUTF16emptyStringEmpty($parsedFrame['description']); 783 784 if (!empty($parsedFrame['framenameshort']) && $parsedFrame['url']) { 785 $info['id3v2']['comments'][$parsedFrame['framenameshort']][] = getid3_lib::iconv_fallback('ISO-8859-1', $info['id3v2']['encoding'], $parsedFrame['url']); 786 } 787 unset($parsedFrame['data']); 788 789 790 } elseif ($parsedFrame['frame_name'][0] == 'W') { // 4.3. W??? URL link frames 791 // There may only be one URL link frame of its kind in a tag, 792 // except when stated otherwise in the frame description 793 // <Header for 'URL link frame', ID: 'W000' - 'WZZZ', excluding 'WXXX' 794 // described in 4.3.2.> 795 // URL <text string> 796 797 $parsedFrame['url'] = trim($parsedFrame['data']); // always ISO-8859-1 798 if (!empty($parsedFrame['framenameshort']) && $parsedFrame['url']) { 799 $info['id3v2']['comments'][$parsedFrame['framenameshort']][] = getid3_lib::iconv_fallback('ISO-8859-1', $info['id3v2']['encoding'], $parsedFrame['url']); 800 } 801 unset($parsedFrame['data']); 802 803 804 } elseif ((($id3v2_majorversion == 3) && ($parsedFrame['frame_name'] == 'IPLS')) || // 4.4 IPLS Involved people list (ID3v2.3 only) 805 (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'IPL'))) { // 4.4 IPL Involved people list (ID3v2.2 only) 806 // http://id3.org/id3v2.3.0#sec4.4 807 // There may only be one 'IPL' frame in each tag 808 // <Header for 'User defined URL link frame', ID: 'IPL'> 809 // Text encoding $xx 810 // People list strings <textstrings> 811 812 $frame_offset = 0; 813 $frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1)); 814 if ((($id3v2_majorversion <= 3) && ($frame_textencoding > 1)) || (($id3v2_majorversion == 4) && ($frame_textencoding > 3))) { 815 $this->warning('Invalid text encoding byte ('.$frame_textencoding.') in frame "'.$parsedFrame['frame_name'].'" - defaulting to ISO-8859-1 encoding'); 816 } 817 $parsedFrame['encodingid'] = $frame_textencoding; 818 $parsedFrame['encoding'] = $this->TextEncodingNameLookup($parsedFrame['encodingid']); 819 $parsedFrame['data_raw'] = (string) substr($parsedFrame['data'], $frame_offset); 820 821 // https://www.getid3.org/phpBB3/viewtopic.php?t=1369 822 // "this tag typically contains null terminated strings, which are associated in pairs" 823 // "there are users that use the tag incorrectly" 824 $IPLS_parts = array(); 825 if (strpos($parsedFrame['data_raw'], "\x00") !== false) { 826 $IPLS_parts_unsorted = array(); 827 if (((strlen($parsedFrame['data_raw']) % 2) == 0) && ((substr($parsedFrame['data_raw'], 0, 2) == "\xFF\xFE") || (substr($parsedFrame['data_raw'], 0, 2) == "\xFE\xFF"))) { 828 // UTF-16, be careful looking for null bytes since most 2-byte characters may contain one; you need to find twin null bytes, and on even padding 829 $thisILPS = ''; 830 for ($i = 0; $i < strlen($parsedFrame['data_raw']); $i += 2) { 831 $twobytes = substr($parsedFrame['data_raw'], $i, 2); 832 if ($twobytes === "\x00\x00") { 833 $IPLS_parts_unsorted[] = getid3_lib::iconv_fallback($parsedFrame['encoding'], $info['id3v2']['encoding'], $thisILPS); 834 $thisILPS = ''; 835 } else { 836 $thisILPS .= $twobytes; 837 } 838 } 839 if (strlen($thisILPS) > 2) { // 2-byte BOM 840 $IPLS_parts_unsorted[] = getid3_lib::iconv_fallback($parsedFrame['encoding'], $info['id3v2']['encoding'], $thisILPS); 841 } 842 } else { 843 // ISO-8859-1 or UTF-8 or other single-byte-null character set 844 $IPLS_parts_unsorted = explode("\x00", $parsedFrame['data_raw']); 845 } 846 if (count($IPLS_parts_unsorted) == 1) { 847 // just a list of names, e.g. "Dino Baptiste, Jimmy Copley, John Gordon, Bernie Marsden, Sharon Watson" 848 foreach ($IPLS_parts_unsorted as $key => $value) { 849 $IPLS_parts_sorted = preg_split('#[;,\\r\\n\\t]#', $value); 850 $position = ''; 851 foreach ($IPLS_parts_sorted as $person) { 852 $IPLS_parts[] = array('position'=>$position, 'person'=>$person); 853 } 854 } 855 } elseif ((count($IPLS_parts_unsorted) % 2) == 0) { 856 $position = ''; 857 $person = ''; 858 foreach ($IPLS_parts_unsorted as $key => $value) { 859 if (($key % 2) == 0) { 860 $position = $value; 861 } else { 862 $person = $value; 863 $IPLS_parts[] = array('position'=>$position, 'person'=>$person); 864 $position = ''; 865 $person = ''; 866 } 867 } 868 } else { 869 foreach ($IPLS_parts_unsorted as $key => $value) { 870 $IPLS_parts[] = array($value); 871 } 872 } 873 874 } else { 875 $IPLS_parts = preg_split('#[;,\\r\\n\\t]#', $parsedFrame['data_raw']); 876 } 877 $parsedFrame['data'] = $IPLS_parts; 878 879 if (!empty($parsedFrame['framenameshort']) && !empty($parsedFrame['data'])) { 880 $info['id3v2']['comments'][$parsedFrame['framenameshort']][] = $parsedFrame['data']; 881 } 882 883 884 } elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'MCDI')) || // 4.4 MCDI Music CD identifier 885 (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'MCI'))) { // 4.5 MCI Music CD identifier 886 // There may only be one 'MCDI' frame in each tag 887 // <Header for 'Music CD identifier', ID: 'MCDI'> 888 // CD TOC <binary data> 889 890 if (!empty($parsedFrame['framenameshort']) && !empty($parsedFrame['data'])) { 891 $info['id3v2']['comments'][$parsedFrame['framenameshort']][] = $parsedFrame['data']; 892 } 893 894 895 } elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'ETCO')) || // 4.5 ETCO Event timing codes 896 (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'ETC'))) { // 4.6 ETC Event timing codes 897 // There may only be one 'ETCO' frame in each tag 898 // <Header for 'Event timing codes', ID: 'ETCO'> 899 // Time stamp format $xx 900 // Where time stamp format is: 901 // $01 (32-bit value) MPEG frames from beginning of file 902 // $02 (32-bit value) milliseconds from beginning of file 903 // Followed by a list of key events in the following format: 904 // Type of event $xx 905 // Time stamp $xx (xx ...) 906 // The 'Time stamp' is set to zero if directly at the beginning of the sound 907 // or after the previous event. All events MUST be sorted in chronological order. 908 909 $frame_offset = 0; 910 $parsedFrame['timestampformat'] = ord(substr($parsedFrame['data'], $frame_offset++, 1)); 911 912 while ($frame_offset < strlen($parsedFrame['data'])) { 913 $parsedFrame['typeid'] = substr($parsedFrame['data'], $frame_offset++, 1); 914 $parsedFrame['type'] = $this->ETCOEventLookup($parsedFrame['typeid']); 915 $parsedFrame['timestamp'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 4)); 916 $frame_offset += 4; 917 } 918 unset($parsedFrame['data']); 919 920 921 } elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'MLLT')) || // 4.6 MLLT MPEG location lookup table 922 (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'MLL'))) { // 4.7 MLL MPEG location lookup table 923 // There may only be one 'MLLT' frame in each tag 924 // <Header for 'Location lookup table', ID: 'MLLT'> 925 // MPEG frames between reference $xx xx 926 // Bytes between reference $xx xx xx 927 // Milliseconds between reference $xx xx xx 928 // Bits for bytes deviation $xx 929 // Bits for milliseconds dev. $xx 930 // Then for every reference the following data is included; 931 // Deviation in bytes %xxx.... 932 // Deviation in milliseconds %xxx.... 933 934 $frame_offset = 0; 935 $parsedFrame['framesbetweenreferences'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], 0, 2)); 936 $parsedFrame['bytesbetweenreferences'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], 2, 3)); 937 $parsedFrame['msbetweenreferences'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], 5, 3)); 938 $parsedFrame['bitsforbytesdeviation'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], 8, 1)); 939 $parsedFrame['bitsformsdeviation'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], 9, 1)); 940 $parsedFrame['data'] = substr($parsedFrame['data'], 10); 941 $deviationbitstream = ''; 942 while ($frame_offset < strlen($parsedFrame['data'])) { 943 $deviationbitstream .= getid3_lib::BigEndian2Bin(substr($parsedFrame['data'], $frame_offset++, 1)); 944 } 945 $reference_counter = 0; 946 while (strlen($deviationbitstream) > 0) { 947 $parsedFrame[$reference_counter]['bytedeviation'] = bindec(substr($deviationbitstream, 0, $parsedFrame['bitsforbytesdeviation'])); 948 $parsedFrame[$reference_counter]['msdeviation'] = bindec(substr($deviationbitstream, $parsedFrame['bitsforbytesdeviation'], $parsedFrame['bitsformsdeviation'])); 949 $deviationbitstream = substr($deviationbitstream, $parsedFrame['bitsforbytesdeviation'] + $parsedFrame['bitsformsdeviation']); 950 $reference_counter++; 951 } 952 unset($parsedFrame['data']); 953 954 955 } elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'SYTC')) || // 4.7 SYTC Synchronised tempo codes 956 (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'STC'))) { // 4.8 STC Synchronised tempo codes 957 // There may only be one 'SYTC' frame in each tag 958 // <Header for 'Synchronised tempo codes', ID: 'SYTC'> 959 // Time stamp format $xx 960 // Tempo data <binary data> 961 // Where time stamp format is: 962 // $01 (32-bit value) MPEG frames from beginning of file 963 // $02 (32-bit value) milliseconds from beginning of file 964 965 $frame_offset = 0; 966 $parsedFrame['timestampformat'] = ord(substr($parsedFrame['data'], $frame_offset++, 1)); 967 $timestamp_counter = 0; 968 while ($frame_offset < strlen($parsedFrame['data'])) { 969 $parsedFrame[$timestamp_counter]['tempo'] = ord(substr($parsedFrame['data'], $frame_offset++, 1)); 970 if ($parsedFrame[$timestamp_counter]['tempo'] == 255) { 971 $parsedFrame[$timestamp_counter]['tempo'] += ord(substr($parsedFrame['data'], $frame_offset++, 1)); 972 } 973 $parsedFrame[$timestamp_counter]['timestamp'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 4)); 974 $frame_offset += 4; 975 $timestamp_counter++; 976 } 977 unset($parsedFrame['data']); 978 979 980 } elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'USLT')) || // 4.8 USLT Unsynchronised lyric/text transcription 981 (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'ULT'))) { // 4.9 ULT Unsynchronised lyric/text transcription 982 // There may be more than one 'Unsynchronised lyrics/text transcription' frame 983 // in each tag, but only one with the same language and content descriptor. 984 // <Header for 'Unsynchronised lyrics/text transcription', ID: 'USLT'> 985 // Text encoding $xx 986 // Language $xx xx xx 987 // Content descriptor <text string according to encoding> $00 (00) 988 // Lyrics/text <full text string according to encoding> 989 990 $frame_offset = 0; 991 $frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1)); 992 $frame_textencoding_terminator = $this->TextEncodingTerminatorLookup($frame_textencoding); 993 if ((($id3v2_majorversion <= 3) && ($frame_textencoding > 1)) || (($id3v2_majorversion == 4) && ($frame_textencoding > 3))) { 994 $this->warning('Invalid text encoding byte ('.$frame_textencoding.') in frame "'.$parsedFrame['frame_name'].'" - defaulting to ISO-8859-1 encoding'); 995 $frame_textencoding_terminator = "\x00"; 996 } 997 if (strlen($parsedFrame['data']) >= (4 + strlen($frame_textencoding_terminator))) { // shouldn't be an issue but badly-written files have been spotted in the wild with not only no contents but also missing the required language field, see https://github.com/JamesHeinrich/getID3/issues/315 998 $frame_language = substr($parsedFrame['data'], $frame_offset, 3); 999 $frame_offset += 3; 1000 $frame_terminatorpos = strpos($parsedFrame['data'], $frame_textencoding_terminator, $frame_offset); 1001 if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($frame_textencoding_terminator), 1)) === 0) { 1002 $frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00 1003 } 1004 $parsedFrame['description'] = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); 1005 $parsedFrame['description'] = $this->MakeUTF16emptyStringEmpty($parsedFrame['description']); 1006 $parsedFrame['data'] = substr($parsedFrame['data'], $frame_terminatorpos + strlen($frame_textencoding_terminator)); 1007 $parsedFrame['data'] = $this->RemoveStringTerminator($parsedFrame['data'], $frame_textencoding_terminator); 1008 1009 $parsedFrame['encodingid'] = $frame_textencoding; 1010 $parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding); 1011 1012 $parsedFrame['language'] = $frame_language; 1013 $parsedFrame['languagename'] = $this->LanguageLookup($frame_language, false); 1014 if (!empty($parsedFrame['framenameshort']) && !empty($parsedFrame['data'])) { 1015 $info['id3v2']['comments'][$parsedFrame['framenameshort']][] = getid3_lib::iconv_fallback($parsedFrame['encoding'], $info['id3v2']['encoding'], $parsedFrame['data']); 1016 } 1017 } else { 1018 $this->warning('Invalid data in frame "'.$parsedFrame['frame_name'].'" at offset '.$parsedFrame['dataoffset']); 1019 } 1020 unset($parsedFrame['data']); 1021 1022 1023 } elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'SYLT')) || // 4.9 SYLT Synchronised lyric/text 1024 (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'SLT'))) { // 4.10 SLT Synchronised lyric/text 1025 // There may be more than one 'SYLT' frame in each tag, 1026 // but only one with the same language and content descriptor. 1027 // <Header for 'Synchronised lyrics/text', ID: 'SYLT'> 1028 // Text encoding $xx 1029 // Language $xx xx xx 1030 // Time stamp format $xx 1031 // $01 (32-bit value) MPEG frames from beginning of file 1032 // $02 (32-bit value) milliseconds from beginning of file 1033 // Content type $xx 1034 // Content descriptor <text string according to encoding> $00 (00) 1035 // Terminated text to be synced (typically a syllable) 1036 // Sync identifier (terminator to above string) $00 (00) 1037 // Time stamp $xx (xx ...) 1038 1039 $frame_offset = 0; 1040 $frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1)); 1041 $frame_textencoding_terminator = $this->TextEncodingTerminatorLookup($frame_textencoding); 1042 if ((($id3v2_majorversion <= 3) && ($frame_textencoding > 1)) || (($id3v2_majorversion == 4) && ($frame_textencoding > 3))) { 1043 $this->warning('Invalid text encoding byte ('.$frame_textencoding.') in frame "'.$parsedFrame['frame_name'].'" - defaulting to ISO-8859-1 encoding'); 1044 $frame_textencoding_terminator = "\x00"; 1045 } 1046 $frame_language = substr($parsedFrame['data'], $frame_offset, 3); 1047 $frame_offset += 3; 1048 $parsedFrame['timestampformat'] = ord(substr($parsedFrame['data'], $frame_offset++, 1)); 1049 $parsedFrame['contenttypeid'] = ord(substr($parsedFrame['data'], $frame_offset++, 1)); 1050 $parsedFrame['contenttype'] = $this->SYTLContentTypeLookup($parsedFrame['contenttypeid']); 1051 $parsedFrame['encodingid'] = $frame_textencoding; 1052 $parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding); 1053 1054 $parsedFrame['language'] = $frame_language; 1055 $parsedFrame['languagename'] = $this->LanguageLookup($frame_language, false); 1056 1057 $timestampindex = 0; 1058 $frame_remainingdata = substr($parsedFrame['data'], $frame_offset); 1059 while (strlen($frame_remainingdata)) { 1060 $frame_offset = 0; 1061 $frame_terminatorpos = strpos($frame_remainingdata, $frame_textencoding_terminator); 1062 if ($frame_terminatorpos === false) { 1063 $frame_remainingdata = ''; 1064 } else { 1065 if (ord(substr($frame_remainingdata, $frame_terminatorpos + strlen($frame_textencoding_terminator), 1)) === 0) { 1066 $frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00 1067 } 1068 $parsedFrame['lyrics'][$timestampindex]['data'] = substr($frame_remainingdata, $frame_offset, $frame_terminatorpos - $frame_offset); 1069 1070 $frame_remainingdata = substr($frame_remainingdata, $frame_terminatorpos + strlen($frame_textencoding_terminator)); 1071 if (($timestampindex == 0) && (ord($frame_remainingdata[0]) != 0)) { 1072 // timestamp probably omitted for first data item 1073 } else { 1074 $parsedFrame['lyrics'][$timestampindex]['timestamp'] = getid3_lib::BigEndian2Int(substr($frame_remainingdata, 0, 4)); 1075 $frame_remainingdata = substr($frame_remainingdata, 4); 1076 } 1077 $timestampindex++; 1078 } 1079 } 1080 unset($parsedFrame['data']); 1081 1082 1083 } elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'COMM')) || // 4.10 COMM Comments 1084 (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'COM'))) { // 4.11 COM Comments 1085 // There may be more than one comment frame in each tag, 1086 // but only one with the same language and content descriptor. 1087 // <Header for 'Comment', ID: 'COMM'> 1088 // Text encoding $xx 1089 // Language $xx xx xx 1090 // Short content descrip. <text string according to encoding> $00 (00) 1091 // The actual text <full text string according to encoding> 1092 1093 if (strlen($parsedFrame['data']) < 5) { 1094 1095 $this->warning('Invalid data (too short) for "'.$parsedFrame['frame_name'].'" frame at offset '.$parsedFrame['dataoffset']); 1096 1097 } else { 1098 1099 $frame_offset = 0; 1100 $frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1)); 1101 $frame_textencoding_terminator = $this->TextEncodingTerminatorLookup($frame_textencoding); 1102 if ((($id3v2_majorversion <= 3) && ($frame_textencoding > 1)) || (($id3v2_majorversion == 4) && ($frame_textencoding > 3))) { 1103 $this->warning('Invalid text encoding byte ('.$frame_textencoding.') in frame "'.$parsedFrame['frame_name'].'" - defaulting to ISO-8859-1 encoding'); 1104 $frame_textencoding_terminator = "\x00"; 1105 } 1106 $frame_language = substr($parsedFrame['data'], $frame_offset, 3); 1107 $frame_offset += 3; 1108 $frame_terminatorpos = strpos($parsedFrame['data'], $frame_textencoding_terminator, $frame_offset); 1109 if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($frame_textencoding_terminator), 1)) === 0) { 1110 $frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00 1111 } 1112 $parsedFrame['description'] = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); 1113 $parsedFrame['description'] = $this->MakeUTF16emptyStringEmpty($parsedFrame['description']); 1114 $frame_text = (string) substr($parsedFrame['data'], $frame_terminatorpos + strlen($frame_textencoding_terminator)); 1115 $frame_text = $this->RemoveStringTerminator($frame_text, $frame_textencoding_terminator); 1116 1117 $parsedFrame['encodingid'] = $frame_textencoding; 1118 $parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding); 1119 1120 $parsedFrame['language'] = $frame_language; 1121 $parsedFrame['languagename'] = $this->LanguageLookup($frame_language, false); 1122 $parsedFrame['data'] = $frame_text; 1123 if (!empty($parsedFrame['framenameshort']) && !empty($parsedFrame['data'])) { 1124 $commentkey = ($parsedFrame['description'] ? $parsedFrame['description'] : (!empty($info['id3v2']['comments'][$parsedFrame['framenameshort']]) ? count($info['id3v2']['comments'][$parsedFrame['framenameshort']]) : 0)); 1125 if (!isset($info['id3v2']['comments'][$parsedFrame['framenameshort']]) || !array_key_exists($commentkey, $info['id3v2']['comments'][$parsedFrame['framenameshort']])) { 1126 $info['id3v2']['comments'][$parsedFrame['framenameshort']][$commentkey] = getid3_lib::iconv_fallback($parsedFrame['encoding'], $info['id3v2']['encoding'], $parsedFrame['data']); 1127 } else { 1128 $info['id3v2']['comments'][$parsedFrame['framenameshort']][] = getid3_lib::iconv_fallback($parsedFrame['encoding'], $info['id3v2']['encoding'], $parsedFrame['data']); 1129 } 1130 } 1131 1132 } 1133 1134 } elseif (($id3v2_majorversion >= 4) && ($parsedFrame['frame_name'] == 'RVA2')) { // 4.11 RVA2 Relative volume adjustment (2) (ID3v2.4+ only) 1135 // There may be more than one 'RVA2' frame in each tag, 1136 // but only one with the same identification string 1137 // <Header for 'Relative volume adjustment (2)', ID: 'RVA2'> 1138 // Identification <text string> $00 1139 // The 'identification' string is used to identify the situation and/or 1140 // device where this adjustment should apply. The following is then 1141 // repeated for every channel: 1142 // Type of channel $xx 1143 // Volume adjustment $xx xx 1144 // Bits representing peak $xx 1145 // Peak volume $xx (xx ...) 1146 1147 $frame_terminatorpos = strpos($parsedFrame['data'], "\x00"); 1148 $frame_idstring = substr($parsedFrame['data'], 0, $frame_terminatorpos); 1149 if (ord($frame_idstring) === 0) { 1150 $frame_idstring = ''; 1151 } 1152 $frame_remainingdata = substr($parsedFrame['data'], $frame_terminatorpos + strlen("\x00")); 1153 $parsedFrame['description'] = $frame_idstring; 1154 $RVA2channelcounter = 0; 1155 while (strlen($frame_remainingdata) >= 5) { 1156 $frame_offset = 0; 1157 $frame_channeltypeid = ord(substr($frame_remainingdata, $frame_offset++, 1)); 1158 $parsedFrame[$RVA2channelcounter]['channeltypeid'] = $frame_channeltypeid; 1159 $parsedFrame[$RVA2channelcounter]['channeltype'] = $this->RVA2ChannelTypeLookup($frame_channeltypeid); 1160 $parsedFrame[$RVA2channelcounter]['volumeadjust'] = getid3_lib::BigEndian2Int(substr($frame_remainingdata, $frame_offset, 2), false, true); // 16-bit signed 1161 $frame_offset += 2; 1162 $parsedFrame[$RVA2channelcounter]['bitspeakvolume'] = ord(substr($frame_remainingdata, $frame_offset++, 1)); 1163 if (($parsedFrame[$RVA2channelcounter]['bitspeakvolume'] < 1) || ($parsedFrame[$RVA2channelcounter]['bitspeakvolume'] > 4)) { 1164 $this->warning('ID3v2::RVA2 frame['.$RVA2channelcounter.'] contains invalid '.$parsedFrame[$RVA2channelcounter]['bitspeakvolume'].'-byte bits-representing-peak value'); 1165 break; 1166 } 1167 $frame_bytespeakvolume = ceil($parsedFrame[$RVA2channelcounter]['bitspeakvolume'] / 8); 1168 $parsedFrame[$RVA2channelcounter]['peakvolume'] = getid3_lib::BigEndian2Int(substr($frame_remainingdata, $frame_offset, $frame_bytespeakvolume)); 1169 $frame_remainingdata = substr($frame_remainingdata, $frame_offset + $frame_bytespeakvolume); 1170 $RVA2channelcounter++; 1171 } 1172 unset($parsedFrame['data']); 1173 1174 1175 } elseif ((($id3v2_majorversion == 3) && ($parsedFrame['frame_name'] == 'RVAD')) || // 4.12 RVAD Relative volume adjustment (ID3v2.3 only) 1176 (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'RVA'))) { // 4.12 RVA Relative volume adjustment (ID3v2.2 only) 1177 // There may only be one 'RVA' frame in each tag 1178 // <Header for 'Relative volume adjustment', ID: 'RVA'> 1179 // ID3v2.2 => Increment/decrement %000000ba 1180 // ID3v2.3 => Increment/decrement %00fedcba 1181 // Bits used for volume descr. $xx 1182 // Relative volume change, right $xx xx (xx ...) // a 1183 // Relative volume change, left $xx xx (xx ...) // b 1184 // Peak volume right $xx xx (xx ...) 1185 // Peak volume left $xx xx (xx ...) 1186 // ID3v2.3 only, optional (not present in ID3v2.2): 1187 // Relative volume change, right back $xx xx (xx ...) // c 1188 // Relative volume change, left back $xx xx (xx ...) // d 1189 // Peak volume right back $xx xx (xx ...) 1190 // Peak volume left back $xx xx (xx ...) 1191 // ID3v2.3 only, optional (not present in ID3v2.2): 1192 // Relative volume change, center $xx xx (xx ...) // e 1193 // Peak volume center $xx xx (xx ...) 1194 // ID3v2.3 only, optional (not present in ID3v2.2): 1195 // Relative volume change, bass $xx xx (xx ...) // f 1196 // Peak volume bass $xx xx (xx ...) 1197 1198 $frame_offset = 0; 1199 $frame_incrdecrflags = getid3_lib::BigEndian2Bin(substr($parsedFrame['data'], $frame_offset++, 1)); 1200 $parsedFrame['incdec']['right'] = (bool) substr($frame_incrdecrflags, 6, 1); 1201 $parsedFrame['incdec']['left'] = (bool) substr($frame_incrdecrflags, 7, 1); 1202 $parsedFrame['bitsvolume'] = ord(substr($parsedFrame['data'], $frame_offset++, 1)); 1203 $frame_bytesvolume = ceil($parsedFrame['bitsvolume'] / 8); 1204 $parsedFrame['volumechange']['right'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesvolume)); 1205 if ($parsedFrame['incdec']['right'] === false) { 1206 $parsedFrame['volumechange']['right'] *= -1; 1207 } 1208 $frame_offset += $frame_bytesvolume; 1209 $parsedFrame['volumechange']['left'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesvolume)); 1210 if ($parsedFrame['incdec']['left'] === false) { 1211 $parsedFrame['volumechange']['left'] *= -1; 1212 } 1213 $frame_offset += $frame_bytesvolume; 1214 $parsedFrame['peakvolume']['right'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesvolume)); 1215 $frame_offset += $frame_bytesvolume; 1216 $parsedFrame['peakvolume']['left'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesvolume)); 1217 $frame_offset += $frame_bytesvolume; 1218 if ($id3v2_majorversion == 3) { 1219 $parsedFrame['data'] = substr($parsedFrame['data'], $frame_offset); 1220 if (strlen($parsedFrame['data']) > 0) { 1221 $parsedFrame['incdec']['rightrear'] = (bool) substr($frame_incrdecrflags, 4, 1); 1222 $parsedFrame['incdec']['leftrear'] = (bool) substr($frame_incrdecrflags, 5, 1); 1223 $parsedFrame['volumechange']['rightrear'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesvolume)); 1224 if ($parsedFrame['incdec']['rightrear'] === false) { 1225 $parsedFrame['volumechange']['rightrear'] *= -1; 1226 } 1227 $frame_offset += $frame_bytesvolume; 1228 $parsedFrame['volumechange']['leftrear'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesvolume)); 1229 if ($parsedFrame['incdec']['leftrear'] === false) { 1230 $parsedFrame['volumechange']['leftrear'] *= -1; 1231 } 1232 $frame_offset += $frame_bytesvolume; 1233 $parsedFrame['peakvolume']['rightrear'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesvolume)); 1234 $frame_offset += $frame_bytesvolume; 1235 $parsedFrame['peakvolume']['leftrear'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesvolume)); 1236 $frame_offset += $frame_bytesvolume; 1237 } 1238 $parsedFrame['data'] = substr($parsedFrame['data'], $frame_offset); 1239 if (strlen($parsedFrame['data']) > 0) { 1240 $parsedFrame['incdec']['center'] = (bool) substr($frame_incrdecrflags, 3, 1); 1241 $parsedFrame['volumechange']['center'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesvolume)); 1242 if ($parsedFrame['incdec']['center'] === false) { 1243 $parsedFrame['volumechange']['center'] *= -1; 1244 } 1245 $frame_offset += $frame_bytesvolume; 1246 $parsedFrame['peakvolume']['center'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesvolume)); 1247 $frame_offset += $frame_bytesvolume; 1248 } 1249 $parsedFrame['data'] = substr($parsedFrame['data'], $frame_offset); 1250 if (strlen($parsedFrame['data']) > 0) { 1251 $parsedFrame['incdec']['bass'] = (bool) substr($frame_incrdecrflags, 2, 1); 1252 $parsedFrame['volumechange']['bass'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesvolume)); 1253 if ($parsedFrame['incdec']['bass'] === false) { 1254 $parsedFrame['volumechange']['bass'] *= -1; 1255 } 1256 $frame_offset += $frame_bytesvolume; 1257 $parsedFrame['peakvolume']['bass'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesvolume)); 1258 $frame_offset += $frame_bytesvolume; 1259 } 1260 } 1261 unset($parsedFrame['data']); 1262 1263 1264 } elseif (($id3v2_majorversion >= 4) && ($parsedFrame['frame_name'] == 'EQU2')) { // 4.12 EQU2 Equalisation (2) (ID3v2.4+ only) 1265 // There may be more than one 'EQU2' frame in each tag, 1266 // but only one with the same identification string 1267 // <Header of 'Equalisation (2)', ID: 'EQU2'> 1268 // Interpolation method $xx 1269 // $00 Band 1270 // $01 Linear 1271 // Identification <text string> $00 1272 // The following is then repeated for every adjustment point 1273 // Frequency $xx xx 1274 // Volume adjustment $xx xx 1275 1276 $frame_offset = 0; 1277 $frame_interpolationmethod = ord(substr($parsedFrame['data'], $frame_offset++, 1)); 1278 $frame_terminatorpos = strpos($parsedFrame['data'], "\x00", $frame_offset); 1279 $frame_idstring = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); 1280 if (ord($frame_idstring) === 0) { 1281 $frame_idstring = ''; 1282 } 1283 $parsedFrame['description'] = $frame_idstring; 1284 $frame_remainingdata = substr($parsedFrame['data'], $frame_terminatorpos + strlen("\x00")); 1285 while (strlen($frame_remainingdata)) { 1286 $frame_frequency = getid3_lib::BigEndian2Int(substr($frame_remainingdata, 0, 2)) / 2; 1287 $parsedFrame['data'][$frame_frequency] = getid3_lib::BigEndian2Int(substr($frame_remainingdata, 2, 2), false, true); 1288 $frame_remainingdata = substr($frame_remainingdata, 4); 1289 } 1290 $parsedFrame['interpolationmethod'] = $frame_interpolationmethod; 1291 unset($parsedFrame['data']); 1292 1293 1294 } elseif ((($id3v2_majorversion == 3) && ($parsedFrame['frame_name'] == 'EQUA')) || // 4.12 EQUA Equalisation (ID3v2.3 only) 1295 (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'EQU'))) { // 4.13 EQU Equalisation (ID3v2.2 only) 1296 // There may only be one 'EQUA' frame in each tag 1297 // <Header for 'Relative volume adjustment', ID: 'EQU'> 1298 // Adjustment bits $xx 1299 // This is followed by 2 bytes + ('adjustment bits' rounded up to the 1300 // nearest byte) for every equalisation band in the following format, 1301 // giving a frequency range of 0 - 32767Hz: 1302 // Increment/decrement %x (MSB of the Frequency) 1303 // Frequency (lower 15 bits) 1304 // Adjustment $xx (xx ...) 1305 1306 $frame_offset = 0; 1307 $parsedFrame['adjustmentbits'] = substr($parsedFrame['data'], $frame_offset++, 1); 1308 $frame_adjustmentbytes = ceil($parsedFrame['adjustmentbits'] / 8); 1309 1310 $frame_remainingdata = (string) substr($parsedFrame['data'], $frame_offset); 1311 while (strlen($frame_remainingdata) > 0) { 1312 $frame_frequencystr = getid3_lib::BigEndian2Bin(substr($frame_remainingdata, 0, 2)); 1313 $frame_incdec = (bool) substr($frame_frequencystr, 0, 1); 1314 $frame_frequency = bindec(substr($frame_frequencystr, 1, 15)); 1315 $parsedFrame[$frame_frequency]['incdec'] = $frame_incdec; 1316 $parsedFrame[$frame_frequency]['adjustment'] = getid3_lib::BigEndian2Int(substr($frame_remainingdata, 2, $frame_adjustmentbytes)); 1317 if ($parsedFrame[$frame_frequency]['incdec'] === false) { 1318 $parsedFrame[$frame_frequency]['adjustment'] *= -1; 1319 } 1320 $frame_remainingdata = substr($frame_remainingdata, 2 + $frame_adjustmentbytes); 1321 } 1322 unset($parsedFrame['data']); 1323 1324 1325 } elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'RVRB')) || // 4.13 RVRB Reverb 1326 (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'REV'))) { // 4.14 REV Reverb 1327 // There may only be one 'RVRB' frame in each tag. 1328 // <Header for 'Reverb', ID: 'RVRB'> 1329 // Reverb left (ms) $xx xx 1330 // Reverb right (ms) $xx xx 1331 // Reverb bounces, left $xx 1332 // Reverb bounces, right $xx 1333 // Reverb feedback, left to left $xx 1334 // Reverb feedback, left to right $xx 1335 // Reverb feedback, right to right $xx 1336 // Reverb feedback, right to left $xx 1337 // Premix left to right $xx 1338 // Premix right to left $xx 1339 1340 $frame_offset = 0; 1341 $parsedFrame['left'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 2)); 1342 $frame_offset += 2; 1343 $parsedFrame['right'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 2)); 1344 $frame_offset += 2; 1345 $parsedFrame['bouncesL'] = ord(substr($parsedFrame['data'], $frame_offset++, 1)); 1346 $parsedFrame['bouncesR'] = ord(substr($parsedFrame['data'], $frame_offset++, 1)); 1347 $parsedFrame['feedbackLL'] = ord(substr($parsedFrame['data'], $frame_offset++, 1)); 1348 $parsedFrame['feedbackLR'] = ord(substr($parsedFrame['data'], $frame_offset++, 1)); 1349 $parsedFrame['feedbackRR'] = ord(substr($parsedFrame['data'], $frame_offset++, 1)); 1350 $parsedFrame['feedbackRL'] = ord(substr($parsedFrame['data'], $frame_offset++, 1)); 1351 $parsedFrame['premixLR'] = ord(substr($parsedFrame['data'], $frame_offset++, 1)); 1352 $parsedFrame['premixRL'] = ord(substr($parsedFrame['data'], $frame_offset++, 1)); 1353 unset($parsedFrame['data']); 1354 1355 1356 } elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'APIC')) || // 4.14 APIC Attached picture 1357 (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'PIC'))) { // 4.15 PIC Attached picture 1358 // There may be several pictures attached to one file, 1359 // each in their individual 'APIC' frame, but only one 1360 // with the same content descriptor 1361 // <Header for 'Attached picture', ID: 'APIC'> 1362 // Text encoding $xx 1363 // ID3v2.3+ => MIME type <text string> $00 1364 // ID3v2.2 => Image format $xx xx xx 1365 // Picture type $xx 1366 // Description <text string according to encoding> $00 (00) 1367 // Picture data <binary data> 1368 1369 $frame_offset = 0; 1370 $frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1)); 1371 $frame_textencoding_terminator = $this->TextEncodingTerminatorLookup($frame_textencoding); 1372 if ((($id3v2_majorversion <= 3) && ($frame_textencoding > 1)) || (($id3v2_majorversion == 4) && ($frame_textencoding > 3))) { 1373 $this->warning('Invalid text encoding byte ('.$frame_textencoding.') in frame "'.$parsedFrame['frame_name'].'" - defaulting to ISO-8859-1 encoding'); 1374 $frame_textencoding_terminator = "\x00"; 1375 } 1376 1377 $frame_imagetype = null; 1378 $frame_mimetype = null; 1379 if ($id3v2_majorversion == 2 && strlen($parsedFrame['data']) > $frame_offset) { 1380 $frame_imagetype = substr($parsedFrame['data'], $frame_offset, 3); 1381 if (strtolower($frame_imagetype) == 'ima') { 1382 // complete hack for mp3Rage (www.chaoticsoftware.com) that puts ID3v2.3-formatted 1383 // MIME type instead of 3-char ID3v2.2-format image type (thanks xbhoffØpacbell*net) 1384 $frame_terminatorpos = strpos($parsedFrame['data'], "\x00", $frame_offset); 1385 $frame_mimetype = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); 1386 if (ord($frame_mimetype) === 0) { 1387 $frame_mimetype = ''; 1388 } 1389 $frame_imagetype = strtoupper(str_replace('image/', '', strtolower($frame_mimetype))); 1390 if ($frame_imagetype == 'JPEG') { 1391 $frame_imagetype = 'JPG'; 1392 } 1393 $frame_offset = $frame_terminatorpos + strlen("\x00"); 1394 } else { 1395 $frame_offset += 3; 1396 } 1397 } 1398 if ($id3v2_majorversion > 2 && strlen($parsedFrame['data']) > $frame_offset) { 1399 $frame_terminatorpos = strpos($parsedFrame['data'], "\x00", $frame_offset); 1400 $frame_mimetype = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); 1401 if (ord($frame_mimetype) === 0) { 1402 $frame_mimetype = ''; 1403 } 1404 $frame_offset = $frame_terminatorpos + strlen("\x00"); 1405 } 1406 1407 $frame_picturetype = ord(substr($parsedFrame['data'], $frame_offset++, 1)); 1408 1409 if ($frame_offset >= $parsedFrame['datalength']) { 1410 $this->warning('data portion of APIC frame is missing at offset '.($parsedFrame['dataoffset'] + 8 + $frame_offset)); 1411 } else { 1412 $frame_terminatorpos = strpos($parsedFrame['data'], $frame_textencoding_terminator, $frame_offset); 1413 if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($frame_textencoding_terminator), 1)) === 0) { 1414 $frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00 1415 } 1416 $parsedFrame['description'] = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); 1417 $parsedFrame['description'] = $this->MakeUTF16emptyStringEmpty($parsedFrame['description']); 1418 $parsedFrame['encodingid'] = $frame_textencoding; 1419 $parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding); 1420 1421 if ($id3v2_majorversion == 2) { 1422 $parsedFrame['imagetype'] = isset($frame_imagetype) ? $frame_imagetype : null; 1423 } else { 1424 $parsedFrame['mime'] = isset($frame_mimetype) ? $frame_mimetype : null; 1425 } 1426 $parsedFrame['picturetypeid'] = $frame_picturetype; 1427 $parsedFrame['picturetype'] = $this->APICPictureTypeLookup($frame_picturetype); 1428 $parsedFrame['data'] = substr($parsedFrame['data'], $frame_terminatorpos + strlen($frame_textencoding_terminator)); 1429 $parsedFrame['datalength'] = strlen($parsedFrame['data']); 1430 1431 $parsedFrame['image_mime'] = ''; 1432 $imageinfo = array(); 1433 if ($imagechunkcheck = getid3_lib::GetDataImageSize($parsedFrame['data'], $imageinfo)) { 1434 if (($imagechunkcheck[2] >= 1) && ($imagechunkcheck[2] <= 3)) { 1435 $parsedFrame['image_mime'] = image_type_to_mime_type($imagechunkcheck[2]); 1436 if ($imagechunkcheck[0]) { 1437 $parsedFrame['image_width'] = $imagechunkcheck[0]; 1438 } 1439 if ($imagechunkcheck[1]) { 1440 $parsedFrame['image_height'] = $imagechunkcheck[1]; 1441 } 1442 } 1443 } 1444 1445 do { 1446 if ($this->getid3->option_save_attachments === false) { 1447 // skip entirely 1448 unset($parsedFrame['data']); 1449 break; 1450 } 1451 $dir = ''; 1452 if ($this->getid3->option_save_attachments === true) { 1453 // great 1454 /* 1455 } elseif (is_int($this->getid3->option_save_attachments)) { 1456 if ($this->getid3->option_save_attachments < $parsedFrame['data_length']) { 1457 // too big, skip 1458 $this->warning('attachment at '.$frame_offset.' is too large to process inline ('.number_format($parsedFrame['data_length']).' bytes)'); 1459 unset($parsedFrame['data']); 1460 break; 1461 } 1462 */ 1463 } elseif (is_string($this->getid3->option_save_attachments)) { 1464 $dir = rtrim(str_replace(array('/', '\\'), DIRECTORY_SEPARATOR, $this->getid3->option_save_attachments), DIRECTORY_SEPARATOR); 1465 if (!is_dir($dir) || !getID3::is_writable($dir)) { 1466 // cannot write, skip 1467 $this->warning('attachment at '.$frame_offset.' cannot be saved to "'.$dir.'" (not writable)'); 1468 unset($parsedFrame['data']); 1469 break; 1470 } 1471 } 1472 // if we get this far, must be OK 1473 if (is_string($this->getid3->option_save_attachments)) { 1474 $destination_filename = $dir.DIRECTORY_SEPARATOR.md5($info['filenamepath']).'_'.$frame_offset; 1475 if (!file_exists($destination_filename) || getID3::is_writable($destination_filename)) { 1476 file_put_contents($destination_filename, $parsedFrame['data']); 1477 } else { 1478 $this->warning('attachment at '.$frame_offset.' cannot be saved to "'.$destination_filename.'" (not writable)'); 1479 } 1480 $parsedFrame['data_filename'] = $destination_filename; 1481 unset($parsedFrame['data']); 1482 } else { 1483 if (!empty($parsedFrame['framenameshort']) && !empty($parsedFrame['data'])) { 1484 if (!isset($info['id3v2']['comments']['picture'])) { 1485 $info['id3v2']['comments']['picture'] = array(); 1486 } 1487 $comments_picture_data = array(); 1488 foreach (array('data', 'image_mime', 'image_width', 'image_height', 'imagetype', 'picturetype', 'description', 'datalength') as $picture_key) { 1489 if (isset($parsedFrame[$picture_key])) { 1490 $comments_picture_data[$picture_key] = $parsedFrame[$picture_key]; 1491 } 1492 } 1493 $info['id3v2']['comments']['picture'][] = $comments_picture_data; 1494 unset($comments_picture_data); 1495 } 1496 } 1497 } while (false); 1498 } 1499 1500 } elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'GEOB')) || // 4.15 GEOB General encapsulated object 1501 (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'GEO'))) { // 4.16 GEO General encapsulated object 1502 // There may be more than one 'GEOB' frame in each tag, 1503 // but only one with the same content descriptor 1504 // <Header for 'General encapsulated object', ID: 'GEOB'> 1505 // Text encoding $xx 1506 // MIME type <text string> $00 1507 // Filename <text string according to encoding> $00 (00) 1508 // Content description <text string according to encoding> $00 (00) 1509 // Encapsulated object <binary data> 1510 1511 $frame_offset = 0; 1512 $frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1)); 1513 $frame_textencoding_terminator = $this->TextEncodingTerminatorLookup($frame_textencoding); 1514 if ((($id3v2_majorversion <= 3) && ($frame_textencoding > 1)) || (($id3v2_majorversion == 4) && ($frame_textencoding > 3))) { 1515 $this->warning('Invalid text encoding byte ('.$frame_textencoding.') in frame "'.$parsedFrame['frame_name'].'" - defaulting to ISO-8859-1 encoding'); 1516 $frame_textencoding_terminator = "\x00"; 1517 } 1518 $frame_terminatorpos = strpos($parsedFrame['data'], "\x00", $frame_offset); 1519 $frame_mimetype = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); 1520 if (ord($frame_mimetype) === 0) { 1521 $frame_mimetype = ''; 1522 } 1523 $frame_offset = $frame_terminatorpos + strlen("\x00"); 1524 1525 $frame_terminatorpos = strpos($parsedFrame['data'], $frame_textencoding_terminator, $frame_offset); 1526 if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($frame_textencoding_terminator), 1)) === 0) { 1527 $frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00 1528 } 1529 $frame_filename = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); 1530 if (ord($frame_filename) === 0) { 1531 $frame_filename = ''; 1532 } 1533 $frame_offset = $frame_terminatorpos + strlen($frame_textencoding_terminator); 1534 1535 $frame_terminatorpos = strpos($parsedFrame['data'], $frame_textencoding_terminator, $frame_offset); 1536 if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($frame_textencoding_terminator), 1)) === 0) { 1537 $frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00 1538 } 1539 $parsedFrame['description'] = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); 1540 $parsedFrame['description'] = $this->MakeUTF16emptyStringEmpty($parsedFrame['description']); 1541 $frame_offset = $frame_terminatorpos + strlen($frame_textencoding_terminator); 1542 1543 $parsedFrame['objectdata'] = (string) substr($parsedFrame['data'], $frame_offset); 1544 $parsedFrame['encodingid'] = $frame_textencoding; 1545 $parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding); 1546 1547 $parsedFrame['mime'] = $frame_mimetype; 1548 $parsedFrame['filename'] = $frame_filename; 1549 unset($parsedFrame['data']); 1550 1551 1552 } elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'PCNT')) || // 4.16 PCNT Play counter 1553 (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'CNT'))) { // 4.17 CNT Play counter 1554 // There may only be one 'PCNT' frame in each tag. 1555 // When the counter reaches all one's, one byte is inserted in 1556 // front of the counter thus making the counter eight bits bigger 1557 // <Header for 'Play counter', ID: 'PCNT'> 1558 // Counter $xx xx xx xx (xx ...) 1559 1560 $parsedFrame['data'] = getid3_lib::BigEndian2Int($parsedFrame['data']); 1561 1562 1563 } elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'POPM')) || // 4.17 POPM Popularimeter 1564 (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'POP'))) { // 4.18 POP Popularimeter 1565 // There may be more than one 'POPM' frame in each tag, 1566 // but only one with the same email address 1567 // <Header for 'Popularimeter', ID: 'POPM'> 1568 // Email to user <text string> $00 1569 // Rating $xx 1570 // Counter $xx xx xx xx (xx ...) 1571 1572 $frame_offset = 0; 1573 $frame_terminatorpos = strpos($parsedFrame['data'], "\x00", $frame_offset); 1574 $frame_emailaddress = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); 1575 if (ord($frame_emailaddress) === 0) { 1576 $frame_emailaddress = ''; 1577 } 1578 $frame_offset = $frame_terminatorpos + strlen("\x00"); 1579 $frame_rating = ord(substr($parsedFrame['data'], $frame_offset++, 1)); 1580 $parsedFrame['counter'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset)); 1581 $parsedFrame['email'] = $frame_emailaddress; 1582 $parsedFrame['rating'] = $frame_rating; 1583 unset($parsedFrame['data']); 1584 1585 1586 } elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'RBUF')) || // 4.18 RBUF Recommended buffer size 1587 (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'BUF'))) { // 4.19 BUF Recommended buffer size 1588 // There may only be one 'RBUF' frame in each tag 1589 // <Header for 'Recommended buffer size', ID: 'RBUF'> 1590 // Buffer size $xx xx xx 1591 // Embedded info flag %0000000x 1592 // Offset to next tag $xx xx xx xx 1593 1594 $frame_offset = 0; 1595 $parsedFrame['buffersize'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 3)); 1596 $frame_offset += 3; 1597 1598 $frame_embeddedinfoflags = getid3_lib::BigEndian2Bin(substr($parsedFrame['data'], $frame_offset++, 1)); 1599 $parsedFrame['flags']['embededinfo'] = (bool) substr($frame_embeddedinfoflags, 7, 1); 1600 $parsedFrame['nexttagoffset'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 4)); 1601 unset($parsedFrame['data']); 1602 1603 1604 } elseif (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'CRM')) { // 4.20 Encrypted meta frame (ID3v2.2 only) 1605 // There may be more than one 'CRM' frame in a tag, 1606 // but only one with the same 'owner identifier' 1607 // <Header for 'Encrypted meta frame', ID: 'CRM'> 1608 // Owner identifier <textstring> $00 (00) 1609 // Content/explanation <textstring> $00 (00) 1610 // Encrypted datablock <binary data> 1611 1612 $frame_offset = 0; 1613 $frame_terminatorpos = strpos($parsedFrame['data'], "\x00", $frame_offset); 1614 $frame_ownerid = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); 1615 $frame_offset = $frame_terminatorpos + strlen("\x00"); 1616 1617 $frame_terminatorpos = strpos($parsedFrame['data'], "\x00", $frame_offset); 1618 $parsedFrame['description'] = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); 1619 $parsedFrame['description'] = $this->MakeUTF16emptyStringEmpty($parsedFrame['description']); 1620 $frame_offset = $frame_terminatorpos + strlen("\x00"); 1621 1622 $parsedFrame['ownerid'] = $frame_ownerid; 1623 $parsedFrame['data'] = (string) substr($parsedFrame['data'], $frame_offset); 1624 unset($parsedFrame['data']); 1625 1626 1627 } elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'AENC')) || // 4.19 AENC Audio encryption 1628 (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'CRA'))) { // 4.21 CRA Audio encryption 1629 // There may be more than one 'AENC' frames in a tag, 1630 // but only one with the same 'Owner identifier' 1631 // <Header for 'Audio encryption', ID: 'AENC'> 1632 // Owner identifier <text string> $00 1633 // Preview start $xx xx 1634 // Preview length $xx xx 1635 // Encryption info <binary data> 1636 1637 $frame_offset = 0; 1638 $frame_terminatorpos = strpos($parsedFrame['data'], "\x00", $frame_offset); 1639 $frame_ownerid = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); 1640 if (ord($frame_ownerid) === 0) { 1641 $frame_ownerid = ''; 1642 } 1643 $frame_offset = $frame_terminatorpos + strlen("\x00"); 1644 $parsedFrame['ownerid'] = $frame_ownerid; 1645 $parsedFrame['previewstart'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 2)); 1646 $frame_offset += 2; 1647 $parsedFrame['previewlength'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 2)); 1648 $frame_offset += 2; 1649 $parsedFrame['encryptioninfo'] = (string) substr($parsedFrame['data'], $frame_offset); 1650 unset($parsedFrame['data']); 1651 1652 1653 } elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'LINK')) || // 4.20 LINK Linked information 1654 (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'LNK'))) { // 4.22 LNK Linked information 1655 // There may be more than one 'LINK' frame in a tag, 1656 // but only one with the same contents 1657 // <Header for 'Linked information', ID: 'LINK'> 1658 // ID3v2.3+ => Frame identifier $xx xx xx xx 1659 // ID3v2.2 => Frame identifier $xx xx xx 1660 // URL <text string> $00 1661 // ID and additional data <text string(s)> 1662 1663 $frame_offset = 0; 1664 if ($id3v2_majorversion == 2) { 1665 $parsedFrame['frameid'] = substr($parsedFrame['data'], $frame_offset, 3); 1666 $frame_offset += 3; 1667 } else { 1668 $parsedFrame['frameid'] = substr($parsedFrame['data'], $frame_offset, 4); 1669 $frame_offset += 4; 1670 } 1671 1672 $frame_terminatorpos = strpos($parsedFrame['data'], "\x00", $frame_offset); 1673 $frame_url = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); 1674 if (ord($frame_url) === 0) { 1675 $frame_url = ''; 1676 } 1677 $frame_offset = $frame_terminatorpos + strlen("\x00"); 1678 $parsedFrame['url'] = $frame_url; 1679 1680 $parsedFrame['additionaldata'] = (string) substr($parsedFrame['data'], $frame_offset); 1681 if (!empty($parsedFrame['framenameshort']) && $parsedFrame['url']) { 1682 $info['id3v2']['comments'][$parsedFrame['framenameshort']][] = getid3_lib::iconv_fallback_iso88591_utf8($parsedFrame['url']); 1683 } 1684 unset($parsedFrame['data']); 1685 1686 1687 } elseif (($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'POSS')) { // 4.21 POSS Position synchronisation frame (ID3v2.3+ only) 1688 // There may only be one 'POSS' frame in each tag 1689 // <Head for 'Position synchronisation', ID: 'POSS'> 1690 // Time stamp format $xx 1691 // Position $xx (xx ...) 1692 1693 $frame_offset = 0; 1694 $parsedFrame['timestampformat'] = ord(substr($parsedFrame['data'], $frame_offset++, 1)); 1695 $parsedFrame['position'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset)); 1696 unset($parsedFrame['data']); 1697 1698 1699 } elseif (($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'USER')) { // 4.22 USER Terms of use (ID3v2.3+ only) 1700 // There may be more than one 'Terms of use' frame in a tag, 1701 // but only one with the same 'Language' 1702 // <Header for 'Terms of use frame', ID: 'USER'> 1703 // Text encoding $xx 1704 // Language $xx xx xx 1705 // The actual text <text string according to encoding> 1706 1707 $frame_offset = 0; 1708 $frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1)); 1709 if ((($id3v2_majorversion <= 3) && ($frame_textencoding > 1)) || (($id3v2_majorversion == 4) && ($frame_textencoding > 3))) { 1710 $this->warning('Invalid text encoding byte ('.$frame_textencoding.') in frame "'.$parsedFrame['frame_name'].'" - defaulting to ISO-8859-1 encoding'); 1711 } 1712 $frame_language = substr($parsedFrame['data'], $frame_offset, 3); 1713 $frame_offset += 3; 1714 $parsedFrame['language'] = $frame_language; 1715 $parsedFrame['languagename'] = $this->LanguageLookup($frame_language, false); 1716 $parsedFrame['encodingid'] = $frame_textencoding; 1717 $parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding); 1718 1719 $parsedFrame['data'] = (string) substr($parsedFrame['data'], $frame_offset); 1720 $parsedFrame['data'] = $this->RemoveStringTerminator($parsedFrame['data'], $this->TextEncodingTerminatorLookup($frame_textencoding)); 1721 if (!empty($parsedFrame['framenameshort']) && !empty($parsedFrame['data'])) { 1722 $info['id3v2']['comments'][$parsedFrame['framenameshort']][] = getid3_lib::iconv_fallback($parsedFrame['encoding'], $info['id3v2']['encoding'], $parsedFrame['data']); 1723 } 1724 unset($parsedFrame['data']); 1725 1726 1727 } elseif (($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'OWNE')) { // 4.23 OWNE Ownership frame (ID3v2.3+ only) 1728 // There may only be one 'OWNE' frame in a tag 1729 // <Header for 'Ownership frame', ID: 'OWNE'> 1730 // Text encoding $xx 1731 // Price paid <text string> $00 1732 // Date of purch. <text string> 1733 // Seller <text string according to encoding> 1734 1735 $frame_offset = 0; 1736 $frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1)); 1737 if ((($id3v2_majorversion <= 3) && ($frame_textencoding > 1)) || (($id3v2_majorversion == 4) && ($frame_textencoding > 3))) { 1738 $this->warning('Invalid text encoding byte ('.$frame_textencoding.') in frame "'.$parsedFrame['frame_name'].'" - defaulting to ISO-8859-1 encoding'); 1739 } 1740 $parsedFrame['encodingid'] = $frame_textencoding; 1741 $parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding); 1742 1743 $frame_terminatorpos = strpos($parsedFrame['data'], "\x00", $frame_offset); 1744 $frame_pricepaid = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); 1745 $frame_offset = $frame_terminatorpos + strlen("\x00"); 1746 1747 $parsedFrame['pricepaid']['currencyid'] = substr($frame_pricepaid, 0, 3); 1748 $parsedFrame['pricepaid']['currency'] = $this->LookupCurrencyUnits($parsedFrame['pricepaid']['currencyid']); 1749 $parsedFrame['pricepaid']['value'] = substr($frame_pricepaid, 3); 1750 1751 $parsedFrame['purchasedate'] = substr($parsedFrame['data'], $frame_offset, 8); 1752 if ($this->IsValidDateStampString($parsedFrame['purchasedate'])) { 1753 $parsedFrame['purchasedateunix'] = mktime (0, 0, 0, substr($parsedFrame['purchasedate'], 4, 2), substr($parsedFrame['purchasedate'], 6, 2), substr($parsedFrame['purchasedate'], 0, 4)); 1754 } 1755 $frame_offset += 8; 1756 1757 $parsedFrame['seller'] = (string) substr($parsedFrame['data'], $frame_offset); 1758 $parsedFrame['seller'] = $this->RemoveStringTerminator($parsedFrame['seller'], $this->TextEncodingTerminatorLookup($frame_textencoding)); 1759 unset($parsedFrame['data']); 1760 1761 1762 } elseif (($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'COMR')) { // 4.24 COMR Commercial frame (ID3v2.3+ only) 1763 // There may be more than one 'commercial frame' in a tag, 1764 // but no two may be identical 1765 // <Header for 'Commercial frame', ID: 'COMR'> 1766 // Text encoding $xx 1767 // Price string <text string> $00 1768 // Valid until <text string> 1769 // Contact URL <text string> $00 1770 // Received as $xx 1771 // Name of seller <text string according to encoding> $00 (00) 1772 // Description <text string according to encoding> $00 (00) 1773 // Picture MIME type <string> $00 1774 // Seller logo <binary data> 1775 1776 $frame_offset = 0; 1777 $frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1)); 1778 $frame_textencoding_terminator = $this->TextEncodingTerminatorLookup($frame_textencoding); 1779 if ((($id3v2_majorversion <= 3) && ($frame_textencoding > 1)) || (($id3v2_majorversion == 4) && ($frame_textencoding > 3))) { 1780 $this->warning('Invalid text encoding byte ('.$frame_textencoding.') in frame "'.$parsedFrame['frame_name'].'" - defaulting to ISO-8859-1 encoding'); 1781 $frame_textencoding_terminator = "\x00"; 1782 } 1783 1784 $frame_terminatorpos = strpos($parsedFrame['data'], "\x00", $frame_offset); 1785 $frame_pricestring = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); 1786 $frame_offset = $frame_terminatorpos + strlen("\x00"); 1787 $frame_rawpricearray = explode('/', $frame_pricestring); 1788 foreach ($frame_rawpricearray as $key => $val) { 1789 $frame_currencyid = substr($val, 0, 3); 1790 $parsedFrame['price'][$frame_currencyid]['currency'] = $this->LookupCurrencyUnits($frame_currencyid); 1791 $parsedFrame['price'][$frame_currencyid]['value'] = substr($val, 3); 1792 } 1793 1794 $frame_datestring = substr($parsedFrame['data'], $frame_offset, 8); 1795 $frame_offset += 8; 1796 1797 $frame_terminatorpos = strpos($parsedFrame['data'], "\x00", $frame_offset); 1798 $frame_contacturl = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); 1799 $frame_offset = $frame_terminatorpos + strlen("\x00"); 1800 1801 $frame_receivedasid = ord(substr($parsedFrame['data'], $frame_offset++, 1)); 1802 1803 $frame_terminatorpos = strpos($parsedFrame['data'], $frame_textencoding_terminator, $frame_offset); 1804 if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($frame_textencoding_terminator), 1)) === 0) { 1805 $frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00 1806 } 1807 $frame_sellername = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); 1808 if (ord($frame_sellername) === 0) { 1809 $frame_sellername = ''; 1810 } 1811 $frame_offset = $frame_terminatorpos + strlen($frame_textencoding_terminator); 1812 1813 $frame_terminatorpos = strpos($parsedFrame['data'], $frame_textencoding_terminator, $frame_offset); 1814 if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($frame_textencoding_terminator), 1)) === 0) { 1815 $frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00 1816 } 1817 $parsedFrame['description'] = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); 1818 $parsedFrame['description'] = $this->MakeUTF16emptyStringEmpty($parsedFrame['description']); 1819 $frame_offset = $frame_terminatorpos + strlen($frame_textencoding_terminator); 1820 1821 $frame_terminatorpos = strpos($parsedFrame['data'], "\x00", $frame_offset); 1822 $frame_mimetype = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); 1823 $frame_offset = $frame_terminatorpos + strlen("\x00"); 1824 1825 $frame_sellerlogo = substr($parsedFrame['data'], $frame_offset); 1826 1827 $parsedFrame['encodingid'] = $frame_textencoding; 1828 $parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding); 1829 1830 $parsedFrame['pricevaliduntil'] = $frame_datestring; 1831 $parsedFrame['contacturl'] = $frame_contacturl; 1832 $parsedFrame['receivedasid'] = $frame_receivedasid; 1833 $parsedFrame['receivedas'] = $this->COMRReceivedAsLookup($frame_receivedasid); 1834 $parsedFrame['sellername'] = $frame_sellername; 1835 $parsedFrame['mime'] = $frame_mimetype; 1836 $parsedFrame['logo'] = $frame_sellerlogo; 1837 unset($parsedFrame['data']); 1838 1839 1840 } elseif (($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'ENCR')) { // 4.25 ENCR Encryption method registration (ID3v2.3+ only) 1841 // There may be several 'ENCR' frames in a tag, 1842 // but only one containing the same symbol 1843 // and only one containing the same owner identifier 1844 // <Header for 'Encryption method registration', ID: 'ENCR'> 1845 // Owner identifier <text string> $00 1846 // Method symbol $xx 1847 // Encryption data <binary data> 1848 1849 $frame_offset = 0; 1850 $frame_terminatorpos = strpos($parsedFrame['data'], "\x00", $frame_offset); 1851 $frame_ownerid = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); 1852 if (ord($frame_ownerid) === 0) { 1853 $frame_ownerid = ''; 1854 } 1855 $frame_offset = $frame_terminatorpos + strlen("\x00"); 1856 1857 $parsedFrame['ownerid'] = $frame_ownerid; 1858 $parsedFrame['methodsymbol'] = ord(substr($parsedFrame['data'], $frame_offset++, 1)); 1859 $parsedFrame['data'] = (string) substr($parsedFrame['data'], $frame_offset); 1860 1861 1862 } elseif (($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'GRID')) { // 4.26 GRID Group identification registration (ID3v2.3+ only) 1863 1864 // There may be several 'GRID' frames in a tag, 1865 // but only one containing the same symbol 1866 // and only one containing the same owner identifier 1867 // <Header for 'Group ID registration', ID: 'GRID'> 1868 // Owner identifier <text string> $00 1869 // Group symbol $xx 1870 // Group dependent data <binary data> 1871 1872 $frame_offset = 0; 1873 $frame_terminatorpos = strpos($parsedFrame['data'], "\x00", $frame_offset); 1874 $frame_ownerid = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); 1875 if (ord($frame_ownerid) === 0) { 1876 $frame_ownerid = ''; 1877 } 1878 $frame_offset = $frame_terminatorpos + strlen("\x00"); 1879 1880 $parsedFrame['ownerid'] = $frame_ownerid; 1881 $parsedFrame['groupsymbol'] = ord(substr($parsedFrame['data'], $frame_offset++, 1)); 1882 $parsedFrame['data'] = (string) substr($parsedFrame['data'], $frame_offset); 1883 1884 1885 } elseif (($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'PRIV')) { // 4.27 PRIV Private frame (ID3v2.3+ only) 1886 // The tag may contain more than one 'PRIV' frame 1887 // but only with different contents 1888 // <Header for 'Private frame', ID: 'PRIV'> 1889 // Owner identifier <text string> $00 1890 // The private data <binary data> 1891 1892 $frame_offset = 0; 1893 $frame_terminatorpos = strpos($parsedFrame['data'], "\x00", $frame_offset); 1894 $frame_ownerid = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); 1895 if (ord($frame_ownerid) === 0) { 1896 $frame_ownerid = ''; 1897 } 1898 $frame_offset = $frame_terminatorpos + strlen("\x00"); 1899 1900 $parsedFrame['ownerid'] = $frame_ownerid; 1901 $parsedFrame['data'] = (string) substr($parsedFrame['data'], $frame_offset); 1902 1903 1904 } elseif (($id3v2_majorversion >= 4) && ($parsedFrame['frame_name'] == 'SIGN')) { // 4.28 SIGN Signature frame (ID3v2.4+ only) 1905 // There may be more than one 'signature frame' in a tag, 1906 // but no two may be identical 1907 // <Header for 'Signature frame', ID: 'SIGN'> 1908 // Group symbol $xx 1909 // Signature <binary data> 1910 1911 $frame_offset = 0; 1912 $parsedFrame['groupsymbol'] = ord(substr($parsedFrame['data'], $frame_offset++, 1)); 1913 $parsedFrame['data'] = (string) substr($parsedFrame['data'], $frame_offset); 1914 1915 1916 } elseif (($id3v2_majorversion >= 4) && ($parsedFrame['frame_name'] == 'SEEK')) { // 4.29 SEEK Seek frame (ID3v2.4+ only) 1917 // There may only be one 'seek frame' in a tag 1918 // <Header for 'Seek frame', ID: 'SEEK'> 1919 // Minimum offset to next tag $xx xx xx xx 1920 1921 $frame_offset = 0; 1922 $parsedFrame['data'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 4)); 1923 1924 1925 } elseif (($id3v2_majorversion >= 4) && ($parsedFrame['frame_name'] == 'ASPI')) { // 4.30 ASPI Audio seek point index (ID3v2.4+ only) 1926 // There may only be one 'audio seek point index' frame in a tag 1927 // <Header for 'Seek Point Index', ID: 'ASPI'> 1928 // Indexed data start (S) $xx xx xx xx 1929 // Indexed data length (L) $xx xx xx xx 1930 // Number of index points (N) $xx xx 1931 // Bits per index point (b) $xx 1932 // Then for every index point the following data is included: 1933 // Fraction at index (Fi) $xx (xx) 1934 1935 $frame_offset = 0; 1936 $parsedFrame['datastart'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 4)); 1937 $frame_offset += 4; 1938 $parsedFrame['indexeddatalength'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 4)); 1939 $frame_offset += 4; 1940 $parsedFrame['indexpoints'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 2)); 1941 $frame_offset += 2; 1942 $parsedFrame['bitsperpoint'] = ord(substr($parsedFrame['data'], $frame_offset++, 1)); 1943 $frame_bytesperpoint = ceil($parsedFrame['bitsperpoint'] / 8); 1944 for ($i = 0; $i < $parsedFrame['indexpoints']; $i++) { 1945 $parsedFrame['indexes'][$i] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesperpoint)); 1946 $frame_offset += $frame_bytesperpoint; 1947 } 1948 unset($parsedFrame['data']); 1949 1950 } elseif (($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'RGAD')) { // Replay Gain Adjustment 1951 // http://privatewww.essex.ac.uk/~djmrob/replaygain/file_format_id3v2.html 1952 // There may only be one 'RGAD' frame in a tag 1953 // <Header for 'Replay Gain Adjustment', ID: 'RGAD'> 1954 // Peak Amplitude $xx $xx $xx $xx 1955 // Radio Replay Gain Adjustment %aaabbbcd %dddddddd 1956 // Audiophile Replay Gain Adjustment %aaabbbcd %dddddddd 1957 // a - name code 1958 // b - originator code 1959 // c - sign bit 1960 // d - replay gain adjustment 1961 1962 $frame_offset = 0; 1963 $parsedFrame['peakamplitude'] = getid3_lib::BigEndian2Float(substr($parsedFrame['data'], $frame_offset, 4)); 1964 $frame_offset += 4; 1965 foreach (array('track','album') as $rgad_entry_type) { 1966 $rg_adjustment_word = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 2)); 1967 $frame_offset += 2; 1968 $parsedFrame['raw'][$rgad_entry_type]['name'] = ($rg_adjustment_word & 0xE000) >> 13; 1969 $parsedFrame['raw'][$rgad_entry_type]['originator'] = ($rg_adjustment_word & 0x1C00) >> 10; 1970 $parsedFrame['raw'][$rgad_entry_type]['signbit'] = ($rg_adjustment_word & 0x0200) >> 9; 1971 $parsedFrame['raw'][$rgad_entry_type]['adjustment'] = ($rg_adjustment_word & 0x0100); 1972 } 1973 $parsedFrame['track']['name'] = getid3_lib::RGADnameLookup($parsedFrame['raw']['track']['name']); 1974 $parsedFrame['track']['originator'] = getid3_lib::RGADoriginatorLookup($parsedFrame['raw']['track']['originator']); 1975 $parsedFrame['track']['adjustment'] = getid3_lib::RGADadjustmentLookup($parsedFrame['raw']['track']['adjustment'], $parsedFrame['raw']['track']['signbit']); 1976 $parsedFrame['album']['name'] = getid3_lib::RGADnameLookup($parsedFrame['raw']['album']['name']); 1977 $parsedFrame['album']['originator'] = getid3_lib::RGADoriginatorLookup($parsedFrame['raw']['album']['originator']); 1978 $parsedFrame['album']['adjustment'] = getid3_lib::RGADadjustmentLookup($parsedFrame['raw']['album']['adjustment'], $parsedFrame['raw']['album']['signbit']); 1979 1980 $info['replay_gain']['track']['peak'] = $parsedFrame['peakamplitude']; 1981 $info['replay_gain']['track']['originator'] = $parsedFrame['track']['originator']; 1982 $info['replay_gain']['track']['adjustment'] = $parsedFrame['track']['adjustment']; 1983 $info['replay_gain']['album']['originator'] = $parsedFrame['album']['originator']; 1984 $info['replay_gain']['album']['adjustment'] = $parsedFrame['album']['adjustment']; 1985 1986 unset($parsedFrame['data']); 1987 1988 } elseif (($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'CHAP')) { // CHAP Chapters frame (ID3v2.3+ only) 1989 // http://id3.org/id3v2-chapters-1.0 1990 // <ID3v2.3 or ID3v2.4 frame header, ID: "CHAP"> (10 bytes) 1991 // Element ID <text string> $00 1992 // Start time $xx xx xx xx 1993 // End time $xx xx xx xx 1994 // Start offset $xx xx xx xx 1995 // End offset $xx xx xx xx 1996 // <Optional embedded sub-frames> 1997 1998 $frame_offset = 0; 1999 @list($parsedFrame['element_id']) = explode("\x00", $parsedFrame['data'], 2); 2000 $frame_offset += strlen($parsedFrame['element_id']."\x00"); 2001 $parsedFrame['time_begin'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 4)); 2002 $frame_offset += 4; 2003 $parsedFrame['time_end'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 4)); 2004 $frame_offset += 4; 2005 if (substr($parsedFrame['data'], $frame_offset, 4) != "\xFF\xFF\xFF\xFF") { 2006 // "If these bytes are all set to 0xFF then the value should be ignored and the start time value should be utilized." 2007 $parsedFrame['offset_begin'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 4)); 2008 } 2009 $frame_offset += 4; 2010 if (substr($parsedFrame['data'], $frame_offset, 4) != "\xFF\xFF\xFF\xFF") { 2011 // "If these bytes are all set to 0xFF then the value should be ignored and the start time value should be utilized." 2012 $parsedFrame['offset_end'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 4)); 2013 } 2014 $frame_offset += 4; 2015 2016 if ($frame_offset < strlen($parsedFrame['data'])) { 2017 $parsedFrame['subframes'] = array(); 2018 while ($frame_offset < strlen($parsedFrame['data'])) { 2019 // <Optional embedded sub-frames> 2020 $subframe = array(); 2021 $subframe['name'] = substr($parsedFrame['data'], $frame_offset, 4); 2022 $frame_offset += 4; 2023 $subframe['size'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 4)); 2024 $frame_offset += 4; 2025 $subframe['flags_raw'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 2)); 2026 $frame_offset += 2; 2027 if ($subframe['size'] > (strlen($parsedFrame['data']) - $frame_offset)) { 2028 $this->warning('CHAP subframe "'.$subframe['name'].'" at frame offset '.$frame_offset.' claims to be "'.$subframe['size'].'" bytes, which is more than the available data ('.(strlen($parsedFrame['data']) - $frame_offset).' bytes)'); 2029 break; 2030 } 2031 $subframe_rawdata = substr($parsedFrame['data'], $frame_offset, $subframe['size']); 2032 $frame_offset += $subframe['size']; 2033 2034 $subframe['encodingid'] = ord(substr($subframe_rawdata, 0, 1)); 2035 $subframe['text'] = substr($subframe_rawdata, 1); 2036 $subframe['encoding'] = $this->TextEncodingNameLookup($subframe['encodingid']); 2037 $encoding_converted_text = trim(getid3_lib::iconv_fallback($subframe['encoding'], $info['encoding'], $subframe['text'])); 2038 switch (substr($encoding_converted_text, 0, 2)) { 2039 case "\xFF\xFE": 2040 case "\xFE\xFF": 2041 switch (strtoupper($info['id3v2']['encoding'])) { 2042 case 'ISO-8859-1': 2043 case 'UTF-8': 2044 $encoding_converted_text = substr($encoding_converted_text, 2); 2045 // remove unwanted byte-order-marks 2046 break; 2047 default: 2048 // ignore 2049 break; 2050 } 2051 break; 2052 default: 2053 // do not remove BOM 2054 break; 2055 } 2056 2057 switch ($subframe['name']) { 2058 case 'TIT2': 2059 $parsedFrame['chapter_name'] = $encoding_converted_text; 2060 $parsedFrame['subframes'][] = $subframe; 2061 break; 2062 case 'TIT3': 2063 $parsedFrame['chapter_description'] = $encoding_converted_text; 2064 $parsedFrame['subframes'][] = $subframe; 2065 break; 2066 case 'WXXX': 2067 list($subframe['chapter_url_description'], $subframe['chapter_url']) = explode("\x00", $encoding_converted_text, 2); 2068 $parsedFrame['chapter_url'][$subframe['chapter_url_description']] = $subframe['chapter_url']; 2069 $parsedFrame['subframes'][] = $subframe; 2070 break; 2071 case 'APIC': 2072 if (preg_match('#^([^\\x00]+)*\\x00(.)([^\\x00]+)*\\x00(.+)$#s', $subframe['text'], $matches)) { 2073 list($dummy, $subframe_apic_mime, $subframe_apic_picturetype, $subframe_apic_description, $subframe_apic_picturedata) = $matches; 2074 $subframe['image_mime'] = trim(getid3_lib::iconv_fallback($subframe['encoding'], $info['encoding'], $subframe_apic_mime)); 2075 $subframe['picture_type'] = $this->APICPictureTypeLookup($subframe_apic_picturetype); 2076 $subframe['description'] = trim(getid3_lib::iconv_fallback($subframe['encoding'], $info['encoding'], $subframe_apic_description)); 2077 if (strlen($this->TextEncodingTerminatorLookup($subframe['encoding'])) == 2) { 2078 // the null terminator between "description" and "picture data" could be either 1 byte (ISO-8859-1, UTF-8) or two bytes (UTF-16) 2079 // the above regex assumes one byte, if it's actually two then strip the second one here 2080 $subframe_apic_picturedata = substr($subframe_apic_picturedata, 1); 2081 } 2082 $subframe['data'] = $subframe_apic_picturedata; 2083 unset($dummy, $subframe_apic_mime, $subframe_apic_picturetype, $subframe_apic_description, $subframe_apic_picturedata); 2084 unset($subframe['text'], $parsedFrame['text']); 2085 $parsedFrame['subframes'][] = $subframe; 2086 $parsedFrame['picture_present'] = true; 2087 } else { 2088 $this->warning('ID3v2.CHAP subframe #'.(count($parsedFrame['subframes']) + 1).' "'.$subframe['name'].'" not in expected format'); 2089 } 2090 break; 2091 default: 2092 $this->warning('ID3v2.CHAP subframe "'.$subframe['name'].'" not handled (supported: TIT2, TIT3, WXXX, APIC)'); 2093 break; 2094 } 2095 } 2096 unset($subframe_rawdata, $subframe, $encoding_converted_text); 2097 unset($parsedFrame['data']); // debatable whether this this be here, without it the returned structure may contain a large amount of duplicate data if chapters contain APIC 2098 } 2099 2100 $id3v2_chapter_entry = array(); 2101 foreach (array('id', 'time_begin', 'time_end', 'offset_begin', 'offset_end', 'chapter_name', 'chapter_description', 'chapter_url', 'picture_present') as $id3v2_chapter_key) { 2102 if (isset($parsedFrame[$id3v2_chapter_key])) { 2103 $id3v2_chapter_entry[$id3v2_chapter_key] = $parsedFrame[$id3v2_chapter_key]; 2104 } 2105 } 2106 if (!isset($info['id3v2']['chapters'])) { 2107 $info['id3v2']['chapters'] = array(); 2108 } 2109 $info['id3v2']['chapters'][] = $id3v2_chapter_entry; 2110 unset($id3v2_chapter_entry, $id3v2_chapter_key); 2111 2112 2113 } elseif (($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'CTOC')) { // CTOC Chapters Table Of Contents frame (ID3v2.3+ only) 2114 // http://id3.org/id3v2-chapters-1.0 2115 // <ID3v2.3 or ID3v2.4 frame header, ID: "CTOC"> (10 bytes) 2116 // Element ID <text string> $00 2117 // CTOC flags %xx 2118 // Entry count $xx 2119 // Child Element ID <string>$00 /* zero or more child CHAP or CTOC entries */ 2120 // <Optional embedded sub-frames> 2121 2122 $frame_offset = 0; 2123 @list($parsedFrame['element_id']) = explode("\x00", $parsedFrame['data'], 2); 2124 $frame_offset += strlen($parsedFrame['element_id']."\x00"); 2125 $ctoc_flags_raw = ord(substr($parsedFrame['data'], $frame_offset, 1)); 2126 $frame_offset += 1; 2127 $parsedFrame['entry_count'] = ord(substr($parsedFrame['data'], $frame_offset, 1)); 2128 $frame_offset += 1; 2129 2130 $terminator_position = null; 2131 for ($i = 0; $i < $parsedFrame['entry_count']; $i++) { 2132 $terminator_position = strpos($parsedFrame['data'], "\x00", $frame_offset); 2133 $parsedFrame['child_element_ids'][$i] = substr($parsedFrame['data'], $frame_offset, $terminator_position - $frame_offset); 2134 $frame_offset = $terminator_position + 1; 2135 } 2136 2137 $parsedFrame['ctoc_flags']['ordered'] = (bool) ($ctoc_flags_raw & 0x01); 2138 $parsedFrame['ctoc_flags']['top_level'] = (bool) ($ctoc_flags_raw & 0x03); 2139 2140 unset($ctoc_flags_raw, $terminator_position); 2141 2142 if ($frame_offset < strlen($parsedFrame['data'])) { 2143 $parsedFrame['subframes'] = array(); 2144 while ($frame_offset < strlen($parsedFrame['data'])) { 2145 // <Optional embedded sub-frames> 2146 $subframe = array(); 2147 $subframe['name'] = substr($parsedFrame['data'], $frame_offset, 4); 2148 $frame_offset += 4; 2149 $subframe['size'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 4)); 2150 $frame_offset += 4; 2151 $subframe['flags_raw'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 2)); 2152 $frame_offset += 2; 2153 if ($subframe['size'] > (strlen($parsedFrame['data']) - $frame_offset)) { 2154 $this->warning('CTOS subframe "'.$subframe['name'].'" at frame offset '.$frame_offset.' claims to be "'.$subframe['size'].'" bytes, which is more than the available data ('.(strlen($parsedFrame['data']) - $frame_offset).' bytes)'); 2155 break; 2156 } 2157 $subframe_rawdata = substr($parsedFrame['data'], $frame_offset, $subframe['size']); 2158 $frame_offset += $subframe['size']; 2159 2160 $subframe['encodingid'] = ord(substr($subframe_rawdata, 0, 1)); 2161 $subframe['text'] = substr($subframe_rawdata, 1); 2162 $subframe['encoding'] = $this->TextEncodingNameLookup($subframe['encodingid']); 2163 $encoding_converted_text = trim(getid3_lib::iconv_fallback($subframe['encoding'], $info['encoding'], $subframe['text']));; 2164 switch (substr($encoding_converted_text, 0, 2)) { 2165 case "\xFF\xFE": 2166 case "\xFE\xFF": 2167 switch (strtoupper($info['id3v2']['encoding'])) { 2168 case 'ISO-8859-1': 2169 case 'UTF-8': 2170 $encoding_converted_text = substr($encoding_converted_text, 2); 2171 // remove unwanted byte-order-marks 2172 break; 2173 default: 2174 // ignore 2175 break; 2176 } 2177 break; 2178 default: 2179 // do not remove BOM 2180 break; 2181 } 2182 2183 if (($subframe['name'] == 'TIT2') || ($subframe['name'] == 'TIT3')) { 2184 if ($subframe['name'] == 'TIT2') { 2185 $parsedFrame['toc_name'] = $encoding_converted_text; 2186 } elseif ($subframe['name'] == 'TIT3') { 2187 $parsedFrame['toc_description'] = $encoding_converted_text; 2188 } 2189 $parsedFrame['subframes'][] = $subframe; 2190 } else { 2191 $this->warning('ID3v2.CTOC subframe "'.$subframe['name'].'" not handled (only TIT2 and TIT3)'); 2192 } 2193 } 2194 unset($subframe_rawdata, $subframe, $encoding_converted_text); 2195 } 2196 2197 } 2198 2199 return true; 2200 } 2201 2202 /** 2203 * @param string $data 2204 * 2205 * @return string 2206 */ 2207 public function DeUnsynchronise($data) { 2208 return str_replace("\xFF\x00", "\xFF", $data); 2209 } 2210 2211 /** 2212 * @param int $index 2213 * 2214 * @return string 2215 */ 2216 public function LookupExtendedHeaderRestrictionsTagSizeLimits($index) { 2217 static $LookupExtendedHeaderRestrictionsTagSizeLimits = array( 2218 0x00 => 'No more than 128 frames and 1 MB total tag size', 2219 0x01 => 'No more than 64 frames and 128 KB total tag size', 2220 0x02 => 'No more than 32 frames and 40 KB total tag size', 2221 0x03 => 'No more than 32 frames and 4 KB total tag size', 2222 ); 2223 return (isset($LookupExtendedHeaderRestrictionsTagSizeLimits[$index]) ? $LookupExtendedHeaderRestrictionsTagSizeLimits[$index] : ''); 2224 } 2225 2226 /** 2227 * @param int $index 2228 * 2229 * @return string 2230 */ 2231 public function LookupExtendedHeaderRestrictionsTextEncodings($index) { 2232 static $LookupExtendedHeaderRestrictionsTextEncodings = array( 2233 0x00 => 'No restrictions', 2234 0x01 => 'Strings are only encoded with ISO-8859-1 or UTF-8', 2235 ); 2236 return (isset($LookupExtendedHeaderRestrictionsTextEncodings[$index]) ? $LookupExtendedHeaderRestrictionsTextEncodings[$index] : ''); 2237 } 2238 2239 /** 2240 * @param int $index 2241 * 2242 * @return string 2243 */ 2244 public function LookupExtendedHeaderRestrictionsTextFieldSize($index) { 2245 static $LookupExtendedHeaderRestrictionsTextFieldSize = array( 2246 0x00 => 'No restrictions', 2247 0x01 => 'No string is longer than 1024 characters', 2248 0x02 => 'No string is longer than 128 characters', 2249 0x03 => 'No string is longer than 30 characters', 2250 ); 2251 return (isset($LookupExtendedHeaderRestrictionsTextFieldSize[$index]) ? $LookupExtendedHeaderRestrictionsTextFieldSize[$index] : ''); 2252 } 2253 2254 /** 2255 * @param int $index 2256 * 2257 * @return string 2258 */ 2259 public function LookupExtendedHeaderRestrictionsImageEncoding($index) { 2260 static $LookupExtendedHeaderRestrictionsImageEncoding = array( 2261 0x00 => 'No restrictions', 2262 0x01 => 'Images are encoded only with PNG or JPEG', 2263 ); 2264 return (isset($LookupExtendedHeaderRestrictionsImageEncoding[$index]) ? $LookupExtendedHeaderRestrictionsImageEncoding[$index] : ''); 2265 } 2266 2267 /** 2268 * @param int $index 2269 * 2270 * @return string 2271 */ 2272 public function LookupExtendedHeaderRestrictionsImageSizeSize($index) { 2273 static $LookupExtendedHeaderRestrictionsImageSizeSize = array( 2274 0x00 => 'No restrictions', 2275 0x01 => 'All images are 256x256 pixels or smaller', 2276 0x02 => 'All images are 64x64 pixels or smaller', 2277 0x03 => 'All images are exactly 64x64 pixels, unless required otherwise', 2278 ); 2279 return (isset($LookupExtendedHeaderRestrictionsImageSizeSize[$index]) ? $LookupExtendedHeaderRestrictionsImageSizeSize[$index] : ''); 2280 } 2281 2282 /** 2283 * @param string $currencyid 2284 * 2285 * @return string 2286 */ 2287 public function LookupCurrencyUnits($currencyid) { 2288 2289 $begin = __LINE__; 2290 2291 /** This is not a comment! 2292 2293 2294 AED Dirhams 2295 AFA Afghanis 2296 ALL Leke 2297 AMD Drams 2298 ANG Guilders 2299 AOA Kwanza 2300 ARS Pesos 2301 ATS Schillings 2302 AUD Dollars 2303 AWG Guilders 2304 AZM Manats 2305 BAM Convertible Marka 2306 BBD Dollars 2307 BDT Taka 2308 BEF Francs 2309 BGL Leva 2310 BHD Dinars 2311 BIF Francs 2312 BMD Dollars 2313 BND Dollars 2314 BOB Bolivianos 2315 BRL Brazil Real 2316 BSD Dollars 2317 BTN Ngultrum 2318 BWP Pulas 2319 BYR Rubles 2320 BZD Dollars 2321 CAD Dollars 2322 CDF Congolese Francs 2323 CHF Francs 2324 CLP Pesos 2325 CNY Yuan Renminbi 2326 COP Pesos 2327 CRC Colones 2328 CUP Pesos 2329 CVE Escudos 2330 CYP Pounds 2331 CZK Koruny 2332 DEM Deutsche Marks 2333 DJF Francs 2334 DKK Kroner 2335 DOP Pesos 2336 DZD Algeria Dinars 2337 EEK Krooni 2338 EGP Pounds 2339 ERN Nakfa 2340 ESP Pesetas 2341 ETB Birr 2342 EUR Euro 2343 FIM Markkaa 2344 FJD Dollars 2345 FKP Pounds 2346 FRF Francs 2347 GBP Pounds 2348 GEL Lari 2349 GGP Pounds 2350 GHC Cedis 2351 GIP Pounds 2352 GMD Dalasi 2353 GNF Francs 2354 GRD Drachmae 2355 GTQ Quetzales 2356 GYD Dollars 2357 HKD Dollars 2358 HNL Lempiras 2359 HRK Kuna 2360 HTG Gourdes 2361 HUF Forints 2362 IDR Rupiahs 2363 IEP Pounds 2364 ILS New Shekels 2365 IMP Pounds 2366 INR Rupees 2367 IQD Dinars 2368 IRR Rials 2369 ISK Kronur 2370 ITL Lire 2371 JEP Pounds 2372 JMD Dollars 2373 JOD Dinars 2374 JPY Yen 2375 KES Shillings 2376 KGS Soms 2377 KHR Riels 2378 KMF Francs 2379 KPW Won 2380 KWD Dinars 2381 KYD Dollars 2382 KZT Tenge 2383 LAK Kips 2384 LBP Pounds 2385 LKR Rupees 2386 LRD Dollars 2387 LSL Maloti 2388 LTL Litai 2389 LUF Francs 2390 LVL Lati 2391 LYD Dinars 2392 MAD Dirhams 2393 MDL Lei 2394 MGF Malagasy Francs 2395 MKD Denars 2396 MMK Kyats 2397 MNT Tugriks 2398 MOP Patacas 2399 MRO Ouguiyas 2400 MTL Liri 2401 MUR Rupees 2402 MVR Rufiyaa 2403 MWK Kwachas 2404 MXN Pesos 2405 MYR Ringgits 2406 MZM Meticais 2407 NAD Dollars 2408 NGN Nairas 2409 NIO Gold Cordobas 2410 NLG Guilders 2411 NOK Krone 2412 NPR Nepal Rupees 2413 NZD Dollars 2414 OMR Rials 2415 PAB Balboa 2416 PEN Nuevos Soles 2417 PGK Kina 2418 PHP Pesos 2419 PKR Rupees 2420 PLN Zlotych 2421 PTE Escudos 2422 PYG Guarani 2423 QAR Rials 2424 ROL Lei 2425 RUR Rubles 2426 RWF Rwanda Francs 2427 SAR Riyals 2428 SBD Dollars 2429 SCR Rupees 2430 SDD Dinars 2431 SEK Kronor 2432 SGD Dollars 2433 SHP Pounds 2434 SIT Tolars 2435 SKK Koruny 2436 SLL Leones 2437 SOS Shillings 2438 SPL Luigini 2439 SRG Guilders 2440 STD Dobras 2441 SVC Colones 2442 SYP Pounds 2443 SZL Emalangeni 2444 THB Baht 2445 TJR Rubles 2446 TMM Manats 2447 TND Dinars 2448 TOP Pa'anga 2449 TRL Liras (old) 2450 TRY Liras 2451 TTD Dollars 2452 TVD Tuvalu Dollars 2453 TWD New Dollars 2454 TZS Shillings 2455 UAH Hryvnia 2456 UGX Shillings 2457 USD Dollars 2458 UYU Pesos 2459 UZS Sums 2460 VAL Lire 2461 VEB Bolivares 2462 VND Dong 2463 VUV Vatu 2464 WST Tala 2465 XAF Francs 2466 XAG Ounces 2467 XAU Ounces 2468 XCD Dollars 2469 XDR Special Drawing Rights 2470 XPD Ounces 2471 XPF Francs 2472 XPT Ounces 2473 YER Rials 2474 YUM New Dinars 2475 ZAR Rand 2476 ZMK Kwacha 2477 ZWD Zimbabwe Dollars 2478 2479 */ 2480 2481 return getid3_lib::EmbeddedLookup($currencyid, $begin, __LINE__, __FILE__, 'id3v2-currency-units'); 2482 } 2483 2484 /** 2485 * @param string $currencyid 2486 * 2487 * @return string 2488 */ 2489 public function LookupCurrencyCountry($currencyid) { 2490 2491 $begin = __LINE__; 2492 2493 /** This is not a comment! 2494 2495 AED United Arab Emirates 2496 AFA Afghanistan 2497 ALL Albania 2498 AMD Armenia 2499 ANG Netherlands Antilles 2500 AOA Angola 2501 ARS Argentina 2502 ATS Austria 2503 AUD Australia 2504 AWG Aruba 2505 AZM Azerbaijan 2506 BAM Bosnia and Herzegovina 2507 BBD Barbados 2508 BDT Bangladesh 2509 BEF Belgium 2510 BGL Bulgaria 2511 BHD Bahrain 2512 BIF Burundi 2513 BMD Bermuda 2514 BND Brunei Darussalam 2515 BOB Bolivia 2516 BRL Brazil 2517 BSD Bahamas 2518 BTN Bhutan 2519 BWP Botswana 2520 BYR Belarus 2521 BZD Belize 2522 CAD Canada 2523 CDF Congo/Kinshasa 2524 CHF Switzerland 2525 CLP Chile 2526 CNY China 2527 COP Colombia 2528 CRC Costa Rica 2529 CUP Cuba 2530 CVE Cape Verde 2531 CYP Cyprus 2532 CZK Czech Republic 2533 DEM Germany 2534 DJF Djibouti 2535 DKK Denmark 2536 DOP Dominican Republic 2537 DZD Algeria 2538 EEK Estonia 2539 EGP Egypt 2540 ERN Eritrea 2541 ESP Spain 2542 ETB Ethiopia 2543 EUR Euro Member Countries 2544 FIM Finland 2545 FJD Fiji 2546 FKP Falkland Islands (Malvinas) 2547 FRF France 2548 GBP United Kingdom 2549 GEL Georgia 2550 GGP Guernsey 2551 GHC Ghana 2552 GIP Gibraltar 2553 GMD Gambia 2554 GNF Guinea 2555 GRD Greece 2556 GTQ Guatemala 2557 GYD Guyana 2558 HKD Hong Kong 2559 HNL Honduras 2560 HRK Croatia 2561 HTG Haiti 2562 HUF Hungary 2563 IDR Indonesia 2564 IEP Ireland (Eire) 2565 ILS Israel 2566 IMP Isle of Man 2567 INR India 2568 IQD Iraq 2569 IRR Iran 2570 ISK Iceland 2571 ITL Italy 2572 JEP Jersey 2573 JMD Jamaica 2574 JOD Jordan 2575 JPY Japan 2576 KES Kenya 2577 KGS Kyrgyzstan 2578 KHR Cambodia 2579 KMF Comoros 2580 KPW Korea 2581 KWD Kuwait 2582 KYD Cayman Islands 2583 KZT Kazakstan 2584 LAK Laos 2585 LBP Lebanon 2586 LKR Sri Lanka 2587 LRD Liberia 2588 LSL Lesotho 2589 LTL Lithuania 2590 LUF Luxembourg 2591 LVL Latvia 2592 LYD Libya 2593 MAD Morocco 2594 MDL Moldova 2595 MGF Madagascar 2596 MKD Macedonia 2597 MMK Myanmar (Burma) 2598 MNT Mongolia 2599 MOP Macau 2600 MRO Mauritania 2601 MTL Malta 2602 MUR Mauritius 2603 MVR Maldives (Maldive Islands) 2604 MWK Malawi 2605 MXN Mexico 2606 MYR Malaysia 2607 MZM Mozambique 2608 NAD Namibia 2609 NGN Nigeria 2610 NIO Nicaragua 2611 NLG Netherlands (Holland) 2612 NOK Norway 2613 NPR Nepal 2614 NZD New Zealand 2615 OMR Oman 2616 PAB Panama 2617 PEN Peru 2618 PGK Papua New Guinea 2619 PHP Philippines 2620 PKR Pakistan 2621 PLN Poland 2622 PTE Portugal 2623 PYG Paraguay 2624 QAR Qatar 2625 ROL Romania 2626 RUR Russia 2627 RWF Rwanda 2628 SAR Saudi Arabia 2629 SBD Solomon Islands 2630 SCR Seychelles 2631 SDD Sudan 2632 SEK Sweden 2633 SGD Singapore 2634 SHP Saint Helena 2635 SIT Slovenia 2636 SKK Slovakia 2637 SLL Sierra Leone 2638 SOS Somalia 2639 SPL Seborga 2640 SRG Suriname 2641 STD São Tome and Principe 2642 SVC El Salvador 2643 SYP Syria 2644 SZL Swaziland 2645 THB Thailand 2646 TJR Tajikistan 2647 TMM Turkmenistan 2648 TND Tunisia 2649 TOP Tonga 2650 TRL Turkey 2651 TRY Turkey 2652 TTD Trinidad and Tobago 2653 TVD Tuvalu 2654 TWD Taiwan 2655 TZS Tanzania 2656 UAH Ukraine 2657 UGX Uganda 2658 USD United States of America 2659 UYU Uruguay 2660 UZS Uzbekistan 2661 VAL Vatican City 2662 VEB Venezuela 2663 VND Viet Nam 2664 VUV Vanuatu 2665 WST Samoa 2666 XAF Communauté Financière Africaine 2667 XAG Silver 2668 XAU Gold 2669 XCD East Caribbean 2670 XDR International Monetary Fund 2671 XPD Palladium 2672 XPF Comptoirs Français du Pacifique 2673 XPT Platinum 2674 YER Yemen 2675 YUM Yugoslavia 2676 ZAR South Africa 2677 ZMK Zambia 2678 ZWD Zimbabwe 2679 2680 */ 2681 2682 return getid3_lib::EmbeddedLookup($currencyid, $begin, __LINE__, __FILE__, 'id3v2-currency-country'); 2683 } 2684 2685 /** 2686 * @param string $languagecode 2687 * @param bool $casesensitive 2688 * 2689 * @return string 2690 */ 2691 public static function LanguageLookup($languagecode, $casesensitive=false) { 2692 2693 if (!$casesensitive) { 2694 $languagecode = strtolower($languagecode); 2695 } 2696 2697 // http://www.id3.org/id3v2.4.0-structure.txt 2698 // [4. ID3v2 frame overview] 2699 // The three byte language field, present in several frames, is used to 2700 // describe the language of the frame's content, according to ISO-639-2 2701 // [ISO-639-2]. The language should be represented in lower case. If the 2702 // language is not known the string "XXX" should be used. 2703 2704 2705 // ISO 639-2 - http://www.id3.org/iso639-2.html 2706 2707 $begin = __LINE__; 2708 2709 /** This is not a comment! 2710 2711 XXX unknown 2712 xxx unknown 2713 aar Afar 2714 abk Abkhazian 2715 ace Achinese 2716 ach Acoli 2717 ada Adangme 2718 afa Afro-Asiatic (Other) 2719 afh Afrihili 2720 afr Afrikaans 2721 aka Akan 2722 akk Akkadian 2723 alb Albanian 2724 ale Aleut 2725 alg Algonquian Languages 2726 amh Amharic 2727 ang English, Old (ca. 450-1100) 2728 apa Apache Languages 2729 ara Arabic 2730 arc Aramaic 2731 arm Armenian 2732 arn Araucanian 2733 arp Arapaho 2734 art Artificial (Other) 2735 arw Arawak 2736 asm Assamese 2737 ath Athapascan Languages 2738 ava Avaric 2739 ave Avestan 2740 awa Awadhi 2741 aym Aymara 2742 aze Azerbaijani 2743 bad Banda 2744 bai Bamileke Languages 2745 bak Bashkir 2746 bal Baluchi 2747 bam Bambara 2748 ban Balinese 2749 baq Basque 2750 bas Basa 2751 bat Baltic (Other) 2752 bej Beja 2753 bel Byelorussian 2754 bem Bemba 2755 ben Bengali 2756 ber Berber (Other) 2757 bho Bhojpuri 2758 bih Bihari 2759 bik Bikol 2760 bin Bini 2761 bis Bislama 2762 bla Siksika 2763 bnt Bantu (Other) 2764 bod Tibetan 2765 bra Braj 2766 bre Breton 2767 bua Buriat 2768 bug Buginese 2769 bul Bulgarian 2770 bur Burmese 2771 cad Caddo 2772 cai Central American Indian (Other) 2773 car Carib 2774 cat Catalan 2775 cau Caucasian (Other) 2776 ceb Cebuano 2777 cel Celtic (Other) 2778 ces Czech 2779 cha Chamorro 2780 chb Chibcha 2781 che Chechen 2782 chg Chagatai 2783 chi Chinese 2784 chm Mari 2785 chn Chinook jargon 2786 cho Choctaw 2787 chr Cherokee 2788 chu Church Slavic 2789 chv Chuvash 2790 chy Cheyenne 2791 cop Coptic 2792 cor Cornish 2793 cos Corsican 2794 cpe Creoles and Pidgins, English-based (Other) 2795 cpf Creoles and Pidgins, French-based (Other) 2796 cpp Creoles and Pidgins, Portuguese-based (Other) 2797 cre Cree 2798 crp Creoles and Pidgins (Other) 2799 cus Cushitic (Other) 2800 cym Welsh 2801 cze Czech 2802 dak Dakota 2803 dan Danish 2804 del Delaware 2805 deu German 2806 din Dinka 2807 div Divehi 2808 doi Dogri 2809 dra Dravidian (Other) 2810 dua Duala 2811 dum Dutch, Middle (ca. 1050-1350) 2812 dut Dutch 2813 dyu Dyula 2814 dzo Dzongkha 2815 efi Efik 2816 egy Egyptian (Ancient) 2817 eka Ekajuk 2818 ell Greek, Modern (1453-) 2819 elx Elamite 2820 eng English 2821 enm English, Middle (ca. 1100-1500) 2822 epo Esperanto 2823 esk Eskimo (Other) 2824 esl Spanish 2825 est Estonian 2826 eus Basque 2827 ewe Ewe 2828 ewo Ewondo 2829 fan Fang 2830 fao Faroese 2831 fas Persian 2832 fat Fanti 2833 fij Fijian 2834 fin Finnish 2835 fiu Finno-Ugrian (Other) 2836 fon Fon 2837 fra French 2838 fre French 2839 frm French, Middle (ca. 1400-1600) 2840 fro French, Old (842- ca. 1400) 2841 fry Frisian 2842 ful Fulah 2843 gaa Ga 2844 gae Gaelic (Scots) 2845 gai Irish 2846 gay Gayo 2847 gdh Gaelic (Scots) 2848 gem Germanic (Other) 2849 geo Georgian 2850 ger German 2851 gez Geez 2852 gil Gilbertese 2853 glg Gallegan 2854 gmh German, Middle High (ca. 1050-1500) 2855 goh German, Old High (ca. 750-1050) 2856 gon Gondi 2857 got Gothic 2858 grb Grebo 2859 grc Greek, Ancient (to 1453) 2860 gre Greek, Modern (1453-) 2861 grn Guarani 2862 guj Gujarati 2863 hai Haida 2864 hau Hausa 2865 haw Hawaiian 2866 heb Hebrew 2867 her Herero 2868 hil Hiligaynon 2869 him Himachali 2870 hin Hindi 2871 hmo Hiri Motu 2872 hun Hungarian 2873 hup Hupa 2874 hye Armenian 2875 iba Iban 2876 ibo Igbo 2877 ice Icelandic 2878 ijo Ijo 2879 iku Inuktitut 2880 ilo Iloko 2881 ina Interlingua (International Auxiliary language Association) 2882 inc Indic (Other) 2883 ind Indonesian 2884 ine Indo-European (Other) 2885 ine Interlingue 2886 ipk Inupiak 2887 ira Iranian (Other) 2888 iri Irish 2889 iro Iroquoian uages 2890 isl Icelandic 2891 ita Italian 2892 jav Javanese 2893 jaw Javanese 2894 jpn Japanese 2895 jpr Judeo-Persian 2896 jrb Judeo-Arabic 2897 kaa Kara-Kalpak 2898 kab Kabyle 2899 kac Kachin 2900 kal Greenlandic 2901 kam Kamba 2902 kan Kannada 2903 kar Karen 2904 kas Kashmiri 2905 kat Georgian 2906 kau Kanuri 2907 kaw Kawi 2908 kaz Kazakh 2909 kha Khasi 2910 khi Khoisan (Other) 2911 khm Khmer 2912 kho Khotanese 2913 kik Kikuyu 2914 kin Kinyarwanda 2915 kir Kirghiz 2916 kok Konkani 2917 kom Komi 2918 kon Kongo 2919 kor Korean 2920 kpe Kpelle 2921 kro Kru 2922 kru Kurukh 2923 kua Kuanyama 2924 kum Kumyk 2925 kur Kurdish 2926 kus Kusaie 2927 kut Kutenai 2928 lad Ladino 2929 lah Lahnda 2930 lam Lamba 2931 lao Lao 2932 lat Latin 2933 lav Latvian 2934 lez Lezghian 2935 lin Lingala 2936 lit Lithuanian 2937 lol Mongo 2938 loz Lozi 2939 ltz Letzeburgesch 2940 lub Luba-Katanga 2941 lug Ganda 2942 lui Luiseno 2943 lun Lunda 2944 luo Luo (Kenya and Tanzania) 2945 mac Macedonian 2946 mad Madurese 2947 mag Magahi 2948 mah Marshall 2949 mai Maithili 2950 mak Macedonian 2951 mak Makasar 2952 mal Malayalam 2953 man Mandingo 2954 mao Maori 2955 map Austronesian (Other) 2956 mar Marathi 2957 mas Masai 2958 max Manx 2959 may Malay 2960 men Mende 2961 mga Irish, Middle (900 - 1200) 2962 mic Micmac 2963 min Minangkabau 2964 mis Miscellaneous (Other) 2965 mkh Mon-Kmer (Other) 2966 mlg Malagasy 2967 mlt Maltese 2968 mni Manipuri 2969 mno Manobo Languages 2970 moh Mohawk 2971 mol Moldavian 2972 mon Mongolian 2973 mos Mossi 2974 mri Maori 2975 msa Malay 2976 mul Multiple Languages 2977 mun Munda Languages 2978 mus Creek 2979 mwr Marwari 2980 mya Burmese 2981 myn Mayan Languages 2982 nah Aztec 2983 nai North American Indian (Other) 2984 nau Nauru 2985 nav Navajo 2986 nbl Ndebele, South 2987 nde Ndebele, North 2988 ndo Ndongo 2989 nep Nepali 2990 new Newari 2991 nic Niger-Kordofanian (Other) 2992 niu Niuean 2993 nla Dutch 2994 nno Norwegian (Nynorsk) 2995 non Norse, Old 2996 nor Norwegian 2997 nso Sotho, Northern 2998 nub Nubian Languages 2999 nya Nyanja 3000 nym Nyamwezi 3001 nyn Nyankole 3002 nyo Nyoro 3003 nzi Nzima 3004 oci Langue d'Oc (post 1500) 3005 oji Ojibwa 3006 ori Oriya 3007 orm Oromo 3008 osa Osage 3009 oss Ossetic 3010 ota Turkish, Ottoman (1500 - 1928) 3011 oto Otomian Languages 3012 paa Papuan-Australian (Other) 3013 pag Pangasinan 3014 pal Pahlavi 3015 pam Pampanga 3016 pan Panjabi 3017 pap Papiamento 3018 pau Palauan 3019 peo Persian, Old (ca 600 - 400 B.C.) 3020 per Persian 3021 phn Phoenician 3022 pli Pali 3023 pol Polish 3024 pon Ponape 3025 por Portuguese 3026 pra Prakrit uages 3027 pro Provencal, Old (to 1500) 3028 pus Pushto 3029 que Quechua 3030 raj Rajasthani 3031 rar Rarotongan 3032 roa Romance (Other) 3033 roh Rhaeto-Romance 3034 rom Romany 3035 ron Romanian 3036 rum Romanian 3037 run Rundi 3038 rus Russian 3039 sad Sandawe 3040 sag Sango 3041 sah Yakut 3042 sai South American Indian (Other) 3043 sal Salishan Languages 3044 sam Samaritan Aramaic 3045 san Sanskrit 3046 sco Scots 3047 scr Serbo-Croatian 3048 sel Selkup 3049 sem Semitic (Other) 3050 sga Irish, Old (to 900) 3051 shn Shan 3052 sid Sidamo 3053 sin Singhalese 3054 sio Siouan Languages 3055 sit Sino-Tibetan (Other) 3056 sla Slavic (Other) 3057 slk Slovak 3058 slo Slovak 3059 slv Slovenian 3060 smi Sami Languages 3061 smo Samoan 3062 sna Shona 3063 snd Sindhi 3064 sog Sogdian 3065 som Somali 3066 son Songhai 3067 sot Sotho, Southern 3068 spa Spanish 3069 sqi Albanian 3070 srd Sardinian 3071 srr Serer 3072 ssa Nilo-Saharan (Other) 3073 ssw Siswant 3074 ssw Swazi 3075 suk Sukuma 3076 sun Sudanese 3077 sus Susu 3078 sux Sumerian 3079 sve Swedish 3080 swa Swahili 3081 swe Swedish 3082 syr Syriac 3083 tah Tahitian 3084 tam Tamil 3085 tat Tatar 3086 tel Telugu 3087 tem Timne 3088 ter Tereno 3089 tgk Tajik 3090 tgl Tagalog 3091 tha Thai 3092 tib Tibetan 3093 tig Tigre 3094 tir Tigrinya 3095 tiv Tivi 3096 tli Tlingit 3097 tmh Tamashek 3098 tog Tonga (Nyasa) 3099 ton Tonga (Tonga Islands) 3100 tru Truk 3101 tsi Tsimshian 3102 tsn Tswana 3103 tso Tsonga 3104 tuk Turkmen 3105 tum Tumbuka 3106 tur Turkish 3107 tut Altaic (Other) 3108 twi Twi 3109 tyv Tuvinian 3110 uga Ugaritic 3111 uig Uighur 3112 ukr Ukrainian 3113 umb Umbundu 3114 und Undetermined 3115 urd Urdu 3116 uzb Uzbek 3117 vai Vai 3118 ven Venda 3119 vie Vietnamese 3120 vol Volapük 3121 vot Votic 3122 wak Wakashan Languages 3123 wal Walamo 3124 war Waray 3125 was Washo 3126 wel Welsh 3127 wen Sorbian Languages 3128 wol Wolof 3129 xho Xhosa 3130 yao Yao 3131 yap Yap 3132 yid Yiddish 3133 yor Yoruba 3134 zap Zapotec 3135 zen Zenaga 3136 zha Zhuang 3137 zho Chinese 3138 zul Zulu 3139 zun Zuni 3140 3141 */ 3142 3143 return getid3_lib::EmbeddedLookup($languagecode, $begin, __LINE__, __FILE__, 'id3v2-languagecode'); 3144 } 3145 3146 /** 3147 * @param int $index 3148 * 3149 * @return string 3150 */ 3151 public static function ETCOEventLookup($index) { 3152 if (($index >= 0x17) && ($index <= 0xDF)) { 3153 return 'reserved for future use'; 3154 } 3155 if (($index >= 0xE0) && ($index <= 0xEF)) { 3156 return 'not predefined synch 0-F'; 3157 } 3158 if (($index >= 0xF0) && ($index <= 0xFC)) { 3159 return 'reserved for future use'; 3160 } 3161 3162 static $EventLookup = array( 3163 0x00 => 'padding (has no meaning)', 3164 0x01 => 'end of initial silence', 3165 0x02 => 'intro start', 3166 0x03 => 'main part start', 3167 0x04 => 'outro start', 3168 0x05 => 'outro end', 3169 0x06 => 'verse start', 3170 0x07 => 'refrain start', 3171 0x08 => 'interlude start', 3172 0x09 => 'theme start', 3173 0x0A => 'variation start', 3174 0x0B => 'key change', 3175 0x0C => 'time change', 3176 0x0D => 'momentary unwanted noise (Snap, Crackle & Pop)', 3177 0x0E => 'sustained noise', 3178 0x0F => 'sustained noise end', 3179 0x10 => 'intro end', 3180 0x11 => 'main part end', 3181 0x12 => 'verse end', 3182 0x13 => 'refrain end', 3183 0x14 => 'theme end', 3184 0x15 => 'profanity', 3185 0x16 => 'profanity end', 3186 0xFD => 'audio end (start of silence)', 3187 0xFE => 'audio file ends', 3188 0xFF => 'one more byte of events follows' 3189 ); 3190 3191 return (isset($EventLookup[$index]) ? $EventLookup[$index] : ''); 3192 } 3193 3194 /** 3195 * @param int $index 3196 * 3197 * @return string 3198 */ 3199 public static function SYTLContentTypeLookup($index) { 3200 static $SYTLContentTypeLookup = array( 3201 0x00 => 'other', 3202 0x01 => 'lyrics', 3203 0x02 => 'text transcription', 3204 0x03 => 'movement/part name', // (e.g. 'Adagio') 3205 0x04 => 'events', // (e.g. 'Don Quijote enters the stage') 3206 0x05 => 'chord', // (e.g. 'Bb F Fsus') 3207 0x06 => 'trivia/\'pop up\' information', 3208 0x07 => 'URLs to webpages', 3209 0x08 => 'URLs to images' 3210 ); 3211 3212 return (isset($SYTLContentTypeLookup[$index]) ? $SYTLContentTypeLookup[$index] : ''); 3213 } 3214 3215 /** 3216 * @param int $index 3217 * @param bool $returnarray 3218 * 3219 * @return array|string 3220 */ 3221 public static function APICPictureTypeLookup($index, $returnarray=false) { 3222 static $APICPictureTypeLookup = array( 3223 0x00 => 'Other', 3224 0x01 => '32x32 pixels \'file icon\' (PNG only)', 3225 0x02 => 'Other file icon', 3226 0x03 => 'Cover (front)', 3227 0x04 => 'Cover (back)', 3228 0x05 => 'Leaflet page', 3229 0x06 => 'Media (e.g. label side of CD)', 3230 0x07 => 'Lead artist/lead performer/soloist', 3231 0x08 => 'Artist/performer', 3232 0x09 => 'Conductor', 3233 0x0A => 'Band/Orchestra', 3234 0x0B => 'Composer', 3235 0x0C => 'Lyricist/text writer', 3236 0x0D => 'Recording Location', 3237 0x0E => 'During recording', 3238 0x0F => 'During performance', 3239 0x10 => 'Movie/video screen capture', 3240 0x11 => 'A bright coloured fish', 3241 0x12 => 'Illustration', 3242 0x13 => 'Band/artist logotype', 3243 0x14 => 'Publisher/Studio logotype' 3244 ); 3245 if ($returnarray) { 3246 return $APICPictureTypeLookup; 3247 } 3248 return (isset($APICPictureTypeLookup[$index]) ? $APICPictureTypeLookup[$index] : ''); 3249 } 3250 3251 /** 3252 * @param int $index 3253 * 3254 * @return string 3255 */ 3256 public static function COMRReceivedAsLookup($index) { 3257 static $COMRReceivedAsLookup = array( 3258 0x00 => 'Other', 3259 0x01 => 'Standard CD album with other songs', 3260 0x02 => 'Compressed audio on CD', 3261 0x03 => 'File over the Internet', 3262 0x04 => 'Stream over the Internet', 3263 0x05 => 'As note sheets', 3264 0x06 => 'As note sheets in a book with other sheets', 3265 0x07 => 'Music on other media', 3266 0x08 => 'Non-musical merchandise' 3267 ); 3268 3269 return (isset($COMRReceivedAsLookup[$index]) ? $COMRReceivedAsLookup[$index] : ''); 3270 } 3271 3272 /** 3273 * @param int $index 3274 * 3275 * @return string 3276 */ 3277 public static function RVA2ChannelTypeLookup($index) { 3278 static $RVA2ChannelTypeLookup = array( 3279 0x00 => 'Other', 3280 0x01 => 'Master volume', 3281 0x02 => 'Front right', 3282 0x03 => 'Front left', 3283 0x04 => 'Back right', 3284 0x05 => 'Back left', 3285 0x06 => 'Front centre', 3286 0x07 => 'Back centre', 3287 0x08 => 'Subwoofer' 3288 ); 3289 3290 return (isset($RVA2ChannelTypeLookup[$index]) ? $RVA2ChannelTypeLookup[$index] : ''); 3291 } 3292 3293 /** 3294 * @param string $framename 3295 * 3296 * @return string 3297 */ 3298 public static function FrameNameLongLookup($framename) { 3299 3300 $begin = __LINE__; 3301 3302 /** This is not a comment! 3303 3304 AENC Audio encryption 3305 APIC Attached picture 3306 ASPI Audio seek point index 3307 BUF Recommended buffer size 3308 CNT Play counter 3309 COM Comments 3310 COMM Comments 3311 COMR Commercial frame 3312 CRA Audio encryption 3313 CRM Encrypted meta frame 3314 ENCR Encryption method registration 3315 EQU Equalisation 3316 EQU2 Equalisation (2) 3317 EQUA Equalisation 3318 ETC Event timing codes 3319 ETCO Event timing codes 3320 GEO General encapsulated object 3321 GEOB General encapsulated object 3322 GRID Group identification registration 3323 IPL Involved people list 3324 IPLS Involved people list 3325 LINK Linked information 3326 LNK Linked information 3327 MCDI Music CD identifier 3328 MCI Music CD Identifier 3329 MLL MPEG location lookup table 3330 MLLT MPEG location lookup table 3331 OWNE Ownership frame 3332 PCNT Play counter 3333 PIC Attached picture 3334 POP Popularimeter 3335 POPM Popularimeter 3336 POSS Position synchronisation frame 3337 PRIV Private frame 3338 RBUF Recommended buffer size 3339 REV Reverb 3340 RVA Relative volume adjustment 3341 RVA2 Relative volume adjustment (2) 3342 RVAD Relative volume adjustment 3343 RVRB Reverb 3344 SEEK Seek frame 3345 SIGN Signature frame 3346 SLT Synchronised lyric/text 3347 STC Synced tempo codes 3348 SYLT Synchronised lyric/text 3349 SYTC Synchronised tempo codes 3350 TAL Album/Movie/Show title 3351 TALB Album/Movie/Show title 3352 TBP BPM (Beats Per Minute) 3353 TBPM BPM (beats per minute) 3354 TCM Composer 3355 TCMP Part of a compilation 3356 TCO Content type 3357 TCOM Composer 3358 TCON Content type 3359 TCOP Copyright message 3360 TCP Part of a compilation 3361 TCR Copyright message 3362 TDA Date 3363 TDAT Date 3364 TDEN Encoding time 3365 TDLY Playlist delay 3366 TDOR Original release time 3367 TDRC Recording time 3368 TDRL Release time 3369 TDTG Tagging time 3370 TDY Playlist delay 3371 TEN Encoded by 3372 TENC Encoded by 3373 TEXT Lyricist/Text writer 3374 TFLT File type 3375 TFT File type 3376 TIM Time 3377 TIME Time 3378 TIPL Involved people list 3379 TIT1 Content group description 3380 TIT2 Title/songname/content description 3381 TIT3 Subtitle/Description refinement 3382 TKE Initial key 3383 TKEY Initial key 3384 TLA Language(s) 3385 TLAN Language(s) 3386 TLE Length 3387 TLEN Length 3388 TMCL Musician credits list 3389 TMED Media type 3390 TMOO Mood 3391 TMT Media type 3392 TOA Original artist(s)/performer(s) 3393 TOAL Original album/movie/show title 3394 TOF Original filename 3395 TOFN Original filename 3396 TOL Original Lyricist(s)/text writer(s) 3397 TOLY Original lyricist(s)/text writer(s) 3398 TOPE Original artist(s)/performer(s) 3399 TOR Original release year 3400 TORY Original release year 3401 TOT Original album/Movie/Show title 3402 TOWN File owner/licensee 3403 TP1 Lead artist(s)/Lead performer(s)/Soloist(s)/Performing group 3404 TP2 Band/Orchestra/Accompaniment 3405 TP3 Conductor/Performer refinement 3406 TP4 Interpreted, remixed, or otherwise modified by 3407 TPA Part of a set 3408 TPB Publisher 3409 TPE1 Lead performer(s)/Soloist(s) 3410 TPE2 Band/orchestra/accompaniment 3411 TPE3 Conductor/performer refinement 3412 TPE4 Interpreted, remixed, or otherwise modified by 3413 TPOS Part of a set 3414 TPRO Produced notice 3415 TPUB Publisher 3416 TRC ISRC (International Standard Recording Code) 3417 TRCK Track number/Position in set 3418 TRD Recording dates 3419 TRDA Recording dates 3420 TRK Track number/Position in set 3421 TRSN Internet radio station name 3422 TRSO Internet radio station owner 3423 TS2 Album-Artist sort order 3424 TSA Album sort order 3425 TSC Composer sort order 3426 TSI Size 3427 TSIZ Size 3428 TSO2 Album-Artist sort order 3429 TSOA Album sort order 3430 TSOC Composer sort order 3431 TSOP Performer sort order 3432 TSOT Title sort order 3433 TSP Performer sort order 3434 TSRC ISRC (international standard recording code) 3435 TSS Software/hardware and settings used for encoding 3436 TSSE Software/Hardware and settings used for encoding 3437 TSST Set subtitle 3438 TST Title sort order 3439 TT1 Content group description 3440 TT2 Title/Songname/Content description 3441 TT3 Subtitle/Description refinement 3442 TXT Lyricist/text writer 3443 TXX User defined text information frame 3444 TXXX User defined text information frame 3445 TYE Year 3446 TYER Year 3447 UFI Unique file identifier 3448 UFID Unique file identifier 3449 ULT Unsynchronised lyric/text transcription 3450 USER Terms of use 3451 USLT Unsynchronised lyric/text transcription 3452 WAF Official audio file webpage 3453 WAR Official artist/performer webpage 3454 WAS Official audio source webpage 3455 WCM Commercial information 3456 WCOM Commercial information 3457 WCOP Copyright/Legal information 3458 WCP Copyright/Legal information 3459 WOAF Official audio file webpage 3460 WOAR Official artist/performer webpage 3461 WOAS Official audio source webpage 3462 WORS Official Internet radio station homepage 3463 WPAY Payment 3464 WPB Publishers official webpage 3465 WPUB Publishers official webpage 3466 WXX User defined URL link frame 3467 WXXX User defined URL link frame 3468 TFEA Featured Artist 3469 TSTU Recording Studio 3470 rgad Replay Gain Adjustment 3471 3472 */ 3473 3474 return getid3_lib::EmbeddedLookup($framename, $begin, __LINE__, __FILE__, 'id3v2-framename_long'); 3475 3476 // Last three: 3477 // from Helium2 [www.helium2.com] 3478 // from http://privatewww.essex.ac.uk/~djmrob/replaygain/file_format_id3v2.html 3479 } 3480 3481 /** 3482 * @param string $framename 3483 * 3484 * @return string 3485 */ 3486 public static function FrameNameShortLookup($framename) { 3487 3488 $begin = __LINE__; 3489 3490 /** This is not a comment! 3491 3492 AENC audio_encryption 3493 APIC attached_picture 3494 ASPI audio_seek_point_index 3495 BUF recommended_buffer_size 3496 CNT play_counter 3497 COM comment 3498 COMM comment 3499 COMR commercial_frame 3500 CRA audio_encryption 3501 CRM encrypted_meta_frame 3502 ENCR encryption_method_registration 3503 EQU equalisation 3504 EQU2 equalisation 3505 EQUA equalisation 3506 ETC event_timing_codes 3507 ETCO event_timing_codes 3508 GEO general_encapsulated_object 3509 GEOB general_encapsulated_object 3510 GRID group_identification_registration 3511 IPL involved_people_list 3512 IPLS involved_people_list 3513 LINK linked_information 3514 LNK linked_information 3515 MCDI music_cd_identifier 3516 MCI music_cd_identifier 3517 MLL mpeg_location_lookup_table 3518 MLLT mpeg_location_lookup_table 3519 OWNE ownership_frame 3520 PCNT play_counter 3521 PIC attached_picture 3522 POP popularimeter 3523 POPM popularimeter 3524 POSS position_synchronisation_frame 3525 PRIV private_frame 3526 RBUF recommended_buffer_size 3527 REV reverb 3528 RVA relative_volume_adjustment 3529 RVA2 relative_volume_adjustment 3530 RVAD relative_volume_adjustment 3531 RVRB reverb 3532 SEEK seek_frame 3533 SIGN signature_frame 3534 SLT synchronised_lyric 3535 STC synced_tempo_codes 3536 SYLT synchronised_lyric 3537 SYTC synchronised_tempo_codes 3538 TAL album 3539 TALB album 3540 TBP bpm 3541 TBPM bpm 3542 TCM composer 3543 TCMP part_of_a_compilation 3544 TCO genre 3545 TCOM composer 3546 TCON genre 3547 TCOP copyright_message 3548 TCP part_of_a_compilation 3549 TCR copyright_message 3550 TDA date 3551 TDAT date 3552 TDEN encoding_time 3553 TDLY playlist_delay 3554 TDOR original_release_time 3555 TDRC recording_time 3556 TDRL release_time 3557 TDTG tagging_time 3558 TDY playlist_delay 3559 TEN encoded_by 3560 TENC encoded_by 3561 TEXT lyricist 3562 TFLT file_type 3563 TFT file_type 3564 TIM time 3565 TIME time 3566 TIPL involved_people_list 3567 TIT1 content_group_description 3568 TIT2 title 3569 TIT3 subtitle 3570 TKE initial_key 3571 TKEY initial_key 3572 TLA language 3573 TLAN language 3574 TLE length 3575 TLEN length 3576 TMCL musician_credits_list 3577 TMED media_type 3578 TMOO mood 3579 TMT media_type 3580 TOA original_artist 3581 TOAL original_album 3582 TOF original_filename 3583 TOFN original_filename 3584 TOL original_lyricist 3585 TOLY original_lyricist 3586 TOPE original_artist 3587 TOR original_year 3588 TORY original_year 3589 TOT original_album 3590 TOWN file_owner 3591 TP1 artist 3592 TP2 band 3593 TP3 conductor 3594 TP4 remixer 3595 TPA part_of_a_set 3596 TPB publisher 3597 TPE1 artist 3598 TPE2 band 3599 TPE3 conductor 3600 TPE4 remixer 3601 TPOS part_of_a_set 3602 TPRO produced_notice 3603 TPUB publisher 3604 TRC isrc 3605 TRCK track_number 3606 TRD recording_dates 3607 TRDA recording_dates 3608 TRK track_number 3609 TRSN internet_radio_station_name 3610 TRSO internet_radio_station_owner 3611 TS2 album_artist_sort_order 3612 TSA album_sort_order 3613 TSC composer_sort_order 3614 TSI size 3615 TSIZ size 3616 TSO2 album_artist_sort_order 3617 TSOA album_sort_order 3618 TSOC composer_sort_order 3619 TSOP performer_sort_order 3620 TSOT title_sort_order 3621 TSP performer_sort_order 3622 TSRC isrc 3623 TSS encoder_settings 3624 TSSE encoder_settings 3625 TSST set_subtitle 3626 TST title_sort_order 3627 TT1 content_group_description 3628 TT2 title 3629 TT3 subtitle 3630 TXT lyricist 3631 TXX text 3632 TXXX text 3633 TYE year 3634 TYER year 3635 UFI unique_file_identifier 3636 UFID unique_file_identifier 3637 ULT unsynchronised_lyric 3638 USER terms_of_use 3639 USLT unsynchronised_lyric 3640 WAF url_file 3641 WAR url_artist 3642 WAS url_source 3643 WCM commercial_information 3644 WCOM commercial_information 3645 WCOP copyright 3646 WCP copyright 3647 WOAF url_file 3648 WOAR url_artist 3649 WOAS url_source 3650 WORS url_station 3651 WPAY url_payment 3652 WPB url_publisher 3653 WPUB url_publisher 3654 WXX url_user 3655 WXXX url_user 3656 TFEA featured_artist 3657 TSTU recording_studio 3658 rgad replay_gain_adjustment 3659 3660 */ 3661 3662 return getid3_lib::EmbeddedLookup($framename, $begin, __LINE__, __FILE__, 'id3v2-framename_short'); 3663 } 3664 3665 /** 3666 * @param string $encoding 3667 * 3668 * @return string 3669 */ 3670 public static function TextEncodingTerminatorLookup($encoding) { 3671 // http://www.id3.org/id3v2.4.0-structure.txt 3672 // Frames that allow different types of text encoding contains a text encoding description byte. Possible encodings: 3673 static $TextEncodingTerminatorLookup = array( 3674 0 => "\x00", // $00 ISO-8859-1. Terminated with $00. 3675 1 => "\x00\x00", // $01 UTF-16 encoded Unicode with BOM. All strings in the same frame SHALL have the same byteorder. Terminated with $00 00. 3676 2 => "\x00\x00", // $02 UTF-16BE encoded Unicode without BOM. Terminated with $00 00. 3677 3 => "\x00", // $03 UTF-8 encoded Unicode. Terminated with $00. 3678 255 => "\x00\x00" 3679 ); 3680 return (isset($TextEncodingTerminatorLookup[$encoding]) ? $TextEncodingTerminatorLookup[$encoding] : "\x00"); 3681 } 3682 3683 /** 3684 * @param int $encoding 3685 * 3686 * @return string 3687 */ 3688 public static function TextEncodingNameLookup($encoding) { 3689 // http://www.id3.org/id3v2.4.0-structure.txt 3690 // Frames that allow different types of text encoding contains a text encoding description byte. Possible encodings: 3691 static $TextEncodingNameLookup = array( 3692 0 => 'ISO-8859-1', // $00 ISO-8859-1. Terminated with $00. 3693 1 => 'UTF-16', // $01 UTF-16 encoded Unicode with BOM. All strings in the same frame SHALL have the same byteorder. Terminated with $00 00. 3694 2 => 'UTF-16BE', // $02 UTF-16BE encoded Unicode without BOM. Terminated with $00 00. 3695 3 => 'UTF-8', // $03 UTF-8 encoded Unicode. Terminated with $00. 3696 255 => 'UTF-16BE' 3697 ); 3698 return (isset($TextEncodingNameLookup[$encoding]) ? $TextEncodingNameLookup[$encoding] : 'ISO-8859-1'); 3699 } 3700 3701 /** 3702 * @param string $string 3703 * @param string $terminator 3704 * 3705 * @return string 3706 */ 3707 public static function RemoveStringTerminator($string, $terminator) { 3708 // Null terminator at end of comment string is somewhat ambiguous in the specification, may or may not be implemented by various taggers. Remove terminator only if present. 3709 // https://github.com/JamesHeinrich/getID3/issues/121 3710 // https://community.mp3tag.de/t/x-trailing-nulls-in-id3v2-comments/19227 3711 if (substr($string, -strlen($terminator), strlen($terminator)) === $terminator) { 3712 $string = substr($string, 0, -strlen($terminator)); 3713 } 3714 return $string; 3715 } 3716 3717 /** 3718 * @param string $string 3719 * 3720 * @return string 3721 */ 3722 public static function MakeUTF16emptyStringEmpty($string) { 3723 if (in_array($string, array("\x00", "\x00\x00", "\xFF\xFE", "\xFE\xFF"))) { 3724 // if string only contains a BOM or terminator then make it actually an empty string 3725 $string = ''; 3726 } 3727 return $string; 3728 } 3729 3730 /** 3731 * @param string $framename 3732 * @param int $id3v2majorversion 3733 * 3734 * @return bool|int 3735 */ 3736 public static function IsValidID3v2FrameName($framename, $id3v2majorversion) { 3737 switch ($id3v2majorversion) { 3738 case 2: 3739 return preg_match('#[A-Z][A-Z0-9]{2}#', $framename); 3740 3741 case 3: 3742 case 4: 3743 return preg_match('#[A-Z][A-Z0-9]{3}#', $framename); 3744 } 3745 return false; 3746 } 3747 3748 /** 3749 * @param string $numberstring 3750 * @param bool $allowdecimal 3751 * @param bool $allownegative 3752 * 3753 * @return bool 3754 */ 3755 public static function IsANumber($numberstring, $allowdecimal=false, $allownegative=false) { 3756 for ($i = 0; $i < strlen($numberstring); $i++) { 3757 if ((chr($numberstring[$i]) < chr('0')) || (chr($numberstring[$i]) > chr('9'))) { 3758 if (($numberstring[$i] == '.') && $allowdecimal) { 3759 // allowed 3760 } elseif (($numberstring[$i] == '-') && $allownegative && ($i == 0)) { 3761 // allowed 3762 } else { 3763 return false; 3764 } 3765 } 3766 } 3767 return true; 3768 } 3769 3770 /** 3771 * @param string $datestamp 3772 * 3773 * @return bool 3774 */ 3775 public static function IsValidDateStampString($datestamp) { 3776 if (strlen($datestamp) != 8) { 3777 return false; 3778 } 3779 if (!self::IsANumber($datestamp, false)) { 3780 return false; 3781 } 3782 $year = substr($datestamp, 0, 4); 3783 $month = substr($datestamp, 4, 2); 3784 $day = substr($datestamp, 6, 2); 3785 if (($year == 0) || ($month == 0) || ($day == 0)) { 3786 return false; 3787 } 3788 if ($month > 12) { 3789 return false; 3790 } 3791 if ($day > 31) { 3792 return false; 3793 } 3794 if (($day > 30) && (($month == 4) || ($month == 6) || ($month == 9) || ($month == 11))) { 3795 return false; 3796 } 3797 if (($day > 29) && ($month == 2)) { 3798 return false; 3799 } 3800 return true; 3801 } 3802 3803 /** 3804 * @param int $majorversion 3805 * 3806 * @return int 3807 */ 3808 public static function ID3v2HeaderLength($majorversion) { 3809 return (($majorversion == 2) ? 6 : 10); 3810 } 3811 3812 /** 3813 * @param string $frame_name 3814 * 3815 * @return string|false 3816 */ 3817 public static function ID3v22iTunesBrokenFrameName($frame_name) { 3818 // iTunes (multiple versions) has been known to write ID3v2.3 style frames 3819 // but use ID3v2.2 frame names, right-padded using either [space] or [null] 3820 // to make them fit in the 4-byte frame name space of the ID3v2.3 frame. 3821 // This function will detect and translate the corrupt frame name into ID3v2.3 standard. 3822 static $ID3v22_iTunes_BrokenFrames = array( 3823 'BUF' => 'RBUF', // Recommended buffer size 3824 'CNT' => 'PCNT', // Play counter 3825 'COM' => 'COMM', // Comments 3826 'CRA' => 'AENC', // Audio encryption 3827 'EQU' => 'EQUA', // Equalisation 3828 'ETC' => 'ETCO', // Event timing codes 3829 'GEO' => 'GEOB', // General encapsulated object 3830 'IPL' => 'IPLS', // Involved people list 3831 'LNK' => 'LINK', // Linked information 3832 'MCI' => 'MCDI', // Music CD identifier 3833 'MLL' => 'MLLT', // MPEG location lookup table 3834 'PIC' => 'APIC', // Attached picture 3835 'POP' => 'POPM', // Popularimeter 3836 'REV' => 'RVRB', // Reverb 3837 'RVA' => 'RVAD', // Relative volume adjustment 3838 'SLT' => 'SYLT', // Synchronised lyric/text 3839 'STC' => 'SYTC', // Synchronised tempo codes 3840 'TAL' => 'TALB', // Album/Movie/Show title 3841 'TBP' => 'TBPM', // BPM (beats per minute) 3842 'TCM' => 'TCOM', // Composer 3843 'TCO' => 'TCON', // Content type 3844 'TCP' => 'TCMP', // Part of a compilation 3845 'TCR' => 'TCOP', // Copyright message 3846 'TDA' => 'TDAT', // Date 3847 'TDY' => 'TDLY', // Playlist delay 3848 'TEN' => 'TENC', // Encoded by 3849 'TFT' => 'TFLT', // File type 3850 'TIM' => 'TIME', // Time 3851 'TKE' => 'TKEY', // Initial key 3852 'TLA' => 'TLAN', // Language(s) 3853 'TLE' => 'TLEN', // Length 3854 'TMT' => 'TMED', // Media type 3855 'TOA' => 'TOPE', // Original artist(s)/performer(s) 3856 'TOF' => 'TOFN', // Original filename 3857 'TOL' => 'TOLY', // Original lyricist(s)/text writer(s) 3858 'TOR' => 'TORY', // Original release year 3859 'TOT' => 'TOAL', // Original album/movie/show title 3860 'TP1' => 'TPE1', // Lead performer(s)/Soloist(s) 3861 'TP2' => 'TPE2', // Band/orchestra/accompaniment 3862 'TP3' => 'TPE3', // Conductor/performer refinement 3863 'TP4' => 'TPE4', // Interpreted, remixed, or otherwise modified by 3864 'TPA' => 'TPOS', // Part of a set 3865 'TPB' => 'TPUB', // Publisher 3866 'TRC' => 'TSRC', // ISRC (international standard recording code) 3867 'TRD' => 'TRDA', // Recording dates 3868 'TRK' => 'TRCK', // Track number/Position in set 3869 'TS2' => 'TSO2', // Album-Artist sort order 3870 'TSA' => 'TSOA', // Album sort order 3871 'TSC' => 'TSOC', // Composer sort order 3872 'TSI' => 'TSIZ', // Size 3873 'TSP' => 'TSOP', // Performer sort order 3874 'TSS' => 'TSSE', // Software/Hardware and settings used for encoding 3875 'TST' => 'TSOT', // Title sort order 3876 'TT1' => 'TIT1', // Content group description 3877 'TT2' => 'TIT2', // Title/songname/content description 3878 'TT3' => 'TIT3', // Subtitle/Description refinement 3879 'TXT' => 'TEXT', // Lyricist/Text writer 3880 'TXX' => 'TXXX', // User defined text information frame 3881 'TYE' => 'TYER', // Year 3882 'UFI' => 'UFID', // Unique file identifier 3883 'ULT' => 'USLT', // Unsynchronised lyric/text transcription 3884 'WAF' => 'WOAF', // Official audio file webpage 3885 'WAR' => 'WOAR', // Official artist/performer webpage 3886 'WAS' => 'WOAS', // Official audio source webpage 3887 'WCM' => 'WCOM', // Commercial information 3888 'WCP' => 'WCOP', // Copyright/Legal information 3889 'WPB' => 'WPUB', // Publishers official webpage 3890 'WXX' => 'WXXX', // User defined URL link frame 3891 ); 3892 if (strlen($frame_name) == 4) { 3893 if ((substr($frame_name, 3, 1) == ' ') || (substr($frame_name, 3, 1) == "\x00")) { 3894 if (isset($ID3v22_iTunes_BrokenFrames[substr($frame_name, 0, 3)])) { 3895 return $ID3v22_iTunes_BrokenFrames[substr($frame_name, 0, 3)]; 3896 } 3897 } 3898 } 3899 return false; 3900 } 3901 3902 } 3903