Ampache PHP Cross Reference Groupware Applications

Source: /modules/getid3/module.tag.id3v2.php - 3425 lines - 135408 bytes - Summary - Text - Print

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

title

Description

title

Description

title

Description

title

title

Body