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