Passed
Branch master (f2d2e3)
by Michael
18:45
created

getid3_id3v2   F

Complexity

Total Complexity 408

Size/Duplication

Total Lines 3250
Duplicated Lines 9.2 %

Coupling/Cohesion

Components 1
Dependencies 7

Importance

Changes 0
Metric Value
dl 299
loc 3250
rs 0.8
c 0
b 0
f 0
wmc 408
lcom 1
cbo 7

18 Methods

Rating   Name   Duplication   Size   Complexity  
B FrameNameShortLookup() 0 84 1
B LanguageLookup() 0 449 2
B ETCOEventLookup() 0 42 7
A RVA2ChannelTypeLookup() 0 15 1
F ParseID3v2Frame() 0 1409 270
F Analyze() 0 431 74
A TextEncodingNameLookup() 0 17 3
B LookupCurrencyUnits() 0 189 1
B LookupCurrencyCountry() 0 189 1
C IsValidDateStampString() 0 22 15
A SYTLContentTypeLookup() 0 15 1
A APICPictureTypeLookup() 0 30 2
C ParseID3v2GenreString() 0 56 14
B array_merge_noclobber() 0 13 8
B FrameNameLongLookup() 0 164 1
A IsValidID3v2FrameName() 0 11 4
A COMRReceivedAsLookup() 0 15 2
A TextEncodingTerminatorLookup() 0 18 1

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like getid3_id3v2 often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use getid3_id3v2, and based on these observations, apply Extract Interface, too.

1
<?php
2
// +----------------------------------------------------------------------+
3
// | PHP version 5                                                        |
4
// +----------------------------------------------------------------------+
5
// | Copyright (c) 2002-2006 James Heinrich, Allan Hansen                 |
6
// +----------------------------------------------------------------------+
7
// | This source file is subject to version 2 of the GPL license,         |
8
// | that is bundled with this package in the file license.txt and is     |
9
// | available through the world-wide-web at the following url:           |
10
// | http://www.gnu.org/copyleft/gpl.html                                 |
11
// +----------------------------------------------------------------------+
12
// | getID3() - http://getid3.sourceforge.net or http://www.getid3.org    |
13
// +----------------------------------------------------------------------+
14
// | Authors: James Heinrich <info�getid3*org>                            |
15
// |          Allan Hansen <ah�artemis*dk>                                |
16
// +----------------------------------------------------------------------+
17
// | module.tag.id3v2.php                                                 |
18
// | module for analyzing ID3v2 tags                                      |
19
// | dependencies: module.tag.id3v1.php                                   |
20
// |               module.lib.image_size.php (optional)                   |
21
// |               zlib support in PHP (optional)                         |
22
// +----------------------------------------------------------------------+
23
//
24
// $Id: module.tag.id3v2.php,v 1.15 2006/12/03 23:47:29 ah Exp $
25
26
27
        
28
        
29
class getid3_id3v2 extends getid3_handler
30
{
31
32
    public $option_starting_offset = 0;
33
34
35
    public function Analyze() {
36
37
        $getid3 = $this->getid3;
38
        
39
        // dependency
40
        $getid3->include_module('tag.id3v1');
41
        
42
        if ($getid3->option_tags_images) {        
43
            $getid3->include_module('lib.image_size');
44
        }
45
46
47
        //    Overall tag structure:
48
        //        +-----------------------------+
49
        //        |      Header (10 bytes)      |
50
        //        +-----------------------------+
51
        //        |       Extended Header       |
52
        //        | (variable length, OPTIONAL) |
53
        //        +-----------------------------+
54
        //        |   Frames (variable length)  |
55
        //        +-----------------------------+
56
        //        |           Padding           |
57
        //        | (variable length, OPTIONAL) |
58
        //        +-----------------------------+
59
        //        | Footer (10 bytes, OPTIONAL) |
60
        //        +-----------------------------+
61
        //
62
        //    Header
63
        //        ID3v2/file identifier      "ID3"
64
        //        ID3v2 version              $04 00
65
        //        ID3v2 flags                (%ab000000 in v2.2, %abc00000 in v2.3, %abcd0000 in v2.4.x)
66
        //        ID3v2 size                 4 * %0xxxxxxx
67
68
69
        // shortcuts
70
        $getid3->info['id3v2']['header'] = true;
71
        $info_id3v2          = &$getid3->info['id3v2'];
72
        $info_id3v2['flags'] = array ();
73
        $info_id3v2_flags    = &$info_id3v2['flags'];
74
75
76
        $this->fseek($this->option_starting_offset, SEEK_SET);
77
        $header = $this->fread(10);
78
        if (substr($header, 0, 3) == 'ID3'  &&  strlen($header) == 10) {
79
80
            $info_id3v2['majorversion'] = ord($header{3});
81
            $info_id3v2['minorversion'] = ord($header{4});
82
83
            // shortcut
84
            $id3v2_major_version = &$info_id3v2['majorversion'];
85
86
        } else {
87
            unset($getid3->info['id3v2']);
88
            return false;
89
90
        }
91
92
        if ($id3v2_major_version > 4) { // this script probably won't correctly parse ID3v2.5.x and above (if it ever exists)
93
            throw new getid3_exception('this script only parses up to ID3v2.4.x - this tag is ID3v2.'.$id3v2_major_version.'.'.$info_id3v2['minorversion']);
94
        }
95
96
        $id3_flags = ord($header{5});
97
        switch ($id3v2_major_version) {
98
            case 2:
99
                // %ab000000 in v2.2
100
                $info_id3v2_flags['unsynch']     = (bool)($id3_flags & 0x80); // a - Unsynchronisation
101
                $info_id3v2_flags['compression'] = (bool)($id3_flags & 0x40); // b - Compression
102
                break;
103
104
            case 3:
105
                // %abc00000 in v2.3
106
                $info_id3v2_flags['unsynch']     = (bool)($id3_flags & 0x80); // a - Unsynchronisation
107
                $info_id3v2_flags['exthead']     = (bool)($id3_flags & 0x40); // b - Extended header
108
                $info_id3v2_flags['experim']     = (bool)($id3_flags & 0x20); // c - Experimental indicator
109
                break;
110
111
            case 4:
112
                // %abcd0000 in v2.4
113
                $info_id3v2_flags['unsynch']     = (bool)($id3_flags & 0x80); // a - Unsynchronisation
114
                $info_id3v2_flags['exthead']     = (bool)($id3_flags & 0x40); // b - Extended header
115
                $info_id3v2_flags['experim']     = (bool)($id3_flags & 0x20); // c - Experimental indicator
116
                $info_id3v2_flags['isfooter']    = (bool)($id3_flags & 0x10); // d - Footer present
117
                break;
118
        }
119
120
        $info_id3v2['headerlength'] = getid3_lib::BigEndianSyncSafe2Int(substr($header, 6, 4)) + 10; // length of ID3v2 tag in 10-byte header doesn't include 10-byte header length
121
122
        $info_id3v2['tag_offset_start'] = $this->option_starting_offset;
123
        $info_id3v2['tag_offset_end']   = $info_id3v2['tag_offset_start'] + $info_id3v2['headerlength'];
124
125
126
        // Frames
127
        
128
        //     All ID3v2 frames consists of one frame header followed by one or more
129
        //     fields containing the actual information. The header is always 10
130
        //     bytes and laid out as follows:
131
        //
132
        //     Frame ID      $xx xx xx xx  (four characters)
133
        //     Size      4 * %0xxxxxxx
134
        //     Flags         $xx xx
135
136
        $size_of_frames = $info_id3v2['headerlength'] - 10; // not including 10-byte initial header
137
        if (@$info_id3v2['exthead']['length']) {
138
            $size_of_frames -= ($info_id3v2['exthead']['length'] + 4);
139
        }
140
        
141
        if (@$info_id3v2_flags['isfooter']) {
142
            $size_of_frames -= 10; // footer takes last 10 bytes of ID3v2 header, after frame data, before audio
143
        }
144
        
145
        if ($size_of_frames > 0) {
146
            $frame_data = $this->fread($size_of_frames); // read all frames from file into $frame_data variable
147
148
            //    if entire frame data is unsynched, de-unsynch it now (ID3v2.3.x)
149
            if (@$info_id3v2_flags['unsynch'] && ($id3v2_major_version <= 3)) {
150
                $frame_data = str_replace("\xFF\x00", "\xFF", $frame_data);
151
            }
152
        
153
            //        [in ID3v2.4.0] Unsynchronisation [S:6.1] is done on frame level, instead
154
            //        of on tag level, making it easier to skip frames, increasing the streamability
155
            //        of the tag. The unsynchronisation flag in the header [S:3.1] indicates that
156
            //        there exists an unsynchronised frame, while the new unsynchronisation flag in
157
            //        the frame header [S:4.1.2] indicates unsynchronisation.
158
159
             //$frame_data_offset = 10 + (@$info_id3v2['exthead']['length'] ? $info_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)
160
             $frame_data_offset = 10; // how many bytes into the stream - start from after the 10-byte header
161
162
             //    Extended Header
163
             if (@$info_id3v2_flags['exthead']) {
164
                     $extended_header_offset = 0;
165
166
                 if ($id3v2_major_version == 3) {
167
168
                     // v2.3 definition:
169
                     //Extended header size  $xx xx xx xx   // 32-bit integer
170
                     //Extended Flags        $xx xx
171
                     //     %x0000000 %00000000 // v2.3
172
                     //     x - CRC data present
173
                     //Size of padding       $xx xx xx xx
174
175
                     $info_id3v2['exthead']['length'] = getid3_lib::BigEndian2Int(substr($frame_data, $extended_header_offset, 4), 0);
176
                     $extended_header_offset += 4;
177
178
                     $info_id3v2['exthead']['flag_bytes'] = 2;
179
                     $info_id3v2['exthead']['flag_raw'] = getid3_lib::BigEndian2Int(substr($frame_data, $extended_header_offset, $info_id3v2['exthead']['flag_bytes']));
180
                     $extended_header_offset += $info_id3v2['exthead']['flag_bytes'];
181
182
                     $info_id3v2['exthead']['flags']['crc'] = (bool) ($info_id3v2['exthead']['flag_raw'] & 0x8000);
183
184
                     $info_id3v2['exthead']['padding_size'] = getid3_lib::BigEndian2Int(substr($frame_data, $extended_header_offset, 4));
185
                     $extended_header_offset += 4;
186
187
                     if ($info_id3v2['exthead']['flags']['crc']) {
188
                         $info_id3v2['exthead']['flag_data']['crc'] = getid3_lib::BigEndian2Int(substr($frame_data, $extended_header_offset, 4));
189
                         $extended_header_offset += 4;
190
                     }
191
                     $extended_header_offset += $info_id3v2['exthead']['padding_size'];
192
193
                 } 
194
                 
195
                 elseif ($id3v2_major_version == 4) {
196
197
                     // v2.4 definition:
198
                     //Extended header size   4 * %0xxxxxxx // 28-bit synchsafe integer
199
                     //Number of flag bytes       $01
200
                     //Extended Flags             $xx
201
                     //     %0bcd0000 // v2.4
202
                     //     b - Tag is an update
203
                     //         Flag data length       $00
204
                     //     c - CRC data present
205
                     //         Flag data length       $05
206
                     //         Total frame CRC    5 * %0xxxxxxx
207
                     //     d - Tag restrictions
208
                     //         Flag data length       $01
209
210
                     $info_id3v2['exthead']['length']     = getid3_lib::BigEndian2Int(substr($frame_data, $extended_header_offset, 4), 1);
211
                     $extended_header_offset += 4;
212
213
                     $info_id3v2['exthead']['flag_bytes'] = 1;
214
                     $info_id3v2['exthead']['flag_raw'] = getid3_lib::BigEndian2Int(substr($frame_data, $extended_header_offset, $info_id3v2['exthead']['flag_bytes']));
215
                     $extended_header_offset += $info_id3v2['exthead']['flag_bytes'];
216
217
                     $info_id3v2['exthead']['flags']['update']       = (bool) ($info_id3v2['exthead']['flag_raw'] & 0x4000);
218
                     $info_id3v2['exthead']['flags']['crc']          = (bool) ($info_id3v2['exthead']['flag_raw'] & 0x2000);
219
                     $info_id3v2['exthead']['flags']['restrictions'] = (bool) ($info_id3v2['exthead']['flag_raw'] & 0x1000);
220
221
                     if ($info_id3v2['exthead']['flags']['crc']) {
222
                         $info_id3v2['exthead']['flag_data']['crc'] = getid3_lib::BigEndian2Int(substr($frame_data, $extended_header_offset, 5), 1);
223
                         $extended_header_offset += 5;
224
                     }
225
                     if ($info_id3v2['exthead']['flags']['restrictions']) {
226
                         // %ppqrrstt
227
                         $restrictions_raw = getid3_lib::BigEndian2Int(substr($frame_data, $extended_header_offset, 1));
228
                         $extended_header_offset += 1;
229
                         $info_id3v2['exthead']['flags']['restrictions']['tagsize']  = ($restrictions_raw && 0xC0) >> 6; // p - Tag size restrictions
230
                         $info_id3v2['exthead']['flags']['restrictions']['textenc']  = ($restrictions_raw && 0x20) >> 5; // q - Text encoding restrictions
231
                         $info_id3v2['exthead']['flags']['restrictions']['textsize'] = ($restrictions_raw && 0x18) >> 3; // r - Text fields size restrictions
232
                         $info_id3v2['exthead']['flags']['restrictions']['imgenc']   = ($restrictions_raw && 0x04) >> 2; // s - Image encoding restrictions
233
                             $info_id3v2['exthead']['flags']['restrictions']['imgsize']  = ($restrictions_raw && 0x03) >> 0; // t - Image size restrictions
234
                     }
235
236
                 }
237
                 $frame_data_offset += $extended_header_offset;
238
                 $frame_data = substr($frame_data, $extended_header_offset);
239
             } // end extended header
240
 
241
            
242
            
243
            
244
            
245
            
246
            while (isset($frame_data) && (strlen($frame_data) > 0)) { // cycle through until no more frame data is left to parse
247
                if (strlen($frame_data) <= ($id3v2_major_version == 2 ? 6 : 10)) {
248
                    // insufficient room left in ID3v2 header for actual data - must be padding
249
                    $info_id3v2['padding']['start']  = $frame_data_offset;
250
                    $info_id3v2['padding']['length'] = strlen($frame_data);
251
                    $info_id3v2['padding']['valid']  = true;
252
                    for ($i = 0; $i < $info_id3v2['padding']['length']; $i++) {
253
                        if ($frame_data{$i} != "\x00") {
254
                            $info_id3v2['padding']['valid'] = false;
255
                            $info_id3v2['padding']['errorpos'] = $info_id3v2['padding']['start'] + $i;
256
                            $getid3->warning('Invalid ID3v2 padding found at offset '.$info_id3v2['padding']['errorpos'].' (the remaining '.($info_id3v2['padding']['length'] - $i).' bytes are considered invalid)');
257
                            break;
258
                        }
259
                    }
260
                    break; // skip rest of ID3v2 header
261
                }
262
                
263
                if ($id3v2_major_version == 2) {
264
                    // Frame ID  $xx xx xx (three characters)
265
                    // Size      $xx xx xx (24-bit integer)
266
                    // Flags     $xx xx
267
268
                    $frame_header = substr($frame_data, 0, 6); // take next 6 bytes for header
269
                    $frame_data    = substr($frame_data, 6);    // and leave the rest in $frame_data
270
                    $frame_name   = substr($frame_header, 0, 3);
271
                    $frame_size   = getid3_lib::BigEndian2Int(substr($frame_header, 3, 3));
272
                    $frame_flags  = 0; // not used for anything in ID3v2.2, just set to avoid E_NOTICEs
273
274
275
                } elseif ($id3v2_major_version > 2) {
276
277
                    // Frame ID  $xx xx xx xx (four characters)
278
                    // Size      $xx xx xx xx (32-bit integer in v2.3, 28-bit synchsafe in v2.4+)
279
                    // Flags     $xx xx
280
281
                    $frame_header = substr($frame_data, 0, 10); // take next 10 bytes for header
282
                    $frame_data    = substr($frame_data, 10);    // and leave the rest in $frame_data
283
284
                    $frame_name = substr($frame_header, 0, 4);
285
286
                    if ($id3v2_major_version == 3) {
287
                        $frame_size = getid3_lib::BigEndian2Int(substr($frame_header, 4, 4)); // 32-bit integer
288
289
                    } else { // ID3v2.4+
290
                        $frame_size = getid3_lib::BigEndianSyncSafe2Int(substr($frame_header, 4, 4)); // 32-bit synchsafe integer (28-bit value)
291
                    }
292
293
                    if ($frame_size < (strlen($frame_data) + 4)) {
294
                        $nextFrameID = substr($frame_data, $frame_size, 4);
295
                        if (getid3_id3v2::IsValidID3v2FrameName($nextFrameID, $id3v2_major_version)) {
0 ignored issues
show
Bug Best Practice introduced by
The expression getid3_id3v2::IsValidID3..., $id3v2_major_version) of type false|integer is loosely compared to true; this is ambiguous if the integer can be 0. You might want to explicitly use !== false instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
296
                            // next frame is OK
297
                        } elseif (($frame_name == "\x00".'MP3') || ($frame_name == "\x00\x00".'MP') || ($frame_name == ' MP3') || ($frame_name == 'MP3e')) {
298
                            // MP3ext known broken frames - "ok" for the purposes of this test
299
                        } elseif (($id3v2_major_version == 4) && (getid3_id3v2::IsValidID3v2FrameName(substr($frame_data, getid3_lib::BigEndian2Int(substr($frame_header, 4, 4)), 4), 3))) {
300
                            $getid3->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');
301
                            $id3v2_major_version = 3;
302
                            $frame_size = getid3_lib::BigEndian2Int(substr($frame_header, 4, 4)); // 32-bit integer
303
                        }
304
                    }
305
306
307
                    $frame_flags = getid3_lib::BigEndian2Int(substr($frame_header, 8, 2));
308
                }
309
310
                if ((($id3v2_major_version == 2) && ($frame_name == "\x00\x00\x00")) || ($frame_name == "\x00\x00\x00\x00")) {
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $frame_name does not seem to be defined for all execution paths leading up to this point.
Loading history...
311
                    // padding encountered
312
313
                    $info_id3v2['padding']['start']  = $frame_data_offset;
314
                    $info_id3v2['padding']['length'] = strlen($frame_header) + strlen($frame_data);
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $frame_header does not seem to be defined for all execution paths leading up to this point.
Loading history...
315
                    $info_id3v2['padding']['valid']  = true;
316
                    
317
                    $len = strlen($frame_data);
318
                    for ($i = 0; $i < $len; $i++) {
319
                        if ($frame_data{$i} != "\x00") {
320
                            $info_id3v2['padding']['valid'] = false;
321
                            $info_id3v2['padding']['errorpos'] = $info_id3v2['padding']['start'] + $i;
322
                            $getid3->warning('Invalid ID3v2 padding found at offset '.$info_id3v2['padding']['errorpos'].' (the remaining '.($info_id3v2['padding']['length'] - $i).' bytes are considered invalid)');
323
                            break;
324
                        }
325
                    }
326
                    break; // skip rest of ID3v2 header
327
                }
328
329
                if ($frame_name == 'COM ') {
330
                    $getid3->warning('error parsing "'.$frame_name.'" ('.$frame_data_offset.' bytes into the ID3v2.'.$id3v2_major_version.' tag). (ERROR: IsValidID3v2FrameName("'.str_replace("\x00", ' ', $frame_name).'", '.$id3v2_major_version.'))). [Note: this particular error has been known to happen with tags edited by iTunes (versions "X v2.0.3", "v3.0.1" are known-guilty, probably others too)]');
331
                    $frame_name = 'COMM';
332
                }
333
                if (($frame_size <= strlen($frame_data)) && (getid3_id3v2::IsValidID3v2FrameName($frame_name, $id3v2_major_version))) {
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $frame_size does not seem to be defined for all execution paths leading up to this point.
Loading history...
Bug Best Practice introduced by
The expression getid3_id3v2::IsValidID3..., $id3v2_major_version) of type false|integer is loosely compared to true; this is ambiguous if the integer can be 0. You might want to explicitly use !== false instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
334
335
                    unset($parsed_frame);
336
                    $parsed_frame['frame_name']      = $frame_name;
337
                    $parsed_frame['frame_flags_raw'] = $frame_flags;
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $frame_flags does not seem to be defined for all execution paths leading up to this point.
Loading history...
338
                    $parsed_frame['data']            = substr($frame_data, 0, $frame_size);
339
                    $parsed_frame['datalength']      = (int)($frame_size);
340
                    $parsed_frame['dataoffset']      = $frame_data_offset;
341
342
                    $this->ParseID3v2Frame($parsed_frame);
343
                    $info_id3v2[$frame_name][] = $parsed_frame;
344
345
                    $frame_data = substr($frame_data, $frame_size);
346
347
                } else { // invalid frame length or FrameID
348
349
                    if ($frame_size <= strlen($frame_data)) {
350
351
                        if (getid3_id3v2::IsValidID3v2FrameName(substr($frame_data, $frame_size, 4), $id3v2_major_version)) {
0 ignored issues
show
Bug Best Practice introduced by
The expression getid3_id3v2::IsValidID3..., $id3v2_major_version) of type false|integer is loosely compared to true; this is ambiguous if the integer can be 0. You might want to explicitly use !== false instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
352
353
                            // next frame is valid, just skip the current frame
354
                            $frame_data = substr($frame_data, $frame_size);
355
                            $getid3->warning('Next ID3v2 frame is valid, skipping current frame.');
356
357
                        } else {
358
359
                            // next frame is invalid too, abort processing
360
                            throw new getid3_exception('Next ID3v2 frame is also invalid, aborting processing.');
361
362
                        }
363
364
                    } elseif ($frame_size == strlen($frame_data)) {
365
366
                        // this is the last frame, just skip
367
                        $getid3->warning('This was the last ID3v2 frame.');
368
369
                    } else {
370
371
                        // next frame is invalid too, abort processing
372
                        $frame_data = null;
373
                        $getid3->warning('Invalid ID3v2 frame size, aborting.');
374
375
                    }
376
                    if (!getid3_id3v2::IsValidID3v2FrameName($frame_name, $id3v2_major_version)) {
0 ignored issues
show
Bug Best Practice introduced by
The expression getid3_id3v2::IsValidID3..., $id3v2_major_version) of type false|integer is loosely compared to false; this is ambiguous if the integer can be 0. You might want to explicitly use === false instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
377
378
                        switch ($frame_name) {
379
                            
380
                            case "\x00\x00".'MP':
381
                            case "\x00".'MP3':
382
                            case ' MP3':
383
                            case 'MP3e':
384
                            case "\x00".'MP':
385
                            case ' MP':
386
                            case 'MP3':
387
                                $getid3->warning('error parsing "'.$frame_name.'" ('.$frame_data_offset.' bytes into the ID3v2.'.$id3v2_major_version.' tag). (ERROR: !IsValidID3v2FrameName("'.str_replace("\x00", ' ', $frame_name).'", '.$id3v2_major_version.'))). [Note: this particular error has been known to happen with tags edited by "MP3ext (www.mutschler.de/mp3ext/)"]');
388
                                break;
389
390
                            default:
391
                                $getid3->warning('error parsing "'.$frame_name.'" ('.$frame_data_offset.' bytes into the ID3v2.'.$id3v2_major_version.' tag). (ERROR: !IsValidID3v2FrameName("'.str_replace("\x00", ' ', $frame_name).'", '.$id3v2_major_version.'))).');
392
                                break;
393
                        }
394
395
                    } elseif ($frame_size > strlen(@$frame_data)){
396
397
                        throw new getid3_exception('error parsing "'.$frame_name.'" ('.$frame_data_offset.' bytes into the ID3v2.'.$id3v2_major_version.' tag). (ERROR: $frame_size ('.$frame_size.') > strlen($frame_data) ('.strlen($frame_data).')).');
398
399
                    } else {
400
401
                        throw new getid3_exception('error parsing "'.$frame_name.'" ('.$frame_data_offset.' bytes into the ID3v2.'.$id3v2_major_version.' tag).');
402
403
                    }
404
405
                }
406
                $frame_data_offset += ($frame_size + ($id3v2_major_version == 2 ? 6 : 10));
407
408
            }
409
410
        }
411
412
413
        //    Footer
414
    
415
        //    The footer is a copy of the header, but with a different identifier.
416
        //        ID3v2 identifier           "3DI"
417
        //        ID3v2 version              $04 00
418
        //        ID3v2 flags                %abcd0000
419
        //        ID3v2 size             4 * %0xxxxxxx
420
421
        if (isset($info_id3v2_flags['isfooter']) && $info_id3v2_flags['isfooter']) {
422
            $footer = fread ($getid3->fp, 10);
423
            if (substr($footer, 0, 3) == '3DI') {
424
                $info_id3v2['footer'] = true;
425
                $info_id3v2['majorversion_footer'] = ord($footer{3});
426
                $info_id3v2['minorversion_footer'] = ord($footer{4});
427
            }
428
            if ($info_id3v2['majorversion_footer'] <= 4) {
429
                $id3_flags = ord($footer{5});
430
                $info_id3v2_flags['unsynch_footer']  = (bool)($id3_flags & 0x80);
431
                $info_id3v2_flags['extfoot_footer']  = (bool)($id3_flags & 0x40);
432
                $info_id3v2_flags['experim_footer']  = (bool)($id3_flags & 0x20);
433
                $info_id3v2_flags['isfooter_footer'] = (bool)($id3_flags & 0x10);
434
435
                $info_id3v2['footerlength'] = getid3_lib::BigEndianSyncSafe2Int(substr($footer, 6, 4));
436
            }
437
        } // end footer
438
439
        if (isset($info_id3v2['comments']['genre'])) {
440
            foreach ($info_id3v2['comments']['genre'] as $key => $value) {
441
                unset($info_id3v2['comments']['genre'][$key]);
442
                $info_id3v2['comments'] = getid3_id3v2::array_merge_noclobber($info_id3v2['comments'], getid3_id3v2::ParseID3v2GenreString($value));
443
            }
444
        }
445
446
        if (isset($info_id3v2['comments']['track'])) {
447
            foreach ($info_id3v2['comments']['track'] as $key => $value) {
448
                if (strstr($value, '/')) {
449
                    list($info_id3v2['comments']['track'][$key], $info_id3v2['comments']['totaltracks'][$key]) = explode('/', $info_id3v2['comments']['track'][$key]);
450
                }
451
            }
452
        }
453
        
454
        // Use year from recording time if year not set
455
        if (!isset($info_id3v2['comments']['year']) && ereg('^([0-9]{4})', @$info_id3v2['comments']['recording_time'][0], $matches)) {
0 ignored issues
show
Deprecated Code introduced by
The function ereg() has been deprecated: 5.3.0 Use preg_match() instead ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

455
        if (!isset($info_id3v2['comments']['year']) && /** @scrutinizer ignore-deprecated */ ereg('^([0-9]{4})', @$info_id3v2['comments']['recording_time'][0], $matches)) {

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

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

Using a closing tag in PHP files that only contain PHP code is not recommended as you might accidentally add whitespace after the closing tag which would then be output by PHP. This can cause severe problems, for example headers cannot be sent anymore.

A simple precaution is to leave off the closing tag as it is not required, and it also has no negative effects whatsoever.

Loading history...