• Main Page
  • Related Pages
  • Modules
  • Namespaces
  • Data Structures
  • Files
  • File List
  • Globals

Services/MediaObjects/getid3/getid3/write.id3v2.php

Go to the documentation of this file.
00001 <?php
00004 //  available at http://getid3.sourceforge.net                 //
00005 //            or http://www.getid3.org                         //
00007 // See readme.txt for more details                             //
00010 // write.id3v2.php                                             //
00011 // module for writing ID3v2 tags                               //
00012 // dependencies: module.tag.id3v2.php                          //
00013 //                                                            ///
00015 
00016 getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.tag.id3v2.php', __FILE__, true);
00017 
00018 class getid3_write_id3v2
00019 {
00020         var $filename;
00021         var $tag_data;
00022         var $paddedlength                = 4096;     // minimum length of ID3v2 tag in bytes
00023         var $majorversion                = 3;        // ID3v2 major version (2, 3 (recommended), 4)
00024         var $minorversion                = 0;        // ID3v2 minor version - always 0
00025         var $merge_existing_data         = false;    // if true, merge new data with existing tags; if false, delete old tag data and only write new tags
00026         var $id3v2_default_encodingid    = 0;        // default text encoding (ISO-8859-1) if not explicitly passed
00027         var $id3v2_use_unsynchronisation = false;    // the specs say it should be TRUE, but most other ID3v2-aware programs are broken if unsynchronization is used, so by default don't use it.
00028         var $warnings                    = array();  // any non-critical errors will be stored here
00029         var $errors                      = array();  // any critical errors will be stored here
00030 
00031         function getid3_write_id3v2() {
00032                 return true;
00033         }
00034 
00035         function WriteID3v2() {
00036                 // File MUST be writeable - CHMOD(646) at least. It's best if the
00037                 // directory is also writeable, because that method is both faster and less susceptible to errors.
00038 
00039                 if (is_writeable($this->filename) || (!file_exists($this->filename) && is_writeable(dirname($this->filename)))) {
00040                         // Initialize getID3 engine
00041                         $getID3 = new getID3;
00042                         $OldThisFileInfo = $getID3->analyze($this->filename);
00043                         if ($this->merge_existing_data) {
00044                                 // merge with existing data
00045                                 if (!empty($OldThisFileInfo['id3v2'])) {
00046                                         $this->tag_data = $this->array_join_merge($OldThisFileInfo['id3v2'], $this->tag_data);
00047                                 }
00048                         }
00049                         $this->paddedlength = max(@$OldThisFileInfo['id3v2']['headerlength'], $this->paddedlength);
00050 
00051                         if ($NewID3v2Tag = $this->GenerateID3v2Tag()) {
00052 
00053                                 if (file_exists($this->filename) && is_writeable($this->filename) && isset($OldThisFileInfo['id3v2']['headerlength']) && ($OldThisFileInfo['id3v2']['headerlength'] == strlen($NewID3v2Tag))) {
00054 
00055                                         // best and fastest method - insert-overwrite existing tag (padded to length of old tag if neccesary)
00056                                         if (file_exists($this->filename)) {
00057 
00058                                                 ob_start();
00059                                                 if ($fp = fopen($this->filename, 'r+b')) {
00060                                                         rewind($fp);
00061                                                         fwrite($fp, $NewID3v2Tag, strlen($NewID3v2Tag));
00062                                                         fclose($fp);
00063                                                 } else {
00064                                                         $this->errors[] = 'Could not open '.$this->filename.' mode "r+b" - '.strip_tags(ob_get_contents());
00065                                                 }
00066                                                 ob_end_clean();
00067 
00068                                         } else {
00069 
00070                                                 ob_start();
00071                                                 if ($fp = fopen($this->filename, 'wb')) {
00072                                                         rewind($fp);
00073                                                         fwrite($fp, $NewID3v2Tag, strlen($NewID3v2Tag));
00074                                                         fclose($fp);
00075                                                 } else {
00076                                                         $this->errors[] = 'Could not open '.$this->filename.' mode "wb" - '.strip_tags(ob_get_contents());
00077                                                 }
00078                                                 ob_end_clean();
00079 
00080                                         }
00081 
00082                                 } else {
00083 
00084                                         if ($tempfilename = tempnam('*', 'getID3')) {
00085                                                 ob_start();
00086                                                 if ($fp_source = fopen($this->filename, 'rb')) {
00087                                                         if ($fp_temp = fopen($tempfilename, 'wb')) {
00088 
00089                                                                 fwrite($fp_temp, $NewID3v2Tag, strlen($NewID3v2Tag));
00090 
00091                                                                 rewind($fp_source);
00092                                                                 if (!empty($OldThisFileInfo['avdataoffset'])) {
00093                                                                         fseek($fp_source, $OldThisFileInfo['avdataoffset'], SEEK_SET);
00094                                                                 }
00095 
00096                                                                 while ($buffer = fread($fp_source, GETID3_FREAD_BUFFER_SIZE)) {
00097                                                                         fwrite($fp_temp, $buffer, strlen($buffer));
00098                                                                 }
00099 
00100                                                                 fclose($fp_temp);
00101                                                                 fclose($fp_source);
00102                                                                 copy($tempfilename, $this->filename);
00103                                                                 unlink($tempfilename);
00104                                                                 ob_end_clean();
00105                                                                 return true;
00106 
00107                                                         } else {
00108 
00109                                                                 $this->errors[] = 'Could not open '.$tempfilename.' mode "wb" - '.strip_tags(ob_get_contents());
00110 
00111                                                         }
00112                                                         fclose($fp_source);
00113 
00114                                                 } else {
00115 
00116                                                         $this->errors[] = 'Could not open '.$this->filename.' mode "rb" - '.strip_tags(ob_get_contents());
00117 
00118                                                 }
00119                                                 ob_end_clean();
00120                                         }
00121                                         return false;
00122 
00123                                 }
00124 
00125                         } else {
00126 
00127                                 $this->errors[] = '$this->GenerateID3v2Tag() failed';
00128 
00129                         }
00130 
00131                         if (!empty($this->errors)) {
00132                                 return false;
00133                         }
00134                         return true;
00135                 } else {
00136                         $this->errors[] = '!is_writeable('.$this->filename.')';
00137                 }
00138                 return false;
00139         }
00140 
00141         function RemoveID3v2() {
00142 
00143                 // File MUST be writeable - CHMOD(646) at least. It's best if the
00144                 // directory is also writeable, because that method is both faster and less susceptible to errors.
00145                 if (is_writeable(dirname($this->filename))) {
00146 
00147                         // preferred method - only one copying operation, minimal chance of corrupting
00148                         // original file if script is interrupted, but required directory to be writeable
00149                         if ($fp_source = @fopen($this->filename, 'rb')) {
00150                                 // Initialize getID3 engine
00151                                 $getID3 = new getID3;
00152                                 $OldThisFileInfo = $getID3->analyze($this->filename);
00153                                 rewind($fp_source);
00154                                 if ($OldThisFileInfo['avdataoffset'] !== false) {
00155                                         fseek($fp_source, $OldThisFileInfo['avdataoffset'], SEEK_SET);
00156                                 }
00157                                 if ($fp_temp = @fopen($this->filename.'getid3tmp', 'w+b')) {
00158                                         while ($buffer = fread($fp_source, GETID3_FREAD_BUFFER_SIZE)) {
00159                                                 fwrite($fp_temp, $buffer, strlen($buffer));
00160                                         }
00161                                         fclose($fp_temp);
00162                                 } else {
00163                                         $this->errors[] = 'Could not open '.$this->filename.'getid3tmp mode "w+b"';
00164                                 }
00165                                 fclose($fp_source);
00166                         } else {
00167                                 $this->errors[] = 'Could not open '.$this->filename.' mode "rb"';
00168                         }
00169                         if (file_exists($this->filename)) {
00170                                 unlink($this->filename);
00171                         }
00172                         rename($this->filename.'getid3tmp', $this->filename);
00173 
00174                 } elseif (is_writable($this->filename)) {
00175 
00176                         // less desirable alternate method - double-copies the file, overwrites original file
00177                         // and could corrupt source file if the script is interrupted or an error occurs.
00178                         if ($fp_source = @fopen($this->filename, 'rb')) {
00179                                 // Initialize getID3 engine
00180                                 $getID3 = new getID3;
00181                                 $OldThisFileInfo = $getID3->analyze($this->filename);
00182                                 rewind($fp_source);
00183                                 if ($OldThisFileInfo['avdataoffset'] !== false) {
00184                                         fseek($fp_source, $OldThisFileInfo['avdataoffset'], SEEK_SET);
00185                                 }
00186                                 if ($fp_temp = tmpfile()) {
00187                                         while ($buffer = fread($fp_source, GETID3_FREAD_BUFFER_SIZE)) {
00188                                                 fwrite($fp_temp, $buffer, strlen($buffer));
00189                                         }
00190                                         fclose($fp_source);
00191                                         if ($fp_source = @fopen($this->filename, 'wb')) {
00192                                                 rewind($fp_temp);
00193                                                 while ($buffer = fread($fp_temp, GETID3_FREAD_BUFFER_SIZE)) {
00194                                                         fwrite($fp_source, $buffer, strlen($buffer));
00195                                                 }
00196                                                 fseek($fp_temp, -128, SEEK_END);
00197                                                 fclose($fp_source);
00198                                         } else {
00199                                                 $this->errors[] = 'Could not open '.$this->filename.' mode "wb"';
00200                                         }
00201                                         fclose($fp_temp);
00202                                 } else {
00203                                         $this->errors[] = 'Could not create tmpfile()';
00204                                 }
00205                         } else {
00206                                 $this->errors[] = 'Could not open '.$this->filename.' mode "rb"';
00207                         }
00208 
00209                 } else {
00210 
00211                         $this->errors[] = 'Directory and file both not writeable';
00212 
00213                 }
00214 
00215                 if (!empty($this->errors)) {
00216                         return false;
00217                 }
00218                 return true;
00219         }
00220 
00221 
00222         function GenerateID3v2TagFlags($flags) {
00223                 switch ($this->majorversion) {
00224                         case 4:
00225                                 // %abcd0000
00226                                 $flag  = (@$flags['unsynchronisation'] ? '1' : '0'); // a - Unsynchronisation
00227                                 $flag .= (@$flags['extendedheader']    ? '1' : '0'); // b - Extended header
00228                                 $flag .= (@$flags['experimental']      ? '1' : '0'); // c - Experimental indicator
00229                                 $flag .= (@$flags['footer']            ? '1' : '0'); // d - Footer present
00230                                 $flag .= '0000';
00231                                 break;
00232 
00233                         case 3:
00234                                 // %abc00000
00235                                 $flag  = (@$flags['unsynchronisation'] ? '1' : '0'); // a - Unsynchronisation
00236                                 $flag .= (@$flags['extendedheader']    ? '1' : '0'); // b - Extended header
00237                                 $flag .= (@$flags['experimental']      ? '1' : '0'); // c - Experimental indicator
00238                                 $flag .= '00000';
00239                                 break;
00240 
00241                         case 2:
00242                                 // %ab000000
00243                                 $flag  = (@$flags['unsynchronisation'] ? '1' : '0'); // a - Unsynchronisation
00244                                 $flag .= (@$flags['compression']       ? '1' : '0'); // b - Compression
00245                                 $flag .= '000000';
00246                                 break;
00247 
00248                         default:
00249                                 return false;
00250                                 break;
00251                 }
00252                 return chr(bindec($flag));
00253         }
00254 
00255 
00256         function GenerateID3v2FrameFlags($TagAlter=false, $FileAlter=false, $ReadOnly=false, $Compression=false, $Encryption=false, $GroupingIdentity=false, $Unsynchronisation=false, $DataLengthIndicator=false) {
00257                 switch ($this->majorversion) {
00258                         case 4:
00259                                 // %0abc0000 %0h00kmnp
00260                                 $flag1  = '0';
00261                                 $flag1 .= $TagAlter  ? '1' : '0'; // a - Tag alter preservation (true == discard)
00262                                 $flag1 .= $FileAlter ? '1' : '0'; // b - File alter preservation (true == discard)
00263                                 $flag1 .= $ReadOnly  ? '1' : '0'; // c - Read only (true == read only)
00264                                 $flag1 .= '0000';
00265 
00266                                 $flag2  = '0';
00267                                 $flag2 .= $GroupingIdentity    ? '1' : '0'; // h - Grouping identity (true == contains group information)
00268                                 $flag2 .= '00';
00269                                 $flag2 .= $Compression         ? '1' : '0'; // k - Compression (true == compressed)
00270                                 $flag2 .= $Encryption          ? '1' : '0'; // m - Encryption (true == encrypted)
00271                                 $flag2 .= $Unsynchronisation   ? '1' : '0'; // n - Unsynchronisation (true == unsynchronised)
00272                                 $flag2 .= $DataLengthIndicator ? '1' : '0'; // p - Data length indicator (true == data length indicator added)
00273                                 break;
00274 
00275                         case 3:
00276                                 // %abc00000 %ijk00000
00277                                 $flag1  = $TagAlter  ? '1' : '0';  // a - Tag alter preservation (true == discard)
00278                                 $flag1 .= $FileAlter ? '1' : '0';  // b - File alter preservation (true == discard)
00279                                 $flag1 .= $ReadOnly  ? '1' : '0';  // c - Read only (true == read only)
00280                                 $flag1 .= '00000';
00281 
00282                                 $flag2  = $Compression      ? '1' : '0';      // i - Compression (true == compressed)
00283                                 $flag2 .= $Encryption       ? '1' : '0';      // j - Encryption (true == encrypted)
00284                                 $flag2 .= $GroupingIdentity ? '1' : '0';      // k - Grouping identity (true == contains group information)
00285                                 $flag2 .= '00000';
00286                                 break;
00287 
00288                         default:
00289                                 return false;
00290                                 break;
00291 
00292                 }
00293                 return chr(bindec($flag1)).chr(bindec($flag2));
00294         }
00295 
00296         function GenerateID3v2FrameData($frame_name, $source_data_array) {
00297                 if (!getid3_id3v2::IsValidID3v2FrameName($frame_name, $this->majorversion)) {
00298                         return false;
00299                 }
00300                 $framedata = '';
00301 
00302                 if (($this->majorversion < 3) || ($this->majorversion > 4)) {
00303 
00304                         $this->errors[] = 'Only ID3v2.3 and ID3v2.4 are supported in GenerateID3v2FrameData()';
00305 
00306                 } else { // $this->majorversion 3 or 4
00307 
00308                         switch ($frame_name) {
00309                                 case 'UFID':
00310                                         // 4.1   UFID Unique file identifier
00311                                         // Owner identifier        <text string> $00
00312                                         // Identifier              <up to 64 bytes binary data>
00313                                         if (strlen($source_data_array['data']) > 64) {
00314                                                 $this->errors[] = 'Identifier not allowed to be longer than 64 bytes in '.$frame_name.' (supplied data was '.strlen($source_data_array['data']).' bytes long)';
00315                                         } else {
00316                                                 $framedata .= str_replace("\x00", '', $source_data_array['ownerid'])."\x00";
00317                                                 $framedata .= substr($source_data_array['data'], 0, 64); // max 64 bytes - truncate anything longer
00318                                         }
00319                                         break;
00320 
00321                                 case 'TXXX':
00322                                         // 4.2.2 TXXX User defined text information frame
00323                                         // Text encoding     $xx
00324                                         // Description       <text string according to encoding> $00 (00)
00325                                         // Value             <text string according to encoding>
00326                                         $source_data_array['encodingid'] = (isset($source_data_array['encodingid']) ? $source_data_array['encodingid'] : $this->id3v2_default_encodingid);
00327                                         if (!$this->ID3v2IsValidTextEncoding($source_data_array['encodingid'], $this->majorversion)) {
00328                                                 $this->errors[] = 'Invalid Text Encoding in '.$frame_name.' ('.$source_data_array['encodingid'].') for ID3v2.'.$this->majorversion;
00329                                         } else {
00330                                                 $framedata .= chr($source_data_array['encodingid']);
00331                                                 $framedata .= $source_data_array['description'].getid3_id3v2::TextEncodingTerminatorLookup($source_data_array['encodingid']);
00332                                                 $framedata .= $source_data_array['data'];
00333                                         }
00334                                         break;
00335 
00336                                 case 'WXXX':
00337                                         // 4.3.2 WXXX User defined URL link frame
00338                                         // Text encoding     $xx
00339                                         // Description       <text string according to encoding> $00 (00)
00340                                         // URL               <text string>
00341                                         $source_data_array['encodingid'] = (isset($source_data_array['encodingid']) ? $source_data_array['encodingid'] : $this->id3v2_default_encodingid);
00342                                         if (!$this->ID3v2IsValidTextEncoding($source_data_array['encodingid'], $this->majorversion)) {
00343                                                 $this->errors[] = 'Invalid Text Encoding in '.$frame_name.' ('.$source_data_array['encodingid'].') for ID3v2.'.$this->majorversion;
00344                                         } elseif (!isset($source_data_array['data']) || !$this->IsValidURL($source_data_array['data'], false, false)) {
00345                                                 //$this->errors[] = 'Invalid URL in '.$frame_name.' ('.$source_data_array['data'].')';
00346                                                 // probably should be an error, need to rewrite IsValidURL() to handle other encodings
00347                                                 $this->warnings[] = 'Invalid URL in '.$frame_name.' ('.$source_data_array['data'].')';
00348                                         } else {
00349                                                 $framedata .= chr($source_data_array['encodingid']);
00350                                                 $framedata .= $source_data_array['description'].getid3_id3v2::TextEncodingTerminatorLookup($source_data_array['encodingid']);
00351                                                 $framedata .= $source_data_array['data'];
00352                                         }
00353                                         break;
00354 
00355                                 case 'IPLS':
00356                                         // 4.4  IPLS Involved people list (ID3v2.3 only)
00357                                         // Text encoding     $xx
00358                                         // People list strings    <textstrings>
00359                                         $source_data_array['encodingid'] = (isset($source_data_array['encodingid']) ? $source_data_array['encodingid'] : $this->id3v2_default_encodingid);
00360                                         if (!$this->ID3v2IsValidTextEncoding($source_data_array['encodingid'], $this->majorversion)) {
00361                                                 $this->errors[] = 'Invalid Text Encoding in '.$frame_name.' ('.$source_data_array['encodingid'].') for ID3v2.'.$this->majorversion;
00362                                         } else {
00363                                                 $framedata .= chr($source_data_array['encodingid']);
00364                                                 $framedata .= $source_data_array['data'];
00365                                         }
00366                                         break;
00367 
00368                                 case 'MCDI':
00369                                         // 4.4   MCDI Music CD identifier
00370                                         // CD TOC                <binary data>
00371                                         $framedata .= $source_data_array['data'];
00372                                         break;
00373 
00374                                 case 'ETCO':
00375                                         // 4.5   ETCO Event timing codes
00376                                         // Time stamp format    $xx
00377                                         //   Where time stamp format is:
00378                                         // $01  (32-bit value) MPEG frames from beginning of file
00379                                         // $02  (32-bit value) milliseconds from beginning of file
00380                                         //   Followed by a list of key events in the following format:
00381                                         // Type of event   $xx
00382                                         // Time stamp      $xx (xx ...)
00383                                         //   The 'Time stamp' is set to zero if directly at the beginning of the sound
00384                                         //   or after the previous event. All events MUST be sorted in chronological order.
00385                                         if (($source_data_array['timestampformat'] > 2) || ($source_data_array['timestampformat'] < 1)) {
00386                                                 $this->errors[] = 'Invalid Time Stamp Format byte in '.$frame_name.' ('.$source_data_array['timestampformat'].')';
00387                                         } else {
00388                                                 $framedata .= chr($source_data_array['timestampformat']);
00389                                                 foreach ($source_data_array as $key => $val) {
00390                                                         if (!$this->ID3v2IsValidETCOevent($val['typeid'])) {
00391                                                                 $this->errors[] = 'Invalid Event Type byte in '.$frame_name.' ('.$val['typeid'].')';
00392                                                         } elseif (($key != 'timestampformat') && ($key != 'flags')) {
00393                                                                 if (($val['timestamp'] > 0) && ($previousETCOtimestamp >= $val['timestamp'])) {
00394                                                                         //   The 'Time stamp' is set to zero if directly at the beginning of the sound
00395                                                                         //   or after the previous event. All events MUST be sorted in chronological order.
00396                                                                         $this->errors[] = 'Out-of-order timestamp in '.$frame_name.' ('.$val['timestamp'].') for Event Type ('.$val['typeid'].')';
00397                                                                 } else {
00398                                                                         $framedata .= chr($val['typeid']);
00399                                                                         $framedata .= getid3_lib::BigEndian2String($val['timestamp'], 4, false);
00400                                                                 }
00401                                                         }
00402                                                 }
00403                                         }
00404                                         break;
00405 
00406                                 case 'MLLT':
00407                                         // 4.6   MLLT MPEG location lookup table
00408                                         // MPEG frames between reference  $xx xx
00409                                         // Bytes between reference        $xx xx xx
00410                                         // Milliseconds between reference $xx xx xx
00411                                         // Bits for bytes deviation       $xx
00412                                         // Bits for milliseconds dev.     $xx
00413                                         //   Then for every reference the following data is included;
00414                                         // Deviation in bytes         %xxx....
00415                                         // Deviation in milliseconds  %xxx....
00416                                         if (($source_data_array['framesbetweenreferences'] > 0) && ($source_data_array['framesbetweenreferences'] <= 65535)) {
00417                                                 $framedata .= getid3_lib::BigEndian2String($source_data_array['framesbetweenreferences'], 2, false);
00418                                         } else {
00419                                                 $this->errors[] = 'Invalid MPEG Frames Between References in '.$frame_name.' ('.$source_data_array['framesbetweenreferences'].')';
00420                                         }
00421                                         if (($source_data_array['bytesbetweenreferences'] > 0) && ($source_data_array['bytesbetweenreferences'] <= 16777215)) {
00422                                                 $framedata .= getid3_lib::BigEndian2String($source_data_array['bytesbetweenreferences'], 3, false);
00423                                         } else {
00424                                                 $this->errors[] = 'Invalid bytes Between References in '.$frame_name.' ('.$source_data_array['bytesbetweenreferences'].')';
00425                                         }
00426                                         if (($source_data_array['msbetweenreferences'] > 0) && ($source_data_array['msbetweenreferences'] <= 16777215)) {
00427                                                 $framedata .= getid3_lib::BigEndian2String($source_data_array['msbetweenreferences'], 3, false);
00428                                         } else {
00429                                                 $this->errors[] = 'Invalid Milliseconds Between References in '.$frame_name.' ('.$source_data_array['msbetweenreferences'].')';
00430                                         }
00431                                         if (!$this->IsWithinBitRange($source_data_array['bitsforbytesdeviation'], 8, false)) {
00432                                                 if (($source_data_array['bitsforbytesdeviation'] % 4) == 0) {
00433                                                         $framedata .= chr($source_data_array['bitsforbytesdeviation']);
00434                                                 } else {
00435                                                         $this->errors[] = 'Bits For Bytes Deviation in '.$frame_name.' ('.$source_data_array['bitsforbytesdeviation'].') must be a multiple of 4.';
00436                                                 }
00437                                         } else {
00438                                                 $this->errors[] = 'Invalid Bits For Bytes Deviation in '.$frame_name.' ('.$source_data_array['bitsforbytesdeviation'].')';
00439                                         }
00440                                         if (!$this->IsWithinBitRange($source_data_array['bitsformsdeviation'], 8, false)) {
00441                                                 if (($source_data_array['bitsformsdeviation'] % 4) == 0) {
00442                                                         $framedata .= chr($source_data_array['bitsformsdeviation']);
00443                                                 } else {
00444                                                         $this->errors[] = 'Bits For Milliseconds Deviation in '.$frame_name.' ('.$source_data_array['bitsforbytesdeviation'].') must be a multiple of 4.';
00445                                                 }
00446                                         } else {
00447                                                 $this->errors[] = 'Invalid Bits For Milliseconds Deviation in '.$frame_name.' ('.$source_data_array['bitsformsdeviation'].')';
00448                                         }
00449                                         foreach ($source_data_array as $key => $val) {
00450                                                 if (($key != 'framesbetweenreferences') && ($key != 'bytesbetweenreferences') && ($key != 'msbetweenreferences') && ($key != 'bitsforbytesdeviation') && ($key != 'bitsformsdeviation') && ($key != 'flags')) {
00451                                                         $unwrittenbitstream .= str_pad(getid3_lib::Dec2Bin($val['bytedeviation']), $source_data_array['bitsforbytesdeviation'], '0', STR_PAD_LEFT);
00452                                                         $unwrittenbitstream .= str_pad(getid3_lib::Dec2Bin($val['msdeviation']),   $source_data_array['bitsformsdeviation'],    '0', STR_PAD_LEFT);
00453                                                 }
00454                                         }
00455                                         for ($i = 0; $i < strlen($unwrittenbitstream); $i += 8) {
00456                                                 $highnibble = bindec(substr($unwrittenbitstream, $i, 4)) << 4;
00457                                                 $lownibble  = bindec(substr($unwrittenbitstream, $i + 4, 4));
00458                                                 $framedata .= chr($highnibble & $lownibble);
00459                                         }
00460                                         break;
00461 
00462                                 case 'SYTC':
00463                                         // 4.7   SYTC Synchronised tempo codes
00464                                         // Time stamp format   $xx
00465                                         // Tempo data          <binary data>
00466                                         //   Where time stamp format is:
00467                                         // $01  (32-bit value) MPEG frames from beginning of file
00468                                         // $02  (32-bit value) milliseconds from beginning of file
00469                                         if (($source_data_array['timestampformat'] > 2) || ($source_data_array['timestampformat'] < 1)) {
00470                                                 $this->errors[] = 'Invalid Time Stamp Format byte in '.$frame_name.' ('.$source_data_array['timestampformat'].')';
00471                                         } else {
00472                                                 $framedata .= chr($source_data_array['timestampformat']);
00473                                                 foreach ($source_data_array as $key => $val) {
00474                                                         if (!$this->ID3v2IsValidETCOevent($val['typeid'])) {
00475                                                                 $this->errors[] = 'Invalid Event Type byte in '.$frame_name.' ('.$val['typeid'].')';
00476                                                         } elseif (($key != 'timestampformat') && ($key != 'flags')) {
00477                                                                 if (($val['tempo'] < 0) || ($val['tempo'] > 510)) {
00478                                                                         $this->errors[] = 'Invalid Tempo (max = 510) in '.$frame_name.' ('.$val['tempo'].') at timestamp ('.$val['timestamp'].')';
00479                                                                 } else {
00480                                                                         if ($val['tempo'] > 255) {
00481                                                                                 $framedata .= chr(255);
00482                                                                                 $val['tempo'] -= 255;
00483                                                                         }
00484                                                                         $framedata .= chr($val['tempo']);
00485                                                                         $framedata .= getid3_lib::BigEndian2String($val['timestamp'], 4, false);
00486                                                                 }
00487                                                         }
00488                                                 }
00489                                         }
00490                                         break;
00491 
00492                                 case 'USLT':
00493                                         // 4.8   USLT Unsynchronised lyric/text transcription
00494                                         // Text encoding        $xx
00495                                         // Language             $xx xx xx
00496                                         // Content descriptor   <text string according to encoding> $00 (00)
00497                                         // Lyrics/text          <full text string according to encoding>
00498                                         $source_data_array['encodingid'] = (isset($source_data_array['encodingid']) ? $source_data_array['encodingid'] : $this->id3v2_default_encodingid);
00499                                         if (!$this->ID3v2IsValidTextEncoding($source_data_array['encodingid'])) {
00500                                                 $this->errors[] = 'Invalid Text Encoding in '.$frame_name.' ('.$source_data_array['encodingid'].') for ID3v2.'.$this->majorversion;
00501                                         } elseif (getid3_id3v2::LanguageLookup($source_data_array['language'], true) == '') {
00502                                                 $this->errors[] = 'Invalid Language in '.$frame_name.' ('.$source_data_array['language'].')';
00503                                         } else {
00504                                                 $framedata .= chr($source_data_array['encodingid']);
00505                                                 $framedata .= strtolower($source_data_array['language']);
00506                                                 $framedata .= $source_data_array['description'].getid3_id3v2::TextEncodingTerminatorLookup($source_data_array['encodingid']);
00507                                                 $framedata .= $source_data_array['data'];
00508                                         }
00509                                         break;
00510 
00511                                 case 'SYLT':
00512                                         // 4.9   SYLT Synchronised lyric/text
00513                                         // Text encoding        $xx
00514                                         // Language             $xx xx xx
00515                                         // Time stamp format    $xx
00516                                         //   $01  (32-bit value) MPEG frames from beginning of file
00517                                         //   $02  (32-bit value) milliseconds from beginning of file
00518                                         // Content type         $xx
00519                                         // Content descriptor   <text string according to encoding> $00 (00)
00520                                         //   Terminated text to be synced (typically a syllable)
00521                                         //   Sync identifier (terminator to above string)   $00 (00)
00522                                         //   Time stamp                                     $xx (xx ...)
00523                                         $source_data_array['encodingid'] = (isset($source_data_array['encodingid']) ? $source_data_array['encodingid'] : $this->id3v2_default_encodingid);
00524                                         if (!$this->ID3v2IsValidTextEncoding($source_data_array['encodingid'])) {
00525                                                 $this->errors[] = 'Invalid Text Encoding in '.$frame_name.' ('.$source_data_array['encodingid'].') for ID3v2.'.$this->majorversion;
00526                                         } elseif (getid3_id3v2::LanguageLookup($source_data_array['language'], true) == '') {
00527                                                 $this->errors[] = 'Invalid Language in '.$frame_name.' ('.$source_data_array['language'].')';
00528                                         } elseif (($source_data_array['timestampformat'] > 2) || ($source_data_array['timestampformat'] < 1)) {
00529                                                 $this->errors[] = 'Invalid Time Stamp Format byte in '.$frame_name.' ('.$source_data_array['timestampformat'].')';
00530                                         } elseif (!$this->ID3v2IsValidSYLTtype($source_data_array['contenttypeid'])) {
00531                                                 $this->errors[] = 'Invalid Content Type byte in '.$frame_name.' ('.$source_data_array['contenttypeid'].')';
00532                                         } elseif (!is_array($source_data_array['data'])) {
00533                                                 $this->errors[] = 'Invalid Lyric/Timestamp data in '.$frame_name.' (must be an array)';
00534                                         } else {
00535                                                 $framedata .= chr($source_data_array['encodingid']);
00536                                                 $framedata .= strtolower($source_data_array['language']);
00537                                                 $framedata .= chr($source_data_array['timestampformat']);
00538                                                 $framedata .= chr($source_data_array['contenttypeid']);
00539                                                 $framedata .= $source_data_array['description'].getid3_id3v2::TextEncodingTerminatorLookup($source_data_array['encodingid']);
00540                                                 ksort($source_data_array['data']);
00541                                                 foreach ($source_data_array['data'] as $key => $val) {
00542                                                         $framedata .= $val['data'].getid3_id3v2::TextEncodingTerminatorLookup($source_data_array['encodingid']);
00543                                                         $framedata .= getid3_lib::BigEndian2String($val['timestamp'], 4, false);
00544                                                 }
00545                                         }
00546                                         break;
00547 
00548                                 case 'COMM':
00549                                         // 4.10  COMM Comments
00550                                         // Text encoding          $xx
00551                                         // Language               $xx xx xx
00552                                         // Short content descrip. <text string according to encoding> $00 (00)
00553                                         // The actual text        <full text string according to encoding>
00554                                         $source_data_array['encodingid'] = (isset($source_data_array['encodingid']) ? $source_data_array['encodingid'] : $this->id3v2_default_encodingid);
00555                                         if (!$this->ID3v2IsValidTextEncoding($source_data_array['encodingid'])) {
00556                                                 $this->errors[] = 'Invalid Text Encoding in '.$frame_name.' ('.$source_data_array['encodingid'].') for ID3v2.'.$this->majorversion;
00557                                         } elseif (getid3_id3v2::LanguageLookup($source_data_array['language'], true) == '') {
00558                                                 $this->errors[] = 'Invalid Language in '.$frame_name.' ('.$source_data_array['language'].')';
00559                                         } else {
00560                                                 $framedata .= chr($source_data_array['encodingid']);
00561                                                 $framedata .= strtolower($source_data_array['language']);
00562                                                 $framedata .= $source_data_array['description'].getid3_id3v2::TextEncodingTerminatorLookup($source_data_array['encodingid']);
00563                                                 $framedata .= $source_data_array['data'];
00564                                         }
00565                                         break;
00566 
00567                                 case 'RVA2':
00568                                         // 4.11  RVA2 Relative volume adjustment (2) (ID3v2.4+ only)
00569                                         // Identification          <text string> $00
00570                                         //   The 'identification' string is used to identify the situation and/or
00571                                         //   device where this adjustment should apply. The following is then
00572                                         //   repeated for every channel:
00573                                         // Type of channel         $xx
00574                                         // Volume adjustment       $xx xx
00575                                         // Bits representing peak  $xx
00576                                         // Peak volume             $xx (xx ...)
00577                                         $framedata .= str_replace("\x00", '', $source_data_array['description'])."\x00";
00578                                         foreach ($source_data_array as $key => $val) {
00579                                                 if ($key != 'description') {
00580                                                         $framedata .= chr($val['channeltypeid']);
00581                                                         $framedata .= getid3_lib::BigEndian2String($val['volumeadjust'], 2, false, true); // signed 16-bit
00582                                                         if (!$this->IsWithinBitRange($source_data_array['bitspeakvolume'], 8, false)) {
00583                                                                 $framedata .= chr($val['bitspeakvolume']);
00584                                                                 if ($val['bitspeakvolume'] > 0) {
00585                                                                         $framedata .= getid3_lib::BigEndian2String($val['peakvolume'], ceil($val['bitspeakvolume'] / 8), false, false);
00586                                                                 }
00587                                                         } else {
00588                                                                 $this->errors[] = 'Invalid Bits Representing Peak Volume in '.$frame_name.' ('.$val['bitspeakvolume'].') (range = 0 to 255)';
00589                                                         }
00590                                                 }
00591                                         }
00592                                         break;
00593 
00594                                 case 'RVAD':
00595                                         // 4.12  RVAD Relative volume adjustment (ID3v2.3 only)
00596                                         // Increment/decrement     %00fedcba
00597                                         // Bits used for volume descr.        $xx
00598                                         // Relative volume change, right      $xx xx (xx ...) // a
00599                                         // Relative volume change, left       $xx xx (xx ...) // b
00600                                         // Peak volume right                  $xx xx (xx ...)
00601                                         // Peak volume left                   $xx xx (xx ...)
00602                                         // Relative volume change, right back $xx xx (xx ...) // c
00603                                         // Relative volume change, left back  $xx xx (xx ...) // d
00604                                         // Peak volume right back             $xx xx (xx ...)
00605                                         // Peak volume left back              $xx xx (xx ...)
00606                                         // Relative volume change, center     $xx xx (xx ...) // e
00607                                         // Peak volume center                 $xx xx (xx ...)
00608                                         // Relative volume change, bass       $xx xx (xx ...) // f
00609                                         // Peak volume bass                   $xx xx (xx ...)
00610                                         if (!$this->IsWithinBitRange($source_data_array['bitsvolume'], 8, false)) {
00611                                                 $this->errors[] = 'Invalid Bits For Volume Description byte in '.$frame_name.' ('.$source_data_array['bitsvolume'].') (range = 1 to 255)';
00612                                         } else {
00613                                                 $incdecflag .= '00';
00614                                                 $incdecflag .= $source_data_array['incdec']['right']     ? '1' : '0';     // a - Relative volume change, right
00615                                                 $incdecflag .= $source_data_array['incdec']['left']      ? '1' : '0';      // b - Relative volume change, left
00616                                                 $incdecflag .= $source_data_array['incdec']['rightrear'] ? '1' : '0'; // c - Relative volume change, right back
00617                                                 $incdecflag .= $source_data_array['incdec']['leftrear']  ? '1' : '0';  // d - Relative volume change, left back
00618                                                 $incdecflag .= $source_data_array['incdec']['center']    ? '1' : '0';    // e - Relative volume change, center
00619                                                 $incdecflag .= $source_data_array['incdec']['bass']      ? '1' : '0';      // f - Relative volume change, bass
00620                                                 $framedata .= chr(bindec($incdecflag));
00621                                                 $framedata .= chr($source_data_array['bitsvolume']);
00622                                                 $framedata .= getid3_lib::BigEndian2String($source_data_array['volumechange']['right'], ceil($source_data_array['bitsvolume'] / 8), false);
00623                                                 $framedata .= getid3_lib::BigEndian2String($source_data_array['volumechange']['left'],  ceil($source_data_array['bitsvolume'] / 8), false);
00624                                                 $framedata .= getid3_lib::BigEndian2String($source_data_array['peakvolume']['right'], ceil($source_data_array['bitsvolume'] / 8), false);
00625                                                 $framedata .= getid3_lib::BigEndian2String($source_data_array['peakvolume']['left'],  ceil($source_data_array['bitsvolume'] / 8), false);
00626                                                 if ($source_data_array['volumechange']['rightrear'] || $source_data_array['volumechange']['leftrear'] ||
00627                                                         $source_data_array['peakvolume']['rightrear'] || $source_data_array['peakvolume']['leftrear'] ||
00628                                                         $source_data_array['volumechange']['center'] || $source_data_array['peakvolume']['center'] ||
00629                                                         $source_data_array['volumechange']['bass'] || $source_data_array['peakvolume']['bass']) {
00630                                                                 $framedata .= getid3_lib::BigEndian2String($source_data_array['volumechange']['rightrear'], ceil($source_data_array['bitsvolume']/8), false);
00631                                                                 $framedata .= getid3_lib::BigEndian2String($source_data_array['volumechange']['leftrear'],  ceil($source_data_array['bitsvolume']/8), false);
00632                                                                 $framedata .= getid3_lib::BigEndian2String($source_data_array['peakvolume']['rightrear'], ceil($source_data_array['bitsvolume']/8), false);
00633                                                                 $framedata .= getid3_lib::BigEndian2String($source_data_array['peakvolume']['leftrear'],  ceil($source_data_array['bitsvolume']/8), false);
00634                                                 }
00635                                                 if ($source_data_array['volumechange']['center'] || $source_data_array['peakvolume']['center'] ||
00636                                                         $source_data_array['volumechange']['bass'] || $source_data_array['peakvolume']['bass']) {
00637                                                                 $framedata .= getid3_lib::BigEndian2String($source_data_array['volumechange']['center'], ceil($source_data_array['bitsvolume']/8), false);
00638                                                                 $framedata .= getid3_lib::BigEndian2String($source_data_array['peakvolume']['center'], ceil($source_data_array['bitsvolume']/8), false);
00639                                                 }
00640                                                 if ($source_data_array['volumechange']['bass'] || $source_data_array['peakvolume']['bass']) {
00641                                                                 $framedata .= getid3_lib::BigEndian2String($source_data_array['volumechange']['bass'], ceil($source_data_array['bitsvolume']/8), false);
00642                                                                 $framedata .= getid3_lib::BigEndian2String($source_data_array['peakvolume']['bass'], ceil($source_data_array['bitsvolume']/8), false);
00643                                                 }
00644                                         }
00645                                         break;
00646 
00647                                 case 'EQU2':
00648                                         // 4.12  EQU2 Equalisation (2) (ID3v2.4+ only)
00649                                         // Interpolation method  $xx
00650                                         //   $00  Band
00651                                         //   $01  Linear
00652                                         // Identification        <text string> $00
00653                                         //   The following is then repeated for every adjustment point
00654                                         // Frequency          $xx xx
00655                                         // Volume adjustment  $xx xx
00656                                         if (($source_data_array['interpolationmethod'] < 0) || ($source_data_array['interpolationmethod'] > 1)) {
00657                                                 $this->errors[] = 'Invalid Interpolation Method byte in '.$frame_name.' ('.$source_data_array['interpolationmethod'].') (valid = 0 or 1)';
00658                                         } else {
00659                                                 $framedata .= chr($source_data_array['interpolationmethod']);
00660                                                 $framedata .= str_replace("\x00", '', $source_data_array['description'])."\x00";
00661                                                 foreach ($source_data_array['data'] as $key => $val) {
00662                                                         $framedata .= getid3_lib::BigEndian2String(intval(round($key * 2)), 2, false);
00663                                                         $framedata .= getid3_lib::BigEndian2String($val, 2, false, true); // signed 16-bit
00664                                                 }
00665                                         }
00666                                         break;
00667 
00668                                 case 'EQUA':
00669                                         // 4.12  EQUA Equalisation (ID3v2.3 only)
00670                                         // Adjustment bits    $xx
00671                                         //   This is followed by 2 bytes + ('adjustment bits' rounded up to the
00672                                         //   nearest byte) for every equalisation band in the following format,
00673                                         //   giving a frequency range of 0 - 32767Hz:
00674                                         // Increment/decrement   %x (MSB of the Frequency)
00675                                         // Frequency             (lower 15 bits)
00676                                         // Adjustment            $xx (xx ...)
00677                                         if (!$this->IsWithinBitRange($source_data_array['bitsvolume'], 8, false)) {
00678                                                 $this->errors[] = 'Invalid Adjustment Bits byte in '.$frame_name.' ('.$source_data_array['bitsvolume'].') (range = 1 to 255)';
00679                                         } else {
00680                                                 $framedata .= chr($source_data_array['adjustmentbits']);
00681                                                 foreach ($source_data_array as $key => $val) {
00682                                                         if ($key != 'bitsvolume') {
00683                                                                 if (($key > 32767) || ($key < 0)) {
00684                                                                         $this->errors[] = 'Invalid Frequency in '.$frame_name.' ('.$key.') (range = 0 to 32767)';
00685                                                                 } else {
00686                                                                         if ($val >= 0) {
00687                                                                                 // put MSB of frequency to 1 if increment, 0 if decrement
00688                                                                                 $key |= 0x8000;
00689                                                                         }
00690                                                                         $framedata .= getid3_lib::BigEndian2String($key, 2, false);
00691                                                                         $framedata .= getid3_lib::BigEndian2String($val, ceil($source_data_array['adjustmentbits'] / 8), false);
00692                                                                 }
00693                                                         }
00694                                                 }
00695                                         }
00696                                         break;
00697 
00698                                 case 'RVRB':
00699                                         // 4.13  RVRB Reverb
00700                                         // Reverb left (ms)                 $xx xx
00701                                         // Reverb right (ms)                $xx xx
00702                                         // Reverb bounces, left             $xx
00703                                         // Reverb bounces, right            $xx
00704                                         // Reverb feedback, left to left    $xx
00705                                         // Reverb feedback, left to right   $xx
00706                                         // Reverb feedback, right to right  $xx
00707                                         // Reverb feedback, right to left   $xx
00708                                         // Premix left to right             $xx
00709                                         // Premix right to left             $xx
00710                                         if (!$this->IsWithinBitRange($source_data_array['left'], 16, false)) {
00711                                                 $this->errors[] = 'Invalid Reverb Left in '.$frame_name.' ('.$source_data_array['left'].') (range = 0 to 65535)';
00712                                         } elseif (!$this->IsWithinBitRange($source_data_array['right'], 16, false)) {
00713                                                 $this->errors[] = 'Invalid Reverb Left in '.$frame_name.' ('.$source_data_array['right'].') (range = 0 to 65535)';
00714                                         } elseif (!$this->IsWithinBitRange($source_data_array['bouncesL'], 8, false)) {
00715                                                 $this->errors[] = 'Invalid Reverb Bounces, Left in '.$frame_name.' ('.$source_data_array['bouncesL'].') (range = 0 to 255)';
00716                                         } elseif (!$this->IsWithinBitRange($source_data_array['bouncesR'], 8, false)) {
00717                                                 $this->errors[] = 'Invalid Reverb Bounces, Right in '.$frame_name.' ('.$source_data_array['bouncesR'].') (range = 0 to 255)';
00718                                         } elseif (!$this->IsWithinBitRange($source_data_array['feedbackLL'], 8, false)) {
00719                                                 $this->errors[] = 'Invalid Reverb Feedback, Left-To-Left in '.$frame_name.' ('.$source_data_array['feedbackLL'].') (range = 0 to 255)';
00720                                         } elseif (!$this->IsWithinBitRange($source_data_array['feedbackLR'], 8, false)) {
00721                                                 $this->errors[] = 'Invalid Reverb Feedback, Left-To-Right in '.$frame_name.' ('.$source_data_array['feedbackLR'].') (range = 0 to 255)';
00722                                         } elseif (!$this->IsWithinBitRange($source_data_array['feedbackRR'], 8, false)) {
00723                                                 $this->errors[] = 'Invalid Reverb Feedback, Right-To-Right in '.$frame_name.' ('.$source_data_array['feedbackRR'].') (range = 0 to 255)';
00724                                         } elseif (!$this->IsWithinBitRange($source_data_array['feedbackRL'], 8, false)) {
00725                                                 $this->errors[] = 'Invalid Reverb Feedback, Right-To-Left in '.$frame_name.' ('.$source_data_array['feedbackRL'].') (range = 0 to 255)';
00726                                         } elseif (!$this->IsWithinBitRange($source_data_array['premixLR'], 8, false)) {
00727                                                 $this->errors[] = 'Invalid Premix, Left-To-Right in '.$frame_name.' ('.$source_data_array['premixLR'].') (range = 0 to 255)';
00728                                         } elseif (!$this->IsWithinBitRange($source_data_array['premixRL'], 8, false)) {
00729                                                 $this->errors[] = 'Invalid Premix, Right-To-Left in '.$frame_name.' ('.$source_data_array['premixRL'].') (range = 0 to 255)';
00730                                         } else {
00731                                                 $framedata .= getid3_lib::BigEndian2String($source_data_array['left'], 2, false);
00732                                                 $framedata .= getid3_lib::BigEndian2String($source_data_array['right'], 2, false);
00733                                                 $framedata .= chr($source_data_array['bouncesL']);
00734                                                 $framedata .= chr($source_data_array['bouncesR']);
00735                                                 $framedata .= chr($source_data_array['feedbackLL']);
00736                                                 $framedata .= chr($source_data_array['feedbackLR']);
00737                                                 $framedata .= chr($source_data_array['feedbackRR']);
00738                                                 $framedata .= chr($source_data_array['feedbackRL']);
00739                                                 $framedata .= chr($source_data_array['premixLR']);
00740                                                 $framedata .= chr($source_data_array['premixRL']);
00741                                         }
00742                                         break;
00743 
00744                                 case 'APIC':
00745                                         // 4.14  APIC Attached picture
00746                                         // Text encoding      $xx
00747                                         // MIME type          <text string> $00
00748                                         // Picture type       $xx
00749                                         // Description        <text string according to encoding> $00 (00)
00750                                         // Picture data       <binary data>
00751                                         $source_data_array['encodingid'] = (isset($source_data_array['encodingid']) ? $source_data_array['encodingid'] : $this->id3v2_default_encodingid);
00752                                         if (!$this->ID3v2IsValidTextEncoding($source_data_array['encodingid'])) {
00753                                                 $this->errors[] = 'Invalid Text Encoding in '.$frame_name.' ('.$source_data_array['encodingid'].') for ID3v2.'.$this->majorversion;
00754                                         } elseif (!$this->ID3v2IsValidAPICpicturetype($source_data_array['picturetypeid'])) {
00755                                                 $this->errors[] = 'Invalid Picture Type byte in '.$frame_name.' ('.$source_data_array['picturetypeid'].') for ID3v2.'.$this->majorversion;
00756                                         } elseif (($this->majorversion >= 3) && (!$this->ID3v2IsValidAPICimageformat($source_data_array['mime']))) {
00757                                                 $this->errors[] = 'Invalid MIME Type in '.$frame_name.' ('.$source_data_array['mime'].') for ID3v2.'.$this->majorversion;
00758                                         } elseif (($source_data_array['mime'] == '-->') && (!$this->IsValidURL($source_data_array['data'], false, false))) {
00759                                                 //$this->errors[] = 'Invalid URL in '.$frame_name.' ('.$source_data_array['data'].')';
00760                                                 // probably should be an error, need to rewrite IsValidURL() to handle other encodings
00761                                                 $this->warnings[] = 'Invalid URL in '.$frame_name.' ('.$source_data_array['data'].')';
00762                                         } else {
00763                                                 $framedata .= chr($source_data_array['encodingid']);
00764                                                 $framedata .= str_replace("\x00", '', $source_data_array['mime'])."\x00";
00765                                                 $framedata .= chr($source_data_array['picturetypeid']);
00766                                                 $framedata .= @$source_data_array['description'].getid3_id3v2::TextEncodingTerminatorLookup($source_data_array['encodingid']);
00767                                                 $framedata .= $source_data_array['data'];
00768                                         }
00769                                         break;
00770 
00771                                 case 'GEOB':
00772                                         // 4.15  GEOB General encapsulated object
00773                                         // Text encoding          $xx
00774                                         // MIME type              <text string> $00
00775                                         // Filename               <text string according to encoding> $00 (00)
00776                                         // Content description    <text string according to encoding> $00 (00)
00777                                         // Encapsulated object    <binary data>
00778                                         $source_data_array['encodingid'] = (isset($source_data_array['encodingid']) ? $source_data_array['encodingid'] : $this->id3v2_default_encodingid);
00779                                         if (!$this->ID3v2IsValidTextEncoding($source_data_array['encodingid'])) {
00780                                                 $this->errors[] = 'Invalid Text Encoding in '.$frame_name.' ('.$source_data_array['encodingid'].') for ID3v2.'.$this->majorversion;
00781                                         } elseif (!$this->IsValidMIMEstring($source_data_array['mime'])) {
00782                                                 $this->errors[] = 'Invalid MIME Type in '.$frame_name.' ('.$source_data_array['mime'].')';
00783                                         } elseif (!$source_data_array['description']) {
00784                                                 $this->errors[] = 'Missing Description in '.$frame_name;
00785                                         } else {
00786                                                 $framedata .= chr($source_data_array['encodingid']);
00787                                                 $framedata .= str_replace("\x00", '', $source_data_array['mime'])."\x00";
00788                                                 $framedata .= $source_data_array['filename'].getid3_id3v2::TextEncodingTerminatorLookup($source_data_array['encodingid']);
00789                                                 $framedata .= $source_data_array['description'].getid3_id3v2::TextEncodingTerminatorLookup($source_data_array['encodingid']);
00790                                                 $framedata .= $source_data_array['data'];
00791                                         }
00792                                         break;
00793 
00794                                 case 'PCNT':
00795                                         // 4.16  PCNT Play counter
00796                                         //   When the counter reaches all one's, one byte is inserted in
00797                                         //   front of the counter thus making the counter eight bits bigger
00798                                         // Counter        $xx xx xx xx (xx ...)
00799                                         $framedata .= getid3_lib::BigEndian2String($source_data_array['data'], 4, false);
00800                                         break;
00801 
00802                                 case 'POPM':
00803                                         // 4.17  POPM Popularimeter
00804                                         //   When the counter reaches all one's, one byte is inserted in
00805                                         //   front of the counter thus making the counter eight bits bigger
00806                                         // Email to user   <text string> $00
00807                                         // Rating          $xx
00808                                         // Counter         $xx xx xx xx (xx ...)
00809                                         if (!$this->IsWithinBitRange($source_data_array['rating'], 8, false)) {
00810                                                 $this->errors[] = 'Invalid Rating byte in '.$frame_name.' ('.$source_data_array['rating'].') (range = 0 to 255)';
00811                                         } elseif (!IsValidEmail($source_data_array['email'])) {
00812                                                 $this->errors[] = 'Invalid Email in '.$frame_name.' ('.$source_data_array['email'].')';
00813                                         } else {
00814                                                 $framedata .= str_replace("\x00", '', $source_data_array['email'])."\x00";
00815                                                 $framedata .= chr($source_data_array['rating']);
00816                                                 $framedata .= getid3_lib::BigEndian2String($source_data_array['data'], 4, false);
00817                                         }
00818                                         break;
00819 
00820                                 case 'RBUF':
00821                                         // 4.18  RBUF Recommended buffer size
00822                                         // Buffer size               $xx xx xx
00823                                         // Embedded info flag        %0000000x
00824                                         // Offset to next tag        $xx xx xx xx
00825                                         if (!$this->IsWithinBitRange($source_data_array['buffersize'], 24, false)) {
00826                                                 $this->errors[] = 'Invalid Buffer Size in '.$frame_name;
00827                                         } elseif (!$this->IsWithinBitRange($source_data_array['nexttagoffset'], 32, false)) {
00828                                                 $this->errors[] = 'Invalid Offset To Next Tag in '.$frame_name;
00829                                         } else {
00830                                                 $framedata .= getid3_lib::BigEndian2String($source_data_array['buffersize'], 3, false);
00831                                                 $flag .= '0000000';
00832                                                 $flag .= $source_data_array['flags']['embededinfo'] ? '1' : '0';
00833                                                 $framedata .= chr(bindec($flag));
00834                                                 $framedata .= getid3_lib::BigEndian2String($source_data_array['nexttagoffset'], 4, false);
00835                                         }
00836                                         break;
00837 
00838                                 case 'AENC':
00839                                         // 4.19  AENC Audio encryption
00840                                         // Owner identifier   <text string> $00
00841                                         // Preview start      $xx xx
00842                                         // Preview length     $xx xx
00843                                         // Encryption info    <binary data>
00844                                         if (!$this->IsWithinBitRange($source_data_array['previewstart'], 16, false)) {
00845                                                 $this->errors[] = 'Invalid Preview Start in '.$frame_name.' ('.$source_data_array['previewstart'].')';
00846                                         } elseif (!$this->IsWithinBitRange($source_data_array['previewlength'], 16, false)) {
00847                                                 $this->errors[] = 'Invalid Preview Length in '.$frame_name.' ('.$source_data_array['previewlength'].')';
00848                                         } else {
00849                                                 $framedata .= str_replace("\x00", '', $source_data_array['ownerid'])."\x00";
00850                                                 $framedata .= getid3_lib::BigEndian2String($source_data_array['previewstart'], 2, false);
00851                                                 $framedata .= getid3_lib::BigEndian2String($source_data_array['previewlength'], 2, false);
00852                                                 $framedata .= $source_data_array['encryptioninfo'];
00853                                         }
00854                                         break;
00855 
00856                                 case 'LINK':
00857                                         // 4.20  LINK Linked information
00858                                         // Frame identifier               $xx xx xx xx
00859                                         // URL                            <text string> $00
00860                                         // ID and additional data         <text string(s)>
00861                                         if (!getid3_id3v2::IsValidID3v2FrameName($source_data_array['frameid'], $this->majorversion)) {
00862                                                 $this->errors[] = 'Invalid Frame Identifier in '.$frame_name.' ('.$source_data_array['frameid'].')';
00863                                         } elseif (!$this->IsValidURL($source_data_array['data'], true, false)) {
00864                                                 //$this->errors[] = 'Invalid URL in '.$frame_name.' ('.$source_data_array['data'].')';
00865                                                 // probably should be an error, need to rewrite IsValidURL() to handle other encodings
00866                                                 $this->warnings[] = 'Invalid URL in '.$frame_name.' ('.$source_data_array['data'].')';
00867                                         } 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'] == '')) {
00868                                                 $this->errors[] = 'Content Descriptor must be specified as additional data for Frame Identifier of '.$source_data_array['frameid'].' in '.$frame_name;
00869                                         } elseif (($source_data_array['frameid'] == 'USER') && (getid3_id3v2::LanguageLookup($source_data_array['additionaldata'], true) == '')) {
00870                                                 $this->errors[] = 'Language must be specified as additional data for Frame Identifier of '.$source_data_array['frameid'].' in '.$frame_name;
00871                                         } elseif (($source_data_array['frameid'] == 'PRIV') && ($source_data_array['additionaldata'] == '')) {
00872                                                 $this->errors[] = 'Owner Identifier must be specified as additional data for Frame Identifier of '.$source_data_array['frameid'].' in '.$frame_name;
00873                                         } elseif ((($source_data_array['frameid'] == 'COMM') || ($source_data_array['frameid'] == 'SYLT') || ($source_data_array['frameid'] == 'USLT')) && ((getid3_id3v2::LanguageLookup(substr($source_data_array['additionaldata'], 0, 3), true) == '') || (substr($source_data_array['additionaldata'], 3) == ''))) {
00874                                                 $this->errors[] = 'Language followed by Content Descriptor must be specified as additional data for Frame Identifier of '.$source_data_array['frameid'].' in '.$frame_name;
00875                                         } else {
00876                                                 $framedata .= $source_data_array['frameid'];
00877                                                 $framedata .= str_replace("\x00", '', $source_data_array['data'])."\x00";
00878                                                 switch ($source_data_array['frameid']) {
00879                                                         case 'COMM':
00880                                                         case 'SYLT':
00881                                                         case 'USLT':
00882                                                         case 'PRIV':
00883                                                         case 'USER':
00884                                                         case 'AENC':
00885                                                         case 'APIC':
00886                                                         case 'GEOB':
00887                                                         case 'TXXX':
00888                                                                 $framedata .= $source_data_array['additionaldata'];
00889                                                                 break;
00890                                                         case 'ASPI':
00891                                                         case 'ETCO':
00892                                                         case 'EQU2':
00893                                                         case 'MCID':
00894                                                         case 'MLLT':
00895                                                         case 'OWNE':
00896                                                         case 'RVA2':
00897                                                         case 'RVRB':
00898                                                         case 'SYTC':
00899                                                         case 'IPLS':
00900                                                         case 'RVAD':
00901                                                         case 'EQUA':
00902                                                                 // no additional data required
00903                                                                 break;
00904                                                         case 'RBUF':
00905                                                                 if ($this->majorversion == 3) {
00906                                                                         // no additional data required
00907                                                                 } else {
00908                                                                         $this->errors[] = $source_data_array['frameid'].' is not a valid Frame Identifier in '.$frame_name.' (in ID3v2.'.$this->majorversion.')';
00909                                                                 }
00910 
00911                                                         default:
00912                                                                 if ((substr($source_data_array['frameid'], 0, 1) == 'T') || (substr($source_data_array['frameid'], 0, 1) == 'W')) {
00913                                                                         // no additional data required
00914                                                                 } else {
00915                                                                         $this->errors[] = $source_data_array['frameid'].' is not a valid Frame Identifier in '.$frame_name.' (in ID3v2.'.$this->majorversion.')';
00916                                                                 }
00917                                                                 break;
00918                                                 }
00919                                         }
00920                                         break;
00921 
00922                                 case 'POSS':
00923                                         // 4.21  POSS Position synchronisation frame (ID3v2.3+ only)
00924                                         // Time stamp format         $xx
00925                                         // Position                  $xx (xx ...)
00926                                         if (($source_data_array['timestampformat'] < 1) || ($source_data_array['timestampformat'] > 2)) {
00927                                                 $this->errors[] = 'Invalid Time Stamp Format in '.$frame_name.' ('.$source_data_array['timestampformat'].') (valid = 1 or 2)';
00928                                         } elseif (!$this->IsWithinBitRange($source_data_array['position'], 32, false)) {
00929                                                 $this->errors[] = 'Invalid Position in '.$frame_name.' ('.$source_data_array['position'].') (range = 0 to 4294967295)';
00930                                         } else {
00931                                                 $framedata .= chr($source_data_array['timestampformat']);
00932                                                 $framedata .= getid3_lib::BigEndian2String($source_data_array['position'], 4, false);
00933                                         }
00934                                         break;
00935 
00936                                 case 'USER':
00937                                         // 4.22  USER Terms of use (ID3v2.3+ only)
00938                                         // Text encoding        $xx
00939                                         // Language             $xx xx xx
00940                                         // The actual text      <text string according to encoding>
00941                                         $source_data_array['encodingid'] = (isset($source_data_array['encodingid']) ? $source_data_array['encodingid'] : $this->id3v2_default_encodingid);
00942                                         if (!$this->ID3v2IsValidTextEncoding($source_data_array['encodingid'])) {
00943                                                 $this->errors[] = 'Invalid Text Encoding in '.$frame_name.' ('.$source_data_array['encodingid'].')';
00944                                         } elseif (getid3_id3v2::LanguageLookup($source_data_array['language'], true) == '') {
00945                                                 $this->errors[] = 'Invalid Language in '.$frame_name.' ('.$source_data_array['language'].')';
00946                                         } else {
00947                                                 $framedata .= chr($source_data_array['encodingid']);
00948                                                 $framedata .= strtolower($source_data_array['language']);
00949                                                 $framedata .= $source_data_array['data'];
00950                                         }
00951                                         break;
00952 
00953                                 case 'OWNE':
00954                                         // 4.23  OWNE Ownership frame (ID3v2.3+ only)
00955                                         // Text encoding     $xx
00956                                         // Price paid        <text string> $00
00957                                         // Date of purch.    <text string>
00958                                         // Seller            <text string according to encoding>
00959                                         $source_data_array['encodingid'] = (isset($source_data_array['encodingid']) ? $source_data_array['encodingid'] : $this->id3v2_default_encodingid);
00960                                         if (!$this->ID3v2IsValidTextEncoding($source_data_array['encodingid'])) {
00961                                                 $this->errors[] = 'Invalid Text Encoding in '.$frame_name.' ('.$source_data_array['encodingid'].')';
00962                                         } elseif (!$this->IsANumber($source_data_array['pricepaid']['value'], false)) {
00963                                                 $this->errors[] = 'Invalid Price Paid in '.$frame_name.' ('.$source_data_array['pricepaid']['value'].')';
00964                                         } elseif (!$this->IsValidDateStampString($source_data_array['purchasedate'])) {
00965                                                 $this->errors[] = 'Invalid Date Of Purchase in '.$frame_name.' ('.$source_data_array['purchasedate'].') (format = YYYYMMDD)';
00966                                         } else {
00967                                                 $framedata .= chr($source_data_array['encodingid']);
00968                                                 $framedata .= str_replace("\x00", '', $source_data_array['pricepaid']['value'])."\x00";
00969                                                 $framedata .= $source_data_array['purchasedate'];
00970                                                 $framedata .= $source_data_array['seller'];
00971                                         }
00972                                         break;
00973 
00974                                 case 'COMR':
00975                                         // 4.24  COMR Commercial frame (ID3v2.3+ only)
00976                                         // Text encoding      $xx
00977                                         // Price string       <text string> $00
00978                                         // Valid until        <text string>
00979                                         // Contact URL        <text string> $00
00980                                         // Received as        $xx
00981                                         // Name of seller     <text string according to encoding> $00 (00)
00982                                         // Description        <text string according to encoding> $00 (00)
00983                                         // Picture MIME type  <string> $00
00984                                         // Seller logo        <binary data>
00985                                         $source_data_array['encodingid'] = (isset($source_data_array['encodingid']) ? $source_data_array['encodingid'] : $this->id3v2_default_encodingid);
00986                                         if (!$this->ID3v2IsValidTextEncoding($source_data_array['encodingid'])) {
00987                                                 $this->errors[] = 'Invalid Text Encoding in '.$frame_name.' ('.$source_data_array['encodingid'].')';
00988                                         } elseif (!$this->IsValidDateStampString($source_data_array['pricevaliduntil'])) {
00989                                                 $this->errors[] = 'Invalid Valid Until date in '.$frame_name.' ('.$source_data_array['pricevaliduntil'].') (format = YYYYMMDD)';
00990                                         } elseif (!$this->IsValidURL($source_data_array['contacturl'], false, true)) {
00991                                                 $this->errors[] = 'Invalid Contact URL in '.$frame_name.' ('.$source_data_array['contacturl'].') (allowed schemes: http, https, ftp, mailto)';
00992                                         } elseif (!$this->ID3v2IsValidCOMRreceivedAs($source_data_array['receivedasid'])) {
00993                                                 $this->errors[] = 'Invalid Received As byte in '.$frame_name.' ('.$source_data_array['contacturl'].') (range = 0 to 8)';
00994                                         } elseif (!$this->IsValidMIMEstring($source_data_array['mime'])) {
00995                                                 $this->errors[] = 'Invalid MIME Type in '.$frame_name.' ('.$source_data_array['mime'].')';
00996                                         } else {
00997                                                 $framedata .= chr($source_data_array['encodingid']);
00998                                                 unset($pricestring);
00999                                                 foreach ($source_data_array['price'] as $key => $val) {
01000                                                         if ($this->ID3v2IsValidPriceString($key.$val['value'])) {
01001                                                                 $pricestrings[] = $key.$val['value'];
01002                                                         } else {
01003                                                                 $this->errors[] = 'Invalid Price String in '.$frame_name.' ('.$key.$val['value'].')';
01004                                                         }
01005                                                 }
01006                                                 $framedata .= implode('/', $pricestrings);
01007                                                 $framedata .= $source_data_array['pricevaliduntil'];
01008                                                 $framedata .= str_replace("\x00", '', $source_data_array['contacturl'])."\x00";
01009                                                 $framedata .= chr($source_data_array['receivedasid']);
01010                                                 $framedata .= $source_data_array['sellername'].getid3_id3v2::TextEncodingTerminatorLookup($source_data_array['encodingid']);
01011                                                 $framedata .= $source_data_array['description'].getid3_id3v2::TextEncodingTerminatorLookup($source_data_array['encodingid']);
01012                                                 $framedata .= $source_data_array['mime']."\x00";
01013                                                 $framedata .= $source_data_array['logo'];
01014                                         }
01015                                         break;
01016 
01017                                 case 'ENCR':
01018                                         // 4.25  ENCR Encryption method registration (ID3v2.3+ only)
01019                                         // Owner identifier    <text string> $00
01020                                         // Method symbol       $xx
01021                                         // Encryption data     <binary data>
01022                                         if (!$this->IsWithinBitRange($source_data_array['methodsymbol'], 8, false)) {
01023                                                 $this->errors[] = 'Invalid Group Symbol in '.$frame_name.' ('.$source_data_array['methodsymbol'].') (range = 0 to 255)';
01024                                         } else {
01025                                                 $framedata .= str_replace("\x00", '', $source_data_array['ownerid'])."\x00";
01026                                                 $framedata .= ord($source_data_array['methodsymbol']);
01027                                                 $framedata .= $source_data_array['data'];
01028                                         }
01029                                         break;
01030 
01031                                 case 'GRID':
01032                                         // 4.26  GRID Group identification registration (ID3v2.3+ only)
01033                                         // Owner identifier      <text string> $00
01034                                         // Group symbol          $xx
01035                                         // Group dependent data  <binary data>
01036                                         if (!$this->IsWithinBitRange($source_data_array['groupsymbol'], 8, false)) {
01037                                                 $this->errors[] = 'Invalid Group Symbol in '.$frame_name.' ('.$source_data_array['groupsymbol'].') (range = 0 to 255)';
01038                                         } else {
01039                                                 $framedata .= str_replace("\x00", '', $source_data_array['ownerid'])."\x00";
01040                                                 $framedata .= ord($source_data_array['groupsymbol']);
01041                                                 $framedata .= $source_data_array['data'];
01042                                         }
01043                                         break;
01044 
01045                                 case 'PRIV':
01046                                         // 4.27  PRIV Private frame (ID3v2.3+ only)
01047                                         // Owner identifier      <text string> $00
01048                                         // The private data      <binary data>
01049                                         $framedata .= str_replace("\x00", '', $source_data_array['ownerid'])."\x00";
01050                                         $framedata .= $source_data_array['data'];
01051                                         break;
01052 
01053                                 case 'SIGN':
01054                                         // 4.28  SIGN Signature frame (ID3v2.4+ only)
01055                                         // Group symbol      $xx
01056                                         // Signature         <binary data>
01057                                         if (!$this->IsWithinBitRange($source_data_array['groupsymbol'], 8, false)) {
01058                                                 $this->errors[] = 'Invalid Group Symbol in '.$frame_name.' ('.$source_data_array['groupsymbol'].') (range = 0 to 255)';
01059                                         } else {
01060                                                 $framedata .= ord($source_data_array['groupsymbol']);
01061                                                 $framedata .= $source_data_array['data'];
01062                                         }
01063                                         break;
01064 
01065                                 case 'SEEK':
01066                                         // 4.29  SEEK Seek frame (ID3v2.4+ only)
01067                                         // Minimum offset to next tag       $xx xx xx xx
01068                                         if (!$this->IsWithinBitRange($source_data_array['data'], 32, false)) {
01069                                                 $this->errors[] = 'Invalid Minimum Offset in '.$frame_name.' ('.$source_data_array['data'].') (range = 0 to 4294967295)';
01070                                         } else {
01071                                                 $framedata .= getid3_lib::BigEndian2String($source_data_array['data'], 4, false);
01072                                         }
01073                                         break;
01074 
01075                                 case 'ASPI':
01076                                         // 4.30  ASPI Audio seek point index (ID3v2.4+ only)
01077                                         // Indexed data start (S)         $xx xx xx xx
01078                                         // Indexed data length (L)        $xx xx xx xx
01079                                         // Number of index points (N)     $xx xx
01080                                         // Bits per index point (b)       $xx
01081                                         //   Then for every index point the following data is included:
01082                                         // Fraction at index (Fi)          $xx (xx)
01083                                         if (!$this->IsWithinBitRange($source_data_array['datastart'], 32, false)) {
01084                                                 $this->errors[] = 'Invalid Indexed Data Start in '.$frame_name.' ('.$source_data_array['datastart'].') (range = 0 to 4294967295)';
01085                                         } elseif (!$this->IsWithinBitRange($source_data_array['datalength'], 32, false)) {
01086                                                 $this->errors[] = 'Invalid Indexed Data Length in '.$frame_name.' ('.$source_data_array['datalength'].') (range = 0 to 4294967295)';
01087                                         } elseif (!$this->IsWithinBitRange($source_data_array['indexpoints'], 16, false)) {
01088                                                 $this->errors[] = 'Invalid Number Of Index Points in '.$frame_name.' ('.$source_data_array['indexpoints'].') (range = 0 to 65535)';
01089                                         } elseif (!$this->IsWithinBitRange($source_data_array['bitsperpoint'], 8, false)) {
01090                                                 $this->errors[] = 'Invalid Bits Per Index Point in '.$frame_name.' ('.$source_data_array['bitsperpoint'].') (range = 0 to 255)';
01091                                         } elseif ($source_data_array['indexpoints'] != count($source_data_array['indexes'])) {
01092                                                 $this->errors[] = 'Number Of Index Points does not match actual supplied data in '.$frame_name;
01093                                         } else {
01094                                                 $framedata .= getid3_lib::BigEndian2String($source_data_array['datastart'], 4, false);
01095                                                 $framedata .= getid3_lib::BigEndian2String($source_data_array['datalength'], 4, false);
01096                                                 $framedata .= getid3_lib::BigEndian2String($source_data_array['indexpoints'], 2, false);
01097                                                 $framedata .= getid3_lib::BigEndian2String($source_data_array['bitsperpoint'], 1, false);
01098                                                 foreach ($source_data_array['indexes'] as $key => $val) {
01099                                                         $framedata .= getid3_lib::BigEndian2String($val, ceil($source_data_array['bitsperpoint'] / 8), false);
01100                                                 }
01101                                         }
01102                                         break;
01103 
01104                                 case 'RGAD':
01105                                         //   RGAD Replay Gain Adjustment
01106                                         //   http://privatewww.essex.ac.uk/~djmrob/replaygain/
01107                                         // Peak Amplitude                     $xx $xx $xx $xx
01108                                         // Radio Replay Gain Adjustment        %aaabbbcd %dddddddd
01109                                         // Audiophile Replay Gain Adjustment   %aaabbbcd %dddddddd
01110                                         //   a - name code
01111                                         //   b - originator code
01112                                         //   c - sign bit
01113                                         //   d - replay gain adjustment
01114 
01115                                         if (($source_data_array['track_adjustment'] > 51) || ($source_data_array['track_adjustment'] < -51)) {
01116                                                 $this->errors[] = 'Invalid Track Adjustment in '.$frame_name.' ('.$source_data_array['track_adjustment'].') (range = -51.0 to +51.0)';
01117                                         } elseif (($source_data_array['album_adjustment'] > 51) || ($source_data_array['album_adjustment'] < -51)) {
01118                                                 $this->errors[] = 'Invalid Album Adjustment in '.$frame_name.' ('.$source_data_array['album_adjustment'].') (range = -51.0 to +51.0)';
01119                                         } elseif (!$this->ID3v2IsValidRGADname($source_data_array['raw']['track_name'])) {
01120                                                 $this->errors[] = 'Invalid Track Name Code in '.$frame_name.' ('.$source_data_array['raw']['track_name'].') (range = 0 to 2)';
01121                                         } elseif (!$this->ID3v2IsValidRGADname($source_data_array['raw']['album_name'])) {
01122                                                 $this->errors[] = 'Invalid Album Name Code in '.$frame_name.' ('.$source_data_array['raw']['album_name'].') (range = 0 to 2)';
01123                                         } elseif (!$this->ID3v2IsValidRGADoriginator($source_data_array['raw']['track_originator'])) {
01124                                                 $this->errors[] = 'Invalid Track Originator Code in '.$frame_name.' ('.$source_data_array['raw']['track_originator'].') (range = 0 to 3)';
01125                                         } elseif (!$this->ID3v2IsValidRGADoriginator($source_data_array['raw']['album_originator'])) {
01126                                                 $this->errors[] = 'Invalid Album Originator Code in '.$frame_name.' ('.$source_data_array['raw']['album_originator'].') (range = 0 to 3)';
01127                                         } else {
01128                                                 $framedata .= getid3_lib::Float2String($source_data_array['peakamplitude'], 32);
01129                                                 $framedata .= getid3_lib::RGADgainString($source_data_array['raw']['track_name'], $source_data_array['raw']['track_originator'], $source_data_array['track_adjustment']);
01130                                                 $framedata .= getid3_lib::RGADgainString($source_data_array['raw']['album_name'], $source_data_array['raw']['album_originator'], $source_data_array['album_adjustment']);
01131                                         }
01132                                         break;
01133 
01134                                 default:
01135                                         if ($frame_name{0} == 'T') {
01136                                                 // 4.2. T???  Text information frames
01137                                                 // Text encoding                $xx
01138                                                 // Information                  <text string(s) according to encoding>
01139                                                 $source_data_array['encodingid'] = (isset($source_data_array['encodingid']) ? $source_data_array['encodingid'] : $this->id3v2_default_encodingid);
01140                                                 if (!$this->ID3v2IsValidTextEncoding($source_data_array['encodingid'])) {
01141                                                         $this->errors[] = 'Invalid Text Encoding in '.$frame_name.' ('.$source_data_array['encodingid'].') for ID3v2.'.$this->majorversion;
01142                                                 } else {
01143                                                         $framedata .= chr($source_data_array['encodingid']);
01144                                                         $framedata .= $source_data_array['data'];
01145                                                 }
01146                                         } elseif ($frame_name{0} == 'W') {
01147                                                 // 4.3. W???  URL link frames
01148                                                 // URL              <text string>
01149                                                 if (!$this->IsValidURL($source_data_array['data'], false, false)) {
01150                                                         //$this->errors[] = 'Invalid URL in '.$frame_name.' ('.$source_data_array['data'].')';
01151                                                         // probably should be an error, need to rewrite IsValidURL() to handle other encodings
01152                                                         $this->warnings[] = 'Invalid URL in '.$frame_name.' ('.$source_data_array['data'].')';
01153                                                 } else {
01154                                                         $framedata .= $source_data_array['data'];
01155                                                 }
01156                                         } else {
01157                                                 $this->errors[] = $frame_name.' not yet supported in $this->GenerateID3v2FrameData()';
01158                                         }
01159                                         break;
01160                         }
01161                 }
01162                 if (!empty($this->errors)) {
01163                         return false;
01164                 }
01165                 return $framedata;
01166         }
01167 
01168         function ID3v2FrameIsAllowed($frame_name, $source_data_array) {
01169                 static $PreviousFrames = array();
01170 
01171                 if ($frame_name === null) {
01172                         // if the writing functions are called multiple times, the static array needs to be
01173                         // cleared - this can be done by calling $this->ID3v2FrameIsAllowed(null, '')
01174                         $PreviousFrames = array();
01175                         return true;
01176                 }
01177 
01178                 if ($this->majorversion == 4) {
01179                         switch ($frame_name) {
01180                                 case 'UFID':
01181                                 case 'AENC':
01182                                 case 'ENCR':
01183                                 case 'GRID':
01184                                         if (!isset($source_data_array['ownerid'])) {
01185                                                 $this->errors[] = '[ownerid] not specified for '.$frame_name;
01186                                         } elseif (in_array($frame_name.$source_data_array['ownerid'], $PreviousFrames)) {
01187                                                 $this->errors[] = 'Only one '.$frame_name.' tag allowed with the same OwnerID ('.$source_data_array['ownerid'].')';
01188                                         } else {
01189                                                 $PreviousFrames[] = $frame_name.$source_data_array['ownerid'];
01190                                         }
01191                                         break;
01192 
01193                                 case 'TXXX':
01194                                 case 'WXXX':
01195                                 case 'RVA2':
01196                                 case 'EQU2':
01197                                 case 'APIC':
01198                                 case 'GEOB':
01199                                         if (!isset($source_data_array['description'])) {
01200                                                 $this->errors[] = '[description] not specified for '.$frame_name;
01201                                         } elseif (in_array($frame_name.$source_data_array['description'], $PreviousFrames)) {
01202                                                 $this->errors[] = 'Only one '.$frame_name.' tag allowed with the same Description ('.$source_data_array['description'].')';
01203                                         } else {
01204                                                 $PreviousFrames[] = $frame_name.$source_data_array['description'];
01205                                         }
01206                                         break;
01207 
01208                                 case 'USER':
01209                                         if (!isset($source_data_array['language'])) {
01210                                                 $this->errors[] = '[language] not specified for '.$frame_name;
01211                                         } elseif (in_array($frame_name.$source_data_array['language'], $PreviousFrames)) {
01212                                                 $this->errors[] = 'Only one '.$frame_name.' tag allowed with the same Language ('.$source_data_array['language'].')';
01213                                         } else {
01214                                                 $PreviousFrames[] = $frame_name.$source_data_array['language'];
01215                                         }
01216                                         break;
01217 
01218                                 case 'USLT':
01219                                 case 'SYLT':
01220                                 case 'COMM':
01221                                         if (!isset($source_data_array['language'])) {
01222                                                 $this->errors[] = '[language] not specified for '.$frame_name;
01223                                         } elseif (!isset($source_data_array['description'])) {
01224                                                 $this->errors[] = '[description] not specified for '.$frame_name;
01225                                         } elseif (in_array($frame_name.$source_data_array['language'].$source_data_array['description'], $PreviousFrames)) {
01226                                                 $this->errors[] = 'Only one '.$frame_name.' tag allowed with the same Language + Description ('.$source_data_array['language'].' + '.$source_data_array['description'].')';
01227                                         } else {
01228                                                 $PreviousFrames[] = $frame_name.$source_data_array['language'].$source_data_array['description'];
01229                                         }
01230                                         break;
01231 
01232                                 case 'POPM':
01233                                         if (!isset($source_data_array['email'])) {
01234                                                 $this->errors[] = '[email] not specified for '.$frame_name;
01235                                         } elseif (in_array($frame_name.$source_data_array['email'], $PreviousFrames)) {
01236                                                 $this->errors[] = 'Only one '.$frame_name.' tag allowed with the same Email ('.$source_data_array['email'].')';
01237                                         } else {
01238                                                 $PreviousFrames[] = $frame_name.$source_data_array['email'];
01239                                         }
01240                                         break;
01241 
01242                                 case 'IPLS':
01243                                 case 'MCDI':
01244                                 case 'ETCO':
01245                                 case 'MLLT':
01246                                 case 'SYTC':
01247                                 case 'RVRB':
01248                                 case 'PCNT':
01249                                 case 'RBUF':
01250                                 case 'POSS':
01251                                 case 'OWNE':
01252                                 case 'SEEK':
01253                                 case 'ASPI':
01254                                 case 'RGAD':
01255                                         if (in_array($frame_name, $PreviousFrames)) {
01256                                                 $this->errors[] = 'Only one '.$frame_name.' tag allowed';
01257                                         } else {
01258                                                 $PreviousFrames[] = $frame_name;
01259                                         }
01260                                         break;
01261 
01262                                 case 'LINK':
01263                                         // this isn't implemented quite right (yet) - it should check the target frame data for compliance
01264                                         // but right now it just allows one linked frame of each type, to be safe.
01265                                         if (!isset($source_data_array['frameid'])) {
01266                                                 $this->errors[] = '[frameid] not specified for '.$frame_name;
01267                                         } elseif (in_array($frame_name.$source_data_array['frameid'], $PreviousFrames)) {
01268                                                 $this->errors[] = 'Only one '.$frame_name.' tag allowed with the same FrameID ('.$source_data_array['frameid'].')';
01269                                         } elseif (in_array($source_data_array['frameid'], $PreviousFrames)) {
01270                                                 // no links to singleton tags
01271                                                 $this->errors[] = 'Cannot specify a '.$frame_name.' tag to a singleton tag that already exists ('.$source_data_array['frameid'].')';
01272                                         } else {
01273                                                 $PreviousFrames[] = $frame_name.$source_data_array['frameid']; // only one linked tag of this type
01274                                                 $PreviousFrames[] = $source_data_array['frameid'];             // no non-linked singleton tags of this type
01275                                         }
01276                                         break;
01277 
01278                                 case 'COMR':
01279                                         //   There may be more than one 'commercial frame' in a tag, but no two may be identical
01280                                         // Checking isn't implemented at all (yet) - just assumes that it's OK.
01281                                         break;
01282 
01283                                 case 'PRIV':
01284                                 case 'SIGN':
01285                                         if (!isset($source_data_array['ownerid'])) {
01286                                                 $this->errors[] = '[ownerid] not specified for '.$frame_name;
01287                                         } elseif (!isset($source_data_array['data'])) {
01288                                                 $this->errors[] = '[data] not specified for '.$frame_name;
01289                                         } elseif (in_array($frame_name.$source_data_array['ownerid'].$source_data_array['data'], $PreviousFrames)) {
01290                                                 $this->errors[] = 'Only one '.$frame_name.' tag allowed with the same OwnerID + Data ('.$source_data_array['ownerid'].' + '.$source_data_array['data'].')';
01291                                         } else {
01292                                                 $PreviousFrames[] = $frame_name.$source_data_array['ownerid'].$source_data_array['data'];
01293                                         }
01294                                         break;
01295 
01296                                 default:
01297                                         if (($frame_name{0} != 'T') && ($frame_name{0} != 'W')) {
01298                                                 $this->errors[] = 'Frame not allowed in ID3v2.'.$this->majorversion.': '.$frame_name;
01299                                         }
01300                                         break;
01301                         }
01302 
01303                 } elseif ($this->majorversion == 3) {
01304 
01305                         switch ($frame_name) {
01306                                 case 'UFID':
01307                                 case 'AENC':
01308                                 case 'ENCR':
01309                                 case 'GRID':
01310                                         if (!isset($source_data_array['ownerid'])) {
01311                                                 $this->errors[] = '[ownerid] not specified for '.$frame_name;
01312                                         } elseif (in_array($frame_name.$source_data_array['ownerid'], $PreviousFrames)) {
01313                                                 $this->errors[] = 'Only one '.$frame_name.' tag allowed with the same OwnerID ('.$source_data_array['ownerid'].')';
01314                                         } else {
01315                                                 $PreviousFrames[] = $frame_name.$source_data_array['ownerid'];
01316                                         }
01317                                         break;
01318 
01319                                 case 'TXXX':
01320                                 case 'WXXX':
01321                                 case 'APIC':
01322                                 case 'GEOB':
01323                                         if (!isset($source_data_array['description'])) {
01324                                                 $this->errors[] = '[description] not specified for '.$frame_name;
01325                                         } elseif (in_array($frame_name.$source_data_array['description'], $PreviousFrames)) {
01326                                                 $this->errors[] = 'Only one '.$frame_name.' tag allowed with the same Description ('.$source_data_array['description'].')';
01327                                         } else {
01328                                                 $PreviousFrames[] = $frame_name.$source_data_array['description'];
01329                                         }
01330                                         break;
01331 
01332                                 case 'USER':
01333                                         if (!isset($source_data_array['language'])) {
01334                                                 $this->errors[] = '[language] not specified for '.$frame_name;
01335                                         } elseif (in_array($frame_name.$source_data_array['language'], $PreviousFrames)) {
01336                                                 $this->errors[] = 'Only one '.$frame_name.' tag allowed with the same Language ('.$source_data_array['language'].')';
01337                                         } else {
01338                                                 $PreviousFrames[] = $frame_name.$source_data_array['language'];
01339                                         }
01340                                         break;
01341 
01342                                 case 'USLT':
01343                                 case 'SYLT':
01344                                 case 'COMM':
01345                                         if (!isset($source_data_array['language'])) {
01346                                                 $this->errors[] = '[language] not specified for '.$frame_name;
01347                                         } elseif (!isset($source_data_array['description'])) {
01348                                                 $this->errors[] = '[description] not specified for '.$frame_name;
01349                                         } elseif (in_array($frame_name.$source_data_array['language'].$source_data_array['description'], $PreviousFrames)) {
01350                                                 $this->errors[] = 'Only one '.$frame_name.' tag allowed with the same Language + Description ('.$source_data_array['language'].' + '.$source_data_array['description'].')';
01351                                         } else {
01352                                                 $PreviousFrames[] = $frame_name.$source_data_array['language'].$source_data_array['description'];
01353                                         }
01354                                         break;
01355 
01356                                 case 'POPM':
01357                                         if (!isset($source_data_array['email'])) {
01358                                                 $this->errors[] = '[email] not specified for '.$frame_name;
01359                                         } elseif (in_array($frame_name.$source_data_array['email'], $PreviousFrames)) {
01360                                                 $this->errors[] = 'Only one '.$frame_name.' tag allowed with the same Email ('.$source_data_array['email'].')';
01361                                         } else {
01362                                                 $PreviousFrames[] = $frame_name.$source_data_array['email'];
01363                                         }
01364                                         break;
01365 
01366                                 case 'IPLS':
01367                                 case 'MCDI':
01368                                 case 'ETCO':
01369                                 case 'MLLT':
01370                                 case 'SYTC':
01371                                 case 'RVAD':
01372                                 case 'EQUA':
01373                                 case 'RVRB':
01374                                 case 'PCNT':
01375                                 case 'RBUF':
01376                                 case 'POSS':
01377                                 case 'OWNE':
01378                                 case 'RGAD':
01379                                         if (in_array($frame_name, $PreviousFrames)) {
01380                                                 $this->errors[] = 'Only one '.$frame_name.' tag allowed';
01381                                         } else {
01382                                                 $PreviousFrames[] = $frame_name;
01383                                         }
01384                                         break;
01385 
01386                                 case 'LINK':
01387                                         // this isn't implemented quite right (yet) - it should check the target frame data for compliance
01388                                         // but right now it just allows one linked frame of each type, to be safe.
01389                                         if (!isset($source_data_array['frameid'])) {
01390                                                 $this->errors[] = '[frameid] not specified for '.$frame_name;
01391                                         } elseif (in_array($frame_name.$source_data_array['frameid'], $PreviousFrames)) {
01392                                                 $this->errors[] = 'Only one '.$frame_name.' tag allowed with the same FrameID ('.$source_data_array['frameid'].')';
01393                                         } elseif (in_array($source_data_array['frameid'], $PreviousFrames)) {
01394                                                 // no links to singleton tags
01395                                                 $this->errors[] = 'Cannot specify a '.$frame_name.' tag to a singleton tag that already exists ('.$source_data_array['frameid'].')';
01396                                         } else {
01397                                                 $PreviousFrames[] = $frame_name.$source_data_array['frameid']; // only one linked tag of this type
01398                                                 $PreviousFrames[] = $source_data_array['frameid'];             // no non-linked singleton tags of this type
01399                                         }
01400                                         break;
01401 
01402                                 case 'COMR':
01403                                         //   There may be more than one 'commercial frame' in a tag, but no two may be identical
01404                                         // Checking isn't implemented at all (yet) - just assumes that it's OK.
01405                                         break;
01406 
01407                                 case 'PRIV':
01408                                         if (!isset($source_data_array['ownerid'])) {
01409                                                 $this->errors[] = '[ownerid] not specified for '.$frame_name;
01410                                         } elseif (!isset($source_data_array['data'])) {
01411                                                 $this->errors[] = '[data] not specified for '.$frame_name;
01412                                         } elseif (in_array($frame_name.$source_data_array['ownerid'].$source_data_array['data'], $PreviousFrames)) {
01413                                                 $this->errors[] = 'Only one '.$frame_name.' tag allowed with the same OwnerID + Data ('.$source_data_array['ownerid'].' + '.$source_data_array['data'].')';
01414                                         } else {
01415                                                 $PreviousFrames[] = $frame_name.$source_data_array['ownerid'].$source_data_array['data'];
01416                                         }
01417                                         break;
01418 
01419                                 default:
01420                                         if (($frame_name{0} != 'T') && ($frame_name{0} != 'W')) {
01421                                                 $this->errors[] = 'Frame not allowed in ID3v2.'.$this->majorversion.': '.$frame_name;
01422                                         }
01423                                         break;
01424                         }
01425 
01426                 } elseif ($this->majorversion == 2) {
01427 
01428                         switch ($frame_name) {
01429                                 case 'UFI':
01430                                 case 'CRM':
01431                                 case 'CRA':
01432                                         if (!isset($source_data_array['ownerid'])) {
01433                                                 $this->errors[] = '[ownerid] not specified for '.$frame_name;
01434                                         } elseif (in_array($frame_name.$source_data_array['ownerid'], $PreviousFrames)) {
01435                                                 $this->errors[] = 'Only one '.$frame_name.' tag allowed with the same OwnerID ('.$source_data_array['ownerid'].')';
01436                                         } else {
01437                                                 $PreviousFrames[] = $frame_name.$source_data_array['ownerid'];
01438                                         }
01439                                         break;
01440 
01441                                 case 'TXX':
01442                                 case 'WXX':
01443                                 case 'PIC':
01444                                 case 'GEO':
01445                                         if (!isset($source_data_array['description'])) {
01446                                                 $this->errors[] = '[description] not specified for '.$frame_name;
01447                                         } elseif (in_array($frame_name.$source_data_array['description'], $PreviousFrames)) {
01448                                                 $this->errors[] = 'Only one '.$frame_name.' tag allowed with the same Description ('.$source_data_array['description'].')';
01449                                         } else {
01450                                                 $PreviousFrames[] = $frame_name.$source_data_array['description'];
01451                                         }
01452                                         break;
01453 
01454                                 case 'ULT':
01455                                 case 'SLT':
01456                                 case 'COM':
01457                                         if (!isset($source_data_array['language'])) {
01458                                                 $this->errors[] = '[language] not specified for '.$frame_name;
01459                                         } elseif (!isset($source_data_array['description'])) {
01460                                                 $this->errors[] = '[description] not specified for '.$frame_name;
01461                                         } elseif (in_array($frame_name.$source_data_array['language'].$source_data_array['description'], $PreviousFrames)) {
01462                                                 $this->errors[] = 'Only one '.$frame_name.' tag allowed with the same Language + Description ('.$source_data_array['language'].' + '.$source_data_array['description'].')';
01463                                         } else {
01464                                                 $PreviousFrames[] = $frame_name.$source_data_array['language'].$source_data_array['description'];
01465                                         }
01466                                         break;
01467 
01468                                 case 'POP':
01469                                         if (!isset($source_data_array['email'])) {
01470                                                 $this->errors[] = '[email] not specified for '.$frame_name;
01471                                         } elseif (in_array($frame_name.$source_data_array['email'], $PreviousFrames)) {
01472                                                 $this->errors[] = 'Only one '.$frame_name.' tag allowed with the same Email ('.$source_data_array['email'].')';
01473                                         } else {
01474                                                 $PreviousFrames[] = $frame_name.$source_data_array['email'];
01475                                         }
01476                                         break;
01477 
01478                                 case 'IPL':
01479                                 case 'MCI':
01480                                 case 'ETC':
01481                                 case 'MLL':
01482                                 case 'STC':
01483                                 case 'RVA':
01484                                 case 'EQU':
01485                                 case 'REV':
01486                                 case 'CNT':
01487                                 case 'BUF':
01488                                         if (in_array($frame_name, $PreviousFrames)) {
01489                                                 $this->errors[] = 'Only one '.$frame_name.' tag allowed';
01490                                         } else {
01491                                                 $PreviousFrames[] = $frame_name;
01492                                         }
01493                                         break;
01494 
01495                                 case 'LNK':
01496                                         // this isn't implemented quite right (yet) - it should check the target frame data for compliance
01497                                         // but right now it just allows one linked frame of each type, to be safe.
01498                                         if (!isset($source_data_array['frameid'])) {
01499                                                 $this->errors[] = '[frameid] not specified for '.$frame_name;
01500                                         } elseif (in_array($frame_name.$source_data_array['frameid'], $PreviousFrames)) {
01501                                                 $this->errors[] = 'Only one '.$frame_name.' tag allowed with the same FrameID ('.$source_data_array['frameid'].')';
01502                                         } elseif (in_array($source_data_array['frameid'], $PreviousFrames)) {
01503                                                 // no links to singleton tags
01504                                                 $this->errors[] = 'Cannot specify a '.$frame_name.' tag to a singleton tag that already exists ('.$source_data_array['frameid'].')';
01505                                         } else {
01506                                                 $PreviousFrames[] = $frame_name.$source_data_array['frameid']; // only one linked tag of this type
01507                                                 $PreviousFrames[] = $source_data_array['frameid'];             // no non-linked singleton tags of this type
01508                                         }
01509                                         break;
01510 
01511                                 default:
01512                                         if (($frame_name{0} != 'T') && ($frame_name{0} != 'W')) {
01513                                                 $this->errors[] = 'Frame not allowed in ID3v2.'.$this->majorversion.': '.$frame_name;
01514                                         }
01515                                         break;
01516                         }
01517                 }
01518 
01519                 if (!empty($this->errors)) {
01520                         return false;
01521                 }
01522                 return true;
01523         }
01524 
01525         function GenerateID3v2Tag($noerrorsonly=true) {
01526                 $this->ID3v2FrameIsAllowed(null, ''); // clear static array in case this isn't the first call to $this->GenerateID3v2Tag()
01527 
01528                 $tagstring = '';
01529                 if (is_array($this->tag_data)) {
01530                         foreach ($this->tag_data as $frame_name => $frame_rawinputdata) {
01531                                 foreach ($frame_rawinputdata as $irrelevantindex => $source_data_array) {
01532                                         if (getid3_id3v2::IsValidID3v2FrameName($frame_name, $this->majorversion)) {
01533                                                 unset($frame_length);
01534                                                 unset($frame_flags);
01535                                                 $frame_data = false;
01536                                                 if ($this->ID3v2FrameIsAllowed($frame_name, $source_data_array)) {
01537                                                         if ($frame_data = $this->GenerateID3v2FrameData($frame_name, $source_data_array)) {
01538                                                                 $FrameUnsynchronisation = false;
01539                                                                 if ($this->majorversion >= 4) {
01540                                                                         // frame-level unsynchronisation
01541                                                                         $unsynchdata = $frame_data;
01542                                                                         if ($this->id3v2_use_unsynchronisation) {
01543                                                                                 $unsynchdata = $this->Unsynchronise($frame_data);
01544                                                                         }
01545                                                                         if (strlen($unsynchdata) != strlen($frame_data)) {
01546                                                                                 // unsynchronisation needed
01547                                                                                 $FrameUnsynchronisation = true;
01548                                                                                 $frame_data = $unsynchdata;
01549                                                                                 if (isset($TagUnsynchronisation) && $TagUnsynchronisation === false) {
01550                                                                                         // only set to true if ALL frames are unsynchronised
01551                                                                                 } else {
01552                                                                                         $TagUnsynchronisation = true;
01553                                                                                 }
01554                                                                         } else {
01555                                                                                 if (isset($TagUnsynchronisation)) {
01556                                                                                         $TagUnsynchronisation = false;
01557                                                                                 }
01558                                                                         }
01559                                                                         unset($unsynchdata);
01560 
01561                                                                         $frame_length = getid3_lib::BigEndian2String(strlen($frame_data), 4, true);
01562                                                                 } else {
01563                                                                         $frame_length = getid3_lib::BigEndian2String(strlen($frame_data), 4, false);
01564                                                                 }
01565                                                                 $frame_flags  = $this->GenerateID3v2FrameFlags($this->ID3v2FrameFlagsLookupTagAlter($frame_name), $this->ID3v2FrameFlagsLookupFileAlter($frame_name), false, false, false, false, $FrameUnsynchronisation, false);
01566                                                         }
01567                                                 } else {
01568                                                         $this->errors[] = 'Frame "'.$frame_name.'" is NOT allowed';
01569                                                 }
01570                                                 if ($frame_data === false) {
01571                                                         $this->errors[] = '$this->GenerateID3v2FrameData() failed for "'.$frame_name.'"';
01572                                                         if ($noerrorsonly) {
01573                                                                 return false;
01574                                                         } else {
01575                                                                 unset($frame_name);
01576                                                         }
01577                                                 }
01578                                         } else {
01579                                                 // ignore any invalid frame names, including 'title', 'header', etc
01580                                                 $this->warnings[] = 'Ignoring invalid ID3v2 frame type: "'.$frame_name.'"';
01581                                                 unset($frame_name);
01582                                                 unset($frame_length);
01583                                                 unset($frame_flags);
01584                                                 unset($frame_data);
01585                                         }
01586                                         if (isset($frame_name) && isset($frame_length) && isset($frame_flags) && isset($frame_data)) {
01587                                                 $tagstring .= $frame_name.$frame_length.$frame_flags.$frame_data;
01588                                         }
01589                                 }
01590                         }
01591 
01592                         if (!isset($TagUnsynchronisation)) {
01593                                 $TagUnsynchronisation = false;
01594                         }
01595                         if (($this->majorversion <= 3) && $this->id3v2_use_unsynchronisation) {
01596                                 // tag-level unsynchronisation
01597                                 $unsynchdata = $this->Unsynchronise($tagstring);
01598                                 if (strlen($unsynchdata) != strlen($tagstring)) {
01599                                         // unsynchronisation needed
01600                                         $TagUnsynchronisation = true;
01601                                         $tagstring = $unsynchdata;
01602                                 }
01603                         }
01604 
01605                         while ($this->paddedlength < (strlen($tagstring) + getid3_id3v2::ID3v2HeaderLength($this->majorversion))) {
01606                                 $this->paddedlength += 1024;
01607                         }
01608 
01609                         $footer = false; // ID3v2 footers not yet supported in getID3()
01610                         if (!$footer && ($this->paddedlength > (strlen($tagstring) + getid3_id3v2::ID3v2HeaderLength($this->majorversion)))) {
01611                                 // pad up to $paddedlength bytes if unpadded tag is shorter than $paddedlength
01612                                 // "Furthermore it MUST NOT have any padding when a tag footer is added to the tag."
01613                                 $tagstring .= @str_repeat("\x00", $this->paddedlength - strlen($tagstring) - getid3_id3v2::ID3v2HeaderLength($this->majorversion));
01614                         }
01615                         if ($this->id3v2_use_unsynchronisation && (substr($tagstring, strlen($tagstring) - 1, 1) == "\xFF")) {
01616                                 // special unsynchronisation case:
01617                                 // if last byte == $FF then appended a $00
01618                                 $TagUnsynchronisation = true;
01619                                 $tagstring .= "\x00";
01620                         }
01621 
01622                         $tagheader  = 'ID3';
01623                         $tagheader .= chr($this->majorversion);
01624                         $tagheader .= chr($this->minorversion);
01625                         $tagheader .= $this->GenerateID3v2TagFlags(array('unsynchronisation'=>$TagUnsynchronisation));
01626                         $tagheader .= getid3_lib::BigEndian2String(strlen($tagstring), 4, true);
01627 
01628                         return $tagheader.$tagstring;
01629                 }
01630                 $this->errors[] = 'tag_data is not an array in GenerateID3v2Tag()';
01631                 return false;
01632         }
01633 
01634         function ID3v2IsValidPriceString($pricestring) {
01635                 if (getid3_id3v2::LanguageLookup(substr($pricestring, 0, 3), true) == '') {
01636                         return false;
01637                 } elseif (!$this->IsANumber(substr($pricestring, 3), true)) {
01638                         return false;
01639                 }
01640                 return true;
01641         }
01642 
01643         function ID3v2FrameFlagsLookupTagAlter($framename) {
01644                 // unfinished
01645                 switch ($framename) {
01646                         case 'RGAD':
01647                                 $allow = true;
01648                         default:
01649                                 $allow = false;
01650                                 break;
01651                 }
01652                 return $allow;
01653         }
01654 
01655         function ID3v2FrameFlagsLookupFileAlter($framename) {
01656                 // unfinished
01657                 switch ($framename) {
01658                         case 'RGAD':
01659                                 return false;
01660                                 break;
01661 
01662                         default:
01663                                 return false;
01664                                 break;
01665                 }
01666         }
01667 
01668         function ID3v2IsValidETCOevent($eventid) {
01669                 if (($eventid < 0) || ($eventid > 0xFF)) {
01670                         // outside range of 1 byte
01671                         return false;
01672                 } elseif (($eventid >= 0xF0) && ($eventid <= 0xFC)) {
01673                         // reserved for future use
01674                         return false;
01675                 } elseif (($eventid >= 0x17) && ($eventid <= 0xDF)) {
01676                         // reserved for future use
01677                         return false;
01678                 } elseif (($eventid >= 0x0E) && ($eventid <= 0x16) && ($this->majorversion == 2)) {
01679                         // not defined in ID3v2.2
01680                         return false;
01681                 } elseif (($eventid >= 0x15) && ($eventid <= 0x16) && ($this->majorversion == 3)) {
01682                         // not defined in ID3v2.3
01683                         return false;
01684                 }
01685                 return true;
01686         }
01687 
01688         function ID3v2IsValidSYLTtype($contenttype) {
01689                 if (($contenttype >= 0) && ($contenttype <= 8) && ($this->majorversion == 4)) {
01690                         return true;
01691                 } elseif (($contenttype >= 0) && ($contenttype <= 6) && ($this->majorversion == 3)) {
01692                         return true;
01693                 }
01694                 return false;
01695         }
01696 
01697         function ID3v2IsValidRVA2channeltype($channeltype) {
01698                 if (($channeltype >= 0) && ($channeltype <= 8) && ($this->majorversion == 4)) {
01699                         return true;
01700                 }
01701                 return false;
01702         }
01703 
01704         function ID3v2IsValidAPICpicturetype($picturetype) {
01705                 if (($picturetype >= 0) && ($picturetype <= 0x14) && ($this->majorversion >= 2) && ($this->majorversion <= 4)) {
01706                         return true;
01707                 }
01708                 return false;
01709         }
01710 
01711         function ID3v2IsValidAPICimageformat($imageformat) {
01712                 if ($imageformat == '-->') {
01713                         return true;
01714                 } elseif ($this->majorversion == 2) {
01715                         if ((strlen($imageformat) == 3) && ($imageformat == strtoupper($imageformat))) {
01716                                 return true;
01717                         }
01718                 } elseif (($this->majorversion == 3) || ($this->majorversion == 4)) {
01719                         if ($this->IsValidMIMEstring($imageformat)) {
01720                                 return true;
01721                         }
01722                 }
01723                 return false;
01724         }
01725 
01726         function ID3v2IsValidCOMRreceivedAs($receivedas) {
01727                 if (($this->majorversion >= 3) && ($receivedas >= 0) && ($receivedas <= 8)) {
01728                         return true;
01729                 }
01730                 return false;
01731         }
01732 
01733         function ID3v2IsValidRGADname($RGADname) {
01734                 if (($RGADname >= 0) && ($RGADname <= 2)) {
01735                         return true;
01736                 }
01737                 return false;
01738         }
01739 
01740         function ID3v2IsValidRGADoriginator($RGADoriginator) {
01741                 if (($RGADoriginator >= 0) && ($RGADoriginator <= 3)) {
01742                         return true;
01743                 }
01744                 return false;
01745         }
01746 
01747         function ID3v2IsValidTextEncoding($textencodingbyte) {
01748                 static $ID3v2IsValidTextEncoding_cache = array(
01749                         2 => array(true, true),
01750                         3 => array(true, true),
01751                         4 => array(true, true, true, true));
01752                 return isset($ID3v2IsValidTextEncoding_cache[$this->majorversion][$textencodingbyte]);
01753         }
01754 
01755         function Unsynchronise($data) {
01756                 // Whenever a false synchronisation is found within the tag, one zeroed
01757                 // byte is inserted after the first false synchronisation byte. The
01758                 // format of a correct sync that should be altered by ID3 encoders is as
01759                 // follows:
01760                 //      %11111111 111xxxxx
01761                 // And should be replaced with:
01762                 //      %11111111 00000000 111xxxxx
01763                 // This has the side effect that all $FF 00 combinations have to be
01764                 // altered, so they won't be affected by the decoding process. Therefore
01765                 // all the $FF 00 combinations have to be replaced with the $FF 00 00
01766                 // combination during the unsynchronisation.
01767 
01768                 $data = str_replace("\xFF\x00", "\xFF\x00\x00", $data);
01769                 $unsyncheddata = '';
01770                 $datalength = strlen($data);
01771                 for ($i = 0; $i < $datalength; $i++) {
01772                         $thischar = $data{$i};
01773                         $unsyncheddata .= $thischar;
01774                         if ($thischar == "\xFF") {
01775                                 $nextchar = ord($data{$i + 1});
01776                                 if (($nextchar & 0xE0) == 0xE0) {
01777                                         // previous byte = 11111111, this byte = 111?????
01778                                         $unsyncheddata .= "\x00";
01779                                 }
01780                         }
01781                 }
01782                 return $unsyncheddata;
01783         }
01784 
01785         function is_hash($var) {
01786                 // written by dev-nullØchristophe*vg
01787                 // taken from http://www.php.net/manual/en/function.array-merge-recursive.php
01788                 if (is_array($var)) {
01789                         $keys = array_keys($var);
01790                         $all_num = true;
01791                         for ($i = 0; $i < count($keys); $i++) {
01792                                 if (is_string($keys[$i])) {
01793                                         return true;
01794                                 }
01795                         }
01796                 }
01797                 return false;
01798         }
01799 
01800         function array_join_merge($arr1, $arr2) {
01801                 // written by dev-nullØchristophe*vg
01802                 // taken from http://www.php.net/manual/en/function.array-merge-recursive.php
01803                 if (is_array($arr1) && is_array($arr2)) {
01804                         // the same -> merge
01805                         $new_array = array();
01806 
01807                         if ($this->is_hash($arr1) && $this->is_hash($arr2)) {
01808                                 // hashes -> merge based on keys
01809                                 $keys = array_merge(array_keys($arr1), array_keys($arr2));
01810                                 foreach ($keys as $key) {
01811                                         $new_array[$key] = $this->array_join_merge(@$arr1[$key], @$arr2[$key]);
01812                                 }
01813                         } else {
01814                                 // two real arrays -> merge
01815                                 $new_array = array_reverse(array_unique(array_reverse(array_merge($arr1, $arr2))));
01816                         }
01817                         return $new_array;
01818                 } else {
01819                         // not the same ... take new one if defined, else the old one stays
01820                         return $arr2 ? $arr2 : $arr1;
01821                 }
01822         }
01823 
01824         function IsValidMIMEstring($mimestring) {
01825                 if ((strlen($mimestring) >= 3) && (strpos($mimestring, '/') > 0) && (strpos($mimestring, '/') < (strlen($mimestring) - 1))) {
01826                         return true;
01827                 }
01828                 return false;
01829         }
01830 
01831         function IsWithinBitRange($number, $maxbits, $signed=false) {
01832                 if ($signed) {
01833                         if (($number > (0 - pow(2, $maxbits - 1))) && ($number <= pow(2, $maxbits - 1))) {
01834                                 return true;
01835                         }
01836                 } else {
01837                         if (($number >= 0) && ($number <= pow(2, $maxbits))) {
01838                                 return true;
01839                         }
01840                 }
01841                 return false;
01842         }
01843 
01844         function safe_parse_url($url) {
01845                 $parts = @parse_url($url);
01846                 $parts['scheme'] = (isset($parts['scheme']) ? $parts['scheme'] : '');
01847                 $parts['host']   = (isset($parts['host'])   ? $parts['host']   : '');
01848                 $parts['user']   = (isset($parts['user'])   ? $parts['user']   : '');
01849                 $parts['pass']   = (isset($parts['pass'])   ? $parts['pass']   : '');
01850                 $parts['path']   = (isset($parts['path'])   ? $parts['path']   : '');
01851                 $parts['query']  = (isset($parts['query'])  ? $parts['query']  : '');
01852                 return $parts;
01853         }
01854 
01855         function IsValidURL($url, $allowUserPass=false) {
01856                 if ($url == '') {
01857                         return false;
01858                 }
01859                 if ($allowUserPass !== true) {
01860                         if (strstr($url, '@')) {
01861                                 // in the format http://user:pass@example.com  or http://user@example.com
01862                                 // but could easily be somebody incorrectly entering an email address in place of a URL
01863                                 return false;
01864                         }
01865                 }
01866                 if ($parts = $this->safe_parse_url($url)) {
01867                         if (($parts['scheme'] != 'http') && ($parts['scheme'] != 'https') && ($parts['scheme'] != 'ftp') && ($parts['scheme'] != 'gopher')) {
01868                                 return false;
01869                         } elseif (!eregi("^[[:alnum:]]([-.]?[0-9a-z])*\.[a-z]{2,3}$", $parts['host'], $regs) && !IsValidDottedIP($parts['host'])) {
01870                                 return false;
01871                         } elseif (!eregi("^([[:alnum:]-]|[\_])*$", $parts['user'], $regs)) {
01872                                 return false;
01873                         } elseif (!eregi("^([[:alnum:]-]|[\_])*$", $parts['pass'], $regs)) {
01874                                 return false;
01875                         } elseif (!eregi("^[[:alnum:]/_\.@~-]*$", $parts['path'], $regs)) {
01876                                 return false;
01877                         } elseif (!eregi("^[[:alnum:]?&=+:;_()%#/,\.-]*$", $parts['query'], $regs)) {
01878                                 return false;
01879                         } else {
01880                                 return true;
01881                         }
01882                 }
01883                 return false;
01884         }
01885 
01886         function ID3v2ShortFrameNameLookup($majorversion, $long_description) {
01887                 $long_description = str_replace(' ', '_', strtolower(trim($long_description)));
01888                 static $ID3v2ShortFrameNameLookup = array();
01889                 if (empty($ID3v2ShortFrameNameLookup)) {
01890 
01891                         // The following are unique to ID3v2.2
01892                         $ID3v2ShortFrameNameLookup[2]['comment']                                          = 'COM';
01893                         $ID3v2ShortFrameNameLookup[2]['album']                                            = 'TAL';
01894                         $ID3v2ShortFrameNameLookup[2]['beats_per_minute']                                 = 'TBP';
01895                         $ID3v2ShortFrameNameLookup[2]['composer']                                         = 'TCM';
01896                         $ID3v2ShortFrameNameLookup[2]['genre']                                            = 'TCO';
01897                         $ID3v2ShortFrameNameLookup[2]['copyright']                                        = 'TCR';
01898                         $ID3v2ShortFrameNameLookup[2]['encoded_by']                                       = 'TEN';
01899                         $ID3v2ShortFrameNameLookup[2]['language']                                         = 'TLA';
01900                         $ID3v2ShortFrameNameLookup[2]['length']                                           = 'TLE';
01901                         $ID3v2ShortFrameNameLookup[2]['original_artist']                                  = 'TOA';
01902                         $ID3v2ShortFrameNameLookup[2]['original_filename']                                = 'TOF';
01903                         $ID3v2ShortFrameNameLookup[2]['original_lyricist']                                = 'TOL';
01904                         $ID3v2ShortFrameNameLookup[2]['original_album_title']                             = 'TOT';
01905                         $ID3v2ShortFrameNameLookup[2]['artist']                                           = 'TP1';
01906                         $ID3v2ShortFrameNameLookup[2]['band']                                             = 'TP2';
01907                         $ID3v2ShortFrameNameLookup[2]['conductor']                                        = 'TP3';
01908                         $ID3v2ShortFrameNameLookup[2]['remixer']                                          = 'TP4';
01909                         $ID3v2ShortFrameNameLookup[2]['publisher']                                        = 'TPB';
01910                         $ID3v2ShortFrameNameLookup[2]['isrc']                                             = 'TRC';
01911                         $ID3v2ShortFrameNameLookup[2]['tracknumber']                                      = 'TRK';
01912                         $ID3v2ShortFrameNameLookup[2]['size']                                             = 'TSI';
01913                         $ID3v2ShortFrameNameLookup[2]['encoder_settings']                                 = 'TSS';
01914                         $ID3v2ShortFrameNameLookup[2]['description']                                      = 'TT1';
01915                         $ID3v2ShortFrameNameLookup[2]['title']                                            = 'TT2';
01916                         $ID3v2ShortFrameNameLookup[2]['subtitle']                                         = 'TT3';
01917                         $ID3v2ShortFrameNameLookup[2]['lyricist']                                         = 'TXT';
01918                         $ID3v2ShortFrameNameLookup[2]['user_text']                                        = 'TXX';
01919                         $ID3v2ShortFrameNameLookup[2]['year']                                             = 'TYE';
01920                         $ID3v2ShortFrameNameLookup[2]['unique_file_identifier']                           = 'UFI';
01921                         $ID3v2ShortFrameNameLookup[2]['unsynchronised_lyrics']                            = 'ULT';
01922                         $ID3v2ShortFrameNameLookup[2]['url_file']                                         = 'WAF';
01923                         $ID3v2ShortFrameNameLookup[2]['url_artist']                                       = 'WAR';
01924                         $ID3v2ShortFrameNameLookup[2]['url_source']                                       = 'WAS';
01925                         $ID3v2ShortFrameNameLookup[2]['copyright_information']                            = 'WCP';
01926                         $ID3v2ShortFrameNameLookup[2]['url_publisher']                                    = 'WPB';
01927                         $ID3v2ShortFrameNameLookup[2]['url_user']                                         = 'WXX';
01928 
01929                         // The following are common to ID3v2.3 and ID3v2.4
01930                         $ID3v2ShortFrameNameLookup[3]['audio_encryption']                                 = 'AENC';
01931                         $ID3v2ShortFrameNameLookup[3]['attached_picture']                                 = 'APIC';
01932                         $ID3v2ShortFrameNameLookup[3]['comment']                                          = 'COMM';
01933                         $ID3v2ShortFrameNameLookup[3]['commercial']                                       = 'COMR';
01934                         $ID3v2ShortFrameNameLookup[3]['encryption_method_registration']                   = 'ENCR';
01935                         $ID3v2ShortFrameNameLookup[3]['event_timing_codes']                               = 'ETCO';
01936                         $ID3v2ShortFrameNameLookup[3]['general_encapsulated_object']                      = 'GEOB';
01937                         $ID3v2ShortFrameNameLookup[3]['group_identification_registration']                = 'GRID';
01938                         $ID3v2ShortFrameNameLookup[3]['linked_information']                               = 'LINK';
01939                         $ID3v2ShortFrameNameLookup[3]['music_cd_identifier']                              = 'MCDI';
01940                         $ID3v2ShortFrameNameLookup[3]['mpeg_location_lookup_table']                       = 'MLLT';
01941                         $ID3v2ShortFrameNameLookup[3]['ownership']                                        = 'OWNE';
01942                         $ID3v2ShortFrameNameLookup[3]['play_counter']                                     = 'PCNT';
01943                         $ID3v2ShortFrameNameLookup[3]['popularimeter']                                    = 'POPM';
01944                         $ID3v2ShortFrameNameLookup[3]['position_synchronisation']                         = 'POSS';
01945                         $ID3v2ShortFrameNameLookup[3]['private']                                          = 'PRIV';
01946                         $ID3v2ShortFrameNameLookup[3]['recommended_buffer_size']                          = 'RBUF';
01947                         $ID3v2ShortFrameNameLookup[3]['reverb']                                           = 'RVRB';
01948                         $ID3v2ShortFrameNameLookup[3]['synchronised_lyrics']                              = 'SYLT';
01949                         $ID3v2ShortFrameNameLookup[3]['synchronised_tempo_codes']                         = 'SYTC';
01950                         $ID3v2ShortFrameNameLookup[3]['album']                                            = 'TALB';
01951                         $ID3v2ShortFrameNameLookup[3]['beats_per_minute']                                 = 'TBPM';
01952                         $ID3v2ShortFrameNameLookup[3]['composer']                                         = 'TCOM';
01953                         $ID3v2ShortFrameNameLookup[3]['genre']                                            = 'TCON';
01954                         $ID3v2ShortFrameNameLookup[3]['copyright']                                        = 'TCOP';
01955                         $ID3v2ShortFrameNameLookup[3]['playlist_delay']                                   = 'TDLY';
01956                         $ID3v2ShortFrameNameLookup[3]['encoded_by']                                       = 'TENC';
01957                         $ID3v2ShortFrameNameLookup[3]['lyricist']                                         = 'TEXT';
01958                         $ID3v2ShortFrameNameLookup[3]['file_type']                                        = 'TFLT';
01959                         $ID3v2ShortFrameNameLookup[3]['content_group_description']                        = 'TIT1';
01960                         $ID3v2ShortFrameNameLookup[3]['title']                                            = 'TIT2';
01961                         $ID3v2ShortFrameNameLookup[3]['subtitle']                                         = 'TIT3';
01962                         $ID3v2ShortFrameNameLookup[3]['initial_key']                                      = 'TKEY';
01963                         $ID3v2ShortFrameNameLookup[3]['language']                                         = 'TLAN';
01964                         $ID3v2ShortFrameNameLookup[3]['length']                                           = 'TLEN';
01965                         $ID3v2ShortFrameNameLookup[3]['media_type']                                       = 'TMED';
01966                         $ID3v2ShortFrameNameLookup[3]['original_album_title']                             = 'TOAL';
01967                         $ID3v2ShortFrameNameLookup[3]['original_filename']                                = 'TOFN';
01968                         $ID3v2ShortFrameNameLookup[3]['original_lyricist']                                = 'TOLY';
01969                         $ID3v2ShortFrameNameLookup[3]['original_artist']                                  = 'TOPE';
01970                         $ID3v2ShortFrameNameLookup[3]['file_owner']                                       = 'TOWN';
01971                         $ID3v2ShortFrameNameLookup[3]['artist']                                           = 'TPE1';
01972                         $ID3v2ShortFrameNameLookup[3]['band']                                             = 'TPE2';
01973                         $ID3v2ShortFrameNameLookup[3]['conductor']                                        = 'TPE3';
01974                         $ID3v2ShortFrameNameLookup[3]['remixer']                                          = 'TPE4';
01975                         $ID3v2ShortFrameNameLookup[3]['part_of_set']                                      = 'TPOS';
01976                         $ID3v2ShortFrameNameLookup[3]['publisher']                                        = 'TPUB';
01977                         $ID3v2ShortFrameNameLookup[3]['tracknumber']                                      = 'TRCK';
01978                         $ID3v2ShortFrameNameLookup[3]['internet_radio_station_name']                      = 'TRSN';
01979                         $ID3v2ShortFrameNameLookup[3]['internet_radio_station_owner']                     = 'TRSO';
01980                         $ID3v2ShortFrameNameLookup[3]['isrc']                                             = 'TSRC';
01981                         $ID3v2ShortFrameNameLookup[3]['encoder_settings']                                 = 'TSSE';
01982                         $ID3v2ShortFrameNameLookup[3]['user_text']                                        = 'TXXX';
01983                         $ID3v2ShortFrameNameLookup[3]['unique_file_identifier']                           = 'UFID';
01984                         $ID3v2ShortFrameNameLookup[3]['terms_of_use']                                     = 'USER';
01985                         $ID3v2ShortFrameNameLookup[3]['unsynchronised_lyrics']                            = 'USLT';
01986                         $ID3v2ShortFrameNameLookup[3]['commercial']                                       = 'WCOM';
01987                         $ID3v2ShortFrameNameLookup[3]['copyright_information']                            = 'WCOP';
01988                         $ID3v2ShortFrameNameLookup[3]['url_file']                                         = 'WOAF';
01989                         $ID3v2ShortFrameNameLookup[3]['url_artist']                                       = 'WOAR';
01990                         $ID3v2ShortFrameNameLookup[3]['url_source']                                       = 'WOAS';
01991                         $ID3v2ShortFrameNameLookup[3]['url_station']                                      = 'WORS';
01992                         $ID3v2ShortFrameNameLookup[3]['payment']                                          = 'WPAY';
01993                         $ID3v2ShortFrameNameLookup[3]['url_publisher']                                    = 'WPUB';
01994                         $ID3v2ShortFrameNameLookup[3]['url_user']                                         = 'WXXX';
01995 
01996                         // The above are common to ID3v2.3 and ID3v2.4
01997                         // so copy them to ID3v2.4 before adding specifics for 2.3 and 2.4
01998                         $ID3v2ShortFrameNameLookup[4] = $ID3v2ShortFrameNameLookup[3];
01999 
02000                         // The following are unique to ID3v2.3
02001                         $ID3v2ShortFrameNameLookup[3]['equalisation']                                     = 'EQUA';
02002                         $ID3v2ShortFrameNameLookup[3]['involved_people_list']                             = 'IPLS';
02003                         $ID3v2ShortFrameNameLookup[3]['relative_volume_adjustment']                       = 'RVAD';
02004                         $ID3v2ShortFrameNameLookup[3]['date']                                             = 'TDAT';
02005                         $ID3v2ShortFrameNameLookup[3]['time']                                             = 'TIME';
02006                         $ID3v2ShortFrameNameLookup[3]['original_release_year']                            = 'TORY';
02007                         $ID3v2ShortFrameNameLookup[3]['recording_dates']                                  = 'TRDA';
02008                         $ID3v2ShortFrameNameLookup[3]['size']                                             = 'TSIZ';
02009                         $ID3v2ShortFrameNameLookup[3]['year']                                             = 'TYER';
02010 
02011 
02012                         // The following are unique to ID3v2.4
02013                         $ID3v2ShortFrameNameLookup[4]['audio_seek_point_index']                           = 'ASPI';
02014                         $ID3v2ShortFrameNameLookup[4]['equalisation']                                     = 'EQU2';
02015                         $ID3v2ShortFrameNameLookup[4]['relative_volume_adjustment']                       = 'RVA2';
02016                         $ID3v2ShortFrameNameLookup[4]['seek']                                             = 'SEEK';
02017                         $ID3v2ShortFrameNameLookup[4]['signature']                                        = 'SIGN';
02018                         $ID3v2ShortFrameNameLookup[4]['encoding_time']                                    = 'TDEN';
02019                         $ID3v2ShortFrameNameLookup[4]['original_release_time']                            = 'TDOR';
02020                         $ID3v2ShortFrameNameLookup[4]['recording_time']                                   = 'TDRC';
02021                         $ID3v2ShortFrameNameLookup[4]['release_time']                                     = 'TDRL';
02022                         $ID3v2ShortFrameNameLookup[4]['tagging_time']                                     = 'TDTG';
02023                         $ID3v2ShortFrameNameLookup[4]['involved_people_list']                             = 'TIPL';
02024                         $ID3v2ShortFrameNameLookup[4]['musician_credits_list']                            = 'TMCL';
02025                         $ID3v2ShortFrameNameLookup[4]['mood']                                             = 'TMOO';
02026                         $ID3v2ShortFrameNameLookup[4]['produced_notice']                                  = 'TPRO';
02027                         $ID3v2ShortFrameNameLookup[4]['album_sort_order']                                 = 'TSOA';
02028                         $ID3v2ShortFrameNameLookup[4]['performer_sort_order']                             = 'TSOP';
02029                         $ID3v2ShortFrameNameLookup[4]['title_sort_order']                                 = 'TSOT';
02030                         $ID3v2ShortFrameNameLookup[4]['set_subtitle']                                     = 'TSST';
02031                 }
02032                 return @$ID3v2ShortFrameNameLookup[$majorversion][strtolower($long_description)];
02033 
02034         }
02035 
02036 }
02037 
02038 ?>

Generated on Fri Dec 13 2013 17:56:59 for ILIAS Release_3_9_x_branch .rev 46835 by  doxygen 1.7.1