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