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