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