ILIAS  release_5-4 Revision v5.4.26-12-gabc799a52e6
getid3_id3v2 Class Reference
+ Inheritance diagram for getid3_id3v2:
+ Collaboration diagram for getid3_id3v2:

Public Member Functions

 Analyze ()
 
 ParseID3v2GenreString ($genrestring)
 
 ParseID3v2Frame (&$parsedFrame)
 
 DeUnsynchronise ($data)
 
 LookupExtendedHeaderRestrictionsTagSizeLimits ($index)
 
 LookupExtendedHeaderRestrictionsTextEncodings ($index)
 
 LookupExtendedHeaderRestrictionsTextFieldSize ($index)
 
 LookupExtendedHeaderRestrictionsImageEncoding ($index)
 
 LookupExtendedHeaderRestrictionsImageSizeSize ($index)
 
 LookupCurrencyUnits ($currencyid)
 
 LookupCurrencyCountry ($currencyid)
 
- Public Member Functions inherited from getid3_handler
 __construct (getID3 $getid3, $call_module=null)
 
 Analyze ()
 
 AnalyzeString ($string)
 
 setStringMode ($string)
 
 saveAttachment ($name, $offset, $length, $image_mime=null)
 

Static Public Member Functions

static LanguageLookup ($languagecode, $casesensitive=false)
 
static ETCOEventLookup ($index)
 
static SYTLContentTypeLookup ($index)
 
static APICPictureTypeLookup ($index, $returnarray=false)
 
static COMRReceivedAsLookup ($index)
 
static RVA2ChannelTypeLookup ($index)
 
static FrameNameLongLookup ($framename)
 
static FrameNameShortLookup ($framename)
 
static TextEncodingTerminatorLookup ($encoding)
 
static TextEncodingNameLookup ($encoding)
 
static IsValidID3v2FrameName ($framename, $id3v2majorversion)
 
static IsANumber ($numberstring, $allowdecimal=false, $allownegative=false)
 
static IsValidDateStampString ($datestamp)
 
static ID3v2HeaderLength ($majorversion)
 
static ID3v22iTunesBrokenFrameName ($frame_name)
 

Data Fields

 $StartingOffset = 0
 

Additional Inherited Members

- Protected Member Functions inherited from getid3_handler
 ftell ()
 
 fread ($bytes)
 
 fseek ($bytes, $whence=SEEK_SET)
 
 feof ()
 
 isDependencyFor ($module)
 
 error ($text)
 
 warning ($text)
 
 notice ($text)
 
- Protected Attributes inherited from getid3_handler
 $getid3
 
 $data_string_flag = false
 
 $data_string = ''
 
 $data_string_position = 0
 
 $data_string_length = 0
 

Detailed Description

Definition at line 19 of file module.tag.id3v2.php.

Member Function Documentation

◆ Analyze()

getid3_id3v2::Analyze ( )

Definition at line 23 of file module.tag.id3v2.php.

References $header, $i, $info, $key, $StartingOffset, getid3_lib\BigEndian2Int(), getid3_lib\CastAsInt(), DeUnsynchronise(), getid3_handler\error(), getid3_handler\fread(), getid3_handler\fseek(), ID3v2HeaderLength(), IsValidID3v2FrameName(), LookupExtendedHeaderRestrictionsImageEncoding(), LookupExtendedHeaderRestrictionsImageSizeSize(), LookupExtendedHeaderRestrictionsTagSizeLimits(), LookupExtendedHeaderRestrictionsTextEncodings(), LookupExtendedHeaderRestrictionsTextFieldSize(), ParseID3v2Frame(), ParseID3v2GenreString(), and getid3_handler\warning().

23  {
24  $info = &$this->getid3->info;
25 
26  // Overall tag structure:
27  // +-----------------------------+
28  // | Header (10 bytes) |
29  // +-----------------------------+
30  // | Extended Header |
31  // | (variable length, OPTIONAL) |
32  // +-----------------------------+
33  // | Frames (variable length) |
34  // +-----------------------------+
35  // | Padding |
36  // | (variable length, OPTIONAL) |
37  // +-----------------------------+
38  // | Footer (10 bytes, OPTIONAL) |
39  // +-----------------------------+
40 
41  // Header
42  // ID3v2/file identifier "ID3"
43  // ID3v2 version $04 00
44  // ID3v2 flags (%ab000000 in v2.2, %abc00000 in v2.3, %abcd0000 in v2.4.x)
45  // ID3v2 size 4 * %0xxxxxxx
46 
47 
48  // shortcuts
49  $info['id3v2']['header'] = true;
50  $thisfile_id3v2 = &$info['id3v2'];
51  $thisfile_id3v2['flags'] = array();
52  $thisfile_id3v2_flags = &$thisfile_id3v2['flags'];
53 
54 
55  $this->fseek($this->StartingOffset);
56  $header = $this->fread(10);
57  if (substr($header, 0, 3) == 'ID3' && strlen($header) == 10) {
58 
59  $thisfile_id3v2['majorversion'] = ord($header{3});
60  $thisfile_id3v2['minorversion'] = ord($header{4});
61 
62  // shortcut
63  $id3v2_majorversion = &$thisfile_id3v2['majorversion'];
64 
65  } else {
66 
67  unset($info['id3v2']);
68  return false;
69 
70  }
71 
72  if ($id3v2_majorversion > 4) { // this script probably won't correctly parse ID3v2.5.x and above (if it ever exists)
73 
74  $this->error('this script only parses up to ID3v2.4.x - this tag is ID3v2.'.$id3v2_majorversion.'.'.$thisfile_id3v2['minorversion']);
75  return false;
76 
77  }
78 
79  $id3_flags = ord($header{5});
80  switch ($id3v2_majorversion) {
81  case 2:
82  // %ab000000 in v2.2
83  $thisfile_id3v2_flags['unsynch'] = (bool) ($id3_flags & 0x80); // a - Unsynchronisation
84  $thisfile_id3v2_flags['compression'] = (bool) ($id3_flags & 0x40); // b - Compression
85  break;
86 
87  case 3:
88  // %abc00000 in v2.3
89  $thisfile_id3v2_flags['unsynch'] = (bool) ($id3_flags & 0x80); // a - Unsynchronisation
90  $thisfile_id3v2_flags['exthead'] = (bool) ($id3_flags & 0x40); // b - Extended header
91  $thisfile_id3v2_flags['experim'] = (bool) ($id3_flags & 0x20); // c - Experimental indicator
92  break;
93 
94  case 4:
95  // %abcd0000 in v2.4
96  $thisfile_id3v2_flags['unsynch'] = (bool) ($id3_flags & 0x80); // a - Unsynchronisation
97  $thisfile_id3v2_flags['exthead'] = (bool) ($id3_flags & 0x40); // b - Extended header
98  $thisfile_id3v2_flags['experim'] = (bool) ($id3_flags & 0x20); // c - Experimental indicator
99  $thisfile_id3v2_flags['isfooter'] = (bool) ($id3_flags & 0x10); // d - Footer present
100  break;
101  }
102 
103  $thisfile_id3v2['headerlength'] = getid3_lib::BigEndian2Int(substr($header, 6, 4), 1) + 10; // length of ID3v2 tag in 10-byte header doesn't include 10-byte header length
104 
105  $thisfile_id3v2['tag_offset_start'] = $this->StartingOffset;
106  $thisfile_id3v2['tag_offset_end'] = $thisfile_id3v2['tag_offset_start'] + $thisfile_id3v2['headerlength'];
107 
108 
109 
110  // create 'encoding' key - used by getid3::HandleAllTags()
111  // in ID3v2 every field can have it's own encoding type
112  // so force everything to UTF-8 so it can be handled consistantly
113  $thisfile_id3v2['encoding'] = 'UTF-8';
114 
115 
116  // Frames
117 
118  // All ID3v2 frames consists of one frame header followed by one or more
119  // fields containing the actual information. The header is always 10
120  // bytes and laid out as follows:
121  //
122  // Frame ID $xx xx xx xx (four characters)
123  // Size 4 * %0xxxxxxx
124  // Flags $xx xx
125 
126  $sizeofframes = $thisfile_id3v2['headerlength'] - 10; // not including 10-byte initial header
127  if (!empty($thisfile_id3v2['exthead']['length'])) {
128  $sizeofframes -= ($thisfile_id3v2['exthead']['length'] + 4);
129  }
130  if (!empty($thisfile_id3v2_flags['isfooter'])) {
131  $sizeofframes -= 10; // footer takes last 10 bytes of ID3v2 header, after frame data, before audio
132  }
133  if ($sizeofframes > 0) {
134 
135  $framedata = $this->fread($sizeofframes); // read all frames from file into $framedata variable
136 
137  // if entire frame data is unsynched, de-unsynch it now (ID3v2.3.x)
138  if (!empty($thisfile_id3v2_flags['unsynch']) && ($id3v2_majorversion <= 3)) {
139  $framedata = $this->DeUnsynchronise($framedata);
140  }
141  // [in ID3v2.4.0] Unsynchronisation [S:6.1] is done on frame level, instead
142  // of on tag level, making it easier to skip frames, increasing the streamability
143  // of the tag. The unsynchronisation flag in the header [S:3.1] indicates that
144  // there exists an unsynchronised frame, while the new unsynchronisation flag in
145  // the frame header [S:4.1.2] indicates unsynchronisation.
146 
147 
148  //$framedataoffset = 10 + ($thisfile_id3v2['exthead']['length'] ? $thisfile_id3v2['exthead']['length'] + 4 : 0); // how many bytes into the stream - start from after the 10-byte header (and extended header length+4, if present)
149  $framedataoffset = 10; // how many bytes into the stream - start from after the 10-byte header
150 
151 
152  // Extended Header
153  if (!empty($thisfile_id3v2_flags['exthead'])) {
154  $extended_header_offset = 0;
155 
156  if ($id3v2_majorversion == 3) {
157 
158  // v2.3 definition:
159  //Extended header size $xx xx xx xx // 32-bit integer
160  //Extended Flags $xx xx
161  // %x0000000 %00000000 // v2.3
162  // x - CRC data present
163  //Size of padding $xx xx xx xx
164 
165  $thisfile_id3v2['exthead']['length'] = getid3_lib::BigEndian2Int(substr($framedata, $extended_header_offset, 4), 0);
166  $extended_header_offset += 4;
167 
168  $thisfile_id3v2['exthead']['flag_bytes'] = 2;
169  $thisfile_id3v2['exthead']['flag_raw'] = getid3_lib::BigEndian2Int(substr($framedata, $extended_header_offset, $thisfile_id3v2['exthead']['flag_bytes']));
170  $extended_header_offset += $thisfile_id3v2['exthead']['flag_bytes'];
171 
172  $thisfile_id3v2['exthead']['flags']['crc'] = (bool) ($thisfile_id3v2['exthead']['flag_raw'] & 0x8000);
173 
174  $thisfile_id3v2['exthead']['padding_size'] = getid3_lib::BigEndian2Int(substr($framedata, $extended_header_offset, 4));
175  $extended_header_offset += 4;
176 
177  if ($thisfile_id3v2['exthead']['flags']['crc']) {
178  $thisfile_id3v2['exthead']['flag_data']['crc'] = getid3_lib::BigEndian2Int(substr($framedata, $extended_header_offset, 4));
179  $extended_header_offset += 4;
180  }
181  $extended_header_offset += $thisfile_id3v2['exthead']['padding_size'];
182 
183  } elseif ($id3v2_majorversion == 4) {
184 
185  // v2.4 definition:
186  //Extended header size 4 * %0xxxxxxx // 28-bit synchsafe integer
187  //Number of flag bytes $01
188  //Extended Flags $xx
189  // %0bcd0000 // v2.4
190  // b - Tag is an update
191  // Flag data length $00
192  // c - CRC data present
193  // Flag data length $05
194  // Total frame CRC 5 * %0xxxxxxx
195  // d - Tag restrictions
196  // Flag data length $01
197 
198  $thisfile_id3v2['exthead']['length'] = getid3_lib::BigEndian2Int(substr($framedata, $extended_header_offset, 4), true);
199  $extended_header_offset += 4;
200 
201  $thisfile_id3v2['exthead']['flag_bytes'] = getid3_lib::BigEndian2Int(substr($framedata, $extended_header_offset, 1)); // should always be 1
202  $extended_header_offset += 1;
203 
204  $thisfile_id3v2['exthead']['flag_raw'] = getid3_lib::BigEndian2Int(substr($framedata, $extended_header_offset, $thisfile_id3v2['exthead']['flag_bytes']));
205  $extended_header_offset += $thisfile_id3v2['exthead']['flag_bytes'];
206 
207  $thisfile_id3v2['exthead']['flags']['update'] = (bool) ($thisfile_id3v2['exthead']['flag_raw'] & 0x40);
208  $thisfile_id3v2['exthead']['flags']['crc'] = (bool) ($thisfile_id3v2['exthead']['flag_raw'] & 0x20);
209  $thisfile_id3v2['exthead']['flags']['restrictions'] = (bool) ($thisfile_id3v2['exthead']['flag_raw'] & 0x10);
210 
211  if ($thisfile_id3v2['exthead']['flags']['update']) {
212  $ext_header_chunk_length = getid3_lib::BigEndian2Int(substr($framedata, $extended_header_offset, 1)); // should be 0
213  $extended_header_offset += 1;
214  }
215 
216  if ($thisfile_id3v2['exthead']['flags']['crc']) {
217  $ext_header_chunk_length = getid3_lib::BigEndian2Int(substr($framedata, $extended_header_offset, 1)); // should be 5
218  $extended_header_offset += 1;
219  $thisfile_id3v2['exthead']['flag_data']['crc'] = getid3_lib::BigEndian2Int(substr($framedata, $extended_header_offset, $ext_header_chunk_length), true, false);
220  $extended_header_offset += $ext_header_chunk_length;
221  }
222 
223  if ($thisfile_id3v2['exthead']['flags']['restrictions']) {
224  $ext_header_chunk_length = getid3_lib::BigEndian2Int(substr($framedata, $extended_header_offset, 1)); // should be 1
225  $extended_header_offset += 1;
226 
227  // %ppqrrstt
228  $restrictions_raw = getid3_lib::BigEndian2Int(substr($framedata, $extended_header_offset, 1));
229  $extended_header_offset += 1;
230  $thisfile_id3v2['exthead']['flags']['restrictions']['tagsize'] = ($restrictions_raw & 0xC0) >> 6; // p - Tag size restrictions
231  $thisfile_id3v2['exthead']['flags']['restrictions']['textenc'] = ($restrictions_raw & 0x20) >> 5; // q - Text encoding restrictions
232  $thisfile_id3v2['exthead']['flags']['restrictions']['textsize'] = ($restrictions_raw & 0x18) >> 3; // r - Text fields size restrictions
233  $thisfile_id3v2['exthead']['flags']['restrictions']['imgenc'] = ($restrictions_raw & 0x04) >> 2; // s - Image encoding restrictions
234  $thisfile_id3v2['exthead']['flags']['restrictions']['imgsize'] = ($restrictions_raw & 0x03) >> 0; // t - Image size restrictions
235 
236  $thisfile_id3v2['exthead']['flags']['restrictions_text']['tagsize'] = $this->LookupExtendedHeaderRestrictionsTagSizeLimits($thisfile_id3v2['exthead']['flags']['restrictions']['tagsize']);
237  $thisfile_id3v2['exthead']['flags']['restrictions_text']['textenc'] = $this->LookupExtendedHeaderRestrictionsTextEncodings($thisfile_id3v2['exthead']['flags']['restrictions']['textenc']);
238  $thisfile_id3v2['exthead']['flags']['restrictions_text']['textsize'] = $this->LookupExtendedHeaderRestrictionsTextFieldSize($thisfile_id3v2['exthead']['flags']['restrictions']['textsize']);
239  $thisfile_id3v2['exthead']['flags']['restrictions_text']['imgenc'] = $this->LookupExtendedHeaderRestrictionsImageEncoding($thisfile_id3v2['exthead']['flags']['restrictions']['imgenc']);
240  $thisfile_id3v2['exthead']['flags']['restrictions_text']['imgsize'] = $this->LookupExtendedHeaderRestrictionsImageSizeSize($thisfile_id3v2['exthead']['flags']['restrictions']['imgsize']);
241  }
242 
243  if ($thisfile_id3v2['exthead']['length'] != $extended_header_offset) {
244  $this->warning('ID3v2.4 extended header length mismatch (expecting '.intval($thisfile_id3v2['exthead']['length']).', found '.intval($extended_header_offset).')');
245  }
246  }
247 
248  $framedataoffset += $extended_header_offset;
249  $framedata = substr($framedata, $extended_header_offset);
250  } // end extended header
251 
252 
253  while (isset($framedata) && (strlen($framedata) > 0)) { // cycle through until no more frame data is left to parse
254  if (strlen($framedata) <= $this->ID3v2HeaderLength($id3v2_majorversion)) {
255  // insufficient room left in ID3v2 header for actual data - must be padding
256  $thisfile_id3v2['padding']['start'] = $framedataoffset;
257  $thisfile_id3v2['padding']['length'] = strlen($framedata);
258  $thisfile_id3v2['padding']['valid'] = true;
259  for ($i = 0; $i < $thisfile_id3v2['padding']['length']; $i++) {
260  if ($framedata{$i} != "\x00") {
261  $thisfile_id3v2['padding']['valid'] = false;
262  $thisfile_id3v2['padding']['errorpos'] = $thisfile_id3v2['padding']['start'] + $i;
263  $this->warning('Invalid ID3v2 padding found at offset '.$thisfile_id3v2['padding']['errorpos'].' (the remaining '.($thisfile_id3v2['padding']['length'] - $i).' bytes are considered invalid)');
264  break;
265  }
266  }
267  break; // skip rest of ID3v2 header
268  }
269  if ($id3v2_majorversion == 2) {
270  // Frame ID $xx xx xx (three characters)
271  // Size $xx xx xx (24-bit integer)
272  // Flags $xx xx
273 
274  $frame_header = substr($framedata, 0, 6); // take next 6 bytes for header
275  $framedata = substr($framedata, 6); // and leave the rest in $framedata
276  $frame_name = substr($frame_header, 0, 3);
277  $frame_size = getid3_lib::BigEndian2Int(substr($frame_header, 3, 3), 0);
278  $frame_flags = 0; // not used for anything in ID3v2.2, just set to avoid E_NOTICEs
279 
280  } elseif ($id3v2_majorversion > 2) {
281 
282  // Frame ID $xx xx xx xx (four characters)
283  // Size $xx xx xx xx (32-bit integer in v2.3, 28-bit synchsafe in v2.4+)
284  // Flags $xx xx
285 
286  $frame_header = substr($framedata, 0, 10); // take next 10 bytes for header
287  $framedata = substr($framedata, 10); // and leave the rest in $framedata
288 
289  $frame_name = substr($frame_header, 0, 4);
290  if ($id3v2_majorversion == 3) {
291  $frame_size = getid3_lib::BigEndian2Int(substr($frame_header, 4, 4), 0); // 32-bit integer
292  } else { // ID3v2.4+
293  $frame_size = getid3_lib::BigEndian2Int(substr($frame_header, 4, 4), 1); // 32-bit synchsafe integer (28-bit value)
294  }
295 
296  if ($frame_size < (strlen($framedata) + 4)) {
297  $nextFrameID = substr($framedata, $frame_size, 4);
298  if ($this->IsValidID3v2FrameName($nextFrameID, $id3v2_majorversion)) {
299  // next frame is OK
300  } elseif (($frame_name == "\x00".'MP3') || ($frame_name == "\x00\x00".'MP') || ($frame_name == ' MP3') || ($frame_name == 'MP3e')) {
301  // MP3ext known broken frames - "ok" for the purposes of this test
302  } elseif (($id3v2_majorversion == 4) && ($this->IsValidID3v2FrameName(substr($framedata, getid3_lib::BigEndian2Int(substr($frame_header, 4, 4), 0), 4), 3))) {
303  $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');
304  $id3v2_majorversion = 3;
305  $frame_size = getid3_lib::BigEndian2Int(substr($frame_header, 4, 4), 0); // 32-bit integer
306  }
307  }
308 
309 
310  $frame_flags = getid3_lib::BigEndian2Int(substr($frame_header, 8, 2));
311  }
312 
313  if ((($id3v2_majorversion == 2) && ($frame_name == "\x00\x00\x00")) || ($frame_name == "\x00\x00\x00\x00")) {
314  // padding encountered
315 
316  $thisfile_id3v2['padding']['start'] = $framedataoffset;
317  $thisfile_id3v2['padding']['length'] = strlen($frame_header) + strlen($framedata);
318  $thisfile_id3v2['padding']['valid'] = true;
319 
320  $len = strlen($framedata);
321  for ($i = 0; $i < $len; $i++) {
322  if ($framedata{$i} != "\x00") {
323  $thisfile_id3v2['padding']['valid'] = false;
324  $thisfile_id3v2['padding']['errorpos'] = $thisfile_id3v2['padding']['start'] + $i;
325  $this->warning('Invalid ID3v2 padding found at offset '.$thisfile_id3v2['padding']['errorpos'].' (the remaining '.($thisfile_id3v2['padding']['length'] - $i).' bytes are considered invalid)');
326  break;
327  }
328  }
329  break; // skip rest of ID3v2 header
330  }
331 
332  if ($iTunesBrokenFrameNameFixed = self::ID3v22iTunesBrokenFrameName($frame_name)) {
333  $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.');
334  $frame_name = $iTunesBrokenFrameNameFixed;
335  }
336  if (($frame_size <= strlen($framedata)) && ($this->IsValidID3v2FrameName($frame_name, $id3v2_majorversion))) {
337 
338  unset($parsedFrame);
339  $parsedFrame['frame_name'] = $frame_name;
340  $parsedFrame['frame_flags_raw'] = $frame_flags;
341  $parsedFrame['data'] = substr($framedata, 0, $frame_size);
342  $parsedFrame['datalength'] = getid3_lib::CastAsInt($frame_size);
343  $parsedFrame['dataoffset'] = $framedataoffset;
344 
345  $this->ParseID3v2Frame($parsedFrame);
346  $thisfile_id3v2[$frame_name][] = $parsedFrame;
347 
348  $framedata = substr($framedata, $frame_size);
349 
350  } else { // invalid frame length or FrameID
351 
352  if ($frame_size <= strlen($framedata)) {
353 
354  if ($this->IsValidID3v2FrameName(substr($framedata, $frame_size, 4), $id3v2_majorversion)) {
355 
356  // next frame is valid, just skip the current frame
357  $framedata = substr($framedata, $frame_size);
358  $this->warning('Next ID3v2 frame is valid, skipping current frame.');
359 
360  } else {
361 
362  // next frame is invalid too, abort processing
363  //unset($framedata);
364  $framedata = null;
365  $this->error('Next ID3v2 frame is also invalid, aborting processing.');
366 
367  }
368 
369  } elseif ($frame_size == strlen($framedata)) {
370 
371  // this is the last frame, just skip
372  $this->warning('This was the last ID3v2 frame.');
373 
374  } else {
375 
376  // next frame is invalid too, abort processing
377  //unset($framedata);
378  $framedata = null;
379  $this->warning('Invalid ID3v2 frame size, aborting.');
380 
381  }
382  if (!$this->IsValidID3v2FrameName($frame_name, $id3v2_majorversion)) {
383 
384  switch ($frame_name) {
385  case "\x00\x00".'MP':
386  case "\x00".'MP3':
387  case ' MP3':
388  case 'MP3e':
389  case "\x00".'MP':
390  case ' MP':
391  case 'MP3':
392  $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/)"]');
393  break;
394 
395  default:
396  $this->warning('error parsing "'.$frame_name.'" ('.$framedataoffset.' bytes into the ID3v2.'.$id3v2_majorversion.' tag). (ERROR: !IsValidID3v2FrameName("'.str_replace("\x00", ' ', $frame_name).'", '.$id3v2_majorversion.'))).');
397  break;
398  }
399 
400  } elseif (!isset($framedata) || ($frame_size > strlen($framedata))) {
401 
402  $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').')).');
403 
404  } else {
405 
406  $this->error('error parsing "'.$frame_name.'" ('.$framedataoffset.' bytes into the ID3v2.'.$id3v2_majorversion.' tag).');
407 
408  }
409 
410  }
411  $framedataoffset += ($frame_size + $this->ID3v2HeaderLength($id3v2_majorversion));
412 
413  }
414 
415  }
416 
417 
418  // Footer
419 
420  // The footer is a copy of the header, but with a different identifier.
421  // ID3v2 identifier "3DI"
422  // ID3v2 version $04 00
423  // ID3v2 flags %abcd0000
424  // ID3v2 size 4 * %0xxxxxxx
425 
426  if (isset($thisfile_id3v2_flags['isfooter']) && $thisfile_id3v2_flags['isfooter']) {
427  $footer = $this->fread(10);
428  if (substr($footer, 0, 3) == '3DI') {
429  $thisfile_id3v2['footer'] = true;
430  $thisfile_id3v2['majorversion_footer'] = ord($footer{3});
431  $thisfile_id3v2['minorversion_footer'] = ord($footer{4});
432  }
433  if ($thisfile_id3v2['majorversion_footer'] <= 4) {
434  $id3_flags = ord(substr($footer{5}));
435  $thisfile_id3v2_flags['unsynch_footer'] = (bool) ($id3_flags & 0x80);
436  $thisfile_id3v2_flags['extfoot_footer'] = (bool) ($id3_flags & 0x40);
437  $thisfile_id3v2_flags['experim_footer'] = (bool) ($id3_flags & 0x20);
438  $thisfile_id3v2_flags['isfooter_footer'] = (bool) ($id3_flags & 0x10);
439 
440  $thisfile_id3v2['footerlength'] = getid3_lib::BigEndian2Int(substr($footer, 6, 4), 1);
441  }
442  } // end footer
443 
444  if (isset($thisfile_id3v2['comments']['genre'])) {
445  $genres = array();
446  foreach ($thisfile_id3v2['comments']['genre'] as $key => $value) {
447  foreach ($this->ParseID3v2GenreString($value) as $genre) {
448  $genres[] = $genre;
449  }
450  }
451  $thisfile_id3v2['comments']['genre'] = array_unique($genres);
452  unset($key, $value, $genres, $genre);
453  }
454 
455  if (isset($thisfile_id3v2['comments']['track'])) {
456  foreach ($thisfile_id3v2['comments']['track'] as $key => $value) {
457  if (strstr($value, '/')) {
458  list($thisfile_id3v2['comments']['tracknum'][$key], $thisfile_id3v2['comments']['totaltracks'][$key]) = explode('/', $thisfile_id3v2['comments']['track'][$key]);
459  }
460  }
461  }
462 
463  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)) {
464  $thisfile_id3v2['comments']['year'] = array($matches[1]);
465  }
466 
467 
468  if (!empty($thisfile_id3v2['TXXX'])) {
469  // MediaMonkey does this, maybe others: write a blank RGAD frame, but put replay-gain adjustment values in TXXX frames
470  foreach ($thisfile_id3v2['TXXX'] as $txxx_array) {
471  switch ($txxx_array['description']) {
472  case 'replaygain_track_gain':
473  if (empty($info['replay_gain']['track']['adjustment']) && !empty($txxx_array['data'])) {
474  $info['replay_gain']['track']['adjustment'] = floatval(trim(str_replace('dB', '', $txxx_array['data'])));
475  }
476  break;
477  case 'replaygain_track_peak':
478  if (empty($info['replay_gain']['track']['peak']) && !empty($txxx_array['data'])) {
479  $info['replay_gain']['track']['peak'] = floatval($txxx_array['data']);
480  }
481  break;
482  case 'replaygain_album_gain':
483  if (empty($info['replay_gain']['album']['adjustment']) && !empty($txxx_array['data'])) {
484  $info['replay_gain']['album']['adjustment'] = floatval(trim(str_replace('dB', '', $txxx_array['data'])));
485  }
486  break;
487  }
488  }
489  }
490 
491 
492  // Set avdataoffset
493  $info['avdataoffset'] = $thisfile_id3v2['headerlength'];
494  if (isset($thisfile_id3v2['footer'])) {
495  $info['avdataoffset'] += 10;
496  }
497 
498  return true;
499  }
ParseID3v2GenreString($genrestring)
LookupExtendedHeaderRestrictionsTextEncodings($index)
error($text)
Definition: getid3.php:1752
LookupExtendedHeaderRestrictionsTagSizeLimits($index)
warning($text)
Definition: getid3.php:1758
static ID3v2HeaderLength($majorversion)
LookupExtendedHeaderRestrictionsTextFieldSize($index)
static CastAsInt($floatnum)
Definition: getid3.lib.php:65
static IsValidID3v2FrameName($framename, $id3v2majorversion)
ParseID3v2Frame(&$parsedFrame)
fread($bytes)
Definition: getid3.php:1683
LookupExtendedHeaderRestrictionsImageSizeSize($index)
$i
Definition: disco.tpl.php:19
fseek($bytes, $whence=SEEK_SET)
Definition: getid3.php:1711
$info
Definition: index.php:5
$key
Definition: croninfo.php:18
static BigEndian2Int($byteword, $synchsafe=false, $signed=false)
Definition: getid3.lib.php:263
LookupExtendedHeaderRestrictionsImageEncoding($index)
+ Here is the call graph for this function:

◆ APICPictureTypeLookup()

static getid3_id3v2::APICPictureTypeLookup (   $index,
  $returnarray = false 
)
static

Definition at line 3144 of file module.tag.id3v2.php.

References $index.

Referenced by ParseID3v2Frame().

3144  {
3145  static $APICPictureTypeLookup = array(
3146  0x00 => 'Other',
3147  0x01 => '32x32 pixels \'file icon\' (PNG only)',
3148  0x02 => 'Other file icon',
3149  0x03 => 'Cover (front)',
3150  0x04 => 'Cover (back)',
3151  0x05 => 'Leaflet page',
3152  0x06 => 'Media (e.g. label side of CD)',
3153  0x07 => 'Lead artist/lead performer/soloist',
3154  0x08 => 'Artist/performer',
3155  0x09 => 'Conductor',
3156  0x0A => 'Band/Orchestra',
3157  0x0B => 'Composer',
3158  0x0C => 'Lyricist/text writer',
3159  0x0D => 'Recording Location',
3160  0x0E => 'During recording',
3161  0x0F => 'During performance',
3162  0x10 => 'Movie/video screen capture',
3163  0x11 => 'A bright coloured fish',
3164  0x12 => 'Illustration',
3165  0x13 => 'Band/artist logotype',
3166  0x14 => 'Publisher/Studio logotype'
3167  );
3168  if ($returnarray) {
3169  return $APICPictureTypeLookup;
3170  }
3171  return (isset($APICPictureTypeLookup[$index]) ? $APICPictureTypeLookup[$index] : '');
3172  }
$index
Definition: metadata.php:60
+ Here is the caller graph for this function:

◆ COMRReceivedAsLookup()

static getid3_id3v2::COMRReceivedAsLookup (   $index)
static

Definition at line 3174 of file module.tag.id3v2.php.

References $index.

Referenced by ParseID3v2Frame().

3174  {
3175  static $COMRReceivedAsLookup = array(
3176  0x00 => 'Other',
3177  0x01 => 'Standard CD album with other songs',
3178  0x02 => 'Compressed audio on CD',
3179  0x03 => 'File over the Internet',
3180  0x04 => 'Stream over the Internet',
3181  0x05 => 'As note sheets',
3182  0x06 => 'As note sheets in a book with other sheets',
3183  0x07 => 'Music on other media',
3184  0x08 => 'Non-musical merchandise'
3185  );
3186 
3187  return (isset($COMRReceivedAsLookup[$index]) ? $COMRReceivedAsLookup[$index] : '');
3188  }
$index
Definition: metadata.php:60
+ Here is the caller graph for this function:

◆ DeUnsynchronise()

getid3_id3v2::DeUnsynchronise (   $data)

Definition at line 2185 of file module.tag.id3v2.php.

References $data.

Referenced by Analyze(), and ParseID3v2Frame().

2185  {
2186  return str_replace("\xFF\x00", "\xFF", $data);
2187  }
$data
Definition: bench.php:6
+ Here is the caller graph for this function:

◆ ETCOEventLookup()

static getid3_id3v2::ETCOEventLookup (   $index)
static

Definition at line 3085 of file module.tag.id3v2.php.

References $index.

Referenced by ParseID3v2Frame().

3085  {
3086  if (($index >= 0x17) && ($index <= 0xDF)) {
3087  return 'reserved for future use';
3088  }
3089  if (($index >= 0xE0) && ($index <= 0xEF)) {
3090  return 'not predefined synch 0-F';
3091  }
3092  if (($index >= 0xF0) && ($index <= 0xFC)) {
3093  return 'reserved for future use';
3094  }
3095 
3096  static $EventLookup = array(
3097  0x00 => 'padding (has no meaning)',
3098  0x01 => 'end of initial silence',
3099  0x02 => 'intro start',
3100  0x03 => 'main part start',
3101  0x04 => 'outro start',
3102  0x05 => 'outro end',
3103  0x06 => 'verse start',
3104  0x07 => 'refrain start',
3105  0x08 => 'interlude start',
3106  0x09 => 'theme start',
3107  0x0A => 'variation start',
3108  0x0B => 'key change',
3109  0x0C => 'time change',
3110  0x0D => 'momentary unwanted noise (Snap, Crackle & Pop)',
3111  0x0E => 'sustained noise',
3112  0x0F => 'sustained noise end',
3113  0x10 => 'intro end',
3114  0x11 => 'main part end',
3115  0x12 => 'verse end',
3116  0x13 => 'refrain end',
3117  0x14 => 'theme end',
3118  0x15 => 'profanity',
3119  0x16 => 'profanity end',
3120  0xFD => 'audio end (start of silence)',
3121  0xFE => 'audio file ends',
3122  0xFF => 'one more byte of events follows'
3123  );
3124 
3125  return (isset($EventLookup[$index]) ? $EventLookup[$index] : '');
3126  }
$index
Definition: metadata.php:60
+ Here is the caller graph for this function:

◆ FrameNameLongLookup()

static getid3_id3v2::FrameNameLongLookup (   $framename)
static

This is not a comment!

AENC        Audio encryption
APIC        Attached picture
ASPI        Audio seek point index
BUF Recommended buffer size
CNT Play counter
COM Comments
COMM        Comments
COMR        Commercial frame
CRA Audio encryption
CRM Encrypted meta frame
ENCR        Encryption method registration
EQU Equalisation
EQU2        Equalisation (2)
EQUA        Equalisation
ETC Event timing codes
ETCO        Event timing codes
GEO General encapsulated object
GEOB        General encapsulated object
GRID        Group identification registration
IPL Involved people list
IPLS        Involved people list
LINK        Linked information
LNK Linked information
MCDI        Music CD identifier
MCI Music CD Identifier
MLL MPEG location lookup table
MLLT        MPEG location lookup table
OWNE        Ownership frame
PCNT        Play counter
PIC Attached picture
POP Popularimeter
POPM        Popularimeter
POSS        Position synchronisation frame
PRIV        Private frame
RBUF        Recommended buffer size
REV Reverb
RVA Relative volume adjustment
RVA2        Relative volume adjustment (2)
RVAD        Relative volume adjustment
RVRB        Reverb
SEEK        Seek frame
SIGN        Signature frame
SLT Synchronised lyric/text
STC Synced tempo codes
SYLT        Synchronised lyric/text
SYTC        Synchronised tempo codes
TAL Album/Movie/Show title
TALB        Album/Movie/Show title
TBP BPM (Beats Per Minute)
TBPM        BPM (beats per minute)
TCM Composer
TCMP        Part of a compilation
TCO Content type
TCOM        Composer
TCON        Content type
TCOP        Copyright message
TCP Part of a compilation
TCR Copyright message
TDA Date
TDAT        Date
TDEN        Encoding time
TDLY        Playlist delay
TDOR        Original release time
TDRC        Recording time
TDRL        Release time
TDTG        Tagging time
TDY Playlist delay
TEN Encoded by
TENC        Encoded by
TEXT        Lyricist/Text writer
TFLT        File type
TFT File type
TIM Time
TIME        Time
TIPL        Involved people list
TIT1        Content group description
TIT2        Title/songname/content description
TIT3        Subtitle/Description refinement
TKE Initial key
TKEY        Initial key
TLA Language(s)
TLAN        Language(s)
TLE Length
TLEN        Length
TMCL        Musician credits list
TMED        Media type
TMOO        Mood
TMT Media type
TOA Original artist(s)/performer(s)
TOAL        Original album/movie/show title
TOF Original filename
TOFN        Original filename
TOL Original Lyricist(s)/text writer(s)
TOLY        Original lyricist(s)/text writer(s)
TOPE        Original artist(s)/performer(s)
TOR Original release year
TORY        Original release year
TOT Original album/Movie/Show title
TOWN        File owner/licensee
TP1 Lead artist(s)/Lead performer(s)/Soloist(s)/Performing group
TP2 Band/Orchestra/Accompaniment
TP3 Conductor/Performer refinement
TP4 Interpreted, remixed, or otherwise modified by
TPA Part of a set
TPB Publisher
TPE1        Lead performer(s)/Soloist(s)
TPE2        Band/orchestra/accompaniment
TPE3        Conductor/performer refinement
TPE4        Interpreted, remixed, or otherwise modified by
TPOS        Part of a set
TPRO        Produced notice
TPUB        Publisher
TRC ISRC (International Standard Recording Code)
TRCK        Track number/Position in set
TRD Recording dates
TRDA        Recording dates
TRK Track number/Position in set
TRSN        Internet radio station name
TRSO        Internet radio station owner
TS2 Album-Artist sort order
TSA Album sort order
TSC Composer sort order
TSI Size
TSIZ        Size
TSO2        Album-Artist sort order
TSOA        Album sort order
TSOC        Composer sort order
TSOP        Performer sort order
TSOT        Title sort order
TSP Performer sort order
TSRC        ISRC (international standard recording code)
TSS Software/hardware and settings used for encoding
TSSE        Software/Hardware and settings used for encoding
TSST        Set subtitle
TST Title sort order
TT1 Content group description
TT2 Title/Songname/Content description
TT3 Subtitle/Description refinement
TXT Lyricist/text writer
TXX User defined text information frame
TXXX        User defined text information frame
TYE Year
TYER        Year
UFI Unique file identifier
UFID        Unique file identifier
ULT Unsychronised lyric/text transcription
USER        Terms of use
USLT        Unsynchronised lyric/text transcription
WAF Official audio file webpage
WAR Official artist/performer webpage
WAS Official audio source webpage
WCM Commercial information
WCOM        Commercial information
WCOP        Copyright/Legal information
WCP Copyright/Legal information
WOAF        Official audio file webpage
WOAR        Official artist/performer webpage
WOAS        Official audio source webpage
WORS        Official Internet radio station homepage
WPAY        Payment
WPB Publishers official webpage
WPUB        Publishers official webpage
WXX User defined URL link frame
WXXX        User defined URL link frame
TFEA        Featured Artist
TSTU        Recording Studio
rgad        Replay Gain Adjustment

Definition at line 3206 of file module.tag.id3v2.php.

References getid3_lib\EmbeddedLookup().

Referenced by ParseID3v2Frame().

3206  {
3207 
3208  $begin = __LINE__;
3209 
3382  return getid3_lib::EmbeddedLookup($framename, $begin, __LINE__, __FILE__, 'id3v2-framename_long');
3383 
3384  // Last three:
3385  // from Helium2 [www.helium2.com]
3386  // from http://privatewww.essex.ac.uk/~djmrob/replaygain/file_format_id3v2.html
3387  }
static EmbeddedLookup($key, $begin, $end, $file, $name)
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ FrameNameShortLookup()

static getid3_id3v2::FrameNameShortLookup (   $framename)
static

This is not a comment!

AENC        audio_encryption
APIC        attached_picture
ASPI        audio_seek_point_index
BUF recommended_buffer_size
CNT play_counter
COM comment
COMM        comment
COMR        commercial_frame
CRA audio_encryption
CRM encrypted_meta_frame
ENCR        encryption_method_registration
EQU equalisation
EQU2        equalisation
EQUA        equalisation
ETC event_timing_codes
ETCO        event_timing_codes
GEO general_encapsulated_object
GEOB        general_encapsulated_object
GRID        group_identification_registration
IPL involved_people_list
IPLS        involved_people_list
LINK        linked_information
LNK linked_information
MCDI        music_cd_identifier
MCI music_cd_identifier
MLL mpeg_location_lookup_table
MLLT        mpeg_location_lookup_table
OWNE        ownership_frame
PCNT        play_counter
PIC attached_picture
POP popularimeter
POPM        popularimeter
POSS        position_synchronisation_frame
PRIV        private_frame
RBUF        recommended_buffer_size
REV reverb
RVA relative_volume_adjustment
RVA2        relative_volume_adjustment
RVAD        relative_volume_adjustment
RVRB        reverb
SEEK        seek_frame
SIGN        signature_frame
SLT synchronised_lyric
STC synced_tempo_codes
SYLT        synchronised_lyric
SYTC        synchronised_tempo_codes
TAL album
TALB        album
TBP bpm
TBPM        bpm
TCM composer
TCMP        part_of_a_compilation
TCO genre
TCOM        composer
TCON        genre
TCOP        copyright_message
TCP part_of_a_compilation
TCR copyright_message
TDA date
TDAT        date
TDEN        encoding_time
TDLY        playlist_delay
TDOR        original_release_time
TDRC        recording_time
TDRL        release_time
TDTG        tagging_time
TDY playlist_delay
TEN encoded_by
TENC        encoded_by
TEXT        lyricist
TFLT        file_type
TFT file_type
TIM time
TIME        time
TIPL        involved_people_list
TIT1        content_group_description
TIT2        title
TIT3        subtitle
TKE initial_key
TKEY        initial_key
TLA language
TLAN        language
TLE length
TLEN        length
TMCL        musician_credits_list
TMED        media_type
TMOO        mood
TMT media_type
TOA original_artist
TOAL        original_album
TOF original_filename
TOFN        original_filename
TOL original_lyricist
TOLY        original_lyricist
TOPE        original_artist
TOR original_year
TORY        original_year
TOT original_album
TOWN        file_owner
TP1 artist
TP2 band
TP3 conductor
TP4 remixer
TPA part_of_a_set
TPB publisher
TPE1        artist
TPE2        band
TPE3        conductor
TPE4        remixer
TPOS        part_of_a_set
TPRO        produced_notice
TPUB        publisher
TRC isrc
TRCK        track_number
TRD recording_dates
TRDA        recording_dates
TRK track_number
TRSN        internet_radio_station_name
TRSO        internet_radio_station_owner
TS2 album_artist_sort_order
TSA album_sort_order
TSC composer_sort_order
TSI size
TSIZ        size
TSO2        album_artist_sort_order
TSOA        album_sort_order
TSOC        composer_sort_order
TSOP        performer_sort_order
TSOT        title_sort_order
TSP performer_sort_order
TSRC        isrc
TSS encoder_settings
TSSE        encoder_settings
TSST        set_subtitle
TST title_sort_order
TT1 content_group_description
TT2 title
TT3 subtitle
TXT lyricist
TXX text
TXXX        text
TYE year
TYER        year
UFI unique_file_identifier
UFID        unique_file_identifier
ULT unsychronised_lyric
USER        terms_of_use
USLT        unsynchronised_lyric
WAF url_file
WAR url_artist
WAS url_source
WCM commercial_information
WCOM        commercial_information
WCOP        copyright
WCP copyright
WOAF        url_file
WOAR        url_artist
WOAS        url_source
WORS        url_station
WPAY        url_payment
WPB url_publisher
WPUB        url_publisher
WXX url_user
WXXX        url_user
TFEA        featured_artist
TSTU        recording_studio
rgad        replay_gain_adjustment

Definition at line 3390 of file module.tag.id3v2.php.

References getid3_lib\EmbeddedLookup().

Referenced by ParseID3v2Frame().

3390  {
3391 
3392  $begin = __LINE__;
3393 
3566  return getid3_lib::EmbeddedLookup($framename, $begin, __LINE__, __FILE__, 'id3v2-framename_short');
3567  }
static EmbeddedLookup($key, $begin, $end, $file, $name)
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ ID3v22iTunesBrokenFrameName()

static getid3_id3v2::ID3v22iTunesBrokenFrameName (   $frame_name)
static

Definition at line 3656 of file module.tag.id3v2.php.

3656  {
3657  // iTunes (multiple versions) has been known to write ID3v2.3 style frames
3658  // but use ID3v2.2 frame names, right-padded using either [space] or [null]
3659  // to make them fit in the 4-byte frame name space of the ID3v2.3 frame.
3660  // This function will detect and translate the corrupt frame name into ID3v2.3 standard.
3661  static $ID3v22_iTunes_BrokenFrames = array(
3662  'BUF' => 'RBUF', // Recommended buffer size
3663  'CNT' => 'PCNT', // Play counter
3664  'COM' => 'COMM', // Comments
3665  'CRA' => 'AENC', // Audio encryption
3666  'EQU' => 'EQUA', // Equalisation
3667  'ETC' => 'ETCO', // Event timing codes
3668  'GEO' => 'GEOB', // General encapsulated object
3669  'IPL' => 'IPLS', // Involved people list
3670  'LNK' => 'LINK', // Linked information
3671  'MCI' => 'MCDI', // Music CD identifier
3672  'MLL' => 'MLLT', // MPEG location lookup table
3673  'PIC' => 'APIC', // Attached picture
3674  'POP' => 'POPM', // Popularimeter
3675  'REV' => 'RVRB', // Reverb
3676  'RVA' => 'RVAD', // Relative volume adjustment
3677  'SLT' => 'SYLT', // Synchronised lyric/text
3678  'STC' => 'SYTC', // Synchronised tempo codes
3679  'TAL' => 'TALB', // Album/Movie/Show title
3680  'TBP' => 'TBPM', // BPM (beats per minute)
3681  'TCM' => 'TCOM', // Composer
3682  'TCO' => 'TCON', // Content type
3683  'TCP' => 'TCMP', // Part of a compilation
3684  'TCR' => 'TCOP', // Copyright message
3685  'TDA' => 'TDAT', // Date
3686  'TDY' => 'TDLY', // Playlist delay
3687  'TEN' => 'TENC', // Encoded by
3688  'TFT' => 'TFLT', // File type
3689  'TIM' => 'TIME', // Time
3690  'TKE' => 'TKEY', // Initial key
3691  'TLA' => 'TLAN', // Language(s)
3692  'TLE' => 'TLEN', // Length
3693  'TMT' => 'TMED', // Media type
3694  'TOA' => 'TOPE', // Original artist(s)/performer(s)
3695  'TOF' => 'TOFN', // Original filename
3696  'TOL' => 'TOLY', // Original lyricist(s)/text writer(s)
3697  'TOR' => 'TORY', // Original release year
3698  'TOT' => 'TOAL', // Original album/movie/show title
3699  'TP1' => 'TPE1', // Lead performer(s)/Soloist(s)
3700  'TP2' => 'TPE2', // Band/orchestra/accompaniment
3701  'TP3' => 'TPE3', // Conductor/performer refinement
3702  'TP4' => 'TPE4', // Interpreted, remixed, or otherwise modified by
3703  'TPA' => 'TPOS', // Part of a set
3704  'TPB' => 'TPUB', // Publisher
3705  'TRC' => 'TSRC', // ISRC (international standard recording code)
3706  'TRD' => 'TRDA', // Recording dates
3707  'TRK' => 'TRCK', // Track number/Position in set
3708  'TS2' => 'TSO2', // Album-Artist sort order
3709  'TSA' => 'TSOA', // Album sort order
3710  'TSC' => 'TSOC', // Composer sort order
3711  'TSI' => 'TSIZ', // Size
3712  'TSP' => 'TSOP', // Performer sort order
3713  'TSS' => 'TSSE', // Software/Hardware and settings used for encoding
3714  'TST' => 'TSOT', // Title sort order
3715  'TT1' => 'TIT1', // Content group description
3716  'TT2' => 'TIT2', // Title/songname/content description
3717  'TT3' => 'TIT3', // Subtitle/Description refinement
3718  'TXT' => 'TEXT', // Lyricist/Text writer
3719  'TXX' => 'TXXX', // User defined text information frame
3720  'TYE' => 'TYER', // Year
3721  'UFI' => 'UFID', // Unique file identifier
3722  'ULT' => 'USLT', // Unsynchronised lyric/text transcription
3723  'WAF' => 'WOAF', // Official audio file webpage
3724  'WAR' => 'WOAR', // Official artist/performer webpage
3725  'WAS' => 'WOAS', // Official audio source webpage
3726  'WCM' => 'WCOM', // Commercial information
3727  'WCP' => 'WCOP', // Copyright/Legal information
3728  'WPB' => 'WPUB', // Publishers official webpage
3729  'WXX' => 'WXXX', // User defined URL link frame
3730  );
3731  if (strlen($frame_name) == 4) {
3732  if ((substr($frame_name, 3, 1) == ' ') || (substr($frame_name, 3, 1) == "\x00")) {
3733  if (isset($ID3v22_iTunes_BrokenFrames[substr($frame_name, 0, 3)])) {
3734  return $ID3v22_iTunes_BrokenFrames[substr($frame_name, 0, 3)];
3735  }
3736  }
3737  }
3738  return false;
3739  }

◆ ID3v2HeaderLength()

static getid3_id3v2::ID3v2HeaderLength (   $majorversion)
static

Definition at line 3652 of file module.tag.id3v2.php.

Referenced by Analyze(), and getid3_write_id3v2\GenerateID3v2Tag().

3652  {
3653  return (($majorversion == 2) ? 6 : 10);
3654  }
+ Here is the caller graph for this function:

◆ IsANumber()

static getid3_id3v2::IsANumber (   $numberstring,
  $allowdecimal = false,
  $allownegative = false 
)
static

Definition at line 3609 of file module.tag.id3v2.php.

References $i.

3609  {
3610  for ($i = 0; $i < strlen($numberstring); $i++) {
3611  if ((chr($numberstring{$i}) < chr('0')) || (chr($numberstring{$i}) > chr('9'))) {
3612  if (($numberstring{$i} == '.') && $allowdecimal) {
3613  // allowed
3614  } elseif (($numberstring{$i} == '-') && $allownegative && ($i == 0)) {
3615  // allowed
3616  } else {
3617  return false;
3618  }
3619  }
3620  }
3621  return true;
3622  }
$i
Definition: disco.tpl.php:19

◆ IsValidDateStampString()

static getid3_id3v2::IsValidDateStampString (   $datestamp)
static

Definition at line 3624 of file module.tag.id3v2.php.

Referenced by ParseID3v2Frame().

3624  {
3625  if (strlen($datestamp) != 8) {
3626  return false;
3627  }
3628  if (!self::IsANumber($datestamp, false)) {
3629  return false;
3630  }
3631  $year = substr($datestamp, 0, 4);
3632  $month = substr($datestamp, 4, 2);
3633  $day = substr($datestamp, 6, 2);
3634  if (($year == 0) || ($month == 0) || ($day == 0)) {
3635  return false;
3636  }
3637  if ($month > 12) {
3638  return false;
3639  }
3640  if ($day > 31) {
3641  return false;
3642  }
3643  if (($day > 30) && (($month == 4) || ($month == 6) || ($month == 9) || ($month == 11))) {
3644  return false;
3645  }
3646  if (($day > 29) && ($month == 2)) {
3647  return false;
3648  }
3649  return true;
3650  }
+ Here is the caller graph for this function:

◆ IsValidID3v2FrameName()

static getid3_id3v2::IsValidID3v2FrameName (   $framename,
  $id3v2majorversion 
)
static

Definition at line 3595 of file module.tag.id3v2.php.

Referenced by Analyze(), getid3_write_id3v2\GenerateID3v2FrameData(), and getid3_write_id3v2\GenerateID3v2Tag().

3595  {
3596  switch ($id3v2majorversion) {
3597  case 2:
3598  return preg_match('#[A-Z][A-Z0-9]{2}#', $framename);
3599  break;
3600 
3601  case 3:
3602  case 4:
3603  return preg_match('#[A-Z][A-Z0-9]{3}#', $framename);
3604  break;
3605  }
3606  return false;
3607  }
+ Here is the caller graph for this function:

◆ LanguageLookup()

static getid3_id3v2::LanguageLookup (   $languagecode,
  $casesensitive = false 
)
static

This is not a comment!

XXX unknown
xxx unknown
aar Afar
abk Abkhazian
ace Achinese
ach Acoli
ada Adangme
afa Afro-Asiatic (Other)
afh Afrihili
afr Afrikaans
aka Akan
akk Akkadian
alb Albanian
ale Aleut
alg Algonquian Languages
amh Amharic
ang English, Old (ca. 450-1100)
apa Apache Languages
ara Arabic
arc Aramaic
arm Armenian
arn Araucanian
arp Arapaho
art Artificial (Other)
arw Arawak
asm Assamese
ath Athapascan Languages
ava Avaric
ave Avestan
awa Awadhi
aym Aymara
aze Azerbaijani
bad Banda
bai Bamileke Languages
bak Bashkir
bal Baluchi
bam Bambara
ban Balinese
baq Basque
bas Basa
bat Baltic (Other)
bej Beja
bel Byelorussian
bem Bemba
ben Bengali
ber Berber (Other)
bho Bhojpuri
bih Bihari
bik Bikol
bin Bini
bis Bislama
bla Siksika
bnt Bantu (Other)
bod Tibetan
bra Braj
bre Breton
bua Buriat
bug Buginese
bul Bulgarian
bur Burmese
cad Caddo
cai Central American Indian (Other)
car Carib
cat Catalan
cau Caucasian (Other)
ceb Cebuano
cel Celtic (Other)
ces Czech
cha Chamorro
chb Chibcha
che Chechen
chg Chagatai
chi Chinese
chm Mari
chn Chinook jargon
cho Choctaw
chr Cherokee
chu Church Slavic
chv Chuvash
chy Cheyenne
cop Coptic
cor Cornish
cos Corsican
cpe Creoles and Pidgins, English-based (Other)
cpf Creoles and Pidgins, French-based (Other)
cpp Creoles and Pidgins, Portuguese-based (Other)
cre Cree
crp Creoles and Pidgins (Other)
cus Cushitic (Other)
cym Welsh
cze Czech
dak Dakota
dan Danish
del Delaware
deu German
din Dinka
div Divehi
doi Dogri
dra Dravidian (Other)
dua Duala
dum Dutch, Middle (ca. 1050-1350)
dut Dutch
dyu Dyula
dzo Dzongkha
efi Efik
egy Egyptian (Ancient)
eka Ekajuk
ell Greek, Modern (1453-)
elx Elamite
eng English
enm English, Middle (ca. 1100-1500)
epo Esperanto
esk Eskimo (Other)
esl Spanish
est Estonian
eus Basque
ewe Ewe
ewo Ewondo
fan Fang
fao Faroese
fas Persian
fat Fanti
fij Fijian
fin Finnish
fiu Finno-Ugrian (Other)
fon Fon
fra French
fre French
frm French, Middle (ca. 1400-1600)
fro French, Old (842- ca. 1400)
fry Frisian
ful Fulah
gaa Ga
gae Gaelic (Scots)
gai Irish
gay Gayo
gdh Gaelic (Scots)
gem Germanic (Other)
geo Georgian
ger German
gez Geez
gil Gilbertese
glg Gallegan
gmh German, Middle High (ca. 1050-1500)
goh German, Old High (ca. 750-1050)
gon Gondi
got Gothic
grb Grebo
grc Greek, Ancient (to 1453)
gre Greek, Modern (1453-)
grn Guarani
guj Gujarati
hai Haida
hau Hausa
haw Hawaiian
heb Hebrew
her Herero
hil Hiligaynon
him Himachali
hin Hindi
hmo Hiri Motu
hun Hungarian
hup Hupa
hye Armenian
iba Iban
ibo Igbo
ice Icelandic
ijo Ijo
iku Inuktitut
ilo Iloko
ina Interlingua (International Auxiliary language Association)
inc Indic (Other)
ind Indonesian
ine Indo-European (Other)
ine Interlingue
ipk Inupiak
ira Iranian (Other)
iri Irish
iro Iroquoian uages
isl Icelandic
ita Italian
jav Javanese
jaw Javanese
jpn Japanese
jpr Judeo-Persian
jrb Judeo-Arabic
kaa Kara-Kalpak
kab Kabyle
kac Kachin
kal Greenlandic
kam Kamba
kan Kannada
kar Karen
kas Kashmiri
kat Georgian
kau Kanuri
kaw Kawi
kaz Kazakh
kha Khasi
khi Khoisan (Other)
khm Khmer
kho Khotanese
kik Kikuyu
kin Kinyarwanda
kir Kirghiz
kok Konkani
kom Komi
kon Kongo
kor Korean
kpe Kpelle
kro Kru
kru Kurukh
kua Kuanyama
kum Kumyk
kur Kurdish
kus Kusaie
kut Kutenai
lad Ladino
lah Lahnda
lam Lamba
lao Lao
lat Latin
lav Latvian
lez Lezghian
lin Lingala
lit Lithuanian
lol Mongo
loz Lozi
ltz Letzeburgesch
lub Luba-Katanga
lug Ganda
lui Luiseno
lun Lunda
luo Luo (Kenya and Tanzania)
mac Macedonian
mad Madurese
mag Magahi
mah Marshall
mai Maithili
mak Macedonian
mak Makasar
mal Malayalam
man Mandingo
mao Maori
map Austronesian (Other)
mar Marathi
mas Masai
max Manx
may Malay
men Mende
mga Irish, Middle (900 - 1200)
mic Micmac
min Minangkabau
mis Miscellaneous (Other)
mkh Mon-Kmer (Other)
mlg Malagasy
mlt Maltese
mni Manipuri
mno Manobo Languages
moh Mohawk
mol Moldavian
mon Mongolian
mos Mossi
mri Maori
msa Malay
mul Multiple Languages
mun Munda Languages
mus Creek
mwr Marwari
mya Burmese
myn Mayan Languages
nah Aztec
nai North American Indian (Other)
nau Nauru
nav Navajo
nbl Ndebele, South
nde Ndebele, North
ndo Ndongo
nep Nepali
new Newari
nic Niger-Kordofanian (Other)
niu Niuean
nla Dutch
nno Norwegian (Nynorsk)
non Norse, Old
nor Norwegian
nso Sotho, Northern
nub Nubian Languages
nya Nyanja
nym Nyamwezi
nyn Nyankole
nyo Nyoro
nzi Nzima
oci Langue d'Oc (post 1500)
oji Ojibwa
ori Oriya
orm Oromo
osa Osage
oss Ossetic
ota Turkish, Ottoman (1500 - 1928)
oto Otomian Languages
paa Papuan-Australian (Other)
pag Pangasinan
pal Pahlavi
pam Pampanga
pan Panjabi
pap Papiamento
pau Palauan
peo Persian, Old (ca 600 - 400 B.C.)
per Persian
phn Phoenician
pli Pali
pol Polish
pon Ponape
por Portuguese
pra Prakrit uages
pro Provencal, Old (to 1500)
pus Pushto
que Quechua
raj Rajasthani
rar Rarotongan
roa Romance (Other)
roh Rhaeto-Romance
rom Romany
ron Romanian
rum Romanian
run Rundi
rus Russian
sad Sandawe
sag Sango
sah Yakut
sai South American Indian (Other)
sal Salishan Languages
sam Samaritan Aramaic
san Sanskrit
sco Scots
scr Serbo-Croatian
sel Selkup
sem Semitic (Other)
sga Irish, Old (to 900)
shn Shan
sid Sidamo
sin Singhalese
sio Siouan Languages
sit Sino-Tibetan (Other)
sla Slavic (Other)
slk Slovak
slo Slovak
slv Slovenian
smi Sami Languages
smo Samoan
sna Shona
snd Sindhi
sog Sogdian
som Somali
son Songhai
sot Sotho, Southern
spa Spanish
sqi Albanian
srd Sardinian
srr Serer
ssa Nilo-Saharan (Other)
ssw Siswant
ssw Swazi
suk Sukuma
sun Sudanese
sus Susu
sux Sumerian
sve Swedish
swa Swahili
swe Swedish
syr Syriac
tah Tahitian
tam Tamil
tat Tatar
tel Telugu
tem Timne
ter Tereno
tgk Tajik
tgl Tagalog
tha Thai
tib Tibetan
tig Tigre
tir Tigrinya
tiv Tivi
tli Tlingit
tmh Tamashek
tog Tonga (Nyasa)
ton Tonga (Tonga Islands)
tru Truk
tsi Tsimshian
tsn Tswana
tso Tsonga
tuk Turkmen
tum Tumbuka
tur Turkish
tut Altaic (Other)
twi Twi
tyv Tuvinian
uga Ugaritic
uig Uighur
ukr Ukrainian
umb Umbundu
und Undetermined
urd Urdu
uzb Uzbek
vai Vai
ven Venda
vie Vietnamese
vol Volapük
vot Votic
wak Wakashan Languages
wal Walamo
war Waray
was Washo
wel Welsh
wen Sorbian Languages
wol Wolof
xho Xhosa
yao Yao
yap Yap
yid Yiddish
yor Yoruba
zap Zapotec
zen Zenaga
zha Zhuang
zho Chinese
zul Zulu
zun Zuni

Definition at line 2629 of file module.tag.id3v2.php.

References getid3_lib\EmbeddedLookup().

Referenced by getid3_write_id3v2\GenerateID3v2FrameData(), getid3_write_id3v2\ID3v2IsValidPriceString(), ParseID3v2Frame(), and getid3_quicktime\QuicktimeLanguageLookup().

2629  {
2630 
2631  if (!$casesensitive) {
2632  $languagecode = strtolower($languagecode);
2633  }
2634 
2635  // http://www.id3.org/id3v2.4.0-structure.txt
2636  // [4. ID3v2 frame overview]
2637  // The three byte language field, present in several frames, is used to
2638  // describe the language of the frame's content, according to ISO-639-2
2639  // [ISO-639-2]. The language should be represented in lower case. If the
2640  // language is not known the string "XXX" should be used.
2641 
2642 
2643  // ISO 639-2 - http://www.id3.org/iso639-2.html
2644 
2645  $begin = __LINE__;
2646 
3081  return getid3_lib::EmbeddedLookup($languagecode, $begin, __LINE__, __FILE__, 'id3v2-languagecode');
3082  }
static EmbeddedLookup($key, $begin, $end, $file, $name)
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ LookupCurrencyCountry()

getid3_id3v2::LookupCurrencyCountry (   $currencyid)

This is not a comment!

AED United Arab Emirates
AFA Afghanistan
ALL Albania
AMD Armenia
ANG Netherlands Antilles
AOA Angola
ARS Argentina
ATS Austria
AUD Australia
AWG Aruba
AZM Azerbaijan
BAM Bosnia and Herzegovina
BBD Barbados
BDT Bangladesh
BEF Belgium
BGL Bulgaria
BHD Bahrain
BIF Burundi
BMD Bermuda
BND Brunei Darussalam
BOB Bolivia
BRL Brazil
BSD Bahamas
BTN Bhutan
BWP Botswana
BYR Belarus
BZD Belize
CAD Canada
CDF Congo/Kinshasa
CHF Switzerland
CLP Chile
CNY China
COP Colombia
CRC Costa Rica
CUP Cuba
CVE Cape Verde
CYP Cyprus
CZK Czech Republic
DEM Germany
DJF Djibouti
DKK Denmark
DOP Dominican Republic
DZD Algeria
EEK Estonia
EGP Egypt
ERN Eritrea
ESP Spain
ETB Ethiopia
EUR Euro Member Countries
FIM Finland
FJD Fiji
FKP Falkland Islands (Malvinas)
FRF France
GBP United Kingdom
GEL Georgia
GGP Guernsey
GHC Ghana
GIP Gibraltar
GMD Gambia
GNF Guinea
GRD Greece
GTQ Guatemala
GYD Guyana
HKD Hong Kong
HNL Honduras
HRK Croatia
HTG Haiti
HUF Hungary
IDR Indonesia
IEP Ireland (Eire)
ILS Israel
IMP Isle of Man
INR India
IQD Iraq
IRR Iran
ISK Iceland
ITL Italy
JEP Jersey
JMD Jamaica
JOD Jordan
JPY Japan
KES Kenya
KGS Kyrgyzstan
KHR Cambodia
KMF Comoros
KPW Korea
KWD Kuwait
KYD Cayman Islands
KZT Kazakstan
LAK Laos
LBP Lebanon
LKR Sri Lanka
LRD Liberia
LSL Lesotho
LTL Lithuania
LUF Luxembourg
LVL Latvia
LYD Libya
MAD Morocco
MDL Moldova
MGF Madagascar
MKD Macedonia
MMK Myanmar (Burma)
MNT Mongolia
MOP Macau
MRO Mauritania
MTL Malta
MUR Mauritius
MVR Maldives (Maldive Islands)
MWK Malawi
MXN Mexico
MYR Malaysia
MZM Mozambique
NAD Namibia
NGN Nigeria
NIO Nicaragua
NLG Netherlands (Holland)
NOK Norway
NPR Nepal
NZD New Zealand
OMR Oman
PAB Panama
PEN Peru
PGK Papua New Guinea
PHP Philippines
PKR Pakistan
PLN Poland
PTE Portugal
PYG Paraguay
QAR Qatar
ROL Romania
RUR Russia
RWF Rwanda
SAR Saudi Arabia
SBD Solomon Islands
SCR Seychelles
SDD Sudan
SEK Sweden
SGD Singapore
SHP Saint Helena
SIT Slovenia
SKK Slovakia
SLL Sierra Leone
SOS Somalia
SPL Seborga
SRG Suriname
STD São Tome and Principe
SVC El Salvador
SYP Syria
SZL Swaziland
THB Thailand
TJR Tajikistan
TMM Turkmenistan
TND Tunisia
TOP Tonga
TRL Turkey
TTD Trinidad and Tobago
TVD Tuvalu
TWD Taiwan
TZS Tanzania
UAH Ukraine
UGX Uganda
USD United States of America
UYU Uruguay
UZS Uzbekistan
VAL Vatican City
VEB Venezuela
VND Viet Nam
VUV Vanuatu
WST Samoa
XAF Communauté Financière Africaine
XAG Silver
XAU Gold
XCD East Caribbean
XDR International Monetary Fund
XPD Palladium
XPF Comptoirs Français du Pacifique
XPT Platinum
YER Yemen
YUM Yugoslavia
ZAR South Africa
ZMK Zambia
ZWD Zimbabwe

Definition at line 2432 of file module.tag.id3v2.php.

References getid3_lib\EmbeddedLookup().

2432  {
2433 
2434  $begin = __LINE__;
2435 
2624  return getid3_lib::EmbeddedLookup($currencyid, $begin, __LINE__, __FILE__, 'id3v2-currency-country');
2625  }
static EmbeddedLookup($key, $begin, $end, $file, $name)
+ Here is the call graph for this function:

◆ LookupCurrencyUnits()

getid3_id3v2::LookupCurrencyUnits (   $currencyid)

This is not a comment!

AED Dirhams
AFA Afghanis
ALL Leke
AMD Drams
ANG Guilders
AOA Kwanza
ARS Pesos
ATS Schillings
AUD Dollars
AWG Guilders
AZM Manats
BAM Convertible Marka
BBD Dollars
BDT Taka
BEF Francs
BGL Leva
BHD Dinars
BIF Francs
BMD Dollars
BND Dollars
BOB Bolivianos
BRL Brazil Real
BSD Dollars
BTN Ngultrum
BWP Pulas
BYR Rubles
BZD Dollars
CAD Dollars
CDF Congolese Francs
CHF Francs
CLP Pesos
CNY Yuan Renminbi
COP Pesos
CRC Colones
CUP Pesos
CVE Escudos
CYP Pounds
CZK Koruny
DEM Deutsche Marks
DJF Francs
DKK Kroner
DOP Pesos
DZD Algeria Dinars
EEK Krooni
EGP Pounds
ERN Nakfa
ESP Pesetas
ETB Birr
EUR Euro
FIM Markkaa
FJD Dollars
FKP Pounds
FRF Francs
GBP Pounds
GEL Lari
GGP Pounds
GHC Cedis
GIP Pounds
GMD Dalasi
GNF Francs
GRD Drachmae
GTQ Quetzales
GYD Dollars
HKD Dollars
HNL Lempiras
HRK Kuna
HTG Gourdes
HUF Forints
IDR Rupiahs
IEP Pounds
ILS New Shekels
IMP Pounds
INR Rupees
IQD Dinars
IRR Rials
ISK Kronur
ITL Lire
JEP Pounds
JMD Dollars
JOD Dinars
JPY Yen
KES Shillings
KGS Soms
KHR Riels
KMF Francs
KPW Won
KWD Dinars
KYD Dollars
KZT Tenge
LAK Kips
LBP Pounds
LKR Rupees
LRD Dollars
LSL Maloti
LTL Litai
LUF Francs
LVL Lati
LYD Dinars
MAD Dirhams
MDL Lei
MGF Malagasy Francs
MKD Denars
MMK Kyats
MNT Tugriks
MOP Patacas
MRO Ouguiyas
MTL Liri
MUR Rupees
MVR Rufiyaa
MWK Kwachas
MXN Pesos
MYR Ringgits
MZM Meticais
NAD Dollars
NGN Nairas
NIO Gold Cordobas
NLG Guilders
NOK Krone
NPR Nepal Rupees
NZD Dollars
OMR Rials
PAB Balboa
PEN Nuevos Soles
PGK Kina
PHP Pesos
PKR Rupees
PLN Zlotych
PTE Escudos
PYG Guarani
QAR Rials
ROL Lei
RUR Rubles
RWF Rwanda Francs
SAR Riyals
SBD Dollars
SCR Rupees
SDD Dinars
SEK Kronor
SGD Dollars
SHP Pounds
SIT Tolars
SKK Koruny
SLL Leones
SOS Shillings
SPL Luigini
SRG Guilders
STD Dobras
SVC Colones
SYP Pounds
SZL Emalangeni
THB Baht
TJR Rubles
TMM Manats
TND Dinars
TOP Pa'anga
TRL Liras
TTD Dollars
TVD Tuvalu Dollars
TWD New Dollars
TZS Shillings
UAH Hryvnia
UGX Shillings
USD Dollars
UYU Pesos
UZS Sums
VAL Lire
VEB Bolivares
VND Dong
VUV Vatu
WST Tala
XAF Francs
XAG Ounces
XAU Ounces
XCD Dollars
XDR Special Drawing Rights
XPD Ounces
XPF Francs
XPT Ounces
YER Rials
YUM New Dinars
ZAR Rand
ZMK Kwacha
ZWD Zimbabwe Dollars

Definition at line 2235 of file module.tag.id3v2.php.

References getid3_lib\EmbeddedLookup().

Referenced by ParseID3v2Frame().

2235  {
2236 
2237  $begin = __LINE__;
2238 
2428  return getid3_lib::EmbeddedLookup($currencyid, $begin, __LINE__, __FILE__, 'id3v2-currency-units');
2429  }
static EmbeddedLookup($key, $begin, $end, $file, $name)
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ LookupExtendedHeaderRestrictionsImageEncoding()

getid3_id3v2::LookupExtendedHeaderRestrictionsImageEncoding (   $index)

Definition at line 2217 of file module.tag.id3v2.php.

References $index.

Referenced by Analyze().

2217  {
2218  static $LookupExtendedHeaderRestrictionsImageEncoding = array(
2219  0x00 => 'No restrictions',
2220  0x01 => 'Images are encoded only with PNG or JPEG',
2221  );
2222  return (isset($LookupExtendedHeaderRestrictionsImageEncoding[$index]) ? $LookupExtendedHeaderRestrictionsImageEncoding[$index] : '');
2223  }
$index
Definition: metadata.php:60
+ Here is the caller graph for this function:

◆ LookupExtendedHeaderRestrictionsImageSizeSize()

getid3_id3v2::LookupExtendedHeaderRestrictionsImageSizeSize (   $index)

Definition at line 2225 of file module.tag.id3v2.php.

References $index.

Referenced by Analyze().

2225  {
2226  static $LookupExtendedHeaderRestrictionsImageSizeSize = array(
2227  0x00 => 'No restrictions',
2228  0x01 => 'All images are 256x256 pixels or smaller',
2229  0x02 => 'All images are 64x64 pixels or smaller',
2230  0x03 => 'All images are exactly 64x64 pixels, unless required otherwise',
2231  );
2232  return (isset($LookupExtendedHeaderRestrictionsImageSizeSize[$index]) ? $LookupExtendedHeaderRestrictionsImageSizeSize[$index] : '');
2233  }
$index
Definition: metadata.php:60
+ Here is the caller graph for this function:

◆ LookupExtendedHeaderRestrictionsTagSizeLimits()

getid3_id3v2::LookupExtendedHeaderRestrictionsTagSizeLimits (   $index)

Definition at line 2189 of file module.tag.id3v2.php.

References $index.

Referenced by Analyze().

2189  {
2190  static $LookupExtendedHeaderRestrictionsTagSizeLimits = array(
2191  0x00 => 'No more than 128 frames and 1 MB total tag size',
2192  0x01 => 'No more than 64 frames and 128 KB total tag size',
2193  0x02 => 'No more than 32 frames and 40 KB total tag size',
2194  0x03 => 'No more than 32 frames and 4 KB total tag size',
2195  );
2196  return (isset($LookupExtendedHeaderRestrictionsTagSizeLimits[$index]) ? $LookupExtendedHeaderRestrictionsTagSizeLimits[$index] : '');
2197  }
$index
Definition: metadata.php:60
+ Here is the caller graph for this function:

◆ LookupExtendedHeaderRestrictionsTextEncodings()

getid3_id3v2::LookupExtendedHeaderRestrictionsTextEncodings (   $index)

Definition at line 2199 of file module.tag.id3v2.php.

References $index.

Referenced by Analyze().

2199  {
2200  static $LookupExtendedHeaderRestrictionsTextEncodings = array(
2201  0x00 => 'No restrictions',
2202  0x01 => 'Strings are only encoded with ISO-8859-1 or UTF-8',
2203  );
2204  return (isset($LookupExtendedHeaderRestrictionsTextEncodings[$index]) ? $LookupExtendedHeaderRestrictionsTextEncodings[$index] : '');
2205  }
$index
Definition: metadata.php:60
+ Here is the caller graph for this function:

◆ LookupExtendedHeaderRestrictionsTextFieldSize()

getid3_id3v2::LookupExtendedHeaderRestrictionsTextFieldSize (   $index)

Definition at line 2207 of file module.tag.id3v2.php.

References $index.

Referenced by Analyze().

2207  {
2208  static $LookupExtendedHeaderRestrictionsTextFieldSize = array(
2209  0x00 => 'No restrictions',
2210  0x01 => 'No string is longer than 1024 characters',
2211  0x02 => 'No string is longer than 128 characters',
2212  0x03 => 'No string is longer than 30 characters',
2213  );
2214  return (isset($LookupExtendedHeaderRestrictionsTextFieldSize[$index]) ? $LookupExtendedHeaderRestrictionsTextFieldSize[$index] : '');
2215  }
$index
Definition: metadata.php:60
+ Here is the caller graph for this function:

◆ ParseID3v2Frame()

getid3_id3v2::ParseID3v2Frame ( $parsedFrame)

Definition at line 544 of file module.tag.id3v2.php.

References $i, $info, $key, $warning, APICPictureTypeLookup(), getid3_lib\BigEndian2Bin(), getid3_lib\BigEndian2Float(), getid3_lib\BigEndian2Int(), getid3_lib\Bin2Dec(), COMRReceivedAsLookup(), getid3_lib\Dec2Bin(), DeUnsynchronise(), ETCOEventLookup(), FrameNameLongLookup(), FrameNameShortLookup(), getid3_lib\GetDataImageSize(), getid3_lib\iconv_fallback(), getid3_lib\iconv_fallback_iso88591_utf8(), IsValidDateStampString(), LanguageLookup(), LookupCurrencyUnits(), getid3_lib\RGADadjustmentLookup(), getid3_lib\RGADnameLookup(), getid3_lib\RGADoriginatorLookup(), RVA2ChannelTypeLookup(), SYTLContentTypeLookup(), TextEncodingNameLookup(), TextEncodingTerminatorLookup(), and getid3_handler\warning().

Referenced by Analyze().

544  {
545 
546  // shortcuts
547  $info = &$this->getid3->info;
548  $id3v2_majorversion = $info['id3v2']['majorversion'];
549 
550  $parsedFrame['framenamelong'] = $this->FrameNameLongLookup($parsedFrame['frame_name']);
551  if (empty($parsedFrame['framenamelong'])) {
552  unset($parsedFrame['framenamelong']);
553  }
554  $parsedFrame['framenameshort'] = $this->FrameNameShortLookup($parsedFrame['frame_name']);
555  if (empty($parsedFrame['framenameshort'])) {
556  unset($parsedFrame['framenameshort']);
557  }
558 
559  if ($id3v2_majorversion >= 3) { // frame flags are not part of the ID3v2.2 standard
560  if ($id3v2_majorversion == 3) {
561  // Frame Header Flags
562  // %abc00000 %ijk00000
563  $parsedFrame['flags']['TagAlterPreservation'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x8000); // a - Tag alter preservation
564  $parsedFrame['flags']['FileAlterPreservation'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x4000); // b - File alter preservation
565  $parsedFrame['flags']['ReadOnly'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x2000); // c - Read only
566  $parsedFrame['flags']['compression'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x0080); // i - Compression
567  $parsedFrame['flags']['Encryption'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x0040); // j - Encryption
568  $parsedFrame['flags']['GroupingIdentity'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x0020); // k - Grouping identity
569 
570  } elseif ($id3v2_majorversion == 4) {
571  // Frame Header Flags
572  // %0abc0000 %0h00kmnp
573  $parsedFrame['flags']['TagAlterPreservation'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x4000); // a - Tag alter preservation
574  $parsedFrame['flags']['FileAlterPreservation'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x2000); // b - File alter preservation
575  $parsedFrame['flags']['ReadOnly'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x1000); // c - Read only
576  $parsedFrame['flags']['GroupingIdentity'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x0040); // h - Grouping identity
577  $parsedFrame['flags']['compression'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x0008); // k - Compression
578  $parsedFrame['flags']['Encryption'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x0004); // m - Encryption
579  $parsedFrame['flags']['Unsynchronisation'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x0002); // n - Unsynchronisation
580  $parsedFrame['flags']['DataLengthIndicator'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x0001); // p - Data length indicator
581 
582  // Frame-level de-unsynchronisation - ID3v2.4
583  if ($parsedFrame['flags']['Unsynchronisation']) {
584  $parsedFrame['data'] = $this->DeUnsynchronise($parsedFrame['data']);
585  }
586 
587  if ($parsedFrame['flags']['DataLengthIndicator']) {
588  $parsedFrame['data_length_indicator'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], 0, 4), 1);
589  $parsedFrame['data'] = substr($parsedFrame['data'], 4);
590  }
591  }
592 
593  // Frame-level de-compression
594  if ($parsedFrame['flags']['compression']) {
595  $parsedFrame['decompressed_size'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], 0, 4));
596  if (!function_exists('gzuncompress')) {
597  $this->warning('gzuncompress() support required to decompress ID3v2 frame "'.$parsedFrame['frame_name'].'"');
598  } else {
599  if ($decompresseddata = @gzuncompress(substr($parsedFrame['data'], 4))) {
600  //if ($decompresseddata = @gzuncompress($parsedFrame['data'])) {
601  $parsedFrame['data'] = $decompresseddata;
602  unset($decompresseddata);
603  } else {
604  $this->warning('gzuncompress() failed on compressed contents of ID3v2 frame "'.$parsedFrame['frame_name'].'"');
605  }
606  }
607  }
608  }
609 
610  if (!empty($parsedFrame['flags']['DataLengthIndicator'])) {
611  if ($parsedFrame['data_length_indicator'] != strlen($parsedFrame['data'])) {
612  $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');
613  }
614  }
615 
616  if (isset($parsedFrame['datalength']) && ($parsedFrame['datalength'] == 0)) {
617 
618  $warning = 'Frame "'.$parsedFrame['frame_name'].'" at offset '.$parsedFrame['dataoffset'].' has no data portion';
619  switch ($parsedFrame['frame_name']) {
620  case 'WCOM':
621  $warning .= ' (this is known to happen with files tagged by RioPort)';
622  break;
623 
624  default:
625  break;
626  }
627  $this->warning($warning);
628 
629  } elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'UFID')) || // 4.1 UFID Unique file identifier
630  (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'UFI'))) { // 4.1 UFI Unique file identifier
631  // There may be more than one 'UFID' frame in a tag,
632  // but only one with the same 'Owner identifier'.
633  // <Header for 'Unique file identifier', ID: 'UFID'>
634  // Owner identifier <text string> $00
635  // Identifier <up to 64 bytes binary data>
636  $exploded = explode("\x00", $parsedFrame['data'], 2);
637  $parsedFrame['ownerid'] = (isset($exploded[0]) ? $exploded[0] : '');
638  $parsedFrame['data'] = (isset($exploded[1]) ? $exploded[1] : '');
639 
640  } elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'TXXX')) || // 4.2.2 TXXX User defined text information frame
641  (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'TXX'))) { // 4.2.2 TXX User defined text information frame
642  // There may be more than one 'TXXX' frame in each tag,
643  // but only one with the same description.
644  // <Header for 'User defined text information frame', ID: 'TXXX'>
645  // Text encoding $xx
646  // Description <text string according to encoding> $00 (00)
647  // Value <text string according to encoding>
648 
649  $frame_offset = 0;
650  $frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1));
651  $frame_textencoding_terminator = $this->TextEncodingTerminatorLookup($frame_textencoding);
652  if ((($id3v2_majorversion <= 3) && ($frame_textencoding > 1)) || (($id3v2_majorversion == 4) && ($frame_textencoding > 3))) {
653  $this->warning('Invalid text encoding byte ('.$frame_textencoding.') in frame "'.$parsedFrame['frame_name'].'" - defaulting to ISO-8859-1 encoding');
654  $frame_textencoding_terminator = "\x00";
655  }
656  $frame_terminatorpos = strpos($parsedFrame['data'], $frame_textencoding_terminator, $frame_offset);
657  if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($frame_textencoding_terminator), 1)) === 0) {
658  $frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00
659  }
660  $frame_description = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
661  if (in_array($frame_description, array("\x00", "\x00\x00", "\xFF\xFE", "\xFE\xFF"))) {
662  // if description only contains a BOM or terminator then make it blank
663  $frame_description = '';
664  }
665  $parsedFrame['encodingid'] = $frame_textencoding;
666  $parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding);
667 
668  $parsedFrame['description'] = trim(getid3_lib::iconv_fallback($parsedFrame['encoding'], $info['id3v2']['encoding'], $frame_description));
669  $parsedFrame['data'] = substr($parsedFrame['data'], $frame_terminatorpos + strlen($frame_textencoding_terminator));
670  if (!empty($parsedFrame['framenameshort']) && !empty($parsedFrame['data'])) {
671  $commentkey = ($parsedFrame['description'] ? $parsedFrame['description'] : (isset($info['id3v2']['comments'][$parsedFrame['framenameshort']]) ? count($info['id3v2']['comments'][$parsedFrame['framenameshort']]) : 0));
672  if (!isset($info['id3v2']['comments'][$parsedFrame['framenameshort']]) || !array_key_exists($commentkey, $info['id3v2']['comments'][$parsedFrame['framenameshort']])) {
673  $info['id3v2']['comments'][$parsedFrame['framenameshort']][$commentkey] = trim(getid3_lib::iconv_fallback($parsedFrame['encoding'], $info['id3v2']['encoding'], $parsedFrame['data']));
674  } else {
675  $info['id3v2']['comments'][$parsedFrame['framenameshort']][] = trim(getid3_lib::iconv_fallback($parsedFrame['encoding'], $info['id3v2']['encoding'], $parsedFrame['data']));
676  }
677  }
678  //unset($parsedFrame['data']); do not unset, may be needed elsewhere, e.g. for replaygain
679 
680 
681  } elseif ($parsedFrame['frame_name']{0} == 'T') { // 4.2. T??[?] Text information frame
682  // There may only be one text information frame of its kind in an tag.
683  // <Header for 'Text information frame', ID: 'T000' - 'TZZZ',
684  // excluding 'TXXX' described in 4.2.6.>
685  // Text encoding $xx
686  // Information <text string(s) according to encoding>
687 
688  $frame_offset = 0;
689  $frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1));
690  if ((($id3v2_majorversion <= 3) && ($frame_textencoding > 1)) || (($id3v2_majorversion == 4) && ($frame_textencoding > 3))) {
691  $this->warning('Invalid text encoding byte ('.$frame_textencoding.') in frame "'.$parsedFrame['frame_name'].'" - defaulting to ISO-8859-1 encoding');
692  }
693 
694  $parsedFrame['data'] = (string) substr($parsedFrame['data'], $frame_offset);
695 
696  $parsedFrame['encodingid'] = $frame_textencoding;
697  $parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding);
698 
699  if (!empty($parsedFrame['framenameshort']) && !empty($parsedFrame['data'])) {
700  // ID3v2.3 specs say that TPE1 (and others) can contain multiple artist values separated with /
701  // This of course breaks when an artist name contains slash character, e.g. "AC/DC"
702  // MP3tag (maybe others) implement alternative system where multiple artists are null-separated, which makes more sense
703  // getID3 will split null-separated artists into multiple artists and leave slash-separated ones to the user
704  switch ($parsedFrame['encoding']) {
705  case 'UTF-16':
706  case 'UTF-16BE':
707  case 'UTF-16LE':
708  $wordsize = 2;
709  break;
710  case 'ISO-8859-1':
711  case 'UTF-8':
712  default:
713  $wordsize = 1;
714  break;
715  }
716  $Txxx_elements = array();
717  $Txxx_elements_start_offset = 0;
718  for ($i = 0; $i < strlen($parsedFrame['data']); $i += $wordsize) {
719  if (substr($parsedFrame['data'], $i, $wordsize) == str_repeat("\x00", $wordsize)) {
720  $Txxx_elements[] = substr($parsedFrame['data'], $Txxx_elements_start_offset, $i - $Txxx_elements_start_offset);
721  $Txxx_elements_start_offset = $i + $wordsize;
722  }
723  }
724  $Txxx_elements[] = substr($parsedFrame['data'], $Txxx_elements_start_offset, $i - $Txxx_elements_start_offset);
725  foreach ($Txxx_elements as $Txxx_element) {
726  $string = getid3_lib::iconv_fallback($parsedFrame['encoding'], $info['id3v2']['encoding'], $Txxx_element);
727  if (!empty($string)) {
728  $info['id3v2']['comments'][$parsedFrame['framenameshort']][] = $string;
729  }
730  }
731  unset($string, $wordsize, $i, $Txxx_elements, $Txxx_element, $Txxx_elements_start_offset);
732  }
733 
734  } elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'WXXX')) || // 4.3.2 WXXX User defined URL link frame
735  (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'WXX'))) { // 4.3.2 WXX User defined URL link frame
736  // There may be more than one 'WXXX' frame in each tag,
737  // but only one with the same description
738  // <Header for 'User defined URL link frame', ID: 'WXXX'>
739  // Text encoding $xx
740  // Description <text string according to encoding> $00 (00)
741  // URL <text string>
742 
743  $frame_offset = 0;
744  $frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1));
745  $frame_textencoding_terminator = $this->TextEncodingTerminatorLookup($frame_textencoding);
746  if ((($id3v2_majorversion <= 3) && ($frame_textencoding > 1)) || (($id3v2_majorversion == 4) && ($frame_textencoding > 3))) {
747  $this->warning('Invalid text encoding byte ('.$frame_textencoding.') in frame "'.$parsedFrame['frame_name'].'" - defaulting to ISO-8859-1 encoding');
748  $frame_textencoding_terminator = "\x00";
749  }
750  $frame_terminatorpos = strpos($parsedFrame['data'], $frame_textencoding_terminator, $frame_offset);
751  if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($frame_textencoding_terminator), 1)) === 0) {
752  $frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00
753  }
754  $frame_description = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
755  if (in_array($frame_description, array("\x00", "\x00\x00", "\xFF\xFE", "\xFE\xFF"))) {
756  // if description only contains a BOM or terminator then make it blank
757  $frame_description = '';
758  }
759  $parsedFrame['data'] = substr($parsedFrame['data'], $frame_terminatorpos + strlen($frame_textencoding_terminator));
760 
761  $frame_terminatorpos = strpos($parsedFrame['data'], $frame_textencoding_terminator);
762  if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($frame_textencoding_terminator), 1)) === 0) {
763  $frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00
764  }
765  if ($frame_terminatorpos) {
766  // there are null bytes after the data - this is not according to spec
767  // only use data up to first null byte
768  $frame_urldata = (string) substr($parsedFrame['data'], 0, $frame_terminatorpos);
769  } else {
770  // no null bytes following data, just use all data
771  $frame_urldata = (string) $parsedFrame['data'];
772  }
773 
774  $parsedFrame['encodingid'] = $frame_textencoding;
775  $parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding);
776 
777  $parsedFrame['url'] = $frame_urldata;
778  $parsedFrame['description'] = $frame_description;
779  if (!empty($parsedFrame['framenameshort']) && $parsedFrame['url']) {
780  $info['id3v2']['comments'][$parsedFrame['framenameshort']][] = getid3_lib::iconv_fallback($parsedFrame['encoding'], $info['id3v2']['encoding'], $parsedFrame['url']);
781  }
782  unset($parsedFrame['data']);
783 
784 
785  } elseif ($parsedFrame['frame_name']{0} == 'W') { // 4.3. W??? URL link frames
786  // There may only be one URL link frame of its kind in a tag,
787  // except when stated otherwise in the frame description
788  // <Header for 'URL link frame', ID: 'W000' - 'WZZZ', excluding 'WXXX'
789  // described in 4.3.2.>
790  // URL <text string>
791 
792  $parsedFrame['url'] = trim($parsedFrame['data']);
793  if (!empty($parsedFrame['framenameshort']) && $parsedFrame['url']) {
794  $info['id3v2']['comments'][$parsedFrame['framenameshort']][] = $parsedFrame['url'];
795  }
796  unset($parsedFrame['data']);
797 
798 
799  } elseif ((($id3v2_majorversion == 3) && ($parsedFrame['frame_name'] == 'IPLS')) || // 4.4 IPLS Involved people list (ID3v2.3 only)
800  (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'IPL'))) { // 4.4 IPL Involved people list (ID3v2.2 only)
801  // http://id3.org/id3v2.3.0#sec4.4
802  // There may only be one 'IPL' frame in each tag
803  // <Header for 'User defined URL link frame', ID: 'IPL'>
804  // Text encoding $xx
805  // People list strings <textstrings>
806 
807  $frame_offset = 0;
808  $frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1));
809  if ((($id3v2_majorversion <= 3) && ($frame_textencoding > 1)) || (($id3v2_majorversion == 4) && ($frame_textencoding > 3))) {
810  $this->warning('Invalid text encoding byte ('.$frame_textencoding.') in frame "'.$parsedFrame['frame_name'].'" - defaulting to ISO-8859-1 encoding');
811  }
812  $parsedFrame['encodingid'] = $frame_textencoding;
813  $parsedFrame['encoding'] = $this->TextEncodingNameLookup($parsedFrame['encodingid']);
814  $parsedFrame['data_raw'] = (string) substr($parsedFrame['data'], $frame_offset);
815 
816  // http://www.getid3.org/phpBB3/viewtopic.php?t=1369
817  // "this tag typically contains null terminated strings, which are associated in pairs"
818  // "there are users that use the tag incorrectly"
819  $IPLS_parts = array();
820  if (strpos($parsedFrame['data_raw'], "\x00") !== false) {
821  $IPLS_parts_unsorted = array();
822  if (((strlen($parsedFrame['data_raw']) % 2) == 0) && ((substr($parsedFrame['data_raw'], 0, 2) == "\xFF\xFE") || (substr($parsedFrame['data_raw'], 0, 2) == "\xFE\xFF"))) {
823  // 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
824  $thisILPS = '';
825  for ($i = 0; $i < strlen($parsedFrame['data_raw']); $i += 2) {
826  $twobytes = substr($parsedFrame['data_raw'], $i, 2);
827  if ($twobytes === "\x00\x00") {
828  $IPLS_parts_unsorted[] = getid3_lib::iconv_fallback($parsedFrame['encoding'], $info['id3v2']['encoding'], $thisILPS);
829  $thisILPS = '';
830  } else {
831  $thisILPS .= $twobytes;
832  }
833  }
834  if (strlen($thisILPS) > 2) { // 2-byte BOM
835  $IPLS_parts_unsorted[] = getid3_lib::iconv_fallback($parsedFrame['encoding'], $info['id3v2']['encoding'], $thisILPS);
836  }
837  } else {
838  // ISO-8859-1 or UTF-8 or other single-byte-null character set
839  $IPLS_parts_unsorted = explode("\x00", $parsedFrame['data_raw']);
840  }
841  if (count($IPLS_parts_unsorted) == 1) {
842  // just a list of names, e.g. "Dino Baptiste, Jimmy Copley, John Gordon, Bernie Marsden, Sharon Watson"
843  foreach ($IPLS_parts_unsorted as $key => $value) {
844  $IPLS_parts_sorted = preg_split('#[;,\\r\\n\\t]#', $value);
845  $position = '';
846  foreach ($IPLS_parts_sorted as $person) {
847  $IPLS_parts[] = array('position'=>$position, 'person'=>$person);
848  }
849  }
850  } elseif ((count($IPLS_parts_unsorted) % 2) == 0) {
851  $position = '';
852  $person = '';
853  foreach ($IPLS_parts_unsorted as $key => $value) {
854  if (($key % 2) == 0) {
855  $position = $value;
856  } else {
857  $person = $value;
858  $IPLS_parts[] = array('position'=>$position, 'person'=>$person);
859  $position = '';
860  $person = '';
861  }
862  }
863  } else {
864  foreach ($IPLS_parts_unsorted as $key => $value) {
865  $IPLS_parts[] = array($value);
866  }
867  }
868 
869  } else {
870  $IPLS_parts = preg_split('#[;,\\r\\n\\t]#', $parsedFrame['data_raw']);
871  }
872  $parsedFrame['data'] = $IPLS_parts;
873 
874  if (!empty($parsedFrame['framenameshort']) && !empty($parsedFrame['data'])) {
875  $info['id3v2']['comments'][$parsedFrame['framenameshort']][] = $parsedFrame['data'];
876  }
877 
878 
879  } elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'MCDI')) || // 4.4 MCDI Music CD identifier
880  (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'MCI'))) { // 4.5 MCI Music CD identifier
881  // There may only be one 'MCDI' frame in each tag
882  // <Header for 'Music CD identifier', ID: 'MCDI'>
883  // CD TOC <binary data>
884 
885  if (!empty($parsedFrame['framenameshort']) && !empty($parsedFrame['data'])) {
886  $info['id3v2']['comments'][$parsedFrame['framenameshort']][] = $parsedFrame['data'];
887  }
888 
889 
890  } elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'ETCO')) || // 4.5 ETCO Event timing codes
891  (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'ETC'))) { // 4.6 ETC Event timing codes
892  // There may only be one 'ETCO' frame in each tag
893  // <Header for 'Event timing codes', ID: 'ETCO'>
894  // Time stamp format $xx
895  // Where time stamp format is:
896  // $01 (32-bit value) MPEG frames from beginning of file
897  // $02 (32-bit value) milliseconds from beginning of file
898  // Followed by a list of key events in the following format:
899  // Type of event $xx
900  // Time stamp $xx (xx ...)
901  // The 'Time stamp' is set to zero if directly at the beginning of the sound
902  // or after the previous event. All events MUST be sorted in chronological order.
903 
904  $frame_offset = 0;
905  $parsedFrame['timestampformat'] = ord(substr($parsedFrame['data'], $frame_offset++, 1));
906 
907  while ($frame_offset < strlen($parsedFrame['data'])) {
908  $parsedFrame['typeid'] = substr($parsedFrame['data'], $frame_offset++, 1);
909  $parsedFrame['type'] = $this->ETCOEventLookup($parsedFrame['typeid']);
910  $parsedFrame['timestamp'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 4));
911  $frame_offset += 4;
912  }
913  unset($parsedFrame['data']);
914 
915 
916  } elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'MLLT')) || // 4.6 MLLT MPEG location lookup table
917  (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'MLL'))) { // 4.7 MLL MPEG location lookup table
918  // There may only be one 'MLLT' frame in each tag
919  // <Header for 'Location lookup table', ID: 'MLLT'>
920  // MPEG frames between reference $xx xx
921  // Bytes between reference $xx xx xx
922  // Milliseconds between reference $xx xx xx
923  // Bits for bytes deviation $xx
924  // Bits for milliseconds dev. $xx
925  // Then for every reference the following data is included;
926  // Deviation in bytes %xxx....
927  // Deviation in milliseconds %xxx....
928 
929  $frame_offset = 0;
930  $parsedFrame['framesbetweenreferences'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], 0, 2));
931  $parsedFrame['bytesbetweenreferences'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], 2, 3));
932  $parsedFrame['msbetweenreferences'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], 5, 3));
933  $parsedFrame['bitsforbytesdeviation'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], 8, 1));
934  $parsedFrame['bitsformsdeviation'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], 9, 1));
935  $parsedFrame['data'] = substr($parsedFrame['data'], 10);
936  while ($frame_offset < strlen($parsedFrame['data'])) {
937  $deviationbitstream .= getid3_lib::BigEndian2Bin(substr($parsedFrame['data'], $frame_offset++, 1));
938  }
939  $reference_counter = 0;
940  while (strlen($deviationbitstream) > 0) {
941  $parsedFrame[$reference_counter]['bytedeviation'] = bindec(substr($deviationbitstream, 0, $parsedFrame['bitsforbytesdeviation']));
942  $parsedFrame[$reference_counter]['msdeviation'] = bindec(substr($deviationbitstream, $parsedFrame['bitsforbytesdeviation'], $parsedFrame['bitsformsdeviation']));
943  $deviationbitstream = substr($deviationbitstream, $parsedFrame['bitsforbytesdeviation'] + $parsedFrame['bitsformsdeviation']);
944  $reference_counter++;
945  }
946  unset($parsedFrame['data']);
947 
948 
949  } elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'SYTC')) || // 4.7 SYTC Synchronised tempo codes
950  (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'STC'))) { // 4.8 STC Synchronised tempo codes
951  // There may only be one 'SYTC' frame in each tag
952  // <Header for 'Synchronised tempo codes', ID: 'SYTC'>
953  // Time stamp format $xx
954  // Tempo data <binary data>
955  // Where time stamp format is:
956  // $01 (32-bit value) MPEG frames from beginning of file
957  // $02 (32-bit value) milliseconds from beginning of file
958 
959  $frame_offset = 0;
960  $parsedFrame['timestampformat'] = ord(substr($parsedFrame['data'], $frame_offset++, 1));
961  $timestamp_counter = 0;
962  while ($frame_offset < strlen($parsedFrame['data'])) {
963  $parsedFrame[$timestamp_counter]['tempo'] = ord(substr($parsedFrame['data'], $frame_offset++, 1));
964  if ($parsedFrame[$timestamp_counter]['tempo'] == 255) {
965  $parsedFrame[$timestamp_counter]['tempo'] += ord(substr($parsedFrame['data'], $frame_offset++, 1));
966  }
967  $parsedFrame[$timestamp_counter]['timestamp'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 4));
968  $frame_offset += 4;
969  $timestamp_counter++;
970  }
971  unset($parsedFrame['data']);
972 
973 
974  } elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'USLT')) || // 4.8 USLT Unsynchronised lyric/text transcription
975  (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'ULT'))) { // 4.9 ULT Unsynchronised lyric/text transcription
976  // There may be more than one 'Unsynchronised lyrics/text transcription' frame
977  // in each tag, but only one with the same language and content descriptor.
978  // <Header for 'Unsynchronised lyrics/text transcription', ID: 'USLT'>
979  // Text encoding $xx
980  // Language $xx xx xx
981  // Content descriptor <text string according to encoding> $00 (00)
982  // Lyrics/text <full text string according to encoding>
983 
984  $frame_offset = 0;
985  $frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1));
986  $frame_textencoding_terminator = $this->TextEncodingTerminatorLookup($frame_textencoding);
987  if ((($id3v2_majorversion <= 3) && ($frame_textencoding > 1)) || (($id3v2_majorversion == 4) && ($frame_textencoding > 3))) {
988  $this->warning('Invalid text encoding byte ('.$frame_textencoding.') in frame "'.$parsedFrame['frame_name'].'" - defaulting to ISO-8859-1 encoding');
989  $frame_textencoding_terminator = "\x00";
990  }
991  $frame_language = substr($parsedFrame['data'], $frame_offset, 3);
992  $frame_offset += 3;
993  $frame_terminatorpos = strpos($parsedFrame['data'], $frame_textencoding_terminator, $frame_offset);
994  if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($frame_textencoding_terminator), 1)) === 0) {
995  $frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00
996  }
997  $frame_description = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
998  if (in_array($frame_description, array("\x00", "\x00\x00", "\xFF\xFE", "\xFE\xFF"))) {
999  // if description only contains a BOM or terminator then make it blank
1000  $frame_description = '';
1001  }
1002  $parsedFrame['data'] = substr($parsedFrame['data'], $frame_terminatorpos + strlen($frame_textencoding_terminator));
1003 
1004  $parsedFrame['encodingid'] = $frame_textencoding;
1005  $parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding);
1006 
1007  $parsedFrame['language'] = $frame_language;
1008  $parsedFrame['languagename'] = $this->LanguageLookup($frame_language, false);
1009  $parsedFrame['description'] = $frame_description;
1010  if (!empty($parsedFrame['framenameshort']) && !empty($parsedFrame['data'])) {
1011  $info['id3v2']['comments'][$parsedFrame['framenameshort']][] = getid3_lib::iconv_fallback($parsedFrame['encoding'], $info['id3v2']['encoding'], $parsedFrame['data']);
1012  }
1013  unset($parsedFrame['data']);
1014 
1015 
1016  } elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'SYLT')) || // 4.9 SYLT Synchronised lyric/text
1017  (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'SLT'))) { // 4.10 SLT Synchronised lyric/text
1018  // There may be more than one 'SYLT' frame in each tag,
1019  // but only one with the same language and content descriptor.
1020  // <Header for 'Synchronised lyrics/text', ID: 'SYLT'>
1021  // Text encoding $xx
1022  // Language $xx xx xx
1023  // Time stamp format $xx
1024  // $01 (32-bit value) MPEG frames from beginning of file
1025  // $02 (32-bit value) milliseconds from beginning of file
1026  // Content type $xx
1027  // Content descriptor <text string according to encoding> $00 (00)
1028  // Terminated text to be synced (typically a syllable)
1029  // Sync identifier (terminator to above string) $00 (00)
1030  // Time stamp $xx (xx ...)
1031 
1032  $frame_offset = 0;
1033  $frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1));
1034  $frame_textencoding_terminator = $this->TextEncodingTerminatorLookup($frame_textencoding);
1035  if ((($id3v2_majorversion <= 3) && ($frame_textencoding > 1)) || (($id3v2_majorversion == 4) && ($frame_textencoding > 3))) {
1036  $this->warning('Invalid text encoding byte ('.$frame_textencoding.') in frame "'.$parsedFrame['frame_name'].'" - defaulting to ISO-8859-1 encoding');
1037  $frame_textencoding_terminator = "\x00";
1038  }
1039  $frame_language = substr($parsedFrame['data'], $frame_offset, 3);
1040  $frame_offset += 3;
1041  $parsedFrame['timestampformat'] = ord(substr($parsedFrame['data'], $frame_offset++, 1));
1042  $parsedFrame['contenttypeid'] = ord(substr($parsedFrame['data'], $frame_offset++, 1));
1043  $parsedFrame['contenttype'] = $this->SYTLContentTypeLookup($parsedFrame['contenttypeid']);
1044  $parsedFrame['encodingid'] = $frame_textencoding;
1045  $parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding);
1046 
1047  $parsedFrame['language'] = $frame_language;
1048  $parsedFrame['languagename'] = $this->LanguageLookup($frame_language, false);
1049 
1050  $timestampindex = 0;
1051  $frame_remainingdata = substr($parsedFrame['data'], $frame_offset);
1052  while (strlen($frame_remainingdata)) {
1053  $frame_offset = 0;
1054  $frame_terminatorpos = strpos($frame_remainingdata, $frame_textencoding_terminator);
1055  if ($frame_terminatorpos === false) {
1056  $frame_remainingdata = '';
1057  } else {
1058  if (ord(substr($frame_remainingdata, $frame_terminatorpos + strlen($frame_textencoding_terminator), 1)) === 0) {
1059  $frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00
1060  }
1061  $parsedFrame['lyrics'][$timestampindex]['data'] = substr($frame_remainingdata, $frame_offset, $frame_terminatorpos - $frame_offset);
1062 
1063  $frame_remainingdata = substr($frame_remainingdata, $frame_terminatorpos + strlen($frame_textencoding_terminator));
1064  if (($timestampindex == 0) && (ord($frame_remainingdata{0}) != 0)) {
1065  // timestamp probably omitted for first data item
1066  } else {
1067  $parsedFrame['lyrics'][$timestampindex]['timestamp'] = getid3_lib::BigEndian2Int(substr($frame_remainingdata, 0, 4));
1068  $frame_remainingdata = substr($frame_remainingdata, 4);
1069  }
1070  $timestampindex++;
1071  }
1072  }
1073  unset($parsedFrame['data']);
1074 
1075 
1076  } elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'COMM')) || // 4.10 COMM Comments
1077  (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'COM'))) { // 4.11 COM Comments
1078  // There may be more than one comment frame in each tag,
1079  // but only one with the same language and content descriptor.
1080  // <Header for 'Comment', ID: 'COMM'>
1081  // Text encoding $xx
1082  // Language $xx xx xx
1083  // Short content descrip. <text string according to encoding> $00 (00)
1084  // The actual text <full text string according to encoding>
1085 
1086  if (strlen($parsedFrame['data']) < 5) {
1087 
1088  $this->warning('Invalid data (too short) for "'.$parsedFrame['frame_name'].'" frame at offset '.$parsedFrame['dataoffset']);
1089 
1090  } else {
1091 
1092  $frame_offset = 0;
1093  $frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1));
1094  $frame_textencoding_terminator = $this->TextEncodingTerminatorLookup($frame_textencoding);
1095  if ((($id3v2_majorversion <= 3) && ($frame_textencoding > 1)) || (($id3v2_majorversion == 4) && ($frame_textencoding > 3))) {
1096  $this->warning('Invalid text encoding byte ('.$frame_textencoding.') in frame "'.$parsedFrame['frame_name'].'" - defaulting to ISO-8859-1 encoding');
1097  $frame_textencoding_terminator = "\x00";
1098  }
1099  $frame_language = substr($parsedFrame['data'], $frame_offset, 3);
1100  $frame_offset += 3;
1101  $frame_terminatorpos = strpos($parsedFrame['data'], $frame_textencoding_terminator, $frame_offset);
1102  if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($frame_textencoding_terminator), 1)) === 0) {
1103  $frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00
1104  }
1105  $frame_description = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
1106  if (in_array($frame_description, array("\x00", "\x00\x00", "\xFF\xFE", "\xFE\xFF"))) {
1107  // if description only contains a BOM or terminator then make it blank
1108  $frame_description = '';
1109  }
1110  $frame_text = (string) substr($parsedFrame['data'], $frame_terminatorpos + strlen($frame_textencoding_terminator));
1111 
1112  $parsedFrame['encodingid'] = $frame_textencoding;
1113  $parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding);
1114 
1115  $parsedFrame['language'] = $frame_language;
1116  $parsedFrame['languagename'] = $this->LanguageLookup($frame_language, false);
1117  $parsedFrame['description'] = $frame_description;
1118  $parsedFrame['data'] = $frame_text;
1119  if (!empty($parsedFrame['framenameshort']) && !empty($parsedFrame['data'])) {
1120  $commentkey = ($parsedFrame['description'] ? $parsedFrame['description'] : (!empty($info['id3v2']['comments'][$parsedFrame['framenameshort']]) ? count($info['id3v2']['comments'][$parsedFrame['framenameshort']]) : 0));
1121  if (!isset($info['id3v2']['comments'][$parsedFrame['framenameshort']]) || !array_key_exists($commentkey, $info['id3v2']['comments'][$parsedFrame['framenameshort']])) {
1122  $info['id3v2']['comments'][$parsedFrame['framenameshort']][$commentkey] = getid3_lib::iconv_fallback($parsedFrame['encoding'], $info['id3v2']['encoding'], $parsedFrame['data']);
1123  } else {
1124  $info['id3v2']['comments'][$parsedFrame['framenameshort']][] = getid3_lib::iconv_fallback($parsedFrame['encoding'], $info['id3v2']['encoding'], $parsedFrame['data']);
1125  }
1126  }
1127 
1128  }
1129 
1130  } elseif (($id3v2_majorversion >= 4) && ($parsedFrame['frame_name'] == 'RVA2')) { // 4.11 RVA2 Relative volume adjustment (2) (ID3v2.4+ only)
1131  // There may be more than one 'RVA2' frame in each tag,
1132  // but only one with the same identification string
1133  // <Header for 'Relative volume adjustment (2)', ID: 'RVA2'>
1134  // Identification <text string> $00
1135  // The 'identification' string is used to identify the situation and/or
1136  // device where this adjustment should apply. The following is then
1137  // repeated for every channel:
1138  // Type of channel $xx
1139  // Volume adjustment $xx xx
1140  // Bits representing peak $xx
1141  // Peak volume $xx (xx ...)
1142 
1143  $frame_terminatorpos = strpos($parsedFrame['data'], "\x00");
1144  $frame_idstring = substr($parsedFrame['data'], 0, $frame_terminatorpos);
1145  if (ord($frame_idstring) === 0) {
1146  $frame_idstring = '';
1147  }
1148  $frame_remainingdata = substr($parsedFrame['data'], $frame_terminatorpos + strlen("\x00"));
1149  $parsedFrame['description'] = $frame_idstring;
1150  $RVA2channelcounter = 0;
1151  while (strlen($frame_remainingdata) >= 5) {
1152  $frame_offset = 0;
1153  $frame_channeltypeid = ord(substr($frame_remainingdata, $frame_offset++, 1));
1154  $parsedFrame[$RVA2channelcounter]['channeltypeid'] = $frame_channeltypeid;
1155  $parsedFrame[$RVA2channelcounter]['channeltype'] = $this->RVA2ChannelTypeLookup($frame_channeltypeid);
1156  $parsedFrame[$RVA2channelcounter]['volumeadjust'] = getid3_lib::BigEndian2Int(substr($frame_remainingdata, $frame_offset, 2), false, true); // 16-bit signed
1157  $frame_offset += 2;
1158  $parsedFrame[$RVA2channelcounter]['bitspeakvolume'] = ord(substr($frame_remainingdata, $frame_offset++, 1));
1159  if (($parsedFrame[$RVA2channelcounter]['bitspeakvolume'] < 1) || ($parsedFrame[$RVA2channelcounter]['bitspeakvolume'] > 4)) {
1160  $this->warning('ID3v2::RVA2 frame['.$RVA2channelcounter.'] contains invalid '.$parsedFrame[$RVA2channelcounter]['bitspeakvolume'].'-byte bits-representing-peak value');
1161  break;
1162  }
1163  $frame_bytespeakvolume = ceil($parsedFrame[$RVA2channelcounter]['bitspeakvolume'] / 8);
1164  $parsedFrame[$RVA2channelcounter]['peakvolume'] = getid3_lib::BigEndian2Int(substr($frame_remainingdata, $frame_offset, $frame_bytespeakvolume));
1165  $frame_remainingdata = substr($frame_remainingdata, $frame_offset + $frame_bytespeakvolume);
1166  $RVA2channelcounter++;
1167  }
1168  unset($parsedFrame['data']);
1169 
1170 
1171  } elseif ((($id3v2_majorversion == 3) && ($parsedFrame['frame_name'] == 'RVAD')) || // 4.12 RVAD Relative volume adjustment (ID3v2.3 only)
1172  (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'RVA'))) { // 4.12 RVA Relative volume adjustment (ID3v2.2 only)
1173  // There may only be one 'RVA' frame in each tag
1174  // <Header for 'Relative volume adjustment', ID: 'RVA'>
1175  // ID3v2.2 => Increment/decrement %000000ba
1176  // ID3v2.3 => Increment/decrement %00fedcba
1177  // Bits used for volume descr. $xx
1178  // Relative volume change, right $xx xx (xx ...) // a
1179  // Relative volume change, left $xx xx (xx ...) // b
1180  // Peak volume right $xx xx (xx ...)
1181  // Peak volume left $xx xx (xx ...)
1182  // ID3v2.3 only, optional (not present in ID3v2.2):
1183  // Relative volume change, right back $xx xx (xx ...) // c
1184  // Relative volume change, left back $xx xx (xx ...) // d
1185  // Peak volume right back $xx xx (xx ...)
1186  // Peak volume left back $xx xx (xx ...)
1187  // ID3v2.3 only, optional (not present in ID3v2.2):
1188  // Relative volume change, center $xx xx (xx ...) // e
1189  // Peak volume center $xx xx (xx ...)
1190  // ID3v2.3 only, optional (not present in ID3v2.2):
1191  // Relative volume change, bass $xx xx (xx ...) // f
1192  // Peak volume bass $xx xx (xx ...)
1193 
1194  $frame_offset = 0;
1195  $frame_incrdecrflags = getid3_lib::BigEndian2Bin(substr($parsedFrame['data'], $frame_offset++, 1));
1196  $parsedFrame['incdec']['right'] = (bool) substr($frame_incrdecrflags, 6, 1);
1197  $parsedFrame['incdec']['left'] = (bool) substr($frame_incrdecrflags, 7, 1);
1198  $parsedFrame['bitsvolume'] = ord(substr($parsedFrame['data'], $frame_offset++, 1));
1199  $frame_bytesvolume = ceil($parsedFrame['bitsvolume'] / 8);
1200  $parsedFrame['volumechange']['right'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesvolume));
1201  if ($parsedFrame['incdec']['right'] === false) {
1202  $parsedFrame['volumechange']['right'] *= -1;
1203  }
1204  $frame_offset += $frame_bytesvolume;
1205  $parsedFrame['volumechange']['left'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesvolume));
1206  if ($parsedFrame['incdec']['left'] === false) {
1207  $parsedFrame['volumechange']['left'] *= -1;
1208  }
1209  $frame_offset += $frame_bytesvolume;
1210  $parsedFrame['peakvolume']['right'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesvolume));
1211  $frame_offset += $frame_bytesvolume;
1212  $parsedFrame['peakvolume']['left'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesvolume));
1213  $frame_offset += $frame_bytesvolume;
1214  if ($id3v2_majorversion == 3) {
1215  $parsedFrame['data'] = substr($parsedFrame['data'], $frame_offset);
1216  if (strlen($parsedFrame['data']) > 0) {
1217  $parsedFrame['incdec']['rightrear'] = (bool) substr($frame_incrdecrflags, 4, 1);
1218  $parsedFrame['incdec']['leftrear'] = (bool) substr($frame_incrdecrflags, 5, 1);
1219  $parsedFrame['volumechange']['rightrear'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesvolume));
1220  if ($parsedFrame['incdec']['rightrear'] === false) {
1221  $parsedFrame['volumechange']['rightrear'] *= -1;
1222  }
1223  $frame_offset += $frame_bytesvolume;
1224  $parsedFrame['volumechange']['leftrear'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesvolume));
1225  if ($parsedFrame['incdec']['leftrear'] === false) {
1226  $parsedFrame['volumechange']['leftrear'] *= -1;
1227  }
1228  $frame_offset += $frame_bytesvolume;
1229  $parsedFrame['peakvolume']['rightrear'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesvolume));
1230  $frame_offset += $frame_bytesvolume;
1231  $parsedFrame['peakvolume']['leftrear'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesvolume));
1232  $frame_offset += $frame_bytesvolume;
1233  }
1234  $parsedFrame['data'] = substr($parsedFrame['data'], $frame_offset);
1235  if (strlen($parsedFrame['data']) > 0) {
1236  $parsedFrame['incdec']['center'] = (bool) substr($frame_incrdecrflags, 3, 1);
1237  $parsedFrame['volumechange']['center'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesvolume));
1238  if ($parsedFrame['incdec']['center'] === false) {
1239  $parsedFrame['volumechange']['center'] *= -1;
1240  }
1241  $frame_offset += $frame_bytesvolume;
1242  $parsedFrame['peakvolume']['center'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesvolume));
1243  $frame_offset += $frame_bytesvolume;
1244  }
1245  $parsedFrame['data'] = substr($parsedFrame['data'], $frame_offset);
1246  if (strlen($parsedFrame['data']) > 0) {
1247  $parsedFrame['incdec']['bass'] = (bool) substr($frame_incrdecrflags, 2, 1);
1248  $parsedFrame['volumechange']['bass'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesvolume));
1249  if ($parsedFrame['incdec']['bass'] === false) {
1250  $parsedFrame['volumechange']['bass'] *= -1;
1251  }
1252  $frame_offset += $frame_bytesvolume;
1253  $parsedFrame['peakvolume']['bass'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesvolume));
1254  $frame_offset += $frame_bytesvolume;
1255  }
1256  }
1257  unset($parsedFrame['data']);
1258 
1259 
1260  } elseif (($id3v2_majorversion >= 4) && ($parsedFrame['frame_name'] == 'EQU2')) { // 4.12 EQU2 Equalisation (2) (ID3v2.4+ only)
1261  // There may be more than one 'EQU2' frame in each tag,
1262  // but only one with the same identification string
1263  // <Header of 'Equalisation (2)', ID: 'EQU2'>
1264  // Interpolation method $xx
1265  // $00 Band
1266  // $01 Linear
1267  // Identification <text string> $00
1268  // The following is then repeated for every adjustment point
1269  // Frequency $xx xx
1270  // Volume adjustment $xx xx
1271 
1272  $frame_offset = 0;
1273  $frame_interpolationmethod = ord(substr($parsedFrame['data'], $frame_offset++, 1));
1274  $frame_terminatorpos = strpos($parsedFrame['data'], "\x00", $frame_offset);
1275  $frame_idstring = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
1276  if (ord($frame_idstring) === 0) {
1277  $frame_idstring = '';
1278  }
1279  $parsedFrame['description'] = $frame_idstring;
1280  $frame_remainingdata = substr($parsedFrame['data'], $frame_terminatorpos + strlen("\x00"));
1281  while (strlen($frame_remainingdata)) {
1282  $frame_frequency = getid3_lib::BigEndian2Int(substr($frame_remainingdata, 0, 2)) / 2;
1283  $parsedFrame['data'][$frame_frequency] = getid3_lib::BigEndian2Int(substr($frame_remainingdata, 2, 2), false, true);
1284  $frame_remainingdata = substr($frame_remainingdata, 4);
1285  }
1286  $parsedFrame['interpolationmethod'] = $frame_interpolationmethod;
1287  unset($parsedFrame['data']);
1288 
1289 
1290  } elseif ((($id3v2_majorversion == 3) && ($parsedFrame['frame_name'] == 'EQUA')) || // 4.12 EQUA Equalisation (ID3v2.3 only)
1291  (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'EQU'))) { // 4.13 EQU Equalisation (ID3v2.2 only)
1292  // There may only be one 'EQUA' frame in each tag
1293  // <Header for 'Relative volume adjustment', ID: 'EQU'>
1294  // Adjustment bits $xx
1295  // This is followed by 2 bytes + ('adjustment bits' rounded up to the
1296  // nearest byte) for every equalisation band in the following format,
1297  // giving a frequency range of 0 - 32767Hz:
1298  // Increment/decrement %x (MSB of the Frequency)
1299  // Frequency (lower 15 bits)
1300  // Adjustment $xx (xx ...)
1301 
1302  $frame_offset = 0;
1303  $parsedFrame['adjustmentbits'] = substr($parsedFrame['data'], $frame_offset++, 1);
1304  $frame_adjustmentbytes = ceil($parsedFrame['adjustmentbits'] / 8);
1305 
1306  $frame_remainingdata = (string) substr($parsedFrame['data'], $frame_offset);
1307  while (strlen($frame_remainingdata) > 0) {
1308  $frame_frequencystr = getid3_lib::BigEndian2Bin(substr($frame_remainingdata, 0, 2));
1309  $frame_incdec = (bool) substr($frame_frequencystr, 0, 1);
1310  $frame_frequency = bindec(substr($frame_frequencystr, 1, 15));
1311  $parsedFrame[$frame_frequency]['incdec'] = $frame_incdec;
1312  $parsedFrame[$frame_frequency]['adjustment'] = getid3_lib::BigEndian2Int(substr($frame_remainingdata, 2, $frame_adjustmentbytes));
1313  if ($parsedFrame[$frame_frequency]['incdec'] === false) {
1314  $parsedFrame[$frame_frequency]['adjustment'] *= -1;
1315  }
1316  $frame_remainingdata = substr($frame_remainingdata, 2 + $frame_adjustmentbytes);
1317  }
1318  unset($parsedFrame['data']);
1319 
1320 
1321  } elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'RVRB')) || // 4.13 RVRB Reverb
1322  (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'REV'))) { // 4.14 REV Reverb
1323  // There may only be one 'RVRB' frame in each tag.
1324  // <Header for 'Reverb', ID: 'RVRB'>
1325  // Reverb left (ms) $xx xx
1326  // Reverb right (ms) $xx xx
1327  // Reverb bounces, left $xx
1328  // Reverb bounces, right $xx
1329  // Reverb feedback, left to left $xx
1330  // Reverb feedback, left to right $xx
1331  // Reverb feedback, right to right $xx
1332  // Reverb feedback, right to left $xx
1333  // Premix left to right $xx
1334  // Premix right to left $xx
1335 
1336  $frame_offset = 0;
1337  $parsedFrame['left'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 2));
1338  $frame_offset += 2;
1339  $parsedFrame['right'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 2));
1340  $frame_offset += 2;
1341  $parsedFrame['bouncesL'] = ord(substr($parsedFrame['data'], $frame_offset++, 1));
1342  $parsedFrame['bouncesR'] = ord(substr($parsedFrame['data'], $frame_offset++, 1));
1343  $parsedFrame['feedbackLL'] = ord(substr($parsedFrame['data'], $frame_offset++, 1));
1344  $parsedFrame['feedbackLR'] = ord(substr($parsedFrame['data'], $frame_offset++, 1));
1345  $parsedFrame['feedbackRR'] = ord(substr($parsedFrame['data'], $frame_offset++, 1));
1346  $parsedFrame['feedbackRL'] = ord(substr($parsedFrame['data'], $frame_offset++, 1));
1347  $parsedFrame['premixLR'] = ord(substr($parsedFrame['data'], $frame_offset++, 1));
1348  $parsedFrame['premixRL'] = ord(substr($parsedFrame['data'], $frame_offset++, 1));
1349  unset($parsedFrame['data']);
1350 
1351 
1352  } elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'APIC')) || // 4.14 APIC Attached picture
1353  (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'PIC'))) { // 4.15 PIC Attached picture
1354  // There may be several pictures attached to one file,
1355  // each in their individual 'APIC' frame, but only one
1356  // with the same content descriptor
1357  // <Header for 'Attached picture', ID: 'APIC'>
1358  // Text encoding $xx
1359  // ID3v2.3+ => MIME type <text string> $00
1360  // ID3v2.2 => Image format $xx xx xx
1361  // Picture type $xx
1362  // Description <text string according to encoding> $00 (00)
1363  // Picture data <binary data>
1364 
1365  $frame_offset = 0;
1366  $frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1));
1367  $frame_textencoding_terminator = $this->TextEncodingTerminatorLookup($frame_textencoding);
1368  if ((($id3v2_majorversion <= 3) && ($frame_textencoding > 1)) || (($id3v2_majorversion == 4) && ($frame_textencoding > 3))) {
1369  $this->warning('Invalid text encoding byte ('.$frame_textencoding.') in frame "'.$parsedFrame['frame_name'].'" - defaulting to ISO-8859-1 encoding');
1370  $frame_textencoding_terminator = "\x00";
1371  }
1372 
1373  if ($id3v2_majorversion == 2 && strlen($parsedFrame['data']) > $frame_offset) {
1374  $frame_imagetype = substr($parsedFrame['data'], $frame_offset, 3);
1375  if (strtolower($frame_imagetype) == 'ima') {
1376  // complete hack for mp3Rage (www.chaoticsoftware.com) that puts ID3v2.3-formatted
1377  // MIME type instead of 3-char ID3v2.2-format image type (thanks xbhoffØpacbell*net)
1378  $frame_terminatorpos = strpos($parsedFrame['data'], "\x00", $frame_offset);
1379  $frame_mimetype = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
1380  if (ord($frame_mimetype) === 0) {
1381  $frame_mimetype = '';
1382  }
1383  $frame_imagetype = strtoupper(str_replace('image/', '', strtolower($frame_mimetype)));
1384  if ($frame_imagetype == 'JPEG') {
1385  $frame_imagetype = 'JPG';
1386  }
1387  $frame_offset = $frame_terminatorpos + strlen("\x00");
1388  } else {
1389  $frame_offset += 3;
1390  }
1391  }
1392  if ($id3v2_majorversion > 2 && strlen($parsedFrame['data']) > $frame_offset) {
1393  $frame_terminatorpos = strpos($parsedFrame['data'], "\x00", $frame_offset);
1394  $frame_mimetype = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
1395  if (ord($frame_mimetype) === 0) {
1396  $frame_mimetype = '';
1397  }
1398  $frame_offset = $frame_terminatorpos + strlen("\x00");
1399  }
1400 
1401  $frame_picturetype = ord(substr($parsedFrame['data'], $frame_offset++, 1));
1402 
1403  if ($frame_offset >= $parsedFrame['datalength']) {
1404  $this->warning('data portion of APIC frame is missing at offset '.($parsedFrame['dataoffset'] + 8 + $frame_offset));
1405  } else {
1406  $frame_terminatorpos = strpos($parsedFrame['data'], $frame_textencoding_terminator, $frame_offset);
1407  if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($frame_textencoding_terminator), 1)) === 0) {
1408  $frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00
1409  }
1410  $frame_description = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
1411  if (in_array($frame_description, array("\x00", "\x00\x00", "\xFF\xFE", "\xFE\xFF"))) {
1412  // if description only contains a BOM or terminator then make it blank
1413  $frame_description = '';
1414  }
1415  $parsedFrame['encodingid'] = $frame_textencoding;
1416  $parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding);
1417 
1418  if ($id3v2_majorversion == 2) {
1419  $parsedFrame['imagetype'] = $frame_imagetype;
1420  } else {
1421  $parsedFrame['mime'] = $frame_mimetype;
1422  }
1423  $parsedFrame['picturetypeid'] = $frame_picturetype;
1424  $parsedFrame['picturetype'] = $this->APICPictureTypeLookup($frame_picturetype);
1425  $parsedFrame['description'] = $frame_description;
1426  $parsedFrame['data'] = substr($parsedFrame['data'], $frame_terminatorpos + strlen($frame_textencoding_terminator));
1427  $parsedFrame['datalength'] = strlen($parsedFrame['data']);
1428 
1429  $parsedFrame['image_mime'] = '';
1430  $imageinfo = array();
1431  if ($imagechunkcheck = getid3_lib::GetDataImageSize($parsedFrame['data'], $imageinfo)) {
1432  if (($imagechunkcheck[2] >= 1) && ($imagechunkcheck[2] <= 3)) {
1433  $parsedFrame['image_mime'] = 'image/'.getid3_lib::ImageTypesLookup($imagechunkcheck[2]);
1434  if ($imagechunkcheck[0]) {
1435  $parsedFrame['image_width'] = $imagechunkcheck[0];
1436  }
1437  if ($imagechunkcheck[1]) {
1438  $parsedFrame['image_height'] = $imagechunkcheck[1];
1439  }
1440  }
1441  }
1442 
1443  do {
1444  if ($this->getid3->option_save_attachments === false) {
1445  // skip entirely
1446  unset($parsedFrame['data']);
1447  break;
1448  }
1449  if ($this->getid3->option_save_attachments === true) {
1450  // great
1451 /*
1452  } elseif (is_int($this->getid3->option_save_attachments)) {
1453  if ($this->getid3->option_save_attachments < $parsedFrame['data_length']) {
1454  // too big, skip
1455  $this->warning('attachment at '.$frame_offset.' is too large to process inline ('.number_format($parsedFrame['data_length']).' bytes)');
1456  unset($parsedFrame['data']);
1457  break;
1458  }
1459 */
1460  } elseif (is_string($this->getid3->option_save_attachments)) {
1461  $dir = rtrim(str_replace(array('/', '\\'), DIRECTORY_SEPARATOR, $this->getid3->option_save_attachments), DIRECTORY_SEPARATOR);
1462  if (!is_dir($dir) || !is_writable($dir)) {
1463  // cannot write, skip
1464  $this->warning('attachment at '.$frame_offset.' cannot be saved to "'.$dir.'" (not writable)');
1465  unset($parsedFrame['data']);
1466  break;
1467  }
1468  }
1469  // if we get this far, must be OK
1470  if (is_string($this->getid3->option_save_attachments)) {
1471  $destination_filename = $dir.DIRECTORY_SEPARATOR.md5($info['filenamepath']).'_'.$frame_offset;
1472  if (!file_exists($destination_filename) || is_writable($destination_filename)) {
1473  file_put_contents($destination_filename, $parsedFrame['data']);
1474  } else {
1475  $this->warning('attachment at '.$frame_offset.' cannot be saved to "'.$destination_filename.'" (not writable)');
1476  }
1477  $parsedFrame['data_filename'] = $destination_filename;
1478  unset($parsedFrame['data']);
1479  } else {
1480  if (!empty($parsedFrame['framenameshort']) && !empty($parsedFrame['data'])) {
1481  if (!isset($info['id3v2']['comments']['picture'])) {
1482  $info['id3v2']['comments']['picture'] = array();
1483  }
1484  $comments_picture_data = array();
1485  foreach (array('data', 'image_mime', 'image_width', 'image_height', 'imagetype', 'picturetype', 'description', 'datalength') as $picture_key) {
1486  if (isset($parsedFrame[$picture_key])) {
1487  $comments_picture_data[$picture_key] = $parsedFrame[$picture_key];
1488  }
1489  }
1490  $info['id3v2']['comments']['picture'][] = $comments_picture_data;
1491  unset($comments_picture_data);
1492  }
1493  }
1494  } while (false);
1495  }
1496 
1497  } elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'GEOB')) || // 4.15 GEOB General encapsulated object
1498  (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'GEO'))) { // 4.16 GEO General encapsulated object
1499  // There may be more than one 'GEOB' frame in each tag,
1500  // but only one with the same content descriptor
1501  // <Header for 'General encapsulated object', ID: 'GEOB'>
1502  // Text encoding $xx
1503  // MIME type <text string> $00
1504  // Filename <text string according to encoding> $00 (00)
1505  // Content description <text string according to encoding> $00 (00)
1506  // Encapsulated object <binary data>
1507 
1508  $frame_offset = 0;
1509  $frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1));
1510  $frame_textencoding_terminator = $this->TextEncodingTerminatorLookup($frame_textencoding);
1511  if ((($id3v2_majorversion <= 3) && ($frame_textencoding > 1)) || (($id3v2_majorversion == 4) && ($frame_textencoding > 3))) {
1512  $this->warning('Invalid text encoding byte ('.$frame_textencoding.') in frame "'.$parsedFrame['frame_name'].'" - defaulting to ISO-8859-1 encoding');
1513  $frame_textencoding_terminator = "\x00";
1514  }
1515  $frame_terminatorpos = strpos($parsedFrame['data'], "\x00", $frame_offset);
1516  $frame_mimetype = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
1517  if (ord($frame_mimetype) === 0) {
1518  $frame_mimetype = '';
1519  }
1520  $frame_offset = $frame_terminatorpos + strlen("\x00");
1521 
1522  $frame_terminatorpos = strpos($parsedFrame['data'], $frame_textencoding_terminator, $frame_offset);
1523  if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($frame_textencoding_terminator), 1)) === 0) {
1524  $frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00
1525  }
1526  $frame_filename = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
1527  if (ord($frame_filename) === 0) {
1528  $frame_filename = '';
1529  }
1530  $frame_offset = $frame_terminatorpos + strlen($frame_textencoding_terminator);
1531 
1532  $frame_terminatorpos = strpos($parsedFrame['data'], $frame_textencoding_terminator, $frame_offset);
1533  if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($frame_textencoding_terminator), 1)) === 0) {
1534  $frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00
1535  }
1536  $frame_description = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
1537  if (in_array($frame_description, array("\x00", "\x00\x00", "\xFF\xFE", "\xFE\xFF"))) {
1538  // if description only contains a BOM or terminator then make it blank
1539  $frame_description = '';
1540  }
1541  $frame_offset = $frame_terminatorpos + strlen($frame_textencoding_terminator);
1542 
1543  $parsedFrame['objectdata'] = (string) substr($parsedFrame['data'], $frame_offset);
1544  $parsedFrame['encodingid'] = $frame_textencoding;
1545  $parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding);
1546 
1547  $parsedFrame['mime'] = $frame_mimetype;
1548  $parsedFrame['filename'] = $frame_filename;
1549  $parsedFrame['description'] = $frame_description;
1550  unset($parsedFrame['data']);
1551 
1552 
1553  } elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'PCNT')) || // 4.16 PCNT Play counter
1554  (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'CNT'))) { // 4.17 CNT Play counter
1555  // There may only be one 'PCNT' frame in each tag.
1556  // When the counter reaches all one's, one byte is inserted in
1557  // front of the counter thus making the counter eight bits bigger
1558  // <Header for 'Play counter', ID: 'PCNT'>
1559  // Counter $xx xx xx xx (xx ...)
1560 
1561  $parsedFrame['data'] = getid3_lib::BigEndian2Int($parsedFrame['data']);
1562 
1563 
1564  } elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'POPM')) || // 4.17 POPM Popularimeter
1565  (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'POP'))) { // 4.18 POP Popularimeter
1566  // There may be more than one 'POPM' frame in each tag,
1567  // but only one with the same email address
1568  // <Header for 'Popularimeter', ID: 'POPM'>
1569  // Email to user <text string> $00
1570  // Rating $xx
1571  // Counter $xx xx xx xx (xx ...)
1572 
1573  $frame_offset = 0;
1574  $frame_terminatorpos = strpos($parsedFrame['data'], "\x00", $frame_offset);
1575  $frame_emailaddress = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
1576  if (ord($frame_emailaddress) === 0) {
1577  $frame_emailaddress = '';
1578  }
1579  $frame_offset = $frame_terminatorpos + strlen("\x00");
1580  $frame_rating = ord(substr($parsedFrame['data'], $frame_offset++, 1));
1581  $parsedFrame['counter'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset));
1582  $parsedFrame['email'] = $frame_emailaddress;
1583  $parsedFrame['rating'] = $frame_rating;
1584  unset($parsedFrame['data']);
1585 
1586 
1587  } elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'RBUF')) || // 4.18 RBUF Recommended buffer size
1588  (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'BUF'))) { // 4.19 BUF Recommended buffer size
1589  // There may only be one 'RBUF' frame in each tag
1590  // <Header for 'Recommended buffer size', ID: 'RBUF'>
1591  // Buffer size $xx xx xx
1592  // Embedded info flag %0000000x
1593  // Offset to next tag $xx xx xx xx
1594 
1595  $frame_offset = 0;
1596  $parsedFrame['buffersize'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 3));
1597  $frame_offset += 3;
1598 
1599  $frame_embeddedinfoflags = getid3_lib::BigEndian2Bin(substr($parsedFrame['data'], $frame_offset++, 1));
1600  $parsedFrame['flags']['embededinfo'] = (bool) substr($frame_embeddedinfoflags, 7, 1);
1601  $parsedFrame['nexttagoffset'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 4));
1602  unset($parsedFrame['data']);
1603 
1604 
1605  } elseif (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'CRM')) { // 4.20 Encrypted meta frame (ID3v2.2 only)
1606  // There may be more than one 'CRM' frame in a tag,
1607  // but only one with the same 'owner identifier'
1608  // <Header for 'Encrypted meta frame', ID: 'CRM'>
1609  // Owner identifier <textstring> $00 (00)
1610  // Content/explanation <textstring> $00 (00)
1611  // Encrypted datablock <binary data>
1612 
1613  $frame_offset = 0;
1614  $frame_terminatorpos = strpos($parsedFrame['data'], "\x00", $frame_offset);
1615  $frame_ownerid = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
1616  $frame_offset = $frame_terminatorpos + strlen("\x00");
1617 
1618  $frame_terminatorpos = strpos($parsedFrame['data'], "\x00", $frame_offset);
1619  $frame_description = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
1620  if (in_array($frame_description, array("\x00", "\x00\x00", "\xFF\xFE", "\xFE\xFF"))) {
1621  // if description only contains a BOM or terminator then make it blank
1622  $frame_description = '';
1623  }
1624  $frame_offset = $frame_terminatorpos + strlen("\x00");
1625 
1626  $parsedFrame['ownerid'] = $frame_ownerid;
1627  $parsedFrame['data'] = (string) substr($parsedFrame['data'], $frame_offset);
1628  $parsedFrame['description'] = $frame_description;
1629  unset($parsedFrame['data']);
1630 
1631 
1632  } elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'AENC')) || // 4.19 AENC Audio encryption
1633  (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'CRA'))) { // 4.21 CRA Audio encryption
1634  // There may be more than one 'AENC' frames in a tag,
1635  // but only one with the same 'Owner identifier'
1636  // <Header for 'Audio encryption', ID: 'AENC'>
1637  // Owner identifier <text string> $00
1638  // Preview start $xx xx
1639  // Preview length $xx xx
1640  // Encryption info <binary data>
1641 
1642  $frame_offset = 0;
1643  $frame_terminatorpos = strpos($parsedFrame['data'], "\x00", $frame_offset);
1644  $frame_ownerid = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
1645  if (ord($frame_ownerid) === 0) {
1646  $frame_ownerid = '';
1647  }
1648  $frame_offset = $frame_terminatorpos + strlen("\x00");
1649  $parsedFrame['ownerid'] = $frame_ownerid;
1650  $parsedFrame['previewstart'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 2));
1651  $frame_offset += 2;
1652  $parsedFrame['previewlength'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 2));
1653  $frame_offset += 2;
1654  $parsedFrame['encryptioninfo'] = (string) substr($parsedFrame['data'], $frame_offset);
1655  unset($parsedFrame['data']);
1656 
1657 
1658  } elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'LINK')) || // 4.20 LINK Linked information
1659  (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'LNK'))) { // 4.22 LNK Linked information
1660  // There may be more than one 'LINK' frame in a tag,
1661  // but only one with the same contents
1662  // <Header for 'Linked information', ID: 'LINK'>
1663  // ID3v2.3+ => Frame identifier $xx xx xx xx
1664  // ID3v2.2 => Frame identifier $xx xx xx
1665  // URL <text string> $00
1666  // ID and additional data <text string(s)>
1667 
1668  $frame_offset = 0;
1669  if ($id3v2_majorversion == 2) {
1670  $parsedFrame['frameid'] = substr($parsedFrame['data'], $frame_offset, 3);
1671  $frame_offset += 3;
1672  } else {
1673  $parsedFrame['frameid'] = substr($parsedFrame['data'], $frame_offset, 4);
1674  $frame_offset += 4;
1675  }
1676 
1677  $frame_terminatorpos = strpos($parsedFrame['data'], "\x00", $frame_offset);
1678  $frame_url = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
1679  if (ord($frame_url) === 0) {
1680  $frame_url = '';
1681  }
1682  $frame_offset = $frame_terminatorpos + strlen("\x00");
1683  $parsedFrame['url'] = $frame_url;
1684 
1685  $parsedFrame['additionaldata'] = (string) substr($parsedFrame['data'], $frame_offset);
1686  if (!empty($parsedFrame['framenameshort']) && $parsedFrame['url']) {
1687  $info['id3v2']['comments'][$parsedFrame['framenameshort']][] = getid3_lib::iconv_fallback_iso88591_utf8($parsedFrame['url']);
1688  }
1689  unset($parsedFrame['data']);
1690 
1691 
1692  } elseif (($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'POSS')) { // 4.21 POSS Position synchronisation frame (ID3v2.3+ only)
1693  // There may only be one 'POSS' frame in each tag
1694  // <Head for 'Position synchronisation', ID: 'POSS'>
1695  // Time stamp format $xx
1696  // Position $xx (xx ...)
1697 
1698  $frame_offset = 0;
1699  $parsedFrame['timestampformat'] = ord(substr($parsedFrame['data'], $frame_offset++, 1));
1700  $parsedFrame['position'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset));
1701  unset($parsedFrame['data']);
1702 
1703 
1704  } elseif (($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'USER')) { // 4.22 USER Terms of use (ID3v2.3+ only)
1705  // There may be more than one 'Terms of use' frame in a tag,
1706  // but only one with the same 'Language'
1707  // <Header for 'Terms of use frame', ID: 'USER'>
1708  // Text encoding $xx
1709  // Language $xx xx xx
1710  // The actual text <text string according to encoding>
1711 
1712  $frame_offset = 0;
1713  $frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1));
1714  if ((($id3v2_majorversion <= 3) && ($frame_textencoding > 1)) || (($id3v2_majorversion == 4) && ($frame_textencoding > 3))) {
1715  $this->warning('Invalid text encoding byte ('.$frame_textencoding.') in frame "'.$parsedFrame['frame_name'].'" - defaulting to ISO-8859-1 encoding');
1716  }
1717  $frame_language = substr($parsedFrame['data'], $frame_offset, 3);
1718  $frame_offset += 3;
1719  $parsedFrame['language'] = $frame_language;
1720  $parsedFrame['languagename'] = $this->LanguageLookup($frame_language, false);
1721  $parsedFrame['encodingid'] = $frame_textencoding;
1722  $parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding);
1723 
1724  $parsedFrame['data'] = (string) substr($parsedFrame['data'], $frame_offset);
1725  if (!empty($parsedFrame['framenameshort']) && !empty($parsedFrame['data'])) {
1726  $info['id3v2']['comments'][$parsedFrame['framenameshort']][] = getid3_lib::iconv_fallback($parsedFrame['encoding'], $info['id3v2']['encoding'], $parsedFrame['data']);
1727  }
1728  unset($parsedFrame['data']);
1729 
1730 
1731  } elseif (($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'OWNE')) { // 4.23 OWNE Ownership frame (ID3v2.3+ only)
1732  // There may only be one 'OWNE' frame in a tag
1733  // <Header for 'Ownership frame', ID: 'OWNE'>
1734  // Text encoding $xx
1735  // Price paid <text string> $00
1736  // Date of purch. <text string>
1737  // Seller <text string according to encoding>
1738 
1739  $frame_offset = 0;
1740  $frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1));
1741  if ((($id3v2_majorversion <= 3) && ($frame_textencoding > 1)) || (($id3v2_majorversion == 4) && ($frame_textencoding > 3))) {
1742  $this->warning('Invalid text encoding byte ('.$frame_textencoding.') in frame "'.$parsedFrame['frame_name'].'" - defaulting to ISO-8859-1 encoding');
1743  }
1744  $parsedFrame['encodingid'] = $frame_textencoding;
1745  $parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding);
1746 
1747  $frame_terminatorpos = strpos($parsedFrame['data'], "\x00", $frame_offset);
1748  $frame_pricepaid = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
1749  $frame_offset = $frame_terminatorpos + strlen("\x00");
1750 
1751  $parsedFrame['pricepaid']['currencyid'] = substr($frame_pricepaid, 0, 3);
1752  $parsedFrame['pricepaid']['currency'] = $this->LookupCurrencyUnits($parsedFrame['pricepaid']['currencyid']);
1753  $parsedFrame['pricepaid']['value'] = substr($frame_pricepaid, 3);
1754 
1755  $parsedFrame['purchasedate'] = substr($parsedFrame['data'], $frame_offset, 8);
1756  if ($this->IsValidDateStampString($parsedFrame['purchasedate'])) {
1757  $parsedFrame['purchasedateunix'] = mktime (0, 0, 0, substr($parsedFrame['purchasedate'], 4, 2), substr($parsedFrame['purchasedate'], 6, 2), substr($parsedFrame['purchasedate'], 0, 4));
1758  }
1759  $frame_offset += 8;
1760 
1761  $parsedFrame['seller'] = (string) substr($parsedFrame['data'], $frame_offset);
1762  unset($parsedFrame['data']);
1763 
1764 
1765  } elseif (($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'COMR')) { // 4.24 COMR Commercial frame (ID3v2.3+ only)
1766  // There may be more than one 'commercial frame' in a tag,
1767  // but no two may be identical
1768  // <Header for 'Commercial frame', ID: 'COMR'>
1769  // Text encoding $xx
1770  // Price string <text string> $00
1771  // Valid until <text string>
1772  // Contact URL <text string> $00
1773  // Received as $xx
1774  // Name of seller <text string according to encoding> $00 (00)
1775  // Description <text string according to encoding> $00 (00)
1776  // Picture MIME type <string> $00
1777  // Seller logo <binary data>
1778 
1779  $frame_offset = 0;
1780  $frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1));
1781  $frame_textencoding_terminator = $this->TextEncodingTerminatorLookup($frame_textencoding);
1782  if ((($id3v2_majorversion <= 3) && ($frame_textencoding > 1)) || (($id3v2_majorversion == 4) && ($frame_textencoding > 3))) {
1783  $this->warning('Invalid text encoding byte ('.$frame_textencoding.') in frame "'.$parsedFrame['frame_name'].'" - defaulting to ISO-8859-1 encoding');
1784  $frame_textencoding_terminator = "\x00";
1785  }
1786 
1787  $frame_terminatorpos = strpos($parsedFrame['data'], "\x00", $frame_offset);
1788  $frame_pricestring = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
1789  $frame_offset = $frame_terminatorpos + strlen("\x00");
1790  $frame_rawpricearray = explode('/', $frame_pricestring);
1791  foreach ($frame_rawpricearray as $key => $val) {
1792  $frame_currencyid = substr($val, 0, 3);
1793  $parsedFrame['price'][$frame_currencyid]['currency'] = $this->LookupCurrencyUnits($frame_currencyid);
1794  $parsedFrame['price'][$frame_currencyid]['value'] = substr($val, 3);
1795  }
1796 
1797  $frame_datestring = substr($parsedFrame['data'], $frame_offset, 8);
1798  $frame_offset += 8;
1799 
1800  $frame_terminatorpos = strpos($parsedFrame['data'], "\x00", $frame_offset);
1801  $frame_contacturl = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
1802  $frame_offset = $frame_terminatorpos + strlen("\x00");
1803 
1804  $frame_receivedasid = ord(substr($parsedFrame['data'], $frame_offset++, 1));
1805 
1806  $frame_terminatorpos = strpos($parsedFrame['data'], $frame_textencoding_terminator, $frame_offset);
1807  if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($frame_textencoding_terminator), 1)) === 0) {
1808  $frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00
1809  }
1810  $frame_sellername = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
1811  if (ord($frame_sellername) === 0) {
1812  $frame_sellername = '';
1813  }
1814  $frame_offset = $frame_terminatorpos + strlen($frame_textencoding_terminator);
1815 
1816  $frame_terminatorpos = strpos($parsedFrame['data'], $frame_textencoding_terminator, $frame_offset);
1817  if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($frame_textencoding_terminator), 1)) === 0) {
1818  $frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00
1819  }
1820  $frame_description = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
1821  if (in_array($frame_description, array("\x00", "\x00\x00", "\xFF\xFE", "\xFE\xFF"))) {
1822  // if description only contains a BOM or terminator then make it blank
1823  $frame_description = '';
1824  }
1825  $frame_offset = $frame_terminatorpos + strlen($frame_textencoding_terminator);
1826 
1827  $frame_terminatorpos = strpos($parsedFrame['data'], "\x00", $frame_offset);
1828  $frame_mimetype = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
1829  $frame_offset = $frame_terminatorpos + strlen("\x00");
1830 
1831  $frame_sellerlogo = substr($parsedFrame['data'], $frame_offset);
1832 
1833  $parsedFrame['encodingid'] = $frame_textencoding;
1834  $parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding);
1835 
1836  $parsedFrame['pricevaliduntil'] = $frame_datestring;
1837  $parsedFrame['contacturl'] = $frame_contacturl;
1838  $parsedFrame['receivedasid'] = $frame_receivedasid;
1839  $parsedFrame['receivedas'] = $this->COMRReceivedAsLookup($frame_receivedasid);
1840  $parsedFrame['sellername'] = $frame_sellername;
1841  $parsedFrame['description'] = $frame_description;
1842  $parsedFrame['mime'] = $frame_mimetype;
1843  $parsedFrame['logo'] = $frame_sellerlogo;
1844  unset($parsedFrame['data']);
1845 
1846 
1847  } elseif (($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'ENCR')) { // 4.25 ENCR Encryption method registration (ID3v2.3+ only)
1848  // There may be several 'ENCR' frames in a tag,
1849  // but only one containing the same symbol
1850  // and only one containing the same owner identifier
1851  // <Header for 'Encryption method registration', ID: 'ENCR'>
1852  // Owner identifier <text string> $00
1853  // Method symbol $xx
1854  // Encryption data <binary data>
1855 
1856  $frame_offset = 0;
1857  $frame_terminatorpos = strpos($parsedFrame['data'], "\x00", $frame_offset);
1858  $frame_ownerid = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
1859  if (ord($frame_ownerid) === 0) {
1860  $frame_ownerid = '';
1861  }
1862  $frame_offset = $frame_terminatorpos + strlen("\x00");
1863 
1864  $parsedFrame['ownerid'] = $frame_ownerid;
1865  $parsedFrame['methodsymbol'] = ord(substr($parsedFrame['data'], $frame_offset++, 1));
1866  $parsedFrame['data'] = (string) substr($parsedFrame['data'], $frame_offset);
1867 
1868 
1869  } elseif (($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'GRID')) { // 4.26 GRID Group identification registration (ID3v2.3+ only)
1870 
1871  // There may be several 'GRID' frames in a tag,
1872  // but only one containing the same symbol
1873  // and only one containing the same owner identifier
1874  // <Header for 'Group ID registration', ID: 'GRID'>
1875  // Owner identifier <text string> $00
1876  // Group symbol $xx
1877  // Group dependent data <binary data>
1878 
1879  $frame_offset = 0;
1880  $frame_terminatorpos = strpos($parsedFrame['data'], "\x00", $frame_offset);
1881  $frame_ownerid = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
1882  if (ord($frame_ownerid) === 0) {
1883  $frame_ownerid = '';
1884  }
1885  $frame_offset = $frame_terminatorpos + strlen("\x00");
1886 
1887  $parsedFrame['ownerid'] = $frame_ownerid;
1888  $parsedFrame['groupsymbol'] = ord(substr($parsedFrame['data'], $frame_offset++, 1));
1889  $parsedFrame['data'] = (string) substr($parsedFrame['data'], $frame_offset);
1890 
1891 
1892  } elseif (($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'PRIV')) { // 4.27 PRIV Private frame (ID3v2.3+ only)
1893  // The tag may contain more than one 'PRIV' frame
1894  // but only with different contents
1895  // <Header for 'Private frame', ID: 'PRIV'>
1896  // Owner identifier <text string> $00
1897  // The private data <binary data>
1898 
1899  $frame_offset = 0;
1900  $frame_terminatorpos = strpos($parsedFrame['data'], "\x00", $frame_offset);
1901  $frame_ownerid = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
1902  if (ord($frame_ownerid) === 0) {
1903  $frame_ownerid = '';
1904  }
1905  $frame_offset = $frame_terminatorpos + strlen("\x00");
1906 
1907  $parsedFrame['ownerid'] = $frame_ownerid;
1908  $parsedFrame['data'] = (string) substr($parsedFrame['data'], $frame_offset);
1909 
1910 
1911  } elseif (($id3v2_majorversion >= 4) && ($parsedFrame['frame_name'] == 'SIGN')) { // 4.28 SIGN Signature frame (ID3v2.4+ only)
1912  // There may be more than one 'signature frame' in a tag,
1913  // but no two may be identical
1914  // <Header for 'Signature frame', ID: 'SIGN'>
1915  // Group symbol $xx
1916  // Signature <binary data>
1917 
1918  $frame_offset = 0;
1919  $parsedFrame['groupsymbol'] = ord(substr($parsedFrame['data'], $frame_offset++, 1));
1920  $parsedFrame['data'] = (string) substr($parsedFrame['data'], $frame_offset);
1921 
1922 
1923  } elseif (($id3v2_majorversion >= 4) && ($parsedFrame['frame_name'] == 'SEEK')) { // 4.29 SEEK Seek frame (ID3v2.4+ only)
1924  // There may only be one 'seek frame' in a tag
1925  // <Header for 'Seek frame', ID: 'SEEK'>
1926  // Minimum offset to next tag $xx xx xx xx
1927 
1928  $frame_offset = 0;
1929  $parsedFrame['data'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 4));
1930 
1931 
1932  } elseif (($id3v2_majorversion >= 4) && ($parsedFrame['frame_name'] == 'ASPI')) { // 4.30 ASPI Audio seek point index (ID3v2.4+ only)
1933  // There may only be one 'audio seek point index' frame in a tag
1934  // <Header for 'Seek Point Index', ID: 'ASPI'>
1935  // Indexed data start (S) $xx xx xx xx
1936  // Indexed data length (L) $xx xx xx xx
1937  // Number of index points (N) $xx xx
1938  // Bits per index point (b) $xx
1939  // Then for every index point the following data is included:
1940  // Fraction at index (Fi) $xx (xx)
1941 
1942  $frame_offset = 0;
1943  $parsedFrame['datastart'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 4));
1944  $frame_offset += 4;
1945  $parsedFrame['indexeddatalength'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 4));
1946  $frame_offset += 4;
1947  $parsedFrame['indexpoints'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 2));
1948  $frame_offset += 2;
1949  $parsedFrame['bitsperpoint'] = ord(substr($parsedFrame['data'], $frame_offset++, 1));
1950  $frame_bytesperpoint = ceil($parsedFrame['bitsperpoint'] / 8);
1951  for ($i = 0; $i < $parsedFrame['indexpoints']; $i++) {
1952  $parsedFrame['indexes'][$i] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesperpoint));
1953  $frame_offset += $frame_bytesperpoint;
1954  }
1955  unset($parsedFrame['data']);
1956 
1957  } elseif (($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'RGAD')) { // Replay Gain Adjustment
1958  // http://privatewww.essex.ac.uk/~djmrob/replaygain/file_format_id3v2.html
1959  // There may only be one 'RGAD' frame in a tag
1960  // <Header for 'Replay Gain Adjustment', ID: 'RGAD'>
1961  // Peak Amplitude $xx $xx $xx $xx
1962  // Radio Replay Gain Adjustment %aaabbbcd %dddddddd
1963  // Audiophile Replay Gain Adjustment %aaabbbcd %dddddddd
1964  // a - name code
1965  // b - originator code
1966  // c - sign bit
1967  // d - replay gain adjustment
1968 
1969  $frame_offset = 0;
1970  $parsedFrame['peakamplitude'] = getid3_lib::BigEndian2Float(substr($parsedFrame['data'], $frame_offset, 4));
1971  $frame_offset += 4;
1972  $rg_track_adjustment = getid3_lib::Dec2Bin(substr($parsedFrame['data'], $frame_offset, 2));
1973  $frame_offset += 2;
1974  $rg_album_adjustment = getid3_lib::Dec2Bin(substr($parsedFrame['data'], $frame_offset, 2));
1975  $frame_offset += 2;
1976  $parsedFrame['raw']['track']['name'] = getid3_lib::Bin2Dec(substr($rg_track_adjustment, 0, 3));
1977  $parsedFrame['raw']['track']['originator'] = getid3_lib::Bin2Dec(substr($rg_track_adjustment, 3, 3));
1978  $parsedFrame['raw']['track']['signbit'] = getid3_lib::Bin2Dec(substr($rg_track_adjustment, 6, 1));
1979  $parsedFrame['raw']['track']['adjustment'] = getid3_lib::Bin2Dec(substr($rg_track_adjustment, 7, 9));
1980  $parsedFrame['raw']['album']['name'] = getid3_lib::Bin2Dec(substr($rg_album_adjustment, 0, 3));
1981  $parsedFrame['raw']['album']['originator'] = getid3_lib::Bin2Dec(substr($rg_album_adjustment, 3, 3));
1982  $parsedFrame['raw']['album']['signbit'] = getid3_lib::Bin2Dec(substr($rg_album_adjustment, 6, 1));
1983  $parsedFrame['raw']['album']['adjustment'] = getid3_lib::Bin2Dec(substr($rg_album_adjustment, 7, 9));
1984  $parsedFrame['track']['name'] = getid3_lib::RGADnameLookup($parsedFrame['raw']['track']['name']);
1985  $parsedFrame['track']['originator'] = getid3_lib::RGADoriginatorLookup($parsedFrame['raw']['track']['originator']);
1986  $parsedFrame['track']['adjustment'] = getid3_lib::RGADadjustmentLookup($parsedFrame['raw']['track']['adjustment'], $parsedFrame['raw']['track']['signbit']);
1987  $parsedFrame['album']['name'] = getid3_lib::RGADnameLookup($parsedFrame['raw']['album']['name']);
1988  $parsedFrame['album']['originator'] = getid3_lib::RGADoriginatorLookup($parsedFrame['raw']['album']['originator']);
1989  $parsedFrame['album']['adjustment'] = getid3_lib::RGADadjustmentLookup($parsedFrame['raw']['album']['adjustment'], $parsedFrame['raw']['album']['signbit']);
1990 
1991  $info['replay_gain']['track']['peak'] = $parsedFrame['peakamplitude'];
1992  $info['replay_gain']['track']['originator'] = $parsedFrame['track']['originator'];
1993  $info['replay_gain']['track']['adjustment'] = $parsedFrame['track']['adjustment'];
1994  $info['replay_gain']['album']['originator'] = $parsedFrame['album']['originator'];
1995  $info['replay_gain']['album']['adjustment'] = $parsedFrame['album']['adjustment'];
1996 
1997  unset($parsedFrame['data']);
1998 
1999  } elseif (($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'CHAP')) { // CHAP Chapters frame (ID3v2.3+ only)
2000  // http://id3.org/id3v2-chapters-1.0
2001  // <ID3v2.3 or ID3v2.4 frame header, ID: "CHAP"> (10 bytes)
2002  // Element ID <text string> $00
2003  // Start time $xx xx xx xx
2004  // End time $xx xx xx xx
2005  // Start offset $xx xx xx xx
2006  // End offset $xx xx xx xx
2007  // <Optional embedded sub-frames>
2008 
2009  $frame_offset = 0;
2010  @list($parsedFrame['element_id']) = explode("\x00", $parsedFrame['data'], 2);
2011  $frame_offset += strlen($parsedFrame['element_id']."\x00");
2012  $parsedFrame['time_begin'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 4));
2013  $frame_offset += 4;
2014  $parsedFrame['time_end'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 4));
2015  $frame_offset += 4;
2016  if (substr($parsedFrame['data'], $frame_offset, 4) != "\xFF\xFF\xFF\xFF") {
2017  // "If these bytes are all set to 0xFF then the value should be ignored and the start time value should be utilized."
2018  $parsedFrame['offset_begin'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 4));
2019  }
2020  $frame_offset += 4;
2021  if (substr($parsedFrame['data'], $frame_offset, 4) != "\xFF\xFF\xFF\xFF") {
2022  // "If these bytes are all set to 0xFF then the value should be ignored and the start time value should be utilized."
2023  $parsedFrame['offset_end'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 4));
2024  }
2025  $frame_offset += 4;
2026 
2027  if ($frame_offset < strlen($parsedFrame['data'])) {
2028  $parsedFrame['subframes'] = array();
2029  while ($frame_offset < strlen($parsedFrame['data'])) {
2030  // <Optional embedded sub-frames>
2031  $subframe = array();
2032  $subframe['name'] = substr($parsedFrame['data'], $frame_offset, 4);
2033  $frame_offset += 4;
2034  $subframe['size'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 4));
2035  $frame_offset += 4;
2036  $subframe['flags_raw'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 2));
2037  $frame_offset += 2;
2038  if ($subframe['size'] > (strlen($parsedFrame['data']) - $frame_offset)) {
2039  $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)');
2040  break;
2041  }
2042  $subframe_rawdata = substr($parsedFrame['data'], $frame_offset, $subframe['size']);
2043  $frame_offset += $subframe['size'];
2044 
2045  $subframe['encodingid'] = ord(substr($subframe_rawdata, 0, 1));
2046  $subframe['text'] = substr($subframe_rawdata, 1);
2047  $subframe['encoding'] = $this->TextEncodingNameLookup($subframe['encodingid']);
2048  $encoding_converted_text = trim(getid3_lib::iconv_fallback($subframe['encoding'], $info['encoding'], $subframe['text']));;
2049  switch (substr($encoding_converted_text, 0, 2)) {
2050  case "\xFF\xFE":
2051  case "\xFE\xFF":
2052  switch (strtoupper($info['id3v2']['encoding'])) {
2053  case 'ISO-8859-1':
2054  case 'UTF-8':
2055  $encoding_converted_text = substr($encoding_converted_text, 2);
2056  // remove unwanted byte-order-marks
2057  break;
2058  default:
2059  // ignore
2060  break;
2061  }
2062  break;
2063  default:
2064  // do not remove BOM
2065  break;
2066  }
2067 
2068  if (($subframe['name'] == 'TIT2') || ($subframe['name'] == 'TIT3')) {
2069  if ($subframe['name'] == 'TIT2') {
2070  $parsedFrame['chapter_name'] = $encoding_converted_text;
2071  } elseif ($subframe['name'] == 'TIT3') {
2072  $parsedFrame['chapter_description'] = $encoding_converted_text;
2073  }
2074  $parsedFrame['subframes'][] = $subframe;
2075  } else {
2076  $this->warning('ID3v2.CHAP subframe "'.$subframe['name'].'" not handled (only TIT2 and TIT3)');
2077  }
2078  }
2079  unset($subframe_rawdata, $subframe, $encoding_converted_text);
2080  }
2081 
2082  $id3v2_chapter_entry = array();
2083  foreach (array('id', 'time_begin', 'time_end', 'offset_begin', 'offset_end', 'chapter_name', 'chapter_description') as $id3v2_chapter_key) {
2084  if (isset($parsedFrame[$id3v2_chapter_key])) {
2085  $id3v2_chapter_entry[$id3v2_chapter_key] = $parsedFrame[$id3v2_chapter_key];
2086  }
2087  }
2088  if (!isset($info['id3v2']['chapters'])) {
2089  $info['id3v2']['chapters'] = array();
2090  }
2091  $info['id3v2']['chapters'][] = $id3v2_chapter_entry;
2092  unset($id3v2_chapter_entry, $id3v2_chapter_key);
2093 
2094 
2095  } elseif (($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'CTOC')) { // CTOC Chapters Table Of Contents frame (ID3v2.3+ only)
2096  // http://id3.org/id3v2-chapters-1.0
2097  // <ID3v2.3 or ID3v2.4 frame header, ID: "CTOC"> (10 bytes)
2098  // Element ID <text string> $00
2099  // CTOC flags %xx
2100  // Entry count $xx
2101  // Child Element ID <string>$00 /* zero or more child CHAP or CTOC entries */
2102  // <Optional embedded sub-frames>
2103 
2104  $frame_offset = 0;
2105  @list($parsedFrame['element_id']) = explode("\x00", $parsedFrame['data'], 2);
2106  $frame_offset += strlen($parsedFrame['element_id']."\x00");
2107  $ctoc_flags_raw = ord(substr($parsedFrame['data'], $frame_offset, 1));
2108  $frame_offset += 1;
2109  $parsedFrame['entry_count'] = ord(substr($parsedFrame['data'], $frame_offset, 1));
2110  $frame_offset += 1;
2111 
2112  $terminator_position = null;
2113  for ($i = 0; $i < $parsedFrame['entry_count']; $i++) {
2114  $terminator_position = strpos($parsedFrame['data'], "\x00", $frame_offset);
2115  $parsedFrame['child_element_ids'][$i] = substr($parsedFrame['data'], $frame_offset, $terminator_position - $frame_offset);
2116  $frame_offset = $terminator_position + 1;
2117  }
2118 
2119  $parsedFrame['ctoc_flags']['ordered'] = (bool) ($ctoc_flags_raw & 0x01);
2120  $parsedFrame['ctoc_flags']['top_level'] = (bool) ($ctoc_flags_raw & 0x03);
2121 
2122  unset($ctoc_flags_raw, $terminator_position);
2123 
2124  if ($frame_offset < strlen($parsedFrame['data'])) {
2125  $parsedFrame['subframes'] = array();
2126  while ($frame_offset < strlen($parsedFrame['data'])) {
2127  // <Optional embedded sub-frames>
2128  $subframe = array();
2129  $subframe['name'] = substr($parsedFrame['data'], $frame_offset, 4);
2130  $frame_offset += 4;
2131  $subframe['size'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 4));
2132  $frame_offset += 4;
2133  $subframe['flags_raw'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 2));
2134  $frame_offset += 2;
2135  if ($subframe['size'] > (strlen($parsedFrame['data']) - $frame_offset)) {
2136  $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)');
2137  break;
2138  }
2139  $subframe_rawdata = substr($parsedFrame['data'], $frame_offset, $subframe['size']);
2140  $frame_offset += $subframe['size'];
2141 
2142  $subframe['encodingid'] = ord(substr($subframe_rawdata, 0, 1));
2143  $subframe['text'] = substr($subframe_rawdata, 1);
2144  $subframe['encoding'] = $this->TextEncodingNameLookup($subframe['encodingid']);
2145  $encoding_converted_text = trim(getid3_lib::iconv_fallback($subframe['encoding'], $info['encoding'], $subframe['text']));;
2146  switch (substr($encoding_converted_text, 0, 2)) {
2147  case "\xFF\xFE":
2148  case "\xFE\xFF":
2149  switch (strtoupper($info['id3v2']['encoding'])) {
2150  case 'ISO-8859-1':
2151  case 'UTF-8':
2152  $encoding_converted_text = substr($encoding_converted_text, 2);
2153  // remove unwanted byte-order-marks
2154  break;
2155  default:
2156  // ignore
2157  break;
2158  }
2159  break;
2160  default:
2161  // do not remove BOM
2162  break;
2163  }
2164 
2165  if (($subframe['name'] == 'TIT2') || ($subframe['name'] == 'TIT3')) {
2166  if ($subframe['name'] == 'TIT2') {
2167  $parsedFrame['toc_name'] = $encoding_converted_text;
2168  } elseif ($subframe['name'] == 'TIT3') {
2169  $parsedFrame['toc_description'] = $encoding_converted_text;
2170  }
2171  $parsedFrame['subframes'][] = $subframe;
2172  } else {
2173  $this->warning('ID3v2.CTOC subframe "'.$subframe['name'].'" not handled (only TIT2 and TIT3)');
2174  }
2175  }
2176  unset($subframe_rawdata, $subframe, $encoding_converted_text);
2177  }
2178 
2179  }
2180 
2181  return true;
2182  }
LookupCurrencyUnits($currencyid)
static RGADnameLookup($namecode)
static Dec2Bin($number)
Definition: getid3.lib.php:328
static RGADoriginatorLookup($originatorcode)
warning($text)
Definition: getid3.php:1758
static Bin2Dec($binstring, $signed=false)
Definition: getid3.lib.php:342
static LanguageLookup($languagecode, $casesensitive=false)
static iconv_fallback($in_charset, $out_charset, $string)
Definition: getid3.lib.php:957
static BigEndian2Bin($byteword)
Definition: getid3.lib.php:297
static BigEndian2Float($byteword)
Definition: getid3.lib.php:185
static IsValidDateStampString($datestamp)
static FrameNameLongLookup($framename)
static RGADadjustmentLookup($rawadjustment, $signbit)
static TextEncodingNameLookup($encoding)
$warning
Definition: X509warning.php:13
static APICPictureTypeLookup($index, $returnarray=false)
static RVA2ChannelTypeLookup($index)
static GetDataImageSize($imgData, &$imageinfo=array())
static FrameNameShortLookup($framename)
$i
Definition: disco.tpl.php:19
static COMRReceivedAsLookup($index)
static iconv_fallback_iso88591_utf8($string, $bom=false)
Definition: getid3.lib.php:699
static SYTLContentTypeLookup($index)
$info
Definition: index.php:5
$key
Definition: croninfo.php:18
static BigEndian2Int($byteword, $synchsafe=false, $signed=false)
Definition: getid3.lib.php:263
static TextEncodingTerminatorLookup($encoding)
static ETCOEventLookup($index)
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ ParseID3v2GenreString()

getid3_id3v2::ParseID3v2GenreString (   $genrestring)

Definition at line 502 of file module.tag.id3v2.php.

References getid3_id3v1\LookupGenreName().

Referenced by Analyze().

502  {
503  // Parse genres into arrays of genreName and genreID
504  // ID3v2.2.x, ID3v2.3.x: '(21)' or '(4)Eurodisco' or '(51)(39)' or '(55)((I think...)'
505  // ID3v2.4.x: '21' $00 'Eurodisco' $00
506  $clean_genres = array();
507 
508  // hack-fixes for some badly-written ID3v2.3 taggers, while trying not to break correctly-written tags
509  if (($this->getid3->info['id3v2']['majorversion'] == 3) && !preg_match('#[\x00]#', $genrestring)) {
510  // note: MusicBrainz Picard incorrectly stores plaintext genres separated by "/" when writing in ID3v2.3 mode, hack-fix here:
511  // replace / with NULL, then replace back the two ID3v1 genres that legitimately have "/" as part of the single genre name
512  if (preg_match('#/#', $genrestring)) {
513  $genrestring = str_replace('/', "\x00", $genrestring);
514  $genrestring = str_replace('Pop'."\x00".'Funk', 'Pop/Funk', $genrestring);
515  $genrestring = str_replace('Rock'."\x00".'Rock', 'Folk/Rock', $genrestring);
516  }
517 
518  // some other taggers separate multiple genres with semicolon, e.g. "Heavy Metal;Thrash Metal;Metal"
519  if (preg_match('#;#', $genrestring)) {
520  $genrestring = str_replace(';', "\x00", $genrestring);
521  }
522  }
523 
524 
525  if (strpos($genrestring, "\x00") === false) {
526  $genrestring = preg_replace('#\(([0-9]{1,3})\)#', '$1'."\x00", $genrestring);
527  }
528 
529  $genre_elements = explode("\x00", $genrestring);
530  foreach ($genre_elements as $element) {
531  $element = trim($element);
532  if ($element) {
533  if (preg_match('#^[0-9]{1,3}#', $element)) {
534  $clean_genres[] = getid3_id3v1::LookupGenreName($element);
535  } else {
536  $clean_genres[] = str_replace('((', '(', $element);
537  }
538  }
539  }
540  return $clean_genres;
541  }
static LookupGenreName($genreid, $allowSCMPXextended=true)
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ RVA2ChannelTypeLookup()

static getid3_id3v2::RVA2ChannelTypeLookup (   $index)
static

Definition at line 3190 of file module.tag.id3v2.php.

References $index.

Referenced by ParseID3v2Frame().

3190  {
3191  static $RVA2ChannelTypeLookup = array(
3192  0x00 => 'Other',
3193  0x01 => 'Master volume',
3194  0x02 => 'Front right',
3195  0x03 => 'Front left',
3196  0x04 => 'Back right',
3197  0x05 => 'Back left',
3198  0x06 => 'Front centre',
3199  0x07 => 'Back centre',
3200  0x08 => 'Subwoofer'
3201  );
3202 
3203  return (isset($RVA2ChannelTypeLookup[$index]) ? $RVA2ChannelTypeLookup[$index] : '');
3204  }
$index
Definition: metadata.php:60
+ Here is the caller graph for this function:

◆ SYTLContentTypeLookup()

static getid3_id3v2::SYTLContentTypeLookup (   $index)
static

Definition at line 3128 of file module.tag.id3v2.php.

References $index.

Referenced by ParseID3v2Frame().

3128  {
3129  static $SYTLContentTypeLookup = array(
3130  0x00 => 'other',
3131  0x01 => 'lyrics',
3132  0x02 => 'text transcription',
3133  0x03 => 'movement/part name', // (e.g. 'Adagio')
3134  0x04 => 'events', // (e.g. 'Don Quijote enters the stage')
3135  0x05 => 'chord', // (e.g. 'Bb F Fsus')
3136  0x06 => 'trivia/\'pop up\' information',
3137  0x07 => 'URLs to webpages',
3138  0x08 => 'URLs to images'
3139  );
3140 
3141  return (isset($SYTLContentTypeLookup[$index]) ? $SYTLContentTypeLookup[$index] : '');
3142  }
$index
Definition: metadata.php:60
+ Here is the caller graph for this function:

◆ TextEncodingNameLookup()

static getid3_id3v2::TextEncodingNameLookup (   $encoding)
static

Definition at line 3582 of file module.tag.id3v2.php.

Referenced by ParseID3v2Frame().

3582  {
3583  // http://www.id3.org/id3v2.4.0-structure.txt
3584  // Frames that allow different types of text encoding contains a text encoding description byte. Possible encodings:
3585  static $TextEncodingNameLookup = array(
3586  0 => 'ISO-8859-1', // $00 ISO-8859-1. Terminated with $00.
3587  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.
3588  2 => 'UTF-16BE', // $02 UTF-16BE encoded Unicode without BOM. Terminated with $00 00.
3589  3 => 'UTF-8', // $03 UTF-8 encoded Unicode. Terminated with $00.
3590  255 => 'UTF-16BE'
3591  );
3592  return (isset($TextEncodingNameLookup[$encoding]) ? $TextEncodingNameLookup[$encoding] : 'ISO-8859-1');
3593  }
+ Here is the caller graph for this function:

◆ TextEncodingTerminatorLookup()

static getid3_id3v2::TextEncodingTerminatorLookup (   $encoding)
static

Definition at line 3569 of file module.tag.id3v2.php.

Referenced by getid3_write_id3v2\GenerateID3v2FrameData(), and ParseID3v2Frame().

3569  {
3570  // http://www.id3.org/id3v2.4.0-structure.txt
3571  // Frames that allow different types of text encoding contains a text encoding description byte. Possible encodings:
3572  static $TextEncodingTerminatorLookup = array(
3573  0 => "\x00", // $00 ISO-8859-1. Terminated with $00.
3574  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.
3575  2 => "\x00\x00", // $02 UTF-16BE encoded Unicode without BOM. Terminated with $00 00.
3576  3 => "\x00", // $03 UTF-8 encoded Unicode. Terminated with $00.
3577  255 => "\x00\x00"
3578  );
3579  return (isset($TextEncodingTerminatorLookup[$encoding]) ? $TextEncodingTerminatorLookup[$encoding] : "\x00");
3580  }
+ Here is the caller graph for this function:

Field Documentation

◆ $StartingOffset

getid3_id3v2::$StartingOffset = 0

Definition at line 21 of file module.tag.id3v2.php.

Referenced by Analyze().


The documentation for this class was generated from the following file: