[ Index ]

WordPress Cross Reference

title

Body

[close]

/wp-includes/ID3/ -> module.tag.id3v2.php (source)

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


Generated: Tue Mar 25 01:41:18 2014 WordPress honlapkészítés: online1.hu