PageRenderTime 21ms CodeModel.GetById 70ms app.highlight 204ms RepoModel.GetById 14ms app.codeStats 2ms

/wp-includes/ID3/module.tag.id3v2.php

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