1 <?php |
1 <?php |
|
2 |
2 ///////////////////////////////////////////////////////////////// |
3 ///////////////////////////////////////////////////////////////// |
3 /// getID3() by James Heinrich <info@getid3.org> // |
4 /// getID3() by James Heinrich <info@getid3.org> // |
4 // available at http://getid3.sourceforge.net // |
5 // available at https://github.com/JamesHeinrich/getID3 // |
5 // or http://www.getid3.org // |
6 // or https://www.getid3.org // |
6 // also https://github.com/JamesHeinrich/getID3 // |
7 // or http://getid3.sourceforge.net // |
7 ///////////////////////////////////////////////////////////////// |
8 // see readme.txt for more details // |
8 // See readme.txt for more details // |
|
9 ///////////////////////////////////////////////////////////////// |
9 ///////////////////////////////////////////////////////////////// |
10 /// // |
10 /// // |
11 // module.tag.id3v2.php // |
11 // module.tag.id3v2.php // |
12 // module for analyzing ID3v2 tags // |
12 // module for analyzing ID3v2 tags // |
13 // dependencies: module.tag.id3v1.php // |
13 // dependencies: module.tag.id3v1.php // |
14 // /// |
14 // /// |
15 ///////////////////////////////////////////////////////////////// |
15 ///////////////////////////////////////////////////////////////// |
16 |
16 |
|
17 if (!defined('GETID3_INCLUDEPATH')) { // prevent path-exposing attacks that access modules directly on public webservers |
|
18 exit; |
|
19 } |
17 getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.tag.id3v1.php', __FILE__, true); |
20 getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.tag.id3v1.php', __FILE__, true); |
18 |
21 |
19 class getid3_id3v2 extends getid3_handler |
22 class getid3_id3v2 extends getid3_handler |
20 { |
23 { |
21 public $StartingOffset = 0; |
24 public $StartingOffset = 0; |
22 |
25 |
|
26 /** |
|
27 * @return bool |
|
28 */ |
23 public function Analyze() { |
29 public function Analyze() { |
24 $info = &$this->getid3->info; |
30 $info = &$this->getid3->info; |
25 |
31 |
26 // Overall tag structure: |
32 // Overall tag structure: |
27 // +-----------------------------+ |
33 // +-----------------------------+ |
255 // insufficient room left in ID3v2 header for actual data - must be padding |
261 // insufficient room left in ID3v2 header for actual data - must be padding |
256 $thisfile_id3v2['padding']['start'] = $framedataoffset; |
262 $thisfile_id3v2['padding']['start'] = $framedataoffset; |
257 $thisfile_id3v2['padding']['length'] = strlen($framedata); |
263 $thisfile_id3v2['padding']['length'] = strlen($framedata); |
258 $thisfile_id3v2['padding']['valid'] = true; |
264 $thisfile_id3v2['padding']['valid'] = true; |
259 for ($i = 0; $i < $thisfile_id3v2['padding']['length']; $i++) { |
265 for ($i = 0; $i < $thisfile_id3v2['padding']['length']; $i++) { |
260 if ($framedata{$i} != "\x00") { |
266 if ($framedata[$i] != "\x00") { |
261 $thisfile_id3v2['padding']['valid'] = false; |
267 $thisfile_id3v2['padding']['valid'] = false; |
262 $thisfile_id3v2['padding']['errorpos'] = $thisfile_id3v2['padding']['start'] + $i; |
268 $thisfile_id3v2['padding']['errorpos'] = $thisfile_id3v2['padding']['start'] + $i; |
263 $this->warning('Invalid ID3v2 padding found at offset '.$thisfile_id3v2['padding']['errorpos'].' (the remaining '.($thisfile_id3v2['padding']['length'] - $i).' bytes are considered invalid)'); |
269 $this->warning('Invalid ID3v2 padding found at offset '.$thisfile_id3v2['padding']['errorpos'].' (the remaining '.($thisfile_id3v2['padding']['length'] - $i).' bytes are considered invalid)'); |
264 break; |
270 break; |
265 } |
271 } |
266 } |
272 } |
267 break; // skip rest of ID3v2 header |
273 break; // skip rest of ID3v2 header |
268 } |
274 } |
|
275 $frame_header = null; |
|
276 $frame_name = null; |
|
277 $frame_size = null; |
|
278 $frame_flags = null; |
269 if ($id3v2_majorversion == 2) { |
279 if ($id3v2_majorversion == 2) { |
270 // Frame ID $xx xx xx (three characters) |
280 // Frame ID $xx xx xx (three characters) |
271 // Size $xx xx xx (24-bit integer) |
281 // Size $xx xx xx (24-bit integer) |
272 // Flags $xx xx |
282 // Flags $xx xx |
273 |
283 |
425 |
435 |
426 if (isset($thisfile_id3v2_flags['isfooter']) && $thisfile_id3v2_flags['isfooter']) { |
436 if (isset($thisfile_id3v2_flags['isfooter']) && $thisfile_id3v2_flags['isfooter']) { |
427 $footer = $this->fread(10); |
437 $footer = $this->fread(10); |
428 if (substr($footer, 0, 3) == '3DI') { |
438 if (substr($footer, 0, 3) == '3DI') { |
429 $thisfile_id3v2['footer'] = true; |
439 $thisfile_id3v2['footer'] = true; |
430 $thisfile_id3v2['majorversion_footer'] = ord($footer{3}); |
440 $thisfile_id3v2['majorversion_footer'] = ord($footer[3]); |
431 $thisfile_id3v2['minorversion_footer'] = ord($footer{4}); |
441 $thisfile_id3v2['minorversion_footer'] = ord($footer[4]); |
432 } |
442 } |
433 if ($thisfile_id3v2['majorversion_footer'] <= 4) { |
443 if ($thisfile_id3v2['majorversion_footer'] <= 4) { |
434 $id3_flags = ord(substr($footer{5})); |
444 $id3_flags = ord($footer[5]); |
435 $thisfile_id3v2_flags['unsynch_footer'] = (bool) ($id3_flags & 0x80); |
445 $thisfile_id3v2_flags['unsynch_footer'] = (bool) ($id3_flags & 0x80); |
436 $thisfile_id3v2_flags['extfoot_footer'] = (bool) ($id3_flags & 0x40); |
446 $thisfile_id3v2_flags['extfoot_footer'] = (bool) ($id3_flags & 0x40); |
437 $thisfile_id3v2_flags['experim_footer'] = (bool) ($id3_flags & 0x20); |
447 $thisfile_id3v2_flags['experim_footer'] = (bool) ($id3_flags & 0x20); |
438 $thisfile_id3v2_flags['isfooter_footer'] = (bool) ($id3_flags & 0x10); |
448 $thisfile_id3v2_flags['isfooter_footer'] = (bool) ($id3_flags & 0x10); |
439 |
449 |
496 } |
506 } |
497 |
507 |
498 return true; |
508 return true; |
499 } |
509 } |
500 |
510 |
501 |
511 /** |
|
512 * @param string $genrestring |
|
513 * |
|
514 * @return array |
|
515 */ |
502 public function ParseID3v2GenreString($genrestring) { |
516 public function ParseID3v2GenreString($genrestring) { |
503 // Parse genres into arrays of genreName and genreID |
517 // Parse genres into arrays of genreName and genreID |
504 // ID3v2.2.x, ID3v2.3.x: '(21)' or '(4)Eurodisco' or '(51)(39)' or '(55)((I think...)' |
518 // ID3v2.2.x, ID3v2.3.x: '(21)' or '(4)Eurodisco' or '(51)(39)' or '(55)((I think...)' |
505 // ID3v2.4.x: '21' $00 'Eurodisco' $00 |
519 // ID3v2.4.x: '21' $00 'Eurodisco' $00 |
506 $clean_genres = array(); |
520 $clean_genres = array(); |
507 |
521 |
508 // hack-fixes for some badly-written ID3v2.3 taggers, while trying not to break correctly-written tags |
522 // hack-fixes for some badly-written ID3v2.3 taggers, while trying not to break correctly-written tags |
509 if (($this->getid3->info['id3v2']['majorversion'] == 3) && !preg_match('#[\x00]#', $genrestring)) { |
523 if (($this->getid3->info['id3v2']['majorversion'] == 3) && !preg_match('#[\x00]#', $genrestring)) { |
510 // note: MusicBrainz Picard incorrectly stores plaintext genres separated by "/" when writing in ID3v2.3 mode, hack-fix here: |
524 // note: MusicBrainz Picard incorrectly stores plaintext genres separated by "/" when writing in ID3v2.3 mode, hack-fix here: |
511 // replace / with NULL, then replace back the two ID3v1 genres that legitimately have "/" as part of the single genre name |
525 // replace / with NULL, then replace back the two ID3v1 genres that legitimately have "/" as part of the single genre name |
512 if (preg_match('#/#', $genrestring)) { |
526 if (strpos($genrestring, '/') !== false) { |
|
527 $LegitimateSlashedGenreList = array( // https://github.com/JamesHeinrich/getID3/issues/223 |
|
528 'Pop/Funk', // ID3v1 genre #62 - https://en.wikipedia.org/wiki/ID3#standard |
|
529 'Cut-up/DJ', // Discogs - https://www.discogs.com/style/cut-up/dj |
|
530 'RnB/Swing', // Discogs - https://www.discogs.com/style/rnb/swing |
|
531 'Funk / Soul', // Discogs (note spaces) - https://www.discogs.com/genre/funk+%2F+soul |
|
532 ); |
513 $genrestring = str_replace('/', "\x00", $genrestring); |
533 $genrestring = str_replace('/', "\x00", $genrestring); |
514 $genrestring = str_replace('Pop'."\x00".'Funk', 'Pop/Funk', $genrestring); |
534 foreach ($LegitimateSlashedGenreList as $SlashedGenre) { |
515 $genrestring = str_replace('Rock'."\x00".'Rock', 'Folk/Rock', $genrestring); |
535 $genrestring = str_ireplace(str_replace('/', "\x00", $SlashedGenre), $SlashedGenre, $genrestring); |
|
536 } |
516 } |
537 } |
517 |
538 |
518 // some other taggers separate multiple genres with semicolon, e.g. "Heavy Metal;Thrash Metal;Metal" |
539 // some other taggers separate multiple genres with semicolon, e.g. "Heavy Metal;Thrash Metal;Metal" |
519 if (preg_match('#;#', $genrestring)) { |
540 if (strpos($genrestring, ';') !== false) { |
520 $genrestring = str_replace(';', "\x00", $genrestring); |
541 $genrestring = str_replace(';', "\x00", $genrestring); |
521 } |
542 } |
522 } |
543 } |
523 |
544 |
524 |
545 |
655 } |
680 } |
656 $frame_terminatorpos = strpos($parsedFrame['data'], $frame_textencoding_terminator, $frame_offset); |
681 $frame_terminatorpos = strpos($parsedFrame['data'], $frame_textencoding_terminator, $frame_offset); |
657 if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($frame_textencoding_terminator), 1)) === 0) { |
682 if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($frame_textencoding_terminator), 1)) === 0) { |
658 $frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00 |
683 $frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00 |
659 } |
684 } |
660 $frame_description = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); |
685 $parsedFrame['description'] = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); |
661 if (in_array($frame_description, array("\x00", "\x00\x00", "\xFF\xFE", "\xFE\xFF"))) { |
686 $parsedFrame['description'] = $this->MakeUTF16emptyStringEmpty($parsedFrame['description']); |
662 // if description only contains a BOM or terminator then make it blank |
|
663 $frame_description = ''; |
|
664 } |
|
665 $parsedFrame['encodingid'] = $frame_textencoding; |
687 $parsedFrame['encodingid'] = $frame_textencoding; |
666 $parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding); |
688 $parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding); |
667 |
689 |
668 $parsedFrame['description'] = trim(getid3_lib::iconv_fallback($parsedFrame['encoding'], $info['id3v2']['encoding'], $frame_description)); |
690 $parsedFrame['description'] = trim(getid3_lib::iconv_fallback($parsedFrame['encoding'], $info['id3v2']['encoding'], $parsedFrame['description'])); |
669 $parsedFrame['data'] = substr($parsedFrame['data'], $frame_terminatorpos + strlen($frame_textencoding_terminator)); |
691 $parsedFrame['data'] = substr($parsedFrame['data'], $frame_terminatorpos + strlen($frame_textencoding_terminator)); |
|
692 $parsedFrame['data'] = $this->RemoveStringTerminator($parsedFrame['data'], $frame_textencoding_terminator); |
670 if (!empty($parsedFrame['framenameshort']) && !empty($parsedFrame['data'])) { |
693 if (!empty($parsedFrame['framenameshort']) && !empty($parsedFrame['data'])) { |
671 $commentkey = ($parsedFrame['description'] ? $parsedFrame['description'] : (isset($info['id3v2']['comments'][$parsedFrame['framenameshort']]) ? count($info['id3v2']['comments'][$parsedFrame['framenameshort']]) : 0)); |
694 $commentkey = ($parsedFrame['description'] ? $parsedFrame['description'] : (isset($info['id3v2']['comments'][$parsedFrame['framenameshort']]) ? count($info['id3v2']['comments'][$parsedFrame['framenameshort']]) : 0)); |
672 if (!isset($info['id3v2']['comments'][$parsedFrame['framenameshort']]) || !array_key_exists($commentkey, $info['id3v2']['comments'][$parsedFrame['framenameshort']])) { |
695 if (!isset($info['id3v2']['comments'][$parsedFrame['framenameshort']]) || !array_key_exists($commentkey, $info['id3v2']['comments'][$parsedFrame['framenameshort']])) { |
673 $info['id3v2']['comments'][$parsedFrame['framenameshort']][$commentkey] = trim(getid3_lib::iconv_fallback($parsedFrame['encoding'], $info['id3v2']['encoding'], $parsedFrame['data'])); |
696 $info['id3v2']['comments'][$parsedFrame['framenameshort']][$commentkey] = trim(getid3_lib::iconv_fallback($parsedFrame['encoding'], $info['id3v2']['encoding'], $parsedFrame['data'])); |
674 } else { |
697 } else { |
690 if ((($id3v2_majorversion <= 3) && ($frame_textencoding > 1)) || (($id3v2_majorversion == 4) && ($frame_textencoding > 3))) { |
713 if ((($id3v2_majorversion <= 3) && ($frame_textencoding > 1)) || (($id3v2_majorversion == 4) && ($frame_textencoding > 3))) { |
691 $this->warning('Invalid text encoding byte ('.$frame_textencoding.') in frame "'.$parsedFrame['frame_name'].'" - defaulting to ISO-8859-1 encoding'); |
714 $this->warning('Invalid text encoding byte ('.$frame_textencoding.') in frame "'.$parsedFrame['frame_name'].'" - defaulting to ISO-8859-1 encoding'); |
692 } |
715 } |
693 |
716 |
694 $parsedFrame['data'] = (string) substr($parsedFrame['data'], $frame_offset); |
717 $parsedFrame['data'] = (string) substr($parsedFrame['data'], $frame_offset); |
|
718 $parsedFrame['data'] = $this->RemoveStringTerminator($parsedFrame['data'], $this->TextEncodingTerminatorLookup($frame_textencoding)); |
695 |
719 |
696 $parsedFrame['encodingid'] = $frame_textencoding; |
720 $parsedFrame['encodingid'] = $frame_textencoding; |
697 $parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding); |
721 $parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding); |
698 |
|
699 if (!empty($parsedFrame['framenameshort']) && !empty($parsedFrame['data'])) { |
722 if (!empty($parsedFrame['framenameshort']) && !empty($parsedFrame['data'])) { |
700 // ID3v2.3 specs say that TPE1 (and others) can contain multiple artist values separated with / |
723 // ID3v2.3 specs say that TPE1 (and others) can contain multiple artist values separated with / |
701 // This of course breaks when an artist name contains slash character, e.g. "AC/DC" |
724 // This of course breaks when an artist name contains slash character, e.g. "AC/DC" |
702 // MP3tag (maybe others) implement alternative system where multiple artists are null-separated, which makes more sense |
725 // MP3tag (maybe others) implement alternative system where multiple artists are null-separated, which makes more sense |
703 // getID3 will split null-separated artists into multiple artists and leave slash-separated ones to the user |
726 // getID3 will split null-separated artists into multiple artists and leave slash-separated ones to the user |
749 } |
772 } |
750 $frame_terminatorpos = strpos($parsedFrame['data'], $frame_textencoding_terminator, $frame_offset); |
773 $frame_terminatorpos = strpos($parsedFrame['data'], $frame_textencoding_terminator, $frame_offset); |
751 if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($frame_textencoding_terminator), 1)) === 0) { |
774 if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($frame_textencoding_terminator), 1)) === 0) { |
752 $frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00 |
775 $frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00 |
753 } |
776 } |
754 $frame_description = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); |
|
755 if (in_array($frame_description, array("\x00", "\x00\x00", "\xFF\xFE", "\xFE\xFF"))) { |
|
756 // if description only contains a BOM or terminator then make it blank |
|
757 $frame_description = ''; |
|
758 } |
|
759 $parsedFrame['data'] = substr($parsedFrame['data'], $frame_terminatorpos + strlen($frame_textencoding_terminator)); |
|
760 |
|
761 $frame_terminatorpos = strpos($parsedFrame['data'], $frame_textencoding_terminator); |
|
762 if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($frame_textencoding_terminator), 1)) === 0) { |
|
763 $frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00 |
|
764 } |
|
765 if ($frame_terminatorpos) { |
|
766 // there are null bytes after the data - this is not according to spec |
|
767 // only use data up to first null byte |
|
768 $frame_urldata = (string) substr($parsedFrame['data'], 0, $frame_terminatorpos); |
|
769 } else { |
|
770 // no null bytes following data, just use all data |
|
771 $frame_urldata = (string) $parsedFrame['data']; |
|
772 } |
|
773 |
|
774 $parsedFrame['encodingid'] = $frame_textencoding; |
777 $parsedFrame['encodingid'] = $frame_textencoding; |
775 $parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding); |
778 $parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding); |
776 |
779 $parsedFrame['description'] = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); // according to the frame text encoding |
777 $parsedFrame['url'] = $frame_urldata; |
780 $parsedFrame['url'] = substr($parsedFrame['data'], $frame_terminatorpos + strlen($frame_textencoding_terminator)); // always ISO-8859-1 |
778 $parsedFrame['description'] = $frame_description; |
781 $parsedFrame['description'] = $this->RemoveStringTerminator($parsedFrame['description'], $frame_textencoding_terminator); |
|
782 $parsedFrame['description'] = $this->MakeUTF16emptyStringEmpty($parsedFrame['description']); |
|
783 |
779 if (!empty($parsedFrame['framenameshort']) && $parsedFrame['url']) { |
784 if (!empty($parsedFrame['framenameshort']) && $parsedFrame['url']) { |
780 $info['id3v2']['comments'][$parsedFrame['framenameshort']][] = getid3_lib::iconv_fallback($parsedFrame['encoding'], $info['id3v2']['encoding'], $parsedFrame['url']); |
785 $info['id3v2']['comments'][$parsedFrame['framenameshort']][] = getid3_lib::iconv_fallback('ISO-8859-1', $info['id3v2']['encoding'], $parsedFrame['url']); |
781 } |
786 } |
782 unset($parsedFrame['data']); |
787 unset($parsedFrame['data']); |
783 |
788 |
784 |
789 |
785 } elseif ($parsedFrame['frame_name']{0} == 'W') { // 4.3. W??? URL link frames |
790 } elseif ($parsedFrame['frame_name'][0] == 'W') { // 4.3. W??? URL link frames |
786 // There may only be one URL link frame of its kind in a tag, |
791 // There may only be one URL link frame of its kind in a tag, |
787 // except when stated otherwise in the frame description |
792 // except when stated otherwise in the frame description |
788 // <Header for 'URL link frame', ID: 'W000' - 'WZZZ', excluding 'WXXX' |
793 // <Header for 'URL link frame', ID: 'W000' - 'WZZZ', excluding 'WXXX' |
789 // described in 4.3.2.> |
794 // described in 4.3.2.> |
790 // URL <text string> |
795 // URL <text string> |
791 |
796 |
792 $parsedFrame['url'] = trim($parsedFrame['data']); |
797 $parsedFrame['url'] = trim($parsedFrame['data']); // always ISO-8859-1 |
793 if (!empty($parsedFrame['framenameshort']) && $parsedFrame['url']) { |
798 if (!empty($parsedFrame['framenameshort']) && $parsedFrame['url']) { |
794 $info['id3v2']['comments'][$parsedFrame['framenameshort']][] = $parsedFrame['url']; |
799 $info['id3v2']['comments'][$parsedFrame['framenameshort']][] = getid3_lib::iconv_fallback('ISO-8859-1', $info['id3v2']['encoding'], $parsedFrame['url']); |
795 } |
800 } |
796 unset($parsedFrame['data']); |
801 unset($parsedFrame['data']); |
797 |
802 |
798 |
803 |
799 } elseif ((($id3v2_majorversion == 3) && ($parsedFrame['frame_name'] == 'IPLS')) || // 4.4 IPLS Involved people list (ID3v2.3 only) |
804 } elseif ((($id3v2_majorversion == 3) && ($parsedFrame['frame_name'] == 'IPLS')) || // 4.4 IPLS Involved people list (ID3v2.3 only) |
931 $parsedFrame['bytesbetweenreferences'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], 2, 3)); |
936 $parsedFrame['bytesbetweenreferences'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], 2, 3)); |
932 $parsedFrame['msbetweenreferences'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], 5, 3)); |
937 $parsedFrame['msbetweenreferences'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], 5, 3)); |
933 $parsedFrame['bitsforbytesdeviation'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], 8, 1)); |
938 $parsedFrame['bitsforbytesdeviation'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], 8, 1)); |
934 $parsedFrame['bitsformsdeviation'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], 9, 1)); |
939 $parsedFrame['bitsformsdeviation'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], 9, 1)); |
935 $parsedFrame['data'] = substr($parsedFrame['data'], 10); |
940 $parsedFrame['data'] = substr($parsedFrame['data'], 10); |
|
941 $deviationbitstream = ''; |
936 while ($frame_offset < strlen($parsedFrame['data'])) { |
942 while ($frame_offset < strlen($parsedFrame['data'])) { |
937 $deviationbitstream .= getid3_lib::BigEndian2Bin(substr($parsedFrame['data'], $frame_offset++, 1)); |
943 $deviationbitstream .= getid3_lib::BigEndian2Bin(substr($parsedFrame['data'], $frame_offset++, 1)); |
938 } |
944 } |
939 $reference_counter = 0; |
945 $reference_counter = 0; |
940 while (strlen($deviationbitstream) > 0) { |
946 while (strlen($deviationbitstream) > 0) { |
992 $frame_offset += 3; |
998 $frame_offset += 3; |
993 $frame_terminatorpos = strpos($parsedFrame['data'], $frame_textencoding_terminator, $frame_offset); |
999 $frame_terminatorpos = strpos($parsedFrame['data'], $frame_textencoding_terminator, $frame_offset); |
994 if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($frame_textencoding_terminator), 1)) === 0) { |
1000 if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($frame_textencoding_terminator), 1)) === 0) { |
995 $frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00 |
1001 $frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00 |
996 } |
1002 } |
997 $frame_description = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); |
1003 $parsedFrame['description'] = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); |
998 if (in_array($frame_description, array("\x00", "\x00\x00", "\xFF\xFE", "\xFE\xFF"))) { |
1004 $parsedFrame['description'] = $this->MakeUTF16emptyStringEmpty($parsedFrame['description']); |
999 // if description only contains a BOM or terminator then make it blank |
|
1000 $frame_description = ''; |
|
1001 } |
|
1002 $parsedFrame['data'] = substr($parsedFrame['data'], $frame_terminatorpos + strlen($frame_textencoding_terminator)); |
1005 $parsedFrame['data'] = substr($parsedFrame['data'], $frame_terminatorpos + strlen($frame_textencoding_terminator)); |
|
1006 $parsedFrame['data'] = $this->RemoveStringTerminator($parsedFrame['data'], $frame_textencoding_terminator); |
1003 |
1007 |
1004 $parsedFrame['encodingid'] = $frame_textencoding; |
1008 $parsedFrame['encodingid'] = $frame_textencoding; |
1005 $parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding); |
1009 $parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding); |
1006 |
1010 |
1007 $parsedFrame['language'] = $frame_language; |
1011 $parsedFrame['language'] = $frame_language; |
1008 $parsedFrame['languagename'] = $this->LanguageLookup($frame_language, false); |
1012 $parsedFrame['languagename'] = $this->LanguageLookup($frame_language, false); |
1009 $parsedFrame['description'] = $frame_description; |
|
1010 if (!empty($parsedFrame['framenameshort']) && !empty($parsedFrame['data'])) { |
1013 if (!empty($parsedFrame['framenameshort']) && !empty($parsedFrame['data'])) { |
1011 $info['id3v2']['comments'][$parsedFrame['framenameshort']][] = getid3_lib::iconv_fallback($parsedFrame['encoding'], $info['id3v2']['encoding'], $parsedFrame['data']); |
1014 $info['id3v2']['comments'][$parsedFrame['framenameshort']][] = getid3_lib::iconv_fallback($parsedFrame['encoding'], $info['id3v2']['encoding'], $parsedFrame['data']); |
1012 } |
1015 } |
1013 unset($parsedFrame['data']); |
1016 unset($parsedFrame['data']); |
1014 |
1017 |
1100 $frame_offset += 3; |
1103 $frame_offset += 3; |
1101 $frame_terminatorpos = strpos($parsedFrame['data'], $frame_textencoding_terminator, $frame_offset); |
1104 $frame_terminatorpos = strpos($parsedFrame['data'], $frame_textencoding_terminator, $frame_offset); |
1102 if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($frame_textencoding_terminator), 1)) === 0) { |
1105 if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($frame_textencoding_terminator), 1)) === 0) { |
1103 $frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00 |
1106 $frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00 |
1104 } |
1107 } |
1105 $frame_description = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); |
1108 $parsedFrame['description'] = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); |
1106 if (in_array($frame_description, array("\x00", "\x00\x00", "\xFF\xFE", "\xFE\xFF"))) { |
1109 $parsedFrame['description'] = $this->MakeUTF16emptyStringEmpty($parsedFrame['description']); |
1107 // if description only contains a BOM or terminator then make it blank |
|
1108 $frame_description = ''; |
|
1109 } |
|
1110 $frame_text = (string) substr($parsedFrame['data'], $frame_terminatorpos + strlen($frame_textencoding_terminator)); |
1110 $frame_text = (string) substr($parsedFrame['data'], $frame_terminatorpos + strlen($frame_textencoding_terminator)); |
|
1111 $frame_text = $this->RemoveStringTerminator($frame_text, $frame_textencoding_terminator); |
1111 |
1112 |
1112 $parsedFrame['encodingid'] = $frame_textencoding; |
1113 $parsedFrame['encodingid'] = $frame_textencoding; |
1113 $parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding); |
1114 $parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding); |
1114 |
1115 |
1115 $parsedFrame['language'] = $frame_language; |
1116 $parsedFrame['language'] = $frame_language; |
1116 $parsedFrame['languagename'] = $this->LanguageLookup($frame_language, false); |
1117 $parsedFrame['languagename'] = $this->LanguageLookup($frame_language, false); |
1117 $parsedFrame['description'] = $frame_description; |
|
1118 $parsedFrame['data'] = $frame_text; |
1118 $parsedFrame['data'] = $frame_text; |
1119 if (!empty($parsedFrame['framenameshort']) && !empty($parsedFrame['data'])) { |
1119 if (!empty($parsedFrame['framenameshort']) && !empty($parsedFrame['data'])) { |
1120 $commentkey = ($parsedFrame['description'] ? $parsedFrame['description'] : (!empty($info['id3v2']['comments'][$parsedFrame['framenameshort']]) ? count($info['id3v2']['comments'][$parsedFrame['framenameshort']]) : 0)); |
1120 $commentkey = ($parsedFrame['description'] ? $parsedFrame['description'] : (!empty($info['id3v2']['comments'][$parsedFrame['framenameshort']]) ? count($info['id3v2']['comments'][$parsedFrame['framenameshort']]) : 0)); |
1121 if (!isset($info['id3v2']['comments'][$parsedFrame['framenameshort']]) || !array_key_exists($commentkey, $info['id3v2']['comments'][$parsedFrame['framenameshort']])) { |
1121 if (!isset($info['id3v2']['comments'][$parsedFrame['framenameshort']]) || !array_key_exists($commentkey, $info['id3v2']['comments'][$parsedFrame['framenameshort']])) { |
1122 $info['id3v2']['comments'][$parsedFrame['framenameshort']][$commentkey] = getid3_lib::iconv_fallback($parsedFrame['encoding'], $info['id3v2']['encoding'], $parsedFrame['data']); |
1122 $info['id3v2']['comments'][$parsedFrame['framenameshort']][$commentkey] = getid3_lib::iconv_fallback($parsedFrame['encoding'], $info['id3v2']['encoding'], $parsedFrame['data']); |
1405 } else { |
1405 } else { |
1406 $frame_terminatorpos = strpos($parsedFrame['data'], $frame_textencoding_terminator, $frame_offset); |
1406 $frame_terminatorpos = strpos($parsedFrame['data'], $frame_textencoding_terminator, $frame_offset); |
1407 if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($frame_textencoding_terminator), 1)) === 0) { |
1407 if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($frame_textencoding_terminator), 1)) === 0) { |
1408 $frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00 |
1408 $frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00 |
1409 } |
1409 } |
1410 $frame_description = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); |
1410 $parsedFrame['description'] = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); |
1411 if (in_array($frame_description, array("\x00", "\x00\x00", "\xFF\xFE", "\xFE\xFF"))) { |
1411 $parsedFrame['description'] = $this->MakeUTF16emptyStringEmpty($parsedFrame['description']); |
1412 // if description only contains a BOM or terminator then make it blank |
1412 $parsedFrame['encodingid'] = $frame_textencoding; |
1413 $frame_description = ''; |
1413 $parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding); |
1414 } |
|
1415 $parsedFrame['encodingid'] = $frame_textencoding; |
|
1416 $parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding); |
|
1417 |
1414 |
1418 if ($id3v2_majorversion == 2) { |
1415 if ($id3v2_majorversion == 2) { |
1419 $parsedFrame['imagetype'] = $frame_imagetype; |
1416 $parsedFrame['imagetype'] = isset($frame_imagetype) ? $frame_imagetype : null; |
1420 } else { |
1417 } else { |
1421 $parsedFrame['mime'] = $frame_mimetype; |
1418 $parsedFrame['mime'] = isset($frame_mimetype) ? $frame_mimetype : null; |
1422 } |
1419 } |
1423 $parsedFrame['picturetypeid'] = $frame_picturetype; |
1420 $parsedFrame['picturetypeid'] = $frame_picturetype; |
1424 $parsedFrame['picturetype'] = $this->APICPictureTypeLookup($frame_picturetype); |
1421 $parsedFrame['picturetype'] = $this->APICPictureTypeLookup($frame_picturetype); |
1425 $parsedFrame['description'] = $frame_description; |
1422 $parsedFrame['data'] = substr($parsedFrame['data'], $frame_terminatorpos + strlen($frame_textencoding_terminator)); |
1426 $parsedFrame['data'] = substr($parsedFrame['data'], $frame_terminatorpos + strlen($frame_textencoding_terminator)); |
1423 $parsedFrame['datalength'] = strlen($parsedFrame['data']); |
1427 $parsedFrame['datalength'] = strlen($parsedFrame['data']); |
1424 |
1428 |
1425 $parsedFrame['image_mime'] = ''; |
1429 $parsedFrame['image_mime'] = ''; |
|
1430 $imageinfo = array(); |
1426 $imageinfo = array(); |
1431 if ($imagechunkcheck = getid3_lib::GetDataImageSize($parsedFrame['data'], $imageinfo)) { |
1427 if ($imagechunkcheck = getid3_lib::GetDataImageSize($parsedFrame['data'], $imageinfo)) { |
1432 if (($imagechunkcheck[2] >= 1) && ($imagechunkcheck[2] <= 3)) { |
1428 if (($imagechunkcheck[2] >= 1) && ($imagechunkcheck[2] <= 3)) { |
1433 $parsedFrame['image_mime'] = 'image/'.getid3_lib::ImageTypesLookup($imagechunkcheck[2]); |
1429 $parsedFrame['image_mime'] = image_type_to_mime_type($imagechunkcheck[2]); |
1434 if ($imagechunkcheck[0]) { |
1430 if ($imagechunkcheck[0]) { |
1435 $parsedFrame['image_width'] = $imagechunkcheck[0]; |
1431 $parsedFrame['image_width'] = $imagechunkcheck[0]; |
1436 } |
1432 } |
1437 if ($imagechunkcheck[1]) { |
1433 if ($imagechunkcheck[1]) { |
1438 $parsedFrame['image_height'] = $imagechunkcheck[1]; |
1434 $parsedFrame['image_height'] = $imagechunkcheck[1]; |
1531 |
1528 |
1532 $frame_terminatorpos = strpos($parsedFrame['data'], $frame_textencoding_terminator, $frame_offset); |
1529 $frame_terminatorpos = strpos($parsedFrame['data'], $frame_textencoding_terminator, $frame_offset); |
1533 if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($frame_textencoding_terminator), 1)) === 0) { |
1530 if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($frame_textencoding_terminator), 1)) === 0) { |
1534 $frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00 |
1531 $frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00 |
1535 } |
1532 } |
1536 $frame_description = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); |
1533 $parsedFrame['description'] = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); |
1537 if (in_array($frame_description, array("\x00", "\x00\x00", "\xFF\xFE", "\xFE\xFF"))) { |
1534 $parsedFrame['description'] = $this->MakeUTF16emptyStringEmpty($parsedFrame['description']); |
1538 // if description only contains a BOM or terminator then make it blank |
|
1539 $frame_description = ''; |
|
1540 } |
|
1541 $frame_offset = $frame_terminatorpos + strlen($frame_textencoding_terminator); |
1535 $frame_offset = $frame_terminatorpos + strlen($frame_textencoding_terminator); |
1542 |
1536 |
1543 $parsedFrame['objectdata'] = (string) substr($parsedFrame['data'], $frame_offset); |
1537 $parsedFrame['objectdata'] = (string) substr($parsedFrame['data'], $frame_offset); |
1544 $parsedFrame['encodingid'] = $frame_textencoding; |
1538 $parsedFrame['encodingid'] = $frame_textencoding; |
1545 $parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding); |
1539 $parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding); |
1546 |
1540 |
1547 $parsedFrame['mime'] = $frame_mimetype; |
1541 $parsedFrame['mime'] = $frame_mimetype; |
1548 $parsedFrame['filename'] = $frame_filename; |
1542 $parsedFrame['filename'] = $frame_filename; |
1549 $parsedFrame['description'] = $frame_description; |
|
1550 unset($parsedFrame['data']); |
1543 unset($parsedFrame['data']); |
1551 |
1544 |
1552 |
1545 |
1553 } elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'PCNT')) || // 4.16 PCNT Play counter |
1546 } elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'PCNT')) || // 4.16 PCNT Play counter |
1554 (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'CNT'))) { // 4.17 CNT Play counter |
1547 (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'CNT'))) { // 4.17 CNT Play counter |
1614 $frame_terminatorpos = strpos($parsedFrame['data'], "\x00", $frame_offset); |
1607 $frame_terminatorpos = strpos($parsedFrame['data'], "\x00", $frame_offset); |
1615 $frame_ownerid = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); |
1608 $frame_ownerid = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); |
1616 $frame_offset = $frame_terminatorpos + strlen("\x00"); |
1609 $frame_offset = $frame_terminatorpos + strlen("\x00"); |
1617 |
1610 |
1618 $frame_terminatorpos = strpos($parsedFrame['data'], "\x00", $frame_offset); |
1611 $frame_terminatorpos = strpos($parsedFrame['data'], "\x00", $frame_offset); |
1619 $frame_description = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); |
1612 $parsedFrame['description'] = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); |
1620 if (in_array($frame_description, array("\x00", "\x00\x00", "\xFF\xFE", "\xFE\xFF"))) { |
1613 $parsedFrame['description'] = $this->MakeUTF16emptyStringEmpty($parsedFrame['description']); |
1621 // if description only contains a BOM or terminator then make it blank |
|
1622 $frame_description = ''; |
|
1623 } |
|
1624 $frame_offset = $frame_terminatorpos + strlen("\x00"); |
1614 $frame_offset = $frame_terminatorpos + strlen("\x00"); |
1625 |
1615 |
1626 $parsedFrame['ownerid'] = $frame_ownerid; |
1616 $parsedFrame['ownerid'] = $frame_ownerid; |
1627 $parsedFrame['data'] = (string) substr($parsedFrame['data'], $frame_offset); |
1617 $parsedFrame['data'] = (string) substr($parsedFrame['data'], $frame_offset); |
1628 $parsedFrame['description'] = $frame_description; |
|
1629 unset($parsedFrame['data']); |
1618 unset($parsedFrame['data']); |
1630 |
1619 |
1631 |
1620 |
1632 } elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'AENC')) || // 4.19 AENC Audio encryption |
1621 } elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'AENC')) || // 4.19 AENC Audio encryption |
1633 (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'CRA'))) { // 4.21 CRA Audio encryption |
1622 (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'CRA'))) { // 4.21 CRA Audio encryption |
1815 |
1806 |
1816 $frame_terminatorpos = strpos($parsedFrame['data'], $frame_textencoding_terminator, $frame_offset); |
1807 $frame_terminatorpos = strpos($parsedFrame['data'], $frame_textencoding_terminator, $frame_offset); |
1817 if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($frame_textencoding_terminator), 1)) === 0) { |
1808 if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($frame_textencoding_terminator), 1)) === 0) { |
1818 $frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00 |
1809 $frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00 |
1819 } |
1810 } |
1820 $frame_description = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); |
1811 $parsedFrame['description'] = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); |
1821 if (in_array($frame_description, array("\x00", "\x00\x00", "\xFF\xFE", "\xFE\xFF"))) { |
1812 $parsedFrame['description'] = $this->MakeUTF16emptyStringEmpty($parsedFrame['description']); |
1822 // if description only contains a BOM or terminator then make it blank |
|
1823 $frame_description = ''; |
|
1824 } |
|
1825 $frame_offset = $frame_terminatorpos + strlen($frame_textencoding_terminator); |
1813 $frame_offset = $frame_terminatorpos + strlen($frame_textencoding_terminator); |
1826 |
1814 |
1827 $frame_terminatorpos = strpos($parsedFrame['data'], "\x00", $frame_offset); |
1815 $frame_terminatorpos = strpos($parsedFrame['data'], "\x00", $frame_offset); |
1828 $frame_mimetype = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); |
1816 $frame_mimetype = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); |
1829 $frame_offset = $frame_terminatorpos + strlen("\x00"); |
1817 $frame_offset = $frame_terminatorpos + strlen("\x00"); |
2000 // http://id3.org/id3v2-chapters-1.0 |
1987 // http://id3.org/id3v2-chapters-1.0 |
2001 // <ID3v2.3 or ID3v2.4 frame header, ID: "CHAP"> (10 bytes) |
1988 // <ID3v2.3 or ID3v2.4 frame header, ID: "CHAP"> (10 bytes) |
2002 // Element ID <text string> $00 |
1989 // Element ID <text string> $00 |
2003 // Start time $xx xx xx xx |
1990 // Start time $xx xx xx xx |
2004 // End time $xx xx xx xx |
1991 // End time $xx xx xx xx |
2005 // Start offset $xx xx xx xx |
1992 // Start offset $xx xx xx xx |
2006 // End offset $xx xx xx xx |
1993 // End offset $xx xx xx xx |
2007 // <Optional embedded sub-frames> |
1994 // <Optional embedded sub-frames> |
2008 |
1995 |
2009 $frame_offset = 0; |
1996 $frame_offset = 0; |
2010 @list($parsedFrame['element_id']) = explode("\x00", $parsedFrame['data'], 2); |
1997 @list($parsedFrame['element_id']) = explode("\x00", $parsedFrame['data'], 2); |
2011 $frame_offset += strlen($parsedFrame['element_id']."\x00"); |
1998 $frame_offset += strlen($parsedFrame['element_id']."\x00"); |
2012 $parsedFrame['time_begin'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 4)); |
1999 $parsedFrame['time_begin'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 4)); |
2035 $frame_offset += 4; |
2022 $frame_offset += 4; |
2036 $subframe['flags_raw'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 2)); |
2023 $subframe['flags_raw'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 2)); |
2037 $frame_offset += 2; |
2024 $frame_offset += 2; |
2038 if ($subframe['size'] > (strlen($parsedFrame['data']) - $frame_offset)) { |
2025 if ($subframe['size'] > (strlen($parsedFrame['data']) - $frame_offset)) { |
2039 $this->warning('CHAP subframe "'.$subframe['name'].'" at frame offset '.$frame_offset.' claims to be "'.$subframe['size'].'" bytes, which is more than the available data ('.(strlen($parsedFrame['data']) - $frame_offset).' bytes)'); |
2026 $this->warning('CHAP subframe "'.$subframe['name'].'" at frame offset '.$frame_offset.' claims to be "'.$subframe['size'].'" bytes, which is more than the available data ('.(strlen($parsedFrame['data']) - $frame_offset).' bytes)'); |
|
2027 break; |
|
2028 } |
|
2029 $subframe_rawdata = substr($parsedFrame['data'], $frame_offset, $subframe['size']); |
|
2030 $frame_offset += $subframe['size']; |
|
2031 |
|
2032 $subframe['encodingid'] = ord(substr($subframe_rawdata, 0, 1)); |
|
2033 $subframe['text'] = substr($subframe_rawdata, 1); |
|
2034 $subframe['encoding'] = $this->TextEncodingNameLookup($subframe['encodingid']); |
|
2035 $encoding_converted_text = trim(getid3_lib::iconv_fallback($subframe['encoding'], $info['encoding'], $subframe['text'])); |
|
2036 switch (substr($encoding_converted_text, 0, 2)) { |
|
2037 case "\xFF\xFE": |
|
2038 case "\xFE\xFF": |
|
2039 switch (strtoupper($info['id3v2']['encoding'])) { |
|
2040 case 'ISO-8859-1': |
|
2041 case 'UTF-8': |
|
2042 $encoding_converted_text = substr($encoding_converted_text, 2); |
|
2043 // remove unwanted byte-order-marks |
|
2044 break; |
|
2045 default: |
|
2046 // ignore |
|
2047 break; |
|
2048 } |
|
2049 break; |
|
2050 default: |
|
2051 // do not remove BOM |
|
2052 break; |
|
2053 } |
|
2054 |
|
2055 switch ($subframe['name']) { |
|
2056 case 'TIT2': |
|
2057 $parsedFrame['chapter_name'] = $encoding_converted_text; |
|
2058 $parsedFrame['subframes'][] = $subframe; |
|
2059 break; |
|
2060 case 'TIT3': |
|
2061 $parsedFrame['chapter_description'] = $encoding_converted_text; |
|
2062 $parsedFrame['subframes'][] = $subframe; |
|
2063 break; |
|
2064 case 'WXXX': |
|
2065 list($subframe['chapter_url_description'], $subframe['chapter_url']) = explode("\x00", $encoding_converted_text, 2); |
|
2066 $parsedFrame['chapter_url'][$subframe['chapter_url_description']] = $subframe['chapter_url']; |
|
2067 $parsedFrame['subframes'][] = $subframe; |
|
2068 break; |
|
2069 case 'APIC': |
|
2070 if (preg_match('#^([^\\x00]+)*\\x00(.)([^\\x00]+)*\\x00(.+)$#s', $subframe['text'], $matches)) { |
|
2071 list($dummy, $subframe_apic_mime, $subframe_apic_picturetype, $subframe_apic_description, $subframe_apic_picturedata) = $matches; |
|
2072 $subframe['image_mime'] = trim(getid3_lib::iconv_fallback($subframe['encoding'], $info['encoding'], $subframe_apic_mime)); |
|
2073 $subframe['picture_type'] = $this->APICPictureTypeLookup($subframe_apic_picturetype); |
|
2074 $subframe['description'] = trim(getid3_lib::iconv_fallback($subframe['encoding'], $info['encoding'], $subframe_apic_description)); |
|
2075 if (strlen($this->TextEncodingTerminatorLookup($subframe['encoding'])) == 2) { |
|
2076 // the null terminator between "description" and "picture data" could be either 1 byte (ISO-8859-1, UTF-8) or two bytes (UTF-16) |
|
2077 // the above regex assumes one byte, if it's actually two then strip the second one here |
|
2078 $subframe_apic_picturedata = substr($subframe_apic_picturedata, 1); |
|
2079 } |
|
2080 $subframe['data'] = $subframe_apic_picturedata; |
|
2081 unset($dummy, $subframe_apic_mime, $subframe_apic_picturetype, $subframe_apic_description, $subframe_apic_picturedata); |
|
2082 unset($subframe['text'], $parsedFrame['text']); |
|
2083 $parsedFrame['subframes'][] = $subframe; |
|
2084 $parsedFrame['picture_present'] = true; |
|
2085 } else { |
|
2086 $this->warning('ID3v2.CHAP subframe #'.(count($parsedFrame['subframes']) + 1).' "'.$subframe['name'].'" not in expected format'); |
|
2087 } |
|
2088 break; |
|
2089 default: |
|
2090 $this->warning('ID3v2.CHAP subframe "'.$subframe['name'].'" not handled (supported: TIT2, TIT3, WXXX, APIC)'); |
|
2091 break; |
|
2092 } |
|
2093 } |
|
2094 unset($subframe_rawdata, $subframe, $encoding_converted_text); |
|
2095 unset($parsedFrame['data']); // debatable whether this this be here, without it the returned structure may contain a large amount of duplicate data if chapters contain APIC |
|
2096 } |
|
2097 |
|
2098 $id3v2_chapter_entry = array(); |
|
2099 foreach (array('id', 'time_begin', 'time_end', 'offset_begin', 'offset_end', 'chapter_name', 'chapter_description', 'chapter_url', 'picture_present') as $id3v2_chapter_key) { |
|
2100 if (isset($parsedFrame[$id3v2_chapter_key])) { |
|
2101 $id3v2_chapter_entry[$id3v2_chapter_key] = $parsedFrame[$id3v2_chapter_key]; |
|
2102 } |
|
2103 } |
|
2104 if (!isset($info['id3v2']['chapters'])) { |
|
2105 $info['id3v2']['chapters'] = array(); |
|
2106 } |
|
2107 $info['id3v2']['chapters'][] = $id3v2_chapter_entry; |
|
2108 unset($id3v2_chapter_entry, $id3v2_chapter_key); |
|
2109 |
|
2110 |
|
2111 } elseif (($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'CTOC')) { // CTOC Chapters Table Of Contents frame (ID3v2.3+ only) |
|
2112 // http://id3.org/id3v2-chapters-1.0 |
|
2113 // <ID3v2.3 or ID3v2.4 frame header, ID: "CTOC"> (10 bytes) |
|
2114 // Element ID <text string> $00 |
|
2115 // CTOC flags %xx |
|
2116 // Entry count $xx |
|
2117 // Child Element ID <string>$00 /* zero or more child CHAP or CTOC entries */ |
|
2118 // <Optional embedded sub-frames> |
|
2119 |
|
2120 $frame_offset = 0; |
|
2121 @list($parsedFrame['element_id']) = explode("\x00", $parsedFrame['data'], 2); |
|
2122 $frame_offset += strlen($parsedFrame['element_id']."\x00"); |
|
2123 $ctoc_flags_raw = ord(substr($parsedFrame['data'], $frame_offset, 1)); |
|
2124 $frame_offset += 1; |
|
2125 $parsedFrame['entry_count'] = ord(substr($parsedFrame['data'], $frame_offset, 1)); |
|
2126 $frame_offset += 1; |
|
2127 |
|
2128 $terminator_position = null; |
|
2129 for ($i = 0; $i < $parsedFrame['entry_count']; $i++) { |
|
2130 $terminator_position = strpos($parsedFrame['data'], "\x00", $frame_offset); |
|
2131 $parsedFrame['child_element_ids'][$i] = substr($parsedFrame['data'], $frame_offset, $terminator_position - $frame_offset); |
|
2132 $frame_offset = $terminator_position + 1; |
|
2133 } |
|
2134 |
|
2135 $parsedFrame['ctoc_flags']['ordered'] = (bool) ($ctoc_flags_raw & 0x01); |
|
2136 $parsedFrame['ctoc_flags']['top_level'] = (bool) ($ctoc_flags_raw & 0x03); |
|
2137 |
|
2138 unset($ctoc_flags_raw, $terminator_position); |
|
2139 |
|
2140 if ($frame_offset < strlen($parsedFrame['data'])) { |
|
2141 $parsedFrame['subframes'] = array(); |
|
2142 while ($frame_offset < strlen($parsedFrame['data'])) { |
|
2143 // <Optional embedded sub-frames> |
|
2144 $subframe = array(); |
|
2145 $subframe['name'] = substr($parsedFrame['data'], $frame_offset, 4); |
|
2146 $frame_offset += 4; |
|
2147 $subframe['size'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 4)); |
|
2148 $frame_offset += 4; |
|
2149 $subframe['flags_raw'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 2)); |
|
2150 $frame_offset += 2; |
|
2151 if ($subframe['size'] > (strlen($parsedFrame['data']) - $frame_offset)) { |
|
2152 $this->warning('CTOS subframe "'.$subframe['name'].'" at frame offset '.$frame_offset.' claims to be "'.$subframe['size'].'" bytes, which is more than the available data ('.(strlen($parsedFrame['data']) - $frame_offset).' bytes)'); |
2040 break; |
2153 break; |
2041 } |
2154 } |
2042 $subframe_rawdata = substr($parsedFrame['data'], $frame_offset, $subframe['size']); |
2155 $subframe_rawdata = substr($parsedFrame['data'], $frame_offset, $subframe['size']); |
2043 $frame_offset += $subframe['size']; |
2156 $frame_offset += $subframe['size']; |
2044 |
2157 |
2065 break; |
2178 break; |
2066 } |
2179 } |
2067 |
2180 |
2068 if (($subframe['name'] == 'TIT2') || ($subframe['name'] == 'TIT3')) { |
2181 if (($subframe['name'] == 'TIT2') || ($subframe['name'] == 'TIT3')) { |
2069 if ($subframe['name'] == 'TIT2') { |
2182 if ($subframe['name'] == 'TIT2') { |
2070 $parsedFrame['chapter_name'] = $encoding_converted_text; |
|
2071 } elseif ($subframe['name'] == 'TIT3') { |
|
2072 $parsedFrame['chapter_description'] = $encoding_converted_text; |
|
2073 } |
|
2074 $parsedFrame['subframes'][] = $subframe; |
|
2075 } else { |
|
2076 $this->warning('ID3v2.CHAP subframe "'.$subframe['name'].'" not handled (only TIT2 and TIT3)'); |
|
2077 } |
|
2078 } |
|
2079 unset($subframe_rawdata, $subframe, $encoding_converted_text); |
|
2080 } |
|
2081 |
|
2082 $id3v2_chapter_entry = array(); |
|
2083 foreach (array('id', 'time_begin', 'time_end', 'offset_begin', 'offset_end', 'chapter_name', 'chapter_description') as $id3v2_chapter_key) { |
|
2084 if (isset($parsedFrame[$id3v2_chapter_key])) { |
|
2085 $id3v2_chapter_entry[$id3v2_chapter_key] = $parsedFrame[$id3v2_chapter_key]; |
|
2086 } |
|
2087 } |
|
2088 if (!isset($info['id3v2']['chapters'])) { |
|
2089 $info['id3v2']['chapters'] = array(); |
|
2090 } |
|
2091 $info['id3v2']['chapters'][] = $id3v2_chapter_entry; |
|
2092 unset($id3v2_chapter_entry, $id3v2_chapter_key); |
|
2093 |
|
2094 |
|
2095 } elseif (($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'CTOC')) { // CTOC Chapters Table Of Contents frame (ID3v2.3+ only) |
|
2096 // http://id3.org/id3v2-chapters-1.0 |
|
2097 // <ID3v2.3 or ID3v2.4 frame header, ID: "CTOC"> (10 bytes) |
|
2098 // Element ID <text string> $00 |
|
2099 // CTOC flags %xx |
|
2100 // Entry count $xx |
|
2101 // Child Element ID <string>$00 /* zero or more child CHAP or CTOC entries */ |
|
2102 // <Optional embedded sub-frames> |
|
2103 |
|
2104 $frame_offset = 0; |
|
2105 @list($parsedFrame['element_id']) = explode("\x00", $parsedFrame['data'], 2); |
|
2106 $frame_offset += strlen($parsedFrame['element_id']."\x00"); |
|
2107 $ctoc_flags_raw = ord(substr($parsedFrame['data'], $frame_offset, 1)); |
|
2108 $frame_offset += 1; |
|
2109 $parsedFrame['entry_count'] = ord(substr($parsedFrame['data'], $frame_offset, 1)); |
|
2110 $frame_offset += 1; |
|
2111 |
|
2112 $terminator_position = null; |
|
2113 for ($i = 0; $i < $parsedFrame['entry_count']; $i++) { |
|
2114 $terminator_position = strpos($parsedFrame['data'], "\x00", $frame_offset); |
|
2115 $parsedFrame['child_element_ids'][$i] = substr($parsedFrame['data'], $frame_offset, $terminator_position - $frame_offset); |
|
2116 $frame_offset = $terminator_position + 1; |
|
2117 } |
|
2118 |
|
2119 $parsedFrame['ctoc_flags']['ordered'] = (bool) ($ctoc_flags_raw & 0x01); |
|
2120 $parsedFrame['ctoc_flags']['top_level'] = (bool) ($ctoc_flags_raw & 0x03); |
|
2121 |
|
2122 unset($ctoc_flags_raw, $terminator_position); |
|
2123 |
|
2124 if ($frame_offset < strlen($parsedFrame['data'])) { |
|
2125 $parsedFrame['subframes'] = array(); |
|
2126 while ($frame_offset < strlen($parsedFrame['data'])) { |
|
2127 // <Optional embedded sub-frames> |
|
2128 $subframe = array(); |
|
2129 $subframe['name'] = substr($parsedFrame['data'], $frame_offset, 4); |
|
2130 $frame_offset += 4; |
|
2131 $subframe['size'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 4)); |
|
2132 $frame_offset += 4; |
|
2133 $subframe['flags_raw'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 2)); |
|
2134 $frame_offset += 2; |
|
2135 if ($subframe['size'] > (strlen($parsedFrame['data']) - $frame_offset)) { |
|
2136 $this->warning('CTOS subframe "'.$subframe['name'].'" at frame offset '.$frame_offset.' claims to be "'.$subframe['size'].'" bytes, which is more than the available data ('.(strlen($parsedFrame['data']) - $frame_offset).' bytes)'); |
|
2137 break; |
|
2138 } |
|
2139 $subframe_rawdata = substr($parsedFrame['data'], $frame_offset, $subframe['size']); |
|
2140 $frame_offset += $subframe['size']; |
|
2141 |
|
2142 $subframe['encodingid'] = ord(substr($subframe_rawdata, 0, 1)); |
|
2143 $subframe['text'] = substr($subframe_rawdata, 1); |
|
2144 $subframe['encoding'] = $this->TextEncodingNameLookup($subframe['encodingid']); |
|
2145 $encoding_converted_text = trim(getid3_lib::iconv_fallback($subframe['encoding'], $info['encoding'], $subframe['text']));; |
|
2146 switch (substr($encoding_converted_text, 0, 2)) { |
|
2147 case "\xFF\xFE": |
|
2148 case "\xFE\xFF": |
|
2149 switch (strtoupper($info['id3v2']['encoding'])) { |
|
2150 case 'ISO-8859-1': |
|
2151 case 'UTF-8': |
|
2152 $encoding_converted_text = substr($encoding_converted_text, 2); |
|
2153 // remove unwanted byte-order-marks |
|
2154 break; |
|
2155 default: |
|
2156 // ignore |
|
2157 break; |
|
2158 } |
|
2159 break; |
|
2160 default: |
|
2161 // do not remove BOM |
|
2162 break; |
|
2163 } |
|
2164 |
|
2165 if (($subframe['name'] == 'TIT2') || ($subframe['name'] == 'TIT3')) { |
|
2166 if ($subframe['name'] == 'TIT2') { |
|
2167 $parsedFrame['toc_name'] = $encoding_converted_text; |
2183 $parsedFrame['toc_name'] = $encoding_converted_text; |
2168 } elseif ($subframe['name'] == 'TIT3') { |
2184 } elseif ($subframe['name'] == 'TIT3') { |
2169 $parsedFrame['toc_description'] = $encoding_converted_text; |
2185 $parsedFrame['toc_description'] = $encoding_converted_text; |
2170 } |
2186 } |
2171 $parsedFrame['subframes'][] = $subframe; |
2187 $parsedFrame['subframes'][] = $subframe; |
2179 } |
2195 } |
2180 |
2196 |
2181 return true; |
2197 return true; |
2182 } |
2198 } |
2183 |
2199 |
2184 |
2200 /** |
|
2201 * @param string $data |
|
2202 * |
|
2203 * @return string |
|
2204 */ |
2185 public function DeUnsynchronise($data) { |
2205 public function DeUnsynchronise($data) { |
2186 return str_replace("\xFF\x00", "\xFF", $data); |
2206 return str_replace("\xFF\x00", "\xFF", $data); |
2187 } |
2207 } |
2188 |
2208 |
|
2209 /** |
|
2210 * @param int $index |
|
2211 * |
|
2212 * @return string |
|
2213 */ |
2189 public function LookupExtendedHeaderRestrictionsTagSizeLimits($index) { |
2214 public function LookupExtendedHeaderRestrictionsTagSizeLimits($index) { |
2190 static $LookupExtendedHeaderRestrictionsTagSizeLimits = array( |
2215 static $LookupExtendedHeaderRestrictionsTagSizeLimits = array( |
2191 0x00 => 'No more than 128 frames and 1 MB total tag size', |
2216 0x00 => 'No more than 128 frames and 1 MB total tag size', |
2192 0x01 => 'No more than 64 frames and 128 KB total tag size', |
2217 0x01 => 'No more than 64 frames and 128 KB total tag size', |
2193 0x02 => 'No more than 32 frames and 40 KB total tag size', |
2218 0x02 => 'No more than 32 frames and 40 KB total tag size', |
2194 0x03 => 'No more than 32 frames and 4 KB total tag size', |
2219 0x03 => 'No more than 32 frames and 4 KB total tag size', |
2195 ); |
2220 ); |
2196 return (isset($LookupExtendedHeaderRestrictionsTagSizeLimits[$index]) ? $LookupExtendedHeaderRestrictionsTagSizeLimits[$index] : ''); |
2221 return (isset($LookupExtendedHeaderRestrictionsTagSizeLimits[$index]) ? $LookupExtendedHeaderRestrictionsTagSizeLimits[$index] : ''); |
2197 } |
2222 } |
2198 |
2223 |
|
2224 /** |
|
2225 * @param int $index |
|
2226 * |
|
2227 * @return string |
|
2228 */ |
2199 public function LookupExtendedHeaderRestrictionsTextEncodings($index) { |
2229 public function LookupExtendedHeaderRestrictionsTextEncodings($index) { |
2200 static $LookupExtendedHeaderRestrictionsTextEncodings = array( |
2230 static $LookupExtendedHeaderRestrictionsTextEncodings = array( |
2201 0x00 => 'No restrictions', |
2231 0x00 => 'No restrictions', |
2202 0x01 => 'Strings are only encoded with ISO-8859-1 or UTF-8', |
2232 0x01 => 'Strings are only encoded with ISO-8859-1 or UTF-8', |
2203 ); |
2233 ); |
2204 return (isset($LookupExtendedHeaderRestrictionsTextEncodings[$index]) ? $LookupExtendedHeaderRestrictionsTextEncodings[$index] : ''); |
2234 return (isset($LookupExtendedHeaderRestrictionsTextEncodings[$index]) ? $LookupExtendedHeaderRestrictionsTextEncodings[$index] : ''); |
2205 } |
2235 } |
2206 |
2236 |
|
2237 /** |
|
2238 * @param int $index |
|
2239 * |
|
2240 * @return string |
|
2241 */ |
2207 public function LookupExtendedHeaderRestrictionsTextFieldSize($index) { |
2242 public function LookupExtendedHeaderRestrictionsTextFieldSize($index) { |
2208 static $LookupExtendedHeaderRestrictionsTextFieldSize = array( |
2243 static $LookupExtendedHeaderRestrictionsTextFieldSize = array( |
2209 0x00 => 'No restrictions', |
2244 0x00 => 'No restrictions', |
2210 0x01 => 'No string is longer than 1024 characters', |
2245 0x01 => 'No string is longer than 1024 characters', |
2211 0x02 => 'No string is longer than 128 characters', |
2246 0x02 => 'No string is longer than 128 characters', |
2212 0x03 => 'No string is longer than 30 characters', |
2247 0x03 => 'No string is longer than 30 characters', |
2213 ); |
2248 ); |
2214 return (isset($LookupExtendedHeaderRestrictionsTextFieldSize[$index]) ? $LookupExtendedHeaderRestrictionsTextFieldSize[$index] : ''); |
2249 return (isset($LookupExtendedHeaderRestrictionsTextFieldSize[$index]) ? $LookupExtendedHeaderRestrictionsTextFieldSize[$index] : ''); |
2215 } |
2250 } |
2216 |
2251 |
|
2252 /** |
|
2253 * @param int $index |
|
2254 * |
|
2255 * @return string |
|
2256 */ |
2217 public function LookupExtendedHeaderRestrictionsImageEncoding($index) { |
2257 public function LookupExtendedHeaderRestrictionsImageEncoding($index) { |
2218 static $LookupExtendedHeaderRestrictionsImageEncoding = array( |
2258 static $LookupExtendedHeaderRestrictionsImageEncoding = array( |
2219 0x00 => 'No restrictions', |
2259 0x00 => 'No restrictions', |
2220 0x01 => 'Images are encoded only with PNG or JPEG', |
2260 0x01 => 'Images are encoded only with PNG or JPEG', |
2221 ); |
2261 ); |
2222 return (isset($LookupExtendedHeaderRestrictionsImageEncoding[$index]) ? $LookupExtendedHeaderRestrictionsImageEncoding[$index] : ''); |
2262 return (isset($LookupExtendedHeaderRestrictionsImageEncoding[$index]) ? $LookupExtendedHeaderRestrictionsImageEncoding[$index] : ''); |
2223 } |
2263 } |
2224 |
2264 |
|
2265 /** |
|
2266 * @param int $index |
|
2267 * |
|
2268 * @return string |
|
2269 */ |
2225 public function LookupExtendedHeaderRestrictionsImageSizeSize($index) { |
2270 public function LookupExtendedHeaderRestrictionsImageSizeSize($index) { |
2226 static $LookupExtendedHeaderRestrictionsImageSizeSize = array( |
2271 static $LookupExtendedHeaderRestrictionsImageSizeSize = array( |
2227 0x00 => 'No restrictions', |
2272 0x00 => 'No restrictions', |
2228 0x01 => 'All images are 256x256 pixels or smaller', |
2273 0x01 => 'All images are 256x256 pixels or smaller', |
2229 0x02 => 'All images are 64x64 pixels or smaller', |
2274 0x02 => 'All images are 64x64 pixels or smaller', |
2230 0x03 => 'All images are exactly 64x64 pixels, unless required otherwise', |
2275 0x03 => 'All images are exactly 64x64 pixels, unless required otherwise', |
2231 ); |
2276 ); |
2232 return (isset($LookupExtendedHeaderRestrictionsImageSizeSize[$index]) ? $LookupExtendedHeaderRestrictionsImageSizeSize[$index] : ''); |
2277 return (isset($LookupExtendedHeaderRestrictionsImageSizeSize[$index]) ? $LookupExtendedHeaderRestrictionsImageSizeSize[$index] : ''); |
2233 } |
2278 } |
2234 |
2279 |
|
2280 /** |
|
2281 * @param string $currencyid |
|
2282 * |
|
2283 * @return string |
|
2284 */ |
2235 public function LookupCurrencyUnits($currencyid) { |
2285 public function LookupCurrencyUnits($currencyid) { |
2236 |
2286 |
2237 $begin = __LINE__; |
2287 $begin = __LINE__; |
2238 |
2288 |
2239 /** This is not a comment! |
2289 /** This is not a comment! |
3590 255 => 'UTF-16BE' |
3692 255 => 'UTF-16BE' |
3591 ); |
3693 ); |
3592 return (isset($TextEncodingNameLookup[$encoding]) ? $TextEncodingNameLookup[$encoding] : 'ISO-8859-1'); |
3694 return (isset($TextEncodingNameLookup[$encoding]) ? $TextEncodingNameLookup[$encoding] : 'ISO-8859-1'); |
3593 } |
3695 } |
3594 |
3696 |
|
3697 /** |
|
3698 * @param string $string |
|
3699 * @param string $terminator |
|
3700 * |
|
3701 * @return string |
|
3702 */ |
|
3703 public static function RemoveStringTerminator($string, $terminator) { |
|
3704 // Null terminator at end of comment string is somewhat ambiguous in the specification, may or may not be implemented by various taggers. Remove terminator only if present. |
|
3705 // https://github.com/JamesHeinrich/getID3/issues/121 |
|
3706 // https://community.mp3tag.de/t/x-trailing-nulls-in-id3v2-comments/19227 |
|
3707 if (substr($string, -strlen($terminator), strlen($terminator)) === $terminator) { |
|
3708 $string = substr($string, 0, -strlen($terminator)); |
|
3709 } |
|
3710 return $string; |
|
3711 } |
|
3712 |
|
3713 /** |
|
3714 * @param string $string |
|
3715 * |
|
3716 * @return string |
|
3717 */ |
|
3718 public static function MakeUTF16emptyStringEmpty($string) { |
|
3719 if (in_array($string, array("\x00", "\x00\x00", "\xFF\xFE", "\xFE\xFF"))) { |
|
3720 // if string only contains a BOM or terminator then make it actually an empty string |
|
3721 $string = ''; |
|
3722 } |
|
3723 return $string; |
|
3724 } |
|
3725 |
|
3726 /** |
|
3727 * @param string $framename |
|
3728 * @param int $id3v2majorversion |
|
3729 * |
|
3730 * @return bool|int |
|
3731 */ |
3595 public static function IsValidID3v2FrameName($framename, $id3v2majorversion) { |
3732 public static function IsValidID3v2FrameName($framename, $id3v2majorversion) { |
3596 switch ($id3v2majorversion) { |
3733 switch ($id3v2majorversion) { |
3597 case 2: |
3734 case 2: |
3598 return preg_match('#[A-Z][A-Z0-9]{2}#', $framename); |
3735 return preg_match('#[A-Z][A-Z0-9]{2}#', $framename); |
3599 break; |
|
3600 |
3736 |
3601 case 3: |
3737 case 3: |
3602 case 4: |
3738 case 4: |
3603 return preg_match('#[A-Z][A-Z0-9]{3}#', $framename); |
3739 return preg_match('#[A-Z][A-Z0-9]{3}#', $framename); |
3604 break; |
|
3605 } |
3740 } |
3606 return false; |
3741 return false; |
3607 } |
3742 } |
3608 |
3743 |
|
3744 /** |
|
3745 * @param string $numberstring |
|
3746 * @param bool $allowdecimal |
|
3747 * @param bool $allownegative |
|
3748 * |
|
3749 * @return bool |
|
3750 */ |
3609 public static function IsANumber($numberstring, $allowdecimal=false, $allownegative=false) { |
3751 public static function IsANumber($numberstring, $allowdecimal=false, $allownegative=false) { |
3610 for ($i = 0; $i < strlen($numberstring); $i++) { |
3752 for ($i = 0; $i < strlen($numberstring); $i++) { |
3611 if ((chr($numberstring{$i}) < chr('0')) || (chr($numberstring{$i}) > chr('9'))) { |
3753 if ((chr($numberstring[$i]) < chr('0')) || (chr($numberstring[$i]) > chr('9'))) { |
3612 if (($numberstring{$i} == '.') && $allowdecimal) { |
3754 if (($numberstring[$i] == '.') && $allowdecimal) { |
3613 // allowed |
3755 // allowed |
3614 } elseif (($numberstring{$i} == '-') && $allownegative && ($i == 0)) { |
3756 } elseif (($numberstring[$i] == '-') && $allownegative && ($i == 0)) { |
3615 // allowed |
3757 // allowed |
3616 } else { |
3758 } else { |
3617 return false; |
3759 return false; |
3618 } |
3760 } |
3619 } |
3761 } |
3620 } |
3762 } |
3621 return true; |
3763 return true; |
3622 } |
3764 } |
3623 |
3765 |
|
3766 /** |
|
3767 * @param string $datestamp |
|
3768 * |
|
3769 * @return bool |
|
3770 */ |
3624 public static function IsValidDateStampString($datestamp) { |
3771 public static function IsValidDateStampString($datestamp) { |
3625 if (strlen($datestamp) != 8) { |
3772 if (strlen($datestamp) != 8) { |
3626 return false; |
3773 return false; |
3627 } |
3774 } |
3628 if (!self::IsANumber($datestamp, false)) { |
3775 if (!self::IsANumber($datestamp, false)) { |