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