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