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