Completed
Pull Request — master (#2)
by Stephen
09:27
created

getid3_id3v2::Analyze()   F

Complexity

Conditions 82
Paths > 20000

Size

Total Lines 473
Code Lines 235

Duplication

Lines 36
Ratio 7.61 %

Importance

Changes 0
Metric Value
cc 82
eloc 235
nc 429496.7295
nop 0
dl 36
loc 473
rs 2
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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