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