source: spip-zone/_core_/branches/spip-3.2/plugins/medias/lib/getid3/module.tag.id3v2.php @ 113162

Last change on this file since 113162 was 113162, checked in by spip.franck@…, 5 months ago

Mise à jour de la lib getid en version 1.9.16, nous étions en 1.9.14
https://github.com/JamesHeinrich/getID3/blob/master/changelog.txt

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