home *** CD-ROM | disk | FTP | other *** search
/ HTML Examples / WP.iso / wordpress / wp-includes / ID3 / module.tag.id3v2.php < prev    next >
Encoding:
PHP Script  |  2017-07-31  |  146.8 KB  |  3,742 lines

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