396 {
398 return false;
399 }
400 $framedata = '';
401
402 if (($this->majorversion < 3) || ($this->majorversion > 4)) {
403
404 $this->errors[] = 'Only ID3v2.3 and ID3v2.4 are supported in GenerateID3v2FrameData()';
405
406 } else {
407
408 switch ($frame_name) {
409 case 'UFID':
410
411
412
413 if (strlen($source_data_array['data']) > 64) {
414 $this->errors[] = 'Identifier not allowed to be longer than 64 bytes in '.$frame_name.' (supplied data was '.strlen($source_data_array['data']).' bytes long)';
415 } else {
416 $framedata .= str_replace("\x00", '', $source_data_array['ownerid'])."\x00";
417 $framedata .= substr($source_data_array['data'], 0, 64);
418 }
419 break;
420
421 case 'TXXX':
422
423
424
425
426 $source_data_array['encodingid'] = (isset($source_data_array['encodingid']) ? $source_data_array['encodingid'] : $this->id3v2_default_encodingid);
428 $this->errors[] = 'Invalid Text Encoding in '.$frame_name.' ('.$source_data_array['encodingid'].') for ID3v2.'.$this->majorversion;
429 } else {
430 $framedata .= chr($source_data_array['encodingid']);
431 $framedata .= $source_data_array['description'].Tag\Id3v2::TextEncodingTerminatorLookup($source_data_array['encodingid']);
432 $framedata .= $source_data_array['data'];
433 }
434 break;
435
436 case 'WXXX':
437
438
439
440
441 $source_data_array['encodingid'] = (isset($source_data_array['encodingid']) ? $source_data_array['encodingid'] : $this->id3v2_default_encodingid);
443 $this->errors[] = 'Invalid Text Encoding in '.$frame_name.' ('.$source_data_array['encodingid'].') for ID3v2.'.$this->majorversion;
444 } elseif (!isset($source_data_array[
'data']) || !$this->
IsValidURL($source_data_array[
'data'],
false,
false)) {
445
446
447 $this->warnings[] = 'Invalid URL in '.$frame_name.' ('.$source_data_array['data'].')';
448 } else {
449 $framedata .= chr($source_data_array['encodingid']);
450 $framedata .= $source_data_array['description'].Tag\Id3v2::TextEncodingTerminatorLookup($source_data_array['encodingid']);
451 $framedata .= $source_data_array['data'];
452 }
453 break;
454
455 case 'IPLS':
456
457
458
459 $source_data_array['encodingid'] = (isset($source_data_array['encodingid']) ? $source_data_array['encodingid'] : $this->id3v2_default_encodingid);
461 $this->errors[] = 'Invalid Text Encoding in '.$frame_name.' ('.$source_data_array['encodingid'].') for ID3v2.'.$this->majorversion;
462 } else {
463 $framedata .= chr($source_data_array['encodingid']);
464 $framedata .= $source_data_array['data'];
465 }
466 break;
467
468 case 'MCDI':
469
470
471 $framedata .= $source_data_array['data'];
472 break;
473
474 case 'ETCO':
475
476
477
478
479
480
481
482
483
484
485 if (($source_data_array['timestampformat'] > 2) || ($source_data_array['timestampformat'] < 1)) {
486 $this->errors[] = 'Invalid Time Stamp Format byte in '.$frame_name.' ('.$source_data_array['timestampformat'].')';
487 } else {
488 $framedata .= chr($source_data_array['timestampformat']);
489 foreach ($source_data_array as $key => $val) {
491 $this->errors[] = 'Invalid Event Type byte in '.$frame_name.' ('.$val['typeid'].')';
492 } elseif (($key != 'timestampformat') && ($key != 'flags')) {
493 if (($val['timestamp'] > 0) && ($previousETCOtimestamp >= $val['timestamp'])) {
494
495
496 $this->errors[] = 'Out-of-order timestamp in '.$frame_name.' ('.$val['timestamp'].') for Event Type ('.$val['typeid'].')';
497 } else {
498 $framedata .= chr($val['typeid']);
500 }
501 }
502 }
503 }
504 break;
505
506 case 'MLLT':
507
508
509
510
511
512
513
514
515
516 if (($source_data_array['framesbetweenreferences'] > 0) && ($source_data_array['framesbetweenreferences'] <= 65535)) {
518 } else {
519 $this->errors[] = 'Invalid MPEG Frames Between References in '.$frame_name.' ('.$source_data_array['framesbetweenreferences'].')';
520 }
521 if (($source_data_array['bytesbetweenreferences'] > 0) && ($source_data_array['bytesbetweenreferences'] <= 16777215)) {
523 } else {
524 $this->errors[] = 'Invalid bytes Between References in '.$frame_name.' ('.$source_data_array['bytesbetweenreferences'].')';
525 }
526 if (($source_data_array['msbetweenreferences'] > 0) && ($source_data_array['msbetweenreferences'] <= 16777215)) {
528 } else {
529 $this->errors[] = 'Invalid Milliseconds Between References in '.$frame_name.' ('.$source_data_array['msbetweenreferences'].')';
530 }
531 if (!$this->
IsWithinBitRange($source_data_array[
'bitsforbytesdeviation'], 8,
false)) {
532 if (($source_data_array['bitsforbytesdeviation'] % 4) == 0) {
533 $framedata .= chr($source_data_array['bitsforbytesdeviation']);
534 } else {
535 $this->errors[] = 'Bits For Bytes Deviation in '.$frame_name.' ('.$source_data_array['bitsforbytesdeviation'].') must be a multiple of 4.';
536 }
537 } else {
538 $this->errors[] = 'Invalid Bits For Bytes Deviation in '.$frame_name.' ('.$source_data_array['bitsforbytesdeviation'].')';
539 }
540 if (!$this->
IsWithinBitRange($source_data_array[
'bitsformsdeviation'], 8,
false)) {
541 if (($source_data_array['bitsformsdeviation'] % 4) == 0) {
542 $framedata .= chr($source_data_array['bitsformsdeviation']);
543 } else {
544 $this->errors[] = 'Bits For Milliseconds Deviation in '.$frame_name.' ('.$source_data_array['bitsforbytesdeviation'].') must be a multiple of 4.';
545 }
546 } else {
547 $this->errors[] = 'Invalid Bits For Milliseconds Deviation in '.$frame_name.' ('.$source_data_array['bitsformsdeviation'].')';
548 }
549 foreach ($source_data_array as $key => $val) {
550 if (($key != 'framesbetweenreferences') && ($key != 'bytesbetweenreferences') && ($key != 'msbetweenreferences') && ($key != 'bitsforbytesdeviation') && ($key != 'bitsformsdeviation') && ($key != 'flags')) {
551 $unwrittenbitstream .= str_pad(
Helper::Dec2Bin($val[
'bytedeviation']), $source_data_array[
'bitsforbytesdeviation'],
'0', STR_PAD_LEFT);
552 $unwrittenbitstream .= str_pad(
Helper::Dec2Bin($val[
'msdeviation']), $source_data_array[
'bitsformsdeviation'],
'0', STR_PAD_LEFT);
553 }
554 }
555 for ($i = 0; $i < strlen($unwrittenbitstream); $i += 8) {
556 $highnibble = bindec(substr($unwrittenbitstream, $i, 4)) << 4;
557 $lownibble = bindec(substr($unwrittenbitstream, $i + 4, 4));
558 $framedata .= chr($highnibble & $lownibble);
559 }
560 break;
561
562 case 'SYTC':
563
564
565
566
567
568
569 if (($source_data_array['timestampformat'] > 2) || ($source_data_array['timestampformat'] < 1)) {
570 $this->errors[] = 'Invalid Time Stamp Format byte in '.$frame_name.' ('.$source_data_array['timestampformat'].')';
571 } else {
572 $framedata .= chr($source_data_array['timestampformat']);
573 foreach ($source_data_array as $key => $val) {
575 $this->errors[] = 'Invalid Event Type byte in '.$frame_name.' ('.$val['typeid'].')';
576 } elseif (($key != 'timestampformat') && ($key != 'flags')) {
577 if (($val['tempo'] < 0) || ($val['tempo'] > 510)) {
578 $this->errors[] = 'Invalid Tempo (max = 510) in '.$frame_name.' ('.$val['tempo'].') at timestamp ('.$val['timestamp'].')';
579 } else {
580 if ($val['tempo'] > 255) {
581 $framedata .= chr(255);
582 $val['tempo'] -= 255;
583 }
584 $framedata .= chr($val['tempo']);
586 }
587 }
588 }
589 }
590 break;
591
592 case 'USLT':
593
594
595
596
597
598 $source_data_array['encodingid'] = (isset($source_data_array['encodingid']) ? $source_data_array['encodingid'] : $this->id3v2_default_encodingid);
600 $this->errors[] = 'Invalid Text Encoding in '.$frame_name.' ('.$source_data_array['encodingid'].') for ID3v2.'.$this->majorversion;
602 $this->errors[] = 'Invalid Language in '.$frame_name.' ('.$source_data_array['language'].')';
603 } else {
604 $framedata .= chr($source_data_array['encodingid']);
605 $framedata .= strtolower($source_data_array['language']);
606 $framedata .= $source_data_array['description'].Tag\Id3v2::TextEncodingTerminatorLookup($source_data_array['encodingid']);
607 $framedata .= $source_data_array['data'];
608 }
609 break;
610
611 case 'SYLT':
612
613
614
615
616
617
618
619
620
621
622
623 $source_data_array['encodingid'] = (isset($source_data_array['encodingid']) ? $source_data_array['encodingid'] : $this->id3v2_default_encodingid);
625 $this->errors[] = 'Invalid Text Encoding in '.$frame_name.' ('.$source_data_array['encodingid'].') for ID3v2.'.$this->majorversion;
627 $this->errors[] = 'Invalid Language in '.$frame_name.' ('.$source_data_array['language'].')';
628 } elseif (($source_data_array['timestampformat'] > 2) || ($source_data_array['timestampformat'] < 1)) {
629 $this->errors[] = 'Invalid Time Stamp Format byte in '.$frame_name.' ('.$source_data_array['timestampformat'].')';
631 $this->errors[] = 'Invalid Content Type byte in '.$frame_name.' ('.$source_data_array['contenttypeid'].')';
632 } elseif (!is_array($source_data_array['data'])) {
633 $this->errors[] = 'Invalid Lyric/Timestamp data in '.$frame_name.' (must be an array)';
634 } else {
635 $framedata .= chr($source_data_array['encodingid']);
636 $framedata .= strtolower($source_data_array['language']);
637 $framedata .= chr($source_data_array['timestampformat']);
638 $framedata .= chr($source_data_array['contenttypeid']);
639 $framedata .= $source_data_array['description'].Tag\Id3v2::TextEncodingTerminatorLookup($source_data_array['encodingid']);
640 ksort($source_data_array['data']);
641 foreach ($source_data_array['data'] as $key => $val) {
642 $framedata .= $val['data'].Tag\Id3v2::TextEncodingTerminatorLookup($source_data_array['encodingid']);
644 }
645 }
646 break;
647
648 case 'COMM':
649
650
651
652
653
654 $source_data_array['encodingid'] = (isset($source_data_array['encodingid']) ? $source_data_array['encodingid'] : $this->id3v2_default_encodingid);
656 $this->errors[] = 'Invalid Text Encoding in '.$frame_name.' ('.$source_data_array['encodingid'].') for ID3v2.'.$this->majorversion;
658 $this->errors[] = 'Invalid Language in '.$frame_name.' ('.$source_data_array['language'].')';
659 } else {
660 $framedata .= chr($source_data_array['encodingid']);
661 $framedata .= strtolower($source_data_array['language']);
662 $framedata .= $source_data_array['description'].Tag\Id3v2::TextEncodingTerminatorLookup($source_data_array['encodingid']);
663 $framedata .= $source_data_array['data'];
664 }
665 break;
666
667 case 'RVA2':
668
669
670
671
672
673
674
675
676
677 $framedata .= str_replace("\x00", '', $source_data_array['description'])."\x00";
678 foreach ($source_data_array as $key => $val) {
679 if ($key != 'description') {
680 $framedata .= chr($val['channeltypeid']);
682 if (!$this->
IsWithinBitRange($source_data_array[
'bitspeakvolume'], 8,
false)) {
683 $framedata .= chr($val['bitspeakvolume']);
684 if ($val['bitspeakvolume'] > 0) {
686 }
687 } else {
688 $this->errors[] = 'Invalid Bits Representing Peak Volume in '.$frame_name.' ('.$val['bitspeakvolume'].') (range = 0 to 255)';
689 }
690 }
691 }
692 break;
693
694 case 'RVAD':
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
711 $this->errors[] = 'Invalid Bits For Volume Description byte in '.$frame_name.' ('.$source_data_array['bitsvolume'].') (range = 1 to 255)';
712 } else {
713 $incdecflag .= '00';
714 $incdecflag .= $source_data_array['incdec']['right'] ? '1' : '0';
715 $incdecflag .= $source_data_array['incdec']['left'] ? '1' : '0';
716 $incdecflag .= $source_data_array['incdec']['rightrear'] ? '1' : '0';
717 $incdecflag .= $source_data_array['incdec']['leftrear'] ? '1' : '0';
718 $incdecflag .= $source_data_array['incdec']['center'] ? '1' : '0';
719 $incdecflag .= $source_data_array['incdec']['bass'] ? '1' : '0';
720 $framedata .= chr(bindec($incdecflag));
721 $framedata .= chr($source_data_array['bitsvolume']);
722 $framedata .=
Helper::BigEndian2String($source_data_array[
'volumechange'][
'right'], ceil($source_data_array[
'bitsvolume'] / 8),
false);
723 $framedata .=
Helper::BigEndian2String($source_data_array[
'volumechange'][
'left'], ceil($source_data_array[
'bitsvolume'] / 8),
false);
724 $framedata .=
Helper::BigEndian2String($source_data_array[
'peakvolume'][
'right'], ceil($source_data_array[
'bitsvolume'] / 8),
false);
725 $framedata .=
Helper::BigEndian2String($source_data_array[
'peakvolume'][
'left'], ceil($source_data_array[
'bitsvolume'] / 8),
false);
726 if ($source_data_array['volumechange']['rightrear'] || $source_data_array['volumechange']['leftrear'] ||
727 $source_data_array['peakvolume']['rightrear'] || $source_data_array['peakvolume']['leftrear'] ||
728 $source_data_array['volumechange']['center'] || $source_data_array['peakvolume']['center'] ||
729 $source_data_array['volumechange']['bass'] || $source_data_array['peakvolume']['bass']) {
730 $framedata .=
Helper::BigEndian2String($source_data_array[
'volumechange'][
'rightrear'], ceil($source_data_array[
'bitsvolume']/8),
false);
731 $framedata .=
Helper::BigEndian2String($source_data_array[
'volumechange'][
'leftrear'], ceil($source_data_array[
'bitsvolume']/8),
false);
732 $framedata .=
Helper::BigEndian2String($source_data_array[
'peakvolume'][
'rightrear'], ceil($source_data_array[
'bitsvolume']/8),
false);
733 $framedata .=
Helper::BigEndian2String($source_data_array[
'peakvolume'][
'leftrear'], ceil($source_data_array[
'bitsvolume']/8),
false);
734 }
735 if ($source_data_array['volumechange']['center'] || $source_data_array['peakvolume']['center'] ||
736 $source_data_array['volumechange']['bass'] || $source_data_array['peakvolume']['bass']) {
737 $framedata .=
Helper::BigEndian2String($source_data_array[
'volumechange'][
'center'], ceil($source_data_array[
'bitsvolume']/8),
false);
738 $framedata .=
Helper::BigEndian2String($source_data_array[
'peakvolume'][
'center'], ceil($source_data_array[
'bitsvolume']/8),
false);
739 }
740 if ($source_data_array['volumechange']['bass'] || $source_data_array['peakvolume']['bass']) {
741 $framedata .=
Helper::BigEndian2String($source_data_array[
'volumechange'][
'bass'], ceil($source_data_array[
'bitsvolume']/8),
false);
742 $framedata .=
Helper::BigEndian2String($source_data_array[
'peakvolume'][
'bass'], ceil($source_data_array[
'bitsvolume']/8),
false);
743 }
744 }
745 break;
746
747 case 'EQU2':
748
749
750
751
752
753
754
755
756 if (($source_data_array['interpolationmethod'] < 0) || ($source_data_array['interpolationmethod'] > 1)) {
757 $this->errors[] = 'Invalid Interpolation Method byte in '.$frame_name.' ('.$source_data_array['interpolationmethod'].') (valid = 0 or 1)';
758 } else {
759 $framedata .= chr($source_data_array['interpolationmethod']);
760 $framedata .= str_replace("\x00", '', $source_data_array['description'])."\x00";
761 foreach ($source_data_array['data'] as $key => $val) {
764 }
765 }
766 break;
767
768 case 'EQUA':
769
770
771
772
773
774
775
776
778 $this->errors[] = 'Invalid Adjustment Bits byte in '.$frame_name.' ('.$source_data_array['bitsvolume'].') (range = 1 to 255)';
779 } else {
780 $framedata .= chr($source_data_array['adjustmentbits']);
781 foreach ($source_data_array as $key => $val) {
782 if ($key != 'bitsvolume') {
783 if (($key > 32767) || ($key < 0)) {
784 $this->errors[] = 'Invalid Frequency in '.$frame_name.' ('.$key.') (range = 0 to 32767)';
785 } else {
786 if ($val >= 0) {
787
788 $key |= 0x8000;
789 }
792 }
793 }
794 }
795 }
796 break;
797
798 case 'RVRB':
799
800
801
802
803
804
805
806
807
808
809
811 $this->errors[] = 'Invalid Reverb Left in '.$frame_name.' ('.$source_data_array['left'].') (range = 0 to 65535)';
812 } elseif (!$this->
IsWithinBitRange($source_data_array[
'right'], 16,
false)) {
813 $this->errors[] = 'Invalid Reverb Left in '.$frame_name.' ('.$source_data_array['right'].') (range = 0 to 65535)';
814 } elseif (!$this->
IsWithinBitRange($source_data_array[
'bouncesL'], 8,
false)) {
815 $this->errors[] = 'Invalid Reverb Bounces, Left in '.$frame_name.' ('.$source_data_array['bouncesL'].') (range = 0 to 255)';
816 } elseif (!$this->
IsWithinBitRange($source_data_array[
'bouncesR'], 8,
false)) {
817 $this->errors[] = 'Invalid Reverb Bounces, Right in '.$frame_name.' ('.$source_data_array['bouncesR'].') (range = 0 to 255)';
818 } elseif (!$this->
IsWithinBitRange($source_data_array[
'feedbackLL'], 8,
false)) {
819 $this->errors[] = 'Invalid Reverb Feedback, Left-To-Left in '.$frame_name.' ('.$source_data_array['feedbackLL'].') (range = 0 to 255)';
820 } elseif (!$this->
IsWithinBitRange($source_data_array[
'feedbackLR'], 8,
false)) {
821 $this->errors[] = 'Invalid Reverb Feedback, Left-To-Right in '.$frame_name.' ('.$source_data_array['feedbackLR'].') (range = 0 to 255)';
822 } elseif (!$this->
IsWithinBitRange($source_data_array[
'feedbackRR'], 8,
false)) {
823 $this->errors[] = 'Invalid Reverb Feedback, Right-To-Right in '.$frame_name.' ('.$source_data_array['feedbackRR'].') (range = 0 to 255)';
824 } elseif (!$this->
IsWithinBitRange($source_data_array[
'feedbackRL'], 8,
false)) {
825 $this->errors[] = 'Invalid Reverb Feedback, Right-To-Left in '.$frame_name.' ('.$source_data_array['feedbackRL'].') (range = 0 to 255)';
826 } elseif (!$this->
IsWithinBitRange($source_data_array[
'premixLR'], 8,
false)) {
827 $this->errors[] = 'Invalid Premix, Left-To-Right in '.$frame_name.' ('.$source_data_array['premixLR'].') (range = 0 to 255)';
828 } elseif (!$this->
IsWithinBitRange($source_data_array[
'premixRL'], 8,
false)) {
829 $this->errors[] = 'Invalid Premix, Right-To-Left in '.$frame_name.' ('.$source_data_array['premixRL'].') (range = 0 to 255)';
830 } else {
833 $framedata .= chr($source_data_array['bouncesL']);
834 $framedata .= chr($source_data_array['bouncesR']);
835 $framedata .= chr($source_data_array['feedbackLL']);
836 $framedata .= chr($source_data_array['feedbackLR']);
837 $framedata .= chr($source_data_array['feedbackRR']);
838 $framedata .= chr($source_data_array['feedbackRL']);
839 $framedata .= chr($source_data_array['premixLR']);
840 $framedata .= chr($source_data_array['premixRL']);
841 }
842 break;
843
844 case 'APIC':
845
846
847
848
849
850
851 $source_data_array['encodingid'] = (isset($source_data_array['encodingid']) ? $source_data_array['encodingid'] : $this->id3v2_default_encodingid);
853 $this->errors[] = 'Invalid Text Encoding in '.$frame_name.' ('.$source_data_array['encodingid'].') for ID3v2.'.$this->majorversion;
855 $this->errors[] = 'Invalid Picture Type byte in '.$frame_name.' ('.$source_data_array['picturetypeid'].') for ID3v2.'.$this->majorversion;
857 $this->errors[] = 'Invalid MIME Type in '.$frame_name.' ('.$source_data_array['mime'].') for ID3v2.'.$this->majorversion;
858 } elseif (($source_data_array[
'mime'] ==
'-->') && (!$this->
IsValidURL($source_data_array[
'data'],
false,
false))) {
859
860
861 $this->warnings[] = 'Invalid URL in '.$frame_name.' ('.$source_data_array['data'].')';
862 } else {
863 $framedata .= chr($source_data_array['encodingid']);
864 $framedata .= str_replace("\x00", '', $source_data_array['mime'])."\x00";
865 $framedata .= chr($source_data_array['picturetypeid']);
866 $framedata .= (!empty($source_data_array[
'description']) ? $source_data_array[
'description'] :
'').Tag\
Id3v2::TextEncodingTerminatorLookup($source_data_array[
'encodingid']);
867 $framedata .= $source_data_array['data'];
868 }
869 break;
870
871 case 'GEOB':
872
873
874
875
876
877
878 $source_data_array['encodingid'] = (isset($source_data_array['encodingid']) ? $source_data_array['encodingid'] : $this->id3v2_default_encodingid);
880 $this->errors[] = 'Invalid Text Encoding in '.$frame_name.' ('.$source_data_array['encodingid'].') for ID3v2.'.$this->majorversion;
882 $this->errors[] = 'Invalid MIME Type in '.$frame_name.' ('.$source_data_array['mime'].')';
883 } elseif (!$source_data_array['description']) {
884 $this->errors[] = 'Missing Description in '.$frame_name;
885 } else {
886 $framedata .= chr($source_data_array['encodingid']);
887 $framedata .= str_replace("\x00", '', $source_data_array['mime'])."\x00";
888 $framedata .= $source_data_array['filename'].Tag\Id3v2::TextEncodingTerminatorLookup($source_data_array['encodingid']);
889 $framedata .= $source_data_array['description'].Tag\Id3v2::TextEncodingTerminatorLookup($source_data_array['encodingid']);
890 $framedata .= $source_data_array['data'];
891 }
892 break;
893
894 case 'PCNT':
895
896
897
898
900 break;
901
902 case 'POPM':
903
904
905
906
907
908
910 $this->errors[] = 'Invalid Rating byte in '.$frame_name.' ('.$source_data_array['rating'].') (range = 0 to 255)';
911 } elseif (!IsValidEmail($source_data_array['email'])) {
912 $this->errors[] = 'Invalid Email in '.$frame_name.' ('.$source_data_array['email'].')';
913 } else {
914 $framedata .= str_replace("\x00", '', $source_data_array['email'])."\x00";
915 $framedata .= chr($source_data_array['rating']);
917 }
918 break;
919
920 case 'RBUF':
921
922
923
924
925 if (!$this->
IsWithinBitRange($source_data_array[
'buffersize'], 24,
false)) {
926 $this->errors[] = 'Invalid Buffer Size in '.$frame_name;
927 } elseif (!$this->
IsWithinBitRange($source_data_array[
'nexttagoffset'], 32,
false)) {
928 $this->errors[] = 'Invalid Offset To Next Tag in '.$frame_name;
929 } else {
931 $flag .= '0000000';
932 $flag .= $source_data_array['flags']['embededinfo'] ? '1' : '0';
933 $framedata .= chr(bindec($flag));
935 }
936 break;
937
938 case 'AENC':
939
940
941
942
943
944 if (!$this->
IsWithinBitRange($source_data_array[
'previewstart'], 16,
false)) {
945 $this->errors[] = 'Invalid Preview Start in '.$frame_name.' ('.$source_data_array['previewstart'].')';
946 } elseif (!$this->
IsWithinBitRange($source_data_array[
'previewlength'], 16,
false)) {
947 $this->errors[] = 'Invalid Preview Length in '.$frame_name.' ('.$source_data_array['previewlength'].')';
948 } else {
949 $framedata .= str_replace("\x00", '', $source_data_array['ownerid'])."\x00";
952 $framedata .= $source_data_array['encryptioninfo'];
953 }
954 break;
955
956 case 'LINK':
957
958
959
960
962 $this->errors[] = 'Invalid Frame Identifier in '.$frame_name.' ('.$source_data_array['frameid'].')';
963 } elseif (!$this->
IsValidURL($source_data_array[
'data'],
true,
false)) {
964
965
966 $this->warnings[] = 'Invalid URL in '.$frame_name.' ('.$source_data_array['data'].')';
967 } elseif ((($source_data_array['frameid'] == 'AENC') || ($source_data_array['frameid'] == 'APIC') || ($source_data_array['frameid'] == 'GEOB') || ($source_data_array['frameid'] == 'TXXX')) && ($source_data_array['additionaldata'] == '')) {
968 $this->errors[] = 'Content Descriptor must be specified as additional data for Frame Identifier of '.$source_data_array['frameid'].' in '.$frame_name;
969 } elseif (($source_data_array[
'frameid'] ==
'USER') && (Tag\
Id3v2::LanguageLookup($source_data_array[
'additionaldata'],
true) ==
'')) {
970 $this->errors[] = 'Language must be specified as additional data for Frame Identifier of '.$source_data_array['frameid'].' in '.$frame_name;
971 } elseif (($source_data_array['frameid'] == 'PRIV') && ($source_data_array['additionaldata'] == '')) {
972 $this->errors[] = 'Owner Identifier must be specified as additional data for Frame Identifier of '.$source_data_array['frameid'].' in '.$frame_name;
973 } elseif ((($source_data_array[
'frameid'] ==
'COMM') || ($source_data_array[
'frameid'] ==
'SYLT') || ($source_data_array[
'frameid'] ==
'USLT')) && ((Tag\
Id3v2::LanguageLookup(substr($source_data_array[
'additionaldata'], 0, 3),
true) ==
'') || (substr($source_data_array[
'additionaldata'], 3) ==
''))) {
974 $this->errors[] = 'Language followed by Content Descriptor must be specified as additional data for Frame Identifier of '.$source_data_array['frameid'].' in '.$frame_name;
975 } else {
976 $framedata .= $source_data_array['frameid'];
977 $framedata .= str_replace("\x00", '', $source_data_array['data'])."\x00";
978 switch ($source_data_array['frameid']) {
979 case 'COMM':
980 case 'SYLT':
981 case 'USLT':
982 case 'PRIV':
983 case 'USER':
984 case 'AENC':
985 case 'APIC':
986 case 'GEOB':
987 case 'TXXX':
988 $framedata .= $source_data_array['additionaldata'];
989 break;
990 case 'ASPI':
991 case 'ETCO':
992 case 'EQU2':
993 case 'MCID':
994 case 'MLLT':
995 case 'OWNE':
996 case 'RVA2':
997 case 'RVRB':
998 case 'SYTC':
999 case 'IPLS':
1000 case 'RVAD':
1001 case 'EQUA':
1002
1003 break;
1004 case 'RBUF':
1005 if ($this->majorversion == 3) {
1006
1007 } else {
1008 $this->errors[] = $source_data_array['frameid'].' is not a valid Frame Identifier in '.$frame_name.' (in ID3v2.'.$this->majorversion.')';
1009 }
1010
1011 default:
1012 if ((substr($source_data_array['frameid'], 0, 1) == 'T') || (substr($source_data_array['frameid'], 0, 1) == 'W')) {
1013
1014 } else {
1015 $this->errors[] = $source_data_array['frameid'].' is not a valid Frame Identifier in '.$frame_name.' (in ID3v2.'.$this->majorversion.')';
1016 }
1017 break;
1018 }
1019 }
1020 break;
1021
1022 case 'POSS':
1023
1024
1025
1026 if (($source_data_array['timestampformat'] < 1) || ($source_data_array['timestampformat'] > 2)) {
1027 $this->errors[] = 'Invalid Time Stamp Format in '.$frame_name.' ('.$source_data_array['timestampformat'].') (valid = 1 or 2)';
1028 } elseif (!$this->
IsWithinBitRange($source_data_array[
'position'], 32,
false)) {
1029 $this->errors[] = 'Invalid Position in '.$frame_name.' ('.$source_data_array['position'].') (range = 0 to 4294967295)';
1030 } else {
1031 $framedata .= chr($source_data_array['timestampformat']);
1033 }
1034 break;
1035
1036 case 'USER':
1037
1038
1039
1040
1041 $source_data_array['encodingid'] = (isset($source_data_array['encodingid']) ? $source_data_array['encodingid'] : $this->id3v2_default_encodingid);
1043 $this->errors[] = 'Invalid Text Encoding in '.$frame_name.' ('.$source_data_array['encodingid'].')';
1045 $this->errors[] = 'Invalid Language in '.$frame_name.' ('.$source_data_array['language'].')';
1046 } else {
1047 $framedata .= chr($source_data_array['encodingid']);
1048 $framedata .= strtolower($source_data_array['language']);
1049 $framedata .= $source_data_array['data'];
1050 }
1051 break;
1052
1053 case 'OWNE':
1054
1055
1056
1057
1058
1059 $source_data_array['encodingid'] = (isset($source_data_array['encodingid']) ? $source_data_array['encodingid'] : $this->id3v2_default_encodingid);
1061 $this->errors[] = 'Invalid Text Encoding in '.$frame_name.' ('.$source_data_array['encodingid'].')';
1062 } elseif (!$this->
IsANumber($source_data_array[
'pricepaid'][
'value'],
false)) {
1063 $this->errors[] = 'Invalid Price Paid in '.$frame_name.' ('.$source_data_array['pricepaid']['value'].')';
1065 $this->errors[] = 'Invalid Date Of Purchase in '.$frame_name.' ('.$source_data_array['purchasedate'].') (format = YYYYMMDD)';
1066 } else {
1067 $framedata .= chr($source_data_array['encodingid']);
1068 $framedata .= str_replace("\x00", '', $source_data_array['pricepaid']['value'])."\x00";
1069 $framedata .= $source_data_array['purchasedate'];
1070 $framedata .= $source_data_array['seller'];
1071 }
1072 break;
1073
1074 case 'COMR':
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085 $source_data_array['encodingid'] = (isset($source_data_array['encodingid']) ? $source_data_array['encodingid'] : $this->id3v2_default_encodingid);
1087 $this->errors[] = 'Invalid Text Encoding in '.$frame_name.' ('.$source_data_array['encodingid'].')';
1089 $this->errors[] = 'Invalid Valid Until date in '.$frame_name.' ('.$source_data_array['pricevaliduntil'].') (format = YYYYMMDD)';
1090 } elseif (!$this->
IsValidURL($source_data_array[
'contacturl'],
false,
true)) {
1091 $this->errors[] = 'Invalid Contact URL in '.$frame_name.' ('.$source_data_array['contacturl'].') (allowed schemes: http, https, ftp, mailto)';
1093 $this->errors[] = 'Invalid Received As byte in '.$frame_name.' ('.$source_data_array['contacturl'].') (range = 0 to 8)';
1095 $this->errors[] = 'Invalid MIME Type in '.$frame_name.' ('.$source_data_array['mime'].')';
1096 } else {
1097 $framedata .= chr($source_data_array['encodingid']);
1098 unset($pricestring);
1099 foreach ($source_data_array['price'] as $key => $val) {
1101 $pricestrings[] = $key.$val['value'];
1102 } else {
1103 $this->errors[] = 'Invalid Price String in '.$frame_name.' ('.$key.$val['value'].')';
1104 }
1105 }
1106 $framedata .= implode('/', $pricestrings);
1107 $framedata .= $source_data_array['pricevaliduntil'];
1108 $framedata .= str_replace("\x00", '', $source_data_array['contacturl'])."\x00";
1109 $framedata .= chr($source_data_array['receivedasid']);
1110 $framedata .= $source_data_array['sellername'].Tag\Id3v2::TextEncodingTerminatorLookup($source_data_array['encodingid']);
1111 $framedata .= $source_data_array['description'].Tag\Id3v2::TextEncodingTerminatorLookup($source_data_array['encodingid']);
1112 $framedata .= $source_data_array['mime']."\x00";
1113 $framedata .= $source_data_array['logo'];
1114 }
1115 break;
1116
1117 case 'ENCR':
1118
1119
1120
1121
1122 if (!$this->
IsWithinBitRange($source_data_array[
'methodsymbol'], 8,
false)) {
1123 $this->errors[] = 'Invalid Group Symbol in '.$frame_name.' ('.$source_data_array['methodsymbol'].') (range = 0 to 255)';
1124 } else {
1125 $framedata .= str_replace("\x00", '', $source_data_array['ownerid'])."\x00";
1126 $framedata .= ord($source_data_array['methodsymbol']);
1127 $framedata .= $source_data_array['data'];
1128 }
1129 break;
1130
1131 case 'GRID':
1132
1133
1134
1135
1136 if (!$this->
IsWithinBitRange($source_data_array[
'groupsymbol'], 8,
false)) {
1137 $this->errors[] = 'Invalid Group Symbol in '.$frame_name.' ('.$source_data_array['groupsymbol'].') (range = 0 to 255)';
1138 } else {
1139 $framedata .= str_replace("\x00", '', $source_data_array['ownerid'])."\x00";
1140 $framedata .= ord($source_data_array['groupsymbol']);
1141 $framedata .= $source_data_array['data'];
1142 }
1143 break;
1144
1145 case 'PRIV':
1146
1147
1148
1149 $framedata .= str_replace("\x00", '', $source_data_array['ownerid'])."\x00";
1150 $framedata .= $source_data_array['data'];
1151 break;
1152
1153 case 'SIGN':
1154
1155
1156
1157 if (!$this->
IsWithinBitRange($source_data_array[
'groupsymbol'], 8,
false)) {
1158 $this->errors[] = 'Invalid Group Symbol in '.$frame_name.' ('.$source_data_array['groupsymbol'].') (range = 0 to 255)';
1159 } else {
1160 $framedata .= ord($source_data_array['groupsymbol']);
1161 $framedata .= $source_data_array['data'];
1162 }
1163 break;
1164
1165 case 'SEEK':
1166
1167
1169 $this->errors[] = 'Invalid Minimum Offset in '.$frame_name.' ('.$source_data_array['data'].') (range = 0 to 4294967295)';
1170 } else {
1172 }
1173 break;
1174
1175 case 'ASPI':
1176
1177
1178
1179
1180
1181
1182
1183 if (!$this->
IsWithinBitRange($source_data_array[
'datastart'], 32,
false)) {
1184 $this->errors[] = 'Invalid Indexed Data Start in '.$frame_name.' ('.$source_data_array['datastart'].') (range = 0 to 4294967295)';
1185 } elseif (!$this->
IsWithinBitRange($source_data_array[
'datalength'], 32,
false)) {
1186 $this->errors[] = 'Invalid Indexed Data Length in '.$frame_name.' ('.$source_data_array['datalength'].') (range = 0 to 4294967295)';
1187 } elseif (!$this->
IsWithinBitRange($source_data_array[
'indexpoints'], 16,
false)) {
1188 $this->errors[] = 'Invalid Number Of Index Points in '.$frame_name.' ('.$source_data_array['indexpoints'].') (range = 0 to 65535)';
1189 } elseif (!$this->
IsWithinBitRange($source_data_array[
'bitsperpoint'], 8,
false)) {
1190 $this->errors[] = 'Invalid Bits Per Index Point in '.$frame_name.' ('.$source_data_array['bitsperpoint'].') (range = 0 to 255)';
1191 } elseif ($source_data_array['indexpoints'] != count($source_data_array['indexes'])) {
1192 $this->errors[] = 'Number Of Index Points does not match actual supplied data in '.$frame_name;
1193 } else {
1198 foreach ($source_data_array['indexes'] as $key => $val) {
1200 }
1201 }
1202 break;
1203
1204 case 'RGAD':
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215 if (($source_data_array['track_adjustment'] > 51) || ($source_data_array['track_adjustment'] < -51)) {
1216 $this->errors[] = 'Invalid Track Adjustment in '.$frame_name.' ('.$source_data_array['track_adjustment'].') (range = -51.0 to +51.0)';
1217 } elseif (($source_data_array['album_adjustment'] > 51) || ($source_data_array['album_adjustment'] < -51)) {
1218 $this->errors[] = 'Invalid Album Adjustment in '.$frame_name.' ('.$source_data_array['album_adjustment'].') (range = -51.0 to +51.0)';
1220 $this->errors[] = 'Invalid Track Name Code in '.$frame_name.' ('.$source_data_array['raw']['track_name'].') (range = 0 to 2)';
1222 $this->errors[] = 'Invalid Album Name Code in '.$frame_name.' ('.$source_data_array['raw']['album_name'].') (range = 0 to 2)';
1224 $this->errors[] = 'Invalid Track Originator Code in '.$frame_name.' ('.$source_data_array['raw']['track_originator'].') (range = 0 to 3)';
1226 $this->errors[] = 'Invalid Album Originator Code in '.$frame_name.' ('.$source_data_array['raw']['album_originator'].') (range = 0 to 3)';
1227 } else {
1229 $framedata .=
Helper::RGADgainString($source_data_array[
'raw'][
'track_name'], $source_data_array[
'raw'][
'track_originator'], $source_data_array[
'track_adjustment']);
1230 $framedata .=
Helper::RGADgainString($source_data_array[
'raw'][
'album_name'], $source_data_array[
'raw'][
'album_originator'], $source_data_array[
'album_adjustment']);
1231 }
1232 break;
1233
1234 default:
1235 if ((($this->majorversion == 2) && (strlen($frame_name) != 3)) || (($this->majorversion > 2) && (strlen($frame_name) != 4))) {
1236 $this->errors[] = 'Invalid frame name "'.$frame_name.'" for ID3v2.'.$this->majorversion;
1237 } elseif ($frame_name{0} == 'T') {
1238
1239
1240
1241 $source_data_array['encodingid'] = (isset($source_data_array['encodingid']) ? $source_data_array['encodingid'] : $this->id3v2_default_encodingid);
1243 $this->errors[] = 'Invalid Text Encoding in '.$frame_name.' ('.$source_data_array['encodingid'].') for ID3v2.'.$this->majorversion;
1244 } else {
1245 $framedata .= chr($source_data_array['encodingid']);
1246 $framedata .= $source_data_array['data'];
1247 }
1248 } elseif ($frame_name{0} == 'W') {
1249
1250
1251 if (!$this->
IsValidURL($source_data_array[
'data'],
false,
false)) {
1252
1253
1254 $this->warnings[] = 'Invalid URL in '.$frame_name.' ('.$source_data_array['data'].')';
1255 } else {
1256 $framedata .= $source_data_array['data'];
1257 }
1258 } else {
1259 $this->errors[] = $frame_name.' not yet supported in $this->GenerateID3v2FrameData()';
1260 }
1261 break;
1262 }
1263 }
1264 if (!empty($this->errors)) {
1265 return false;
1266 }
1267
1268 return $framedata;
1269 }
static Float2String($floatvalue, $bits)
static RGADgainString($namecode, $originatorcode, $replaygain)
static BigEndian2String($number, $minbytes=1, $synchsafe=false, $signed=false)
static IsValidID3v2FrameName($framename, $id3v2majorversion)
static IsValidDateStampString($datestamp)
static LanguageLookup($languagecode, $casesensitive=false)
static IsANumber($numberstring, $allowdecimal=false, $allownegative=false)
static TextEncodingTerminatorLookup($encoding)
@staticvar array $TextEncodingTerminatorLookup
ID3v2IsValidETCOevent($eventid)
IsWithinBitRange($number, $maxbits, $signed=false)
ID3v2IsValidTextEncoding($textencodingbyte)
@staticvar array $ID3v2IsValidTextEncoding_cache
ID3v2IsValidRGADoriginator($RGADoriginator)
IsValidURL($url, $allowUserPass=false)
ID3v2IsValidCOMRreceivedAs($receivedas)
ID3v2IsValidPriceString($pricestring)
IsValidMIMEstring($mimestring)
ID3v2IsValidAPICimageformat($imageformat)
ID3v2IsValidAPICpicturetype($picturetype)
ID3v2IsValidSYLTtype($contenttype)
ID3v2IsValidRGADname($RGADname)