ILIAS  release_5-2 Revision v5.2.25-18-g3f80b82851
GetId3\Module\Tag\Id3v2 Class Reference

GetId3() by James Heinrich info@.nosp@m.geti.nosp@m.d3.or.nosp@m.g //. More...

+ Inheritance diagram for GetId3\Module\Tag\Id3v2:
+ Collaboration diagram for GetId3\Module\Tag\Id3v2:

Public Member Functions

 analyze ()
 
 ParseID3v2GenreString ($genrestring)
 
 ParseID3v2Frame (&$parsedFrame)
 
 DeUnsynchronise ($data)
 
 LookupExtendedHeaderRestrictionsTagSizeLimits ($index)
 array $LookupExtendedHeaderRestrictionsTagSizeLimits More...
 
 LookupExtendedHeaderRestrictionsTextEncodings ($index)
 array $LookupExtendedHeaderRestrictionsTextEncodings More...
 
 LookupExtendedHeaderRestrictionsTextFieldSize ($index)
 array $LookupExtendedHeaderRestrictionsTextFieldSize More...
 
 LookupExtendedHeaderRestrictionsImageEncoding ($index)
 array $LookupExtendedHeaderRestrictionsImageEncoding More...
 
 LookupExtendedHeaderRestrictionsImageSizeSize ($index)
 array $LookupExtendedHeaderRestrictionsImageSizeSize More...
 
 LookupCurrencyUnits ($currencyid)
 
 LookupCurrencyCountry ($currencyid)
 
- Public Member Functions inherited from GetId3\Handler\BaseHandler
 __construct (GetId3Core $getid3, $call_module=null)
 
 analyze ()
 Analyze from file pointer. More...
 
 AnalyzeString (&$string)
 Analyze from string instead. More...
 
 saveAttachment (&$ThisFileInfoIndex, $filename, $offset, $length)
 

Static Public Member Functions

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

Data Fields

 $inline_attachments = true
 
 $StartingOffset = 0
 

Additional Inherited Members

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

Detailed Description

GetId3() by James Heinrich info@.nosp@m.geti.nosp@m.d3.or.nosp@m.g //.

// module for analyzing ID3v2 tags

Author
James Heinrich info@.nosp@m.geti.nosp@m.d3.or.nosp@m.g http://www.getid3.org GetId3

Definition at line 30 of file Id3v2.php.

Member Function Documentation

◆ analyze()

GetId3\Module\Tag\Id3v2::analyze ( )
Returns
boolean

Definition at line 46 of file Id3v2.php.

References $header, $info, GetId3\Module\Tag\Id3v2\$StartingOffset, array, GetId3\Lib\Helper\array_merge_noclobber(), GetId3\Lib\Helper\BigEndian2Int(), GetId3\Lib\Helper\CastAsInt(), GetId3\Module\Tag\Id3v2\DeUnsynchronise(), GetId3\Handler\BaseHandler\fread(), GetId3\Handler\BaseHandler\fseek(), GetId3\Module\Tag\Id3v2\ID3v2HeaderLength(), GetId3\Module\Tag\Id3v2\IsValidID3v2FrameName(), GetId3\Module\Tag\Id3v2\LookupExtendedHeaderRestrictionsImageEncoding(), GetId3\Module\Tag\Id3v2\LookupExtendedHeaderRestrictionsImageSizeSize(), GetId3\Module\Tag\Id3v2\LookupExtendedHeaderRestrictionsTagSizeLimits(), GetId3\Module\Tag\Id3v2\LookupExtendedHeaderRestrictionsTextEncodings(), GetId3\Module\Tag\Id3v2\LookupExtendedHeaderRestrictionsTextFieldSize(), GetId3\Module\Tag\Id3v2\ParseID3v2Frame(), and GetId3\Module\Tag\Id3v2\ParseID3v2GenreString().

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

◆ APICPictureTypeLookup()

static GetId3\Module\Tag\Id3v2::APICPictureTypeLookup (   $index,
  $returnarray = false 
)
static

array $APICPictureTypeLookup

Parameters
type$index
type$returnarray
Returns
string

Definition at line 2906 of file Id3v2.php.

References array.

Referenced by GetId3\Module\Tag\Id3v2\ParseID3v2Frame().

2907  {
2908  static $APICPictureTypeLookup = array(
2909  0x00 => 'Other',
2910  0x01 => '32x32 pixels \'file icon\' (PNG only)',
2911  0x02 => 'Other file icon',
2912  0x03 => 'Cover (front)',
2913  0x04 => 'Cover (back)',
2914  0x05 => 'Leaflet page',
2915  0x06 => 'Media (e.g. label side of CD)',
2916  0x07 => 'Lead artist/lead performer/soloist',
2917  0x08 => 'Artist/performer',
2918  0x09 => 'Conductor',
2919  0x0A => 'Band/Orchestra',
2920  0x0B => 'Composer',
2921  0x0C => 'Lyricist/text writer',
2922  0x0D => 'Recording Location',
2923  0x0E => 'During recording',
2924  0x0F => 'During performance',
2925  0x10 => 'Movie/video screen capture',
2926  0x11 => 'A bright coloured fish',
2927  0x12 => 'Illustration',
2928  0x13 => 'Band/artist logotype',
2929  0x14 => 'Publisher/Studio logotype'
2930  );
2931  if ($returnarray) {
2932  return $APICPictureTypeLookup;
2933  }
2934 
2935  return (isset($APICPictureTypeLookup[$index]) ? $APICPictureTypeLookup[$index] : '');
2936  }
Create styles array
The data for the language used.
+ Here is the caller graph for this function:

◆ COMRReceivedAsLookup()

static GetId3\Module\Tag\Id3v2::COMRReceivedAsLookup (   $index)
static

array $COMRReceivedAsLookup

Parameters
type$index
Returns
type

Definition at line 2944 of file Id3v2.php.

References array.

Referenced by GetId3\Module\Tag\Id3v2\ParseID3v2Frame().

2945  {
2946  static $COMRReceivedAsLookup = array(
2947  0x00 => 'Other',
2948  0x01 => 'Standard CD album with other songs',
2949  0x02 => 'Compressed audio on CD',
2950  0x03 => 'File over the Internet',
2951  0x04 => 'Stream over the Internet',
2952  0x05 => 'As note sheets',
2953  0x06 => 'As note sheets in a book with other sheets',
2954  0x07 => 'Music on other media',
2955  0x08 => 'Non-musical merchandise'
2956  );
2957 
2958  return (isset($COMRReceivedAsLookup[$index]) ? $COMRReceivedAsLookup[$index] : '');
2959  }
Create styles array
The data for the language used.
+ Here is the caller graph for this function:

◆ DeUnsynchronise()

GetId3\Module\Tag\Id3v2::DeUnsynchronise (   $data)
Parameters
type$data
Returns
type

Definition at line 1875 of file Id3v2.php.

References $data.

Referenced by GetId3\Module\Tag\Id3v2\analyze(), and GetId3\Module\Tag\Id3v2\ParseID3v2Frame().

1876  {
1877  return str_replace("\xFF\x00", "\xFF", $data);
1878  }
+ Here is the caller graph for this function:

◆ ETCOEventLookup()

static GetId3\Module\Tag\Id3v2::ETCOEventLookup (   $index)
static

array $EventLookup

Parameters
type$index
Returns
string

Definition at line 2832 of file Id3v2.php.

References array.

Referenced by GetId3\Module\Tag\Id3v2\ParseID3v2Frame().

2833  {
2834  if (($index >= 0x17) && ($index <= 0xDF)) {
2835  return 'reserved for future use';
2836  }
2837  if (($index >= 0xE0) && ($index <= 0xEF)) {
2838  return 'not predefined synch 0-F';
2839  }
2840  if (($index >= 0xF0) && ($index <= 0xFC)) {
2841  return 'reserved for future use';
2842  }
2843 
2844  static $EventLookup = array(
2845  0x00 => 'padding (has no meaning)',
2846  0x01 => 'end of initial silence',
2847  0x02 => 'intro start',
2848  0x03 => 'main part start',
2849  0x04 => 'outro start',
2850  0x05 => 'outro end',
2851  0x06 => 'verse start',
2852  0x07 => 'refrain start',
2853  0x08 => 'interlude start',
2854  0x09 => 'theme start',
2855  0x0A => 'variation start',
2856  0x0B => 'key change',
2857  0x0C => 'time change',
2858  0x0D => 'momentary unwanted noise (Snap, Crackle & Pop)',
2859  0x0E => 'sustained noise',
2860  0x0F => 'sustained noise end',
2861  0x10 => 'intro end',
2862  0x11 => 'main part end',
2863  0x12 => 'verse end',
2864  0x13 => 'refrain end',
2865  0x14 => 'theme end',
2866  0x15 => 'profanity',
2867  0x16 => 'profanity end',
2868  0xFD => 'audio end (start of silence)',
2869  0xFE => 'audio file ends',
2870  0xFF => 'one more byte of events follows'
2871  );
2872 
2873  return (isset($EventLookup[$index]) ? $EventLookup[$index] : '');
2874  }
Create styles array
The data for the language used.
+ Here is the caller graph for this function:

◆ FrameNameLongLookup()

static GetId3\Module\Tag\Id3v2::FrameNameLongLookup (   $framename)
static
Parameters
type$framename
Returns
type

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 2989 of file Id3v2.php.

References GetId3\Lib\Helper\EmbeddedLookup().

Referenced by GetId3\Module\Tag\Id3v2\ParseID3v2Frame().

2990  {
2991  $begin = __LINE__;
2992 
3165  return Helper::EmbeddedLookup($framename, $begin, __LINE__, __FILE__, 'id3v2-framename_long');
3166 
3167  // Last three:
3168  // from Helium2 [www.helium2.com]
3169  // from http://privatewww.essex.ac.uk/~djmrob/replaygain/file_format_id3v2.html
3170  }
static EmbeddedLookup($key, $begin, $end, $file, $name)
type $cache
Definition: Helper.php:1759
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ FrameNameShortLookup()

static GetId3\Module\Tag\Id3v2::FrameNameShortLookup (   $framename)
static
Parameters
type$framename
Returns
type

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 3177 of file Id3v2.php.

References GetId3\Lib\Helper\EmbeddedLookup().

Referenced by GetId3\Module\Tag\Id3v2\ParseID3v2Frame().

3178  {
3179  $begin = __LINE__;
3180 
3353  return Helper::EmbeddedLookup($framename, $begin, __LINE__, __FILE__, 'id3v2-framename_short');
3354  }
static EmbeddedLookup($key, $begin, $end, $file, $name)
type $cache
Definition: Helper.php:1759
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ ID3v2HeaderLength()

static GetId3\Module\Tag\Id3v2::ID3v2HeaderLength (   $majorversion)
static
Parameters
type$majorversion
Returns
type

Definition at line 3484 of file Id3v2.php.

Referenced by GetId3\Module\Tag\Id3v2\analyze(), and GetId3\Write\Id3v2\GenerateID3v2Tag().

3485  {
3486  return (($majorversion == 2) ? 6 : 10);
3487  }
+ Here is the caller graph for this function:

◆ IsANumber()

static GetId3\Module\Tag\Id3v2::IsANumber (   $numberstring,
  $allowdecimal = false,
  $allownegative = false 
)
static
Parameters
type$numberstring
type$allowdecimal
type$allownegative
Returns
boolean

Definition at line 3427 of file Id3v2.php.

Referenced by GetId3\Write\Id3v2\GenerateID3v2FrameData(), and GetId3\Write\Id3v2\ID3v2IsValidPriceString().

3428  {
3429  for ($i = 0; $i < strlen($numberstring); $i++) {
3430  if ((chr($numberstring{$i}) < chr('0')) || (chr($numberstring{$i}) > chr('9'))) {
3431  if (($numberstring{$i} == '.') && $allowdecimal) {
3432  // allowed
3433  } elseif (($numberstring{$i} == '-') && $allownegative && ($i == 0)) {
3434  // allowed
3435  } else {
3436  return false;
3437  }
3438  }
3439  }
3440 
3441  return true;
3442  }
+ Here is the caller graph for this function:

◆ IsValidDateStampString()

static GetId3\Module\Tag\Id3v2::IsValidDateStampString (   $datestamp)
static
Parameters
type$datestamp
Returns
boolean

Definition at line 3449 of file Id3v2.php.

Referenced by GetId3\Write\Id3v2\GenerateID3v2FrameData(), and GetId3\Module\Tag\Id3v2\ParseID3v2Frame().

3450  {
3451  if (strlen($datestamp) != 8) {
3452  return false;
3453  }
3454  if (!self::IsANumber($datestamp, false)) {
3455  return false;
3456  }
3457  $year = substr($datestamp, 0, 4);
3458  $month = substr($datestamp, 4, 2);
3459  $day = substr($datestamp, 6, 2);
3460  if (($year == 0) || ($month == 0) || ($day == 0)) {
3461  return false;
3462  }
3463  if ($month > 12) {
3464  return false;
3465  }
3466  if ($day > 31) {
3467  return false;
3468  }
3469  if (($day > 30) && (($month == 4) || ($month == 6) || ($month == 9) || ($month == 11))) {
3470  return false;
3471  }
3472  if (($day > 29) && ($month == 2)) {
3473  return false;
3474  }
3475 
3476  return true;
3477  }
+ Here is the caller graph for this function:

◆ IsValidID3v2FrameName()

static GetId3\Module\Tag\Id3v2::IsValidID3v2FrameName (   $framename,
  $id3v2majorversion 
)
static
Parameters
type$framename
type$id3v2majorversion
Returns
boolean

Definition at line 3404 of file Id3v2.php.

Referenced by GetId3\Module\Tag\Id3v2\analyze(), GetId3\Write\Id3v2\GenerateID3v2FrameData(), and GetId3\Write\Id3v2\GenerateID3v2Tag().

3405  {
3406  switch ($id3v2majorversion) {
3407  case 2:
3408  return preg_match('#[A-Z][A-Z0-9]{2}#', $framename);
3409  break;
3410 
3411  case 3:
3412  case 4:
3413  return preg_match('#[A-Z][A-Z0-9]{3}#', $framename);
3414  break;
3415  }
3416 
3417  return false;
3418  }
+ Here is the caller graph for this function:

◆ LanguageLookup()

static GetId3\Module\Tag\Id3v2::LanguageLookup (   $languagecode,
  $casesensitive = false 
)
static
Parameters
type$languagecode
type$casesensitive
Returns
type

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 2372 of file Id3v2.php.

References GetId3\Lib\Helper\EmbeddedLookup().

Referenced by GetId3\Write\Id3v2\GenerateID3v2FrameData(), GetId3\Write\Id3v2\ID3v2IsValidPriceString(), and GetId3\Module\Tag\Id3v2\ParseID3v2Frame().

2373  {
2374  if (!$casesensitive) {
2375  $languagecode = strtolower($languagecode);
2376  }
2377 
2378  // http://www.id3.org/id3v2.4.0-structure.txt
2379  // [4. ID3v2 frame overview]
2380  // The three byte language field, present in several frames, is used to
2381  // describe the language of the frame's content, according to ISO-639-2
2382  // [ISO-639-2]. The language should be represented in lower case. If the
2383  // language is not known the string "XXX" should be used.
2384 
2385  // ISO 639-2 - http://www.id3.org/iso639-2.html
2386 
2387  $begin = __LINE__;
2388 
2823  return Helper::EmbeddedLookup($languagecode, $begin, __LINE__, __FILE__, 'id3v2-languagecode');
2824  }
static EmbeddedLookup($key, $begin, $end, $file, $name)
type $cache
Definition: Helper.php:1759
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ LookupCurrencyCountry()

GetId3\Module\Tag\Id3v2::LookupCurrencyCountry (   $currencyid)
Parameters
type$currencyid
Returns
type

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 2171 of file Id3v2.php.

References GetId3\Lib\Helper\EmbeddedLookup().

2172  {
2173  $begin = __LINE__;
2174 
2363  return Helper::EmbeddedLookup($currencyid, $begin, __LINE__, __FILE__, 'id3v2-currency-country');
2364  }
static EmbeddedLookup($key, $begin, $end, $file, $name)
type $cache
Definition: Helper.php:1759
+ Here is the call graph for this function:

◆ LookupCurrencyUnits()

GetId3\Module\Tag\Id3v2::LookupCurrencyUnits (   $currencyid)
Parameters
type$currencyid
Returns
type

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 1971 of file Id3v2.php.

References GetId3\Lib\Helper\EmbeddedLookup().

Referenced by GetId3\Module\Tag\Id3v2\ParseID3v2Frame().

1972  {
1973  $begin = __LINE__;
1974 
2163  return Helper::EmbeddedLookup($currencyid, $begin, __LINE__, __FILE__, 'id3v2-currency-units');
2164  }
static EmbeddedLookup($key, $begin, $end, $file, $name)
type $cache
Definition: Helper.php:1759
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ LookupExtendedHeaderRestrictionsImageEncoding()

GetId3\Module\Tag\Id3v2::LookupExtendedHeaderRestrictionsImageEncoding (   $index)

array $LookupExtendedHeaderRestrictionsImageEncoding

Parameters
type$index
Returns
type

Definition at line 1938 of file Id3v2.php.

References array.

Referenced by GetId3\Module\Tag\Id3v2\analyze().

1939  {
1940  static $LookupExtendedHeaderRestrictionsImageEncoding = array(
1941  0x00 => 'No restrictions',
1942  0x01 => 'Images are encoded only with PNG or JPEG',
1943  );
1944 
1945  return (isset($LookupExtendedHeaderRestrictionsImageEncoding[$index]) ? $LookupExtendedHeaderRestrictionsImageEncoding[$index] : '');
1946  }
Create styles array
The data for the language used.
+ Here is the caller graph for this function:

◆ LookupExtendedHeaderRestrictionsImageSizeSize()

GetId3\Module\Tag\Id3v2::LookupExtendedHeaderRestrictionsImageSizeSize (   $index)

array $LookupExtendedHeaderRestrictionsImageSizeSize

Parameters
type$index
Returns
type

Definition at line 1954 of file Id3v2.php.

References array.

Referenced by GetId3\Module\Tag\Id3v2\analyze().

1955  {
1956  static $LookupExtendedHeaderRestrictionsImageSizeSize = array(
1957  0x00 => 'No restrictions',
1958  0x01 => 'All images are 256x256 pixels or smaller',
1959  0x02 => 'All images are 64x64 pixels or smaller',
1960  0x03 => 'All images are exactly 64x64 pixels, unless required otherwise',
1961  );
1962 
1963  return (isset($LookupExtendedHeaderRestrictionsImageSizeSize[$index]) ? $LookupExtendedHeaderRestrictionsImageSizeSize[$index] : '');
1964  }
Create styles array
The data for the language used.
+ Here is the caller graph for this function:

◆ LookupExtendedHeaderRestrictionsTagSizeLimits()

GetId3\Module\Tag\Id3v2::LookupExtendedHeaderRestrictionsTagSizeLimits (   $index)

array $LookupExtendedHeaderRestrictionsTagSizeLimits

Parameters
type$index
Returns
type

Definition at line 1886 of file Id3v2.php.

References array.

Referenced by GetId3\Module\Tag\Id3v2\analyze().

1887  {
1888  static $LookupExtendedHeaderRestrictionsTagSizeLimits = array(
1889  0x00 => 'No more than 128 frames and 1 MB total tag size',
1890  0x01 => 'No more than 64 frames and 128 KB total tag size',
1891  0x02 => 'No more than 32 frames and 40 KB total tag size',
1892  0x03 => 'No more than 32 frames and 4 KB total tag size',
1893  );
1894 
1895  return (isset($LookupExtendedHeaderRestrictionsTagSizeLimits[$index]) ? $LookupExtendedHeaderRestrictionsTagSizeLimits[$index] : '');
1896  }
Create styles array
The data for the language used.
+ Here is the caller graph for this function:

◆ LookupExtendedHeaderRestrictionsTextEncodings()

GetId3\Module\Tag\Id3v2::LookupExtendedHeaderRestrictionsTextEncodings (   $index)

array $LookupExtendedHeaderRestrictionsTextEncodings

Parameters
type$index
Returns
type

Definition at line 1904 of file Id3v2.php.

References array.

Referenced by GetId3\Module\Tag\Id3v2\analyze().

1905  {
1906  static $LookupExtendedHeaderRestrictionsTextEncodings = array(
1907  0x00 => 'No restrictions',
1908  0x01 => 'Strings are only encoded with ISO-8859-1 or UTF-8',
1909  );
1910 
1911  return (isset($LookupExtendedHeaderRestrictionsTextEncodings[$index]) ? $LookupExtendedHeaderRestrictionsTextEncodings[$index] : '');
1912  }
Create styles array
The data for the language used.
+ Here is the caller graph for this function:

◆ LookupExtendedHeaderRestrictionsTextFieldSize()

GetId3\Module\Tag\Id3v2::LookupExtendedHeaderRestrictionsTextFieldSize (   $index)

array $LookupExtendedHeaderRestrictionsTextFieldSize

Parameters
type$index
Returns
type

Definition at line 1920 of file Id3v2.php.

References array.

Referenced by GetId3\Module\Tag\Id3v2\analyze().

1921  {
1922  static $LookupExtendedHeaderRestrictionsTextFieldSize = array(
1923  0x00 => 'No restrictions',
1924  0x01 => 'No string is longer than 1024 characters',
1925  0x02 => 'No string is longer than 128 characters',
1926  0x03 => 'No string is longer than 30 characters',
1927  );
1928 
1929  return (isset($LookupExtendedHeaderRestrictionsTextFieldSize[$index]) ? $LookupExtendedHeaderRestrictionsTextFieldSize[$index] : '');
1930  }
Create styles array
The data for the language used.
+ Here is the caller graph for this function:

◆ ParseID3v2Frame()

GetId3\Module\Tag\Id3v2::ParseID3v2Frame ( $parsedFrame)
Parameters
type$parsedFrame
Returns
boolean

Definition at line 553 of file Id3v2.php.

References $info, GetId3\Module\Tag\Id3v2\APICPictureTypeLookup(), array, GetId3\Lib\Helper\BigEndian2Bin(), GetId3\Lib\Helper\BigEndian2Float(), GetId3\Lib\Helper\BigEndian2Int(), GetId3\Lib\Helper\Bin2Dec(), GetId3\Module\Tag\Id3v2\COMRReceivedAsLookup(), GetId3\Lib\Helper\Dec2Bin(), GetId3\Module\Tag\Id3v2\DeUnsynchronise(), GetId3\Module\Tag\Id3v2\ETCOEventLookup(), GetId3\Module\Tag\Id3v2\FrameNameLongLookup(), GetId3\Module\Tag\Id3v2\FrameNameShortLookup(), GetId3\Lib\Helper\GetDataImageSize(), GetId3\Lib\Helper\iconv_fallback(), GetId3\Module\Tag\Id3v2\IsValidDateStampString(), GetId3\Module\Tag\Id3v2\LanguageLookup(), GetId3\Module\Tag\Id3v2\LookupCurrencyUnits(), GetId3\Lib\Helper\RGADadjustmentLookup(), GetId3\Lib\Helper\RGADnameLookup(), GetId3\Lib\Helper\RGADoriginatorLookup(), GetId3\Module\Tag\Id3v2\RVA2ChannelTypeLookup(), string, GetId3\Module\Tag\Id3v2\SYTLContentTypeLookup(), GetId3\Module\Tag\Id3v2\TextEncodingNameLookup(), and GetId3\Module\Tag\Id3v2\TextEncodingTerminatorLookup().

Referenced by GetId3\Module\Tag\Id3v2\analyze().

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

◆ ParseID3v2GenreString()

GetId3\Module\Tag\Id3v2::ParseID3v2GenreString (   $genrestring)
Parameters
type$genrestring
Returns
type

Definition at line 524 of file Id3v2.php.

References array, and GetId3\Module\Tag\Id3v1\LookupGenreName().

Referenced by GetId3\Module\Tag\Id3v2\analyze().

525  {
526  // Parse genres into arrays of genreName and genreID
527  // ID3v2.2.x, ID3v2.3.x: '(21)' or '(4)Eurodisco' or '(51)(39)' or '(55)((I think...)'
528  // ID3v2.4.x: '21' $00 'Eurodisco' $00
529  $clean_genres = array();
530  if (strpos($genrestring, "\x00") === false) {
531  $genrestring = preg_replace('#\(([0-9]{1,3})\)#', '$1'."\x00", $genrestring);
532  }
533  $genre_elements = explode("\x00", $genrestring);
534  foreach ($genre_elements as $element) {
535  $element = trim($element);
536  if ($element) {
537  if (preg_match('#^[0-9]{1,3}#', $element)) {
538  $clean_genres[] = Id3v1::LookupGenreName($element);
539  } else {
540  $clean_genres[] = str_replace('((', '(', $element);
541  }
542  }
543  }
544 
545  return $clean_genres;
546  }
static LookupGenreName($genreid, $allowSCMPXextended=true)
Definition: Id3v1.php:330
Create styles array
The data for the language used.
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ RVA2ChannelTypeLookup()

static GetId3\Module\Tag\Id3v2::RVA2ChannelTypeLookup (   $index)
static

array $RVA2ChannelTypeLookup

Parameters
type$index
Returns
type

Definition at line 2967 of file Id3v2.php.

References array.

Referenced by GetId3\Module\Tag\Id3v2\ParseID3v2Frame().

2968  {
2969  static $RVA2ChannelTypeLookup = array(
2970  0x00 => 'Other',
2971  0x01 => 'Master volume',
2972  0x02 => 'Front right',
2973  0x03 => 'Front left',
2974  0x04 => 'Back right',
2975  0x05 => 'Back left',
2976  0x06 => 'Front centre',
2977  0x07 => 'Back centre',
2978  0x08 => 'Subwoofer'
2979  );
2980 
2981  return (isset($RVA2ChannelTypeLookup[$index]) ? $RVA2ChannelTypeLookup[$index] : '');
2982  }
Create styles array
The data for the language used.
+ Here is the caller graph for this function:

◆ SYTLContentTypeLookup()

static GetId3\Module\Tag\Id3v2::SYTLContentTypeLookup (   $index)
static

array $SYTLContentTypeLookup

Parameters
type$index
Returns
type

Definition at line 2882 of file Id3v2.php.

References array.

Referenced by GetId3\Module\Tag\Id3v2\ParseID3v2Frame().

2883  {
2884  static $SYTLContentTypeLookup = array(
2885  0x00 => 'other',
2886  0x01 => 'lyrics',
2887  0x02 => 'text transcription',
2888  0x03 => 'movement/part name', // (e.g. 'Adagio')
2889  0x04 => 'events', // (e.g. 'Don Quijote enters the stage')
2890  0x05 => 'chord', // (e.g. 'Bb F Fsus')
2891  0x06 => 'trivia/\'pop up\' information',
2892  0x07 => 'URLs to webpages',
2893  0x08 => 'URLs to images'
2894  );
2895 
2896  return (isset($SYTLContentTypeLookup[$index]) ? $SYTLContentTypeLookup[$index] : '');
2897  }
Create styles array
The data for the language used.
+ Here is the caller graph for this function:

◆ TextEncodingNameLookup()

static GetId3\Module\Tag\Id3v2::TextEncodingNameLookup (   $encoding)
static

array $TextEncodingNameLookup

Parameters
type$encoding
Returns
type

Definition at line 3383 of file Id3v2.php.

References array.

Referenced by GetId3\Module\Tag\Id3v2\ParseID3v2Frame().

3384  {
3385  // http://www.id3.org/id3v2.4.0-structure.txt
3386  // Frames that allow different types of text encoding contains a text encoding description byte. Possible encodings:
3387  static $TextEncodingNameLookup = array(
3388  0 => 'ISO-8859-1', // $00 ISO-8859-1. Terminated with $00.
3389  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.
3390  2 => 'UTF-16BE', // $02 UTF-16BE encoded Unicode without BOM. Terminated with $00 00.
3391  3 => 'UTF-8', // $03 UTF-8 encoded Unicode. Terminated with $00.
3392  255 => 'UTF-16BE'
3393  );
3394 
3395  return (isset($TextEncodingNameLookup[$encoding]) ? $TextEncodingNameLookup[$encoding] : 'ISO-8859-1');
3396  }
Create styles array
The data for the language used.
+ Here is the caller graph for this function:

◆ TextEncodingTerminatorLookup()

static GetId3\Module\Tag\Id3v2::TextEncodingTerminatorLookup (   $encoding)
static

array $TextEncodingTerminatorLookup

Parameters
type$encoding
Returns
type

Definition at line 3362 of file Id3v2.php.

References array.

Referenced by GetId3\Write\Id3v2\GenerateID3v2FrameData(), and GetId3\Module\Tag\Id3v2\ParseID3v2Frame().

3363  {
3364  // http://www.id3.org/id3v2.4.0-structure.txt
3365  // Frames that allow different types of text encoding contains a text encoding description byte. Possible encodings:
3366  static $TextEncodingTerminatorLookup = array(
3367  0 => "\x00", // $00 ISO-8859-1. Terminated with $00.
3368  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.
3369  2 => "\x00\x00", // $02 UTF-16BE encoded Unicode without BOM. Terminated with $00 00.
3370  3 => "\x00", // $03 UTF-8 encoded Unicode. Terminated with $00.
3371  255 => "\x00\x00"
3372  );
3373 
3374  return (isset($TextEncodingTerminatorLookup[$encoding]) ? $TextEncodingTerminatorLookup[$encoding] : '');
3375  }
Create styles array
The data for the language used.
+ Here is the caller graph for this function:

Field Documentation

◆ $inline_attachments

GetId3\Module\Tag\Id3v2::$inline_attachments = true

Definition at line 35 of file Id3v2.php.

◆ $StartingOffset

GetId3\Module\Tag\Id3v2::$StartingOffset = 0

Definition at line 40 of file Id3v2.php.

Referenced by GetId3\Module\Tag\Id3v2\analyze().


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