[pLog-svn] r3790 - plog/trunk/class/gallery/getid3

mark at devel.lifetype.net mark at devel.lifetype.net
Mon Jul 24 16:56:08 GMT 2006


Author: mark
Date: 2006-07-24 16:56:08 +0000 (Mon, 24 Jul 2006)
New Revision: 3790

Added:
   plog/trunk/class/gallery/getid3/extension.cache.dbm.php
   plog/trunk/class/gallery/getid3/extension.cache.mysql.php
   plog/trunk/class/gallery/getid3/module.audio-video.flv.php
   plog/trunk/class/gallery/getid3/module.graphic.svg.php
   plog/trunk/class/gallery/getid3/write.apetag.php
   plog/trunk/class/gallery/getid3/write.id3v1.php
   plog/trunk/class/gallery/getid3/write.id3v2.php
   plog/trunk/class/gallery/getid3/write.lyrics3.php
   plog/trunk/class/gallery/getid3/write.metaflac.php
   plog/trunk/class/gallery/getid3/write.php
   plog/trunk/class/gallery/getid3/write.real.php
   plog/trunk/class/gallery/getid3/write.vorbiscomment.php
Modified:
   plog/trunk/class/gallery/getid3/getid3.lib.php
   plog/trunk/class/gallery/getid3/getid3.php
   plog/trunk/class/gallery/getid3/module.archive.gzip.php
   plog/trunk/class/gallery/getid3/module.archive.tar.php
   plog/trunk/class/gallery/getid3/module.archive.zip.php
   plog/trunk/class/gallery/getid3/module.audio-video.asf.php
   plog/trunk/class/gallery/getid3/module.audio-video.quicktime.php
   plog/trunk/class/gallery/getid3/module.audio-video.riff.php
   plog/trunk/class/gallery/getid3/module.audio.aac.php
   plog/trunk/class/gallery/getid3/module.audio.flac.php
   plog/trunk/class/gallery/getid3/module.audio.midi.php
   plog/trunk/class/gallery/getid3/module.audio.mp3.php
   plog/trunk/class/gallery/getid3/module.audio.shorten.php
   plog/trunk/class/gallery/getid3/module.misc.iso.php
   plog/trunk/class/gallery/getid3/module.tag.id3v2.php
Log:
Upgrade getID3 to 1.7.7

Added: plog/trunk/class/gallery/getid3/extension.cache.dbm.php
===================================================================
--- plog/trunk/class/gallery/getid3/extension.cache.dbm.php	2006-07-24 16:40:16 UTC (rev 3789)
+++ plog/trunk/class/gallery/getid3/extension.cache.dbm.php	2006-07-24 16:56:08 UTC (rev 3790)
@@ -0,0 +1,222 @@
+<?php
+/////////////////////////////////////////////////////////////////
+/// getID3() by James Heinrich <info at getid3.org>               //
+//  available at http://getid3.sourceforge.net                 //
+//            or http://www.getid3.org                         //
+/////////////////////////////////////////////////////////////////
+//                                                             //
+// extension.cache.dbm.php - part of getID3()                  //
+// Please see readme.txt for more information                  //
+//                                                            ///
+/////////////////////////////////////////////////////////////////
+//                                                             //
+// This extension written by Allan Hansen <ahØartemis*dk>      //
+//                                                            ///
+/////////////////////////////////////////////////////////////////
+
+
+/**
+* This is a caching extension for getID3(). It works the exact same
+* way as the getID3 class, but return cached information very fast
+*
+* Example:
+*
+*    Normal getID3 usage (example):
+*
+*       require_once 'getid3/getid3.php';
+*       $getID3 = new getID3;
+*       $getID3->encoding = 'UTF-8';
+*       $info1 = $getID3->analyze('file1.flac');
+*       $info2 = $getID3->analyze('file2.wv');
+*
+*    getID3_cached usage:
+*
+*       require_once 'getid3/getid3.php';
+*       require_once 'getid3/getid3/extension.cache.dbm.php';
+*       $getID3 = new getID3_cached('db3', '/tmp/getid3_cache.dbm',
+*                                          '/tmp/getid3_cache.lock');
+*       $getID3->encoding = 'UTF-8';
+*       $info1 = $getID3->analyze('file1.flac');
+*       $info2 = $getID3->analyze('file2.wv');
+*
+*
+* Supported Cache Types
+*
+*   SQL Databases:          (use extension.cache.mysql)
+*
+*   cache_type          cache_options
+*   -------------------------------------------------------------------
+*   mysql               host, database, username, password
+*
+*
+*   DBM-Style Databases:    (this extension)
+*
+*   cache_type          cache_options
+*   -------------------------------------------------------------------
+*   gdbm                dbm_filename, lock_filename
+*   ndbm                dbm_filename, lock_filename
+*   db2                 dbm_filename, lock_filename
+*   db3                 dbm_filename, lock_filename
+*   db4                 dbm_filename, lock_filename  (PHP5 required)
+*
+*   PHP must have write access to both dbm_filename and lock_filename.
+*
+*
+* Recommended Cache Types
+*
+*   Infrequent updates, many reads      any DBM
+*   Frequent updates                    mysql
+*/
+
+
+class getID3_cached_dbm extends getID3
+{
+
+	// public: constructor - see top of this file for cache type and cache_options
+	function getID3_cached_dbm($cache_type, $dbm_filename, $lock_filename) {
+
+		// Check for dba extension
+		if (!extension_loaded('dba')) {
+			die('PHP is not compiled with dba support, required to use DBM style cache.');
+		}
+
+		// Check for specific dba driver
+		if (function_exists('dba_handlers')) {  // PHP 4.3.0+
+			if (!in_array('db3', dba_handlers())) {
+				die('PHP is not compiled --with '.$cache_type.' support, required to use DBM style cache.');
+			}
+		}
+		else { // PHP <= 4.2.3
+			ob_start(); // nasty, buy the only way to check...
+			phpinfo();
+			$contents = ob_get_contents();
+			ob_end_clean();
+			if (!strstr($contents, $cache_type)) {
+				die('PHP is not compiled --with '.$cache_type.' support, required to use DBM style cache.');
+			}
+		}
+
+		// Create lock file if needed
+		if (!file_exists($lock_filename)) {
+			if (!touch($lock_filename)) {
+				die('failed to create lock file: ' . $lock_filename);
+			}
+		}
+
+		// Open lock file for writing
+		if (!is_writeable($lock_filename)) {
+			die('lock file: ' . $lock_filename . ' is not writable');
+		}
+		$this->lock = fopen($lock_filename, 'w');
+
+		// Acquire exclusive write lock to lock file
+		flock($this->lock, LOCK_EX);
+
+		// Create dbm-file if needed
+		if (!file_exists($dbm_filename)) {
+			if (!touch($dbm_filename)) {
+				die('failed to create dbm file: ' . $dbm_filename);
+			}
+		}
+
+		// Try to open dbm file for writing
+		$this->dba = @dba_open($dbm_filename, 'w', $cache_type);
+		if (!$this->dba) {
+
+			// Failed - create new dbm file
+			$this->dba = dba_open($dbm_filename, 'n', $cache_type);
+
+			if (!$this->dba) {
+				die('failed to create dbm file: ' . $dbm_filename);
+			}
+
+			// Insert getID3 version number
+			dba_insert(GETID3_VERSION, GETID3_VERSION, $this->dba);
+		}
+
+		// Init misc values
+		$this->cache_type   = $cache_type;
+		$this->dbm_filename = $dbm_filename;
+
+		// Register destructor
+		register_shutdown_function(array($this, '__destruct'));
+
+		// Check version number and clear cache if changed
+		if (dba_fetch(GETID3_VERSION, $this->dba) != GETID3_VERSION) {
+			$this->clear_cache();
+		}
+
+		parent::getID3();
+	}
+
+
+
+	// public: destuctor
+	function __destruct() {
+
+		// Close dbm file
+		@dba_close($this->dba);
+
+		// Release exclusive lock
+		@flock($this->lock, LOCK_UN);
+
+		// Close lock file
+		@fclose($this->lock);
+	}
+
+
+
+	// public: clear cache
+	function clear_cache() {
+
+		// Close dbm file
+		dba_close($this->dba);
+
+		// Create new dbm file
+		$this->dba = dba_open($this->dbm_filename, 'n', $this->cache_type);
+
+		if (!$this->dba) {
+			die('failed to clear cache/recreate dbm file: ' . $this->dbm_filename);
+		}
+
+		// Insert getID3 version number
+		dba_insert(GETID3_VERSION, GETID3_VERSION, $this->dba);
+
+		// Reregister shutdown function
+		register_shutdown_function(array($this, '__destruct'));
+	}
+
+
+
+	// public: analyze file
+	function analyze($filename) {
+
+		if (file_exists($filename)) {
+
+			// Calc key     filename::mod_time::size    - should be unique
+			$key = $filename . '::' . filemtime($filename) . '::' . filesize($filename);
+
+			// Loopup key
+			$result = dba_fetch($key, $this->dba);
+
+			// Hit
+			if ($result !== false) {
+				return unserialize($result);
+			}
+		}
+
+		// Miss
+		$result = parent::analyze($filename);
+
+		// Save result
+		if (file_exists($filename)) {
+			dba_insert($key, serialize($result), $this->dba);
+		}
+
+		return $result;
+	}
+
+}
+
+
+?>
\ No newline at end of file

Added: plog/trunk/class/gallery/getid3/extension.cache.mysql.php
===================================================================
--- plog/trunk/class/gallery/getid3/extension.cache.mysql.php	2006-07-24 16:40:16 UTC (rev 3789)
+++ plog/trunk/class/gallery/getid3/extension.cache.mysql.php	2006-07-24 16:56:08 UTC (rev 3790)
@@ -0,0 +1,171 @@
+<?php
+/////////////////////////////////////////////////////////////////
+/// getID3() by James Heinrich <info at getid3.org>               //
+//  available at http://getid3.sourceforge.net                 //
+//            or http://www.getid3.org                         //
+/////////////////////////////////////////////////////////////////
+//                                                             //
+// extension.cache.mysql.php - part of getID3()                //
+// Please see readme.txt for more information                  //
+//                                                            ///
+/////////////////////////////////////////////////////////////////
+//                                                             //
+// This extension written by Allan Hansen <ahØartemis*dk>      //
+//                                                            ///
+/////////////////////////////////////////////////////////////////
+
+
+/**
+* This is a caching extension for getID3(). It works the exact same
+* way as the getID3 class, but return cached information very fast
+*
+* Example:  (see also demo.cache.mysql.php in /demo/)
+*
+*    Normal getID3 usage (example):
+*
+*       require_once 'getid3/getid3.php';
+*       $getID3 = new getID3;
+*       $getID3->encoding = 'UTF-8';
+*       $info1 = $getID3->analyze('file1.flac');
+*       $info2 = $getID3->analyze('file2.wv');
+*
+*    getID3_cached usage:
+*
+*       require_once 'getid3/getid3.php';
+*       require_once 'getid3/getid3/extension.cache.mysql.php';
+*       $getID3 = new getID3_cached_mysql('localhost', 'database',
+*                                         'username', 'password');
+*       $getID3->encoding = 'UTF-8';
+*       $info1 = $getID3->analyze('file1.flac');
+*       $info2 = $getID3->analyze('file2.wv');
+*
+*
+* Supported Cache Types    (this extension)
+*
+*   SQL Databases:
+*
+*   cache_type          cache_options
+*   -------------------------------------------------------------------
+*   mysql               host, database, username, password
+*
+*
+*   DBM-Style Databases:    (use extension.cache.dbm)
+*
+*   cache_type          cache_options
+*   -------------------------------------------------------------------
+*   gdbm                dbm_filename, lock_filename
+*   ndbm                dbm_filename, lock_filename
+*   db2                 dbm_filename, lock_filename
+*   db3                 dbm_filename, lock_filename
+*   db4                 dbm_filename, lock_filename  (PHP5 required)
+*
+*   PHP must have write access to both dbm_filename and lock_filename.
+*
+*
+* Recommended Cache Types
+*
+*   Infrequent updates, many reads      any DBM
+*   Frequent updates                    mysql
+*/
+
+
+class getID3_cached_mysql extends getID3
+{
+
+	// private vars
+	var $cursor;
+	var $connection;
+
+
+	// public: constructor - see top of this file for cache type and cache_options
+	function getID3_cached_mysql($host, $database, $username, $password) {
+
+		// Check for mysql support
+		if (!function_exists('mysql_pconnect')) {
+			die('PHP not compiled with mysql support.');
+		}
+
+		// Connect to database
+		$this->connection = mysql_pconnect($host, $username, $password);
+		if (!$this->connection) {
+			die('mysql_pconnect() failed - check permissions and spelling.');
+		}
+
+		// Select database
+		if (!mysql_select_db($database, $this->connection)) {
+			die('Cannot use database '.$database);
+		}
+
+		// Create cache table if not exists
+		$this->create_table();
+
+		// Check version number and clear cache if changed
+		$this->cursor = mysql_query("SELECT `value` FROM `getid3_cache` WHERE (`filename` = '".GETID3_VERSION."') AND (`filesize` = '-1') AND (`filetime` = '-1') AND (`analyzetime` = '-1')", $this->connection);
+		list($version) = @mysql_fetch_array($this->cursor);
+		if ($version != GETID3_VERSION) {
+			$this->clear_cache();
+		}
+
+		parent::getID3();
+	}
+
+
+
+	// public: clear cache
+	function clear_cache() {
+
+		$this->cursor = mysql_query("DELETE FROM `getid3_cache`", $this->connection);
+		$this->cursor = mysql_query("INSERT INTO `getid3_cache` VALUES ('".GETID3_VERSION."', -1, -1, -1, '".GETID3_VERSION."')", $this->connection);
+	}
+
+
+
+	// public: analyze file
+	function analyze($filename) {
+
+		if (file_exists($filename)) {
+
+			// Short-hands
+			$filetime = filemtime($filename);
+			$filesize = filesize($filename);
+			$filenam2 = mysql_escape_string($filename);
+
+			// Loopup file
+			$this->cursor = mysql_query("SELECT `value` FROM `getid3_cache` WHERE (`filename`='".$filenam2."') AND (`filesize`='".$filesize."') AND (`filetime`='".$filetime."')", $this->connection);
+			list($result) = @mysql_fetch_array($this->cursor);
+
+			// Hit
+			if ($result) {
+				return unserialize($result);
+			}
+		}
+
+		// Miss
+		$result = parent::analyze($filename);
+
+		// Save result
+		if (file_exists($filename)) {
+			$res2 = mysql_escape_string(serialize($result));
+			$this->cursor = mysql_query("INSERT INTO `getid3_cache` (`filename`, `filesize`, `filetime`, `analyzetime`, `value`) VALUES ('".$filenam2."', '".$filesize."', '".$filetime."', '".time()."', '".$res2."')", $this->connection);
+		}
+		return $result;
+	}
+
+
+
+	// private: (re)create sql table
+	function create_table($drop = false) {
+
+		$this->cursor = mysql_query("CREATE TABLE IF NOT EXISTS `getid3_cache` (
+			`filename` VARCHAR(255) NOT NULL DEFAULT '',
+			`filesize` INT(11) NOT NULL DEFAULT '0',
+			`filetime` INT(11) NOT NULL DEFAULT '0',
+			`analyzetime` INT(11) NOT NULL DEFAULT '0',
+			`value` TEXT NOT NULL,
+			PRIMARY KEY (`filename`,`filesize`,`filetime`)) TYPE=MyISAM", $this->connection);
+		echo mysql_error($this->connection);
+	}
+}
+
+
+?>
\ No newline at end of file

Modified: plog/trunk/class/gallery/getid3/getid3.lib.php
===================================================================
--- plog/trunk/class/gallery/getid3/getid3.lib.php	2006-07-24 16:40:16 UTC (rev 3789)
+++ plog/trunk/class/gallery/getid3/getid3.lib.php	2006-07-24 16:56:08 UTC (rev 3790)
@@ -7,54 +7,10 @@
 //                                                             //
 // getid3.lib.php - part of getID3()                           //
 // See readme.txt for more details                             //
-//                                                             //
-/////////////////////////////////////////////////////////////////
-// getid3_lib::GetURLImageSize( $urlpic ) determines the       //
-// dimensions of local/remote URL pictures.                    //
-// returns array with ($width, $height, $type)                 //
-//                                                             //
-// Thanks to: Oyvind Hallsteinsen aka Gosub / ELq -            //
-// gosubØelq*org  for the original size determining code       //
-//                                                             //
-// PHP Hack by Filipe Laborde-Basto Oct 21/2000                //
-// FREELY DISTRIBUTABLE -- use at your sole discretion! :)     //
-// Enjoy. (Not to be sold in commercial packages though,       //
-// keep it free!) Feel free to contact me at filØrezox*com     //
-// (http://www.rezox.com)                                      //
-//                                                             //
-// Modified by James Heinrich <getid3Øusers*sourceforge*net>   //
-// June 1, 2001 - created GetDataImageSize($imgData) by        //
-// seperating the fopen() stuff to GetURLImageSize($urlpic)    //
-// which then calls GetDataImageSize($imgData). The idea being //
-// you can call GetDataImageSize($imgData) with image data     //
-// from a database etc.                                        //
 //                                                            ///
 /////////////////////////////////////////////////////////////////
 
 
-define('GETID3_GIF_SIG',     "\x47\x49\x46");  // 'GIF'
-define('GETID3_PNG_SIG',     "\x89\x50\x4E\x47\x0D\x0A\x1A\x0A");
-define('GETID3_JPG_SIG',     "\xFF\xD8\xFF");
-define('GETID3_JPG_SOS',     "\xDA"); // Start Of Scan - image data start
-define('GETID3_JPG_SOF0',    "\xC0"); // Start Of Frame N
-define('GETID3_JPG_SOF1',    "\xC1"); // N indicates which compression process
-define('GETID3_JPG_SOF2',    "\xC2"); // Only SOF0-SOF2 are now in common use
-define('GETID3_JPG_SOF3',    "\xC3");
-// NB: codes C4 and CC are *not* SOF markers
-define('GETID3_JPG_SOF5',    "\xC5");
-define('GETID3_JPG_SOF6',    "\xC6");
-define('GETID3_JPG_SOF7',    "\xC7");
-define('GETID3_JPG_SOF9',    "\xC9");
-define('GETID3_JPG_SOF10',   "\xCA");
-define('GETID3_JPG_SOF11',   "\xCB");
-// NB: codes C4 and CC are *not* SOF markers
-define('GETID3_JPG_SOF13',   "\xCD");
-define('GETID3_JPG_SOF14',   "\xCE");
-define('GETID3_JPG_SOF15',   "\xCF");
-define('GETID3_JPG_EOI',     "\xD9"); // End Of Image (end of datastream)
-
-
-
 class getid3_lib
 {
 
@@ -568,7 +524,7 @@
 					die(implode(' and ', $RequiredFiles).' are required in '.GETID3_HELPERAPPSDIR.' for getid3_lib::md5_file() to function under Windows in PHP < v4.2.0');
 				}
 			}
-			$commandline = GETID3_HELPERAPPSDIR.'md5sum.exe "'.str_replace('/', GETID3_OS_DIRSLASH, $file).'"';
+			$commandline = GETID3_HELPERAPPSDIR.'md5sum.exe "'.str_replace('/', DIRECTORY_SEPARATOR, $file).'"';
 			if (ereg("^[\\]?([0-9a-f]{32})", strtolower(`$commandline`), $r)) {
 				return $r[1];
 			}
@@ -603,14 +559,14 @@
 					die(implode(' and ', $RequiredFiles).' are required in '.GETID3_HELPERAPPSDIR.' for getid3_lib::sha1_file() to function under Windows in PHP < v4.3.0');
 				}
 			}
-			$commandline = GETID3_HELPERAPPSDIR.'sha1sum.exe "'.str_replace('/', GETID3_OS_DIRSLASH, $file).'"';
+			$commandline = GETID3_HELPERAPPSDIR.'sha1sum.exe "'.str_replace('/', DIRECTORY_SEPARATOR, $file).'"';
 			if (ereg("^sha1=([0-9a-f]{40})", strtolower(`$commandline`), $r)) {
 				return $r[1];
 			}
 
 		} else {
 
-			$commandline = 'sha1sum "'.$file.'"';
+			$commandline = 'sha1sum '.escapeshellarg($file).'';
 			if (ereg("^([0-9a-f]{40})[ \t\n\r]", strtolower(`$commandline`), $r)) {
 				return $r[1];
 			}
@@ -661,14 +617,14 @@
 						break;
 					}
 				}
-				$commandline  = GETID3_HELPERAPPSDIR.'head.exe -c '.$end.' "'.str_replace('/', GETID3_OS_DIRSLASH, $file).'" | ';
+				$commandline  = GETID3_HELPERAPPSDIR.'head.exe -c '.$end.' "'.escapeshellarg(str_replace('/', DIRECTORY_SEPARATOR, $file)).'" | ';
 				$commandline .= GETID3_HELPERAPPSDIR.'tail.exe -c '.$size.' | ';
 				$commandline .= GETID3_HELPERAPPSDIR.$windows_call;
 
 			} else {
 
-				$commandline  = 'head -c '.$end.' "'.$file.'" | ';
-				$commandline .= 'tail -c '.$size.' | ';
+				$commandline  = 'head -c'.$end.' '.escapeshellarg($file).' | ';
+				$commandline .= 'tail -c'.$size.' | ';
 				$commandline .= $unix_call;
 
 			}
@@ -1211,84 +1167,19 @@
 	}
 
 
-	function GetURLImageSize($urlpic) {
-		if ($fd = @fopen($urlpic, 'rb')){
-			$imgData = fread($fd, filesize($urlpic));
-			fclose($fd);
-			return getid3_lib::GetDataImageSize($imgData);
+	function GetDataImageSize($imgData) {
+		$GetDataImageSize = false;
+		if ($tempfilename = tempnam('*', 'getID3')) {
+			if ($tmp = @fopen($tempfilename, 'wb')) {
+				fwrite($tmp, $imgData);
+				fclose($tmp);
+				$GetDataImageSize = @GetImageSize($tempfilename);
+			}
+			unlink($tempfilename);
 		}
-		return array('', '', '');
+		return $GetDataImageSize;
 	}
 
-
-	function GetDataImageSize($imgData) {
-		$height = '';
-		$width  = '';
-		$type   = '';
-		if ((substr($imgData, 0, 3) == GETID3_GIF_SIG) && (strlen($imgData) > 10)) {
-			$dim = unpack('v2dim', substr($imgData, 6, 4));
-			$width  = $dim['dim1'];
-			$height = $dim['dim2'];
-			$type = 1;
-		} elseif ((substr($imgData, 0, 8) == GETID3_PNG_SIG) && (strlen($imgData) > 24)) {
-			$dim = unpack('N2dim', substr($imgData, 16, 8));
-			$width  = $dim['dim1'];
-			$height = $dim['dim2'];
-			$type = 3;
-		} elseif ((substr($imgData, 0, 3) == GETID3_JPG_SIG) && (strlen($imgData) > 4)) {
-			///////////////// JPG CHUNK SCAN ////////////////////
-			$imgPos = 2;
-			$type = 2;
-			$buffer = strlen($imgData) - 2;
-			while ($imgPos < strlen($imgData)) {
-				// synchronize to the marker 0xFF
-				$imgPos = strpos($imgData, 0xFF, $imgPos) + 1;
-				$marker = $imgData[$imgPos];
-				do {
-					$marker = ord($imgData[$imgPos++]);
-				} while ($marker == 255);
-				// find dimensions of block
-				switch (chr($marker)) {
-					// Grab width/height from SOF segment (these are acceptable chunk types)
-					case GETID3_JPG_SOF0:
-					case GETID3_JPG_SOF1:
-					case GETID3_JPG_SOF2:
-					case GETID3_JPG_SOF3:
-					case GETID3_JPG_SOF5:
-					case GETID3_JPG_SOF6:
-					case GETID3_JPG_SOF7:
-					case GETID3_JPG_SOF9:
-					case GETID3_JPG_SOF10:
-					case GETID3_JPG_SOF11:
-					case GETID3_JPG_SOF13:
-					case GETID3_JPG_SOF14:
-					case GETID3_JPG_SOF15:
-						$dim = unpack('n2dim', substr($imgData, $imgPos + 3, 4));
-						$height = $dim['dim1'];
-						$width  = $dim['dim2'];
-						break 2; // found it so exit
-					case GETID3_JPG_EOI:
-					case GETID3_JPG_SOS:
-						return false;       // End loop in case we find one of these markers
-					default:            // We're not interested in other markers
-						$skiplen = (ord($imgData[$imgPos++]) << 8) + ord($imgData[$imgPos++]) - 2;
-						// if the skip is more than what we've read in, read more
-						$buffer -= $skiplen;
-						if ($buffer < 512) { // if the buffer of data is too low, read more file.
-							// $imgData .= fread($fd, $skiplen + 1024);
-							// $buffer += $skiplen + 1024;
-							return false; // End loop in case we find run out of data
-						}
-						$imgPos += $skiplen;
-						break;
-				} // endswitch check marker type
-			} // endif loop through JPG chunks
-		} // endif chk for valid file types
-
-		return array($width, $height, $type);
-	} // end function
-
-
 	function ImageTypesLookup($imagetypeid) {
 		static $ImageTypesLookup = array();
 		if (empty($ImageTypesLookup)) {
@@ -1311,7 +1202,8 @@
 	}
 
 	function CopyTagsToComments(&$ThisFileInfo) {
-		// Copy all entries from ['tags'] into common ['comments'] and ['comments_html']
+
+		// Copy all entries from ['tags'] into common ['comments'] 
 		if (!empty($ThisFileInfo['tags'])) {
 			foreach ($ThisFileInfo['tags'] as $tagtype => $tagarray) {
 				foreach ($tagarray as $tagname => $tagdata) {
@@ -1346,14 +1238,18 @@
 							}
 							if (empty($ThisFileInfo['comments'][$tagname]) || !in_array(trim($value), $ThisFileInfo['comments'][$tagname])) {
 								$ThisFileInfo['comments'][$tagname][] = trim($value);
-								if (isset($ThisFileInfo['tags_html'][$tagtype][$tagname][$key])) {
-									$ThisFileInfo['comments_html'][$tagname][] = $ThisFileInfo['tags_html'][$tagtype][$tagname][$key];
-								}
 							}
 						}
 					}
 				}
 			}
+			
+			// Copy to ['comments_html'] 
+    		foreach ($ThisFileInfo['comments'] as $field => $values) {
+    		    foreach ($values as $index => $value) {
+    		        $ThisFileInfo['comments_html'][$field][$index] = str_replace('&#0;', '', getid3_lib::MultiByteCharString2HTML($value, $ThisFileInfo['encoding']));
+    		    }
+            }
 		}
 	}
 

Modified: plog/trunk/class/gallery/getid3/getid3.php
===================================================================
--- plog/trunk/class/gallery/getid3/getid3.php	2006-07-24 16:40:16 UTC (rev 3789)
+++ plog/trunk/class/gallery/getid3/getid3.php	2006-07-24 16:56:08 UTC (rev 3790)
@@ -10,7 +10,7 @@
 /////////////////////////////////////////////////////////////////
 
 // Defines
-define('GETID3_VERSION', '1.7.4');
+define('GETID3_VERSION', '1.7.7');
 define('GETID3_FREAD_BUFFER_SIZE', 16384); // read buffer size in bytes
 
 
@@ -18,28 +18,29 @@
 class getID3
 {
 	// public: Settings
-	var $encoding                 = 'ISO-8859-1';     // CASE SENSITIVE! - i.e. (must be supported by iconv())
-	                                                  // Examples:  ISO-8859-1  UTF-8  UTF-16  UTF-16BE
+	var $encoding        = 'ISO-8859-1';   // CASE SENSITIVE! - i.e. (must be supported by iconv())
+	                                       // Examples:  ISO-8859-1  UTF-8  UTF-16  UTF-16BE
 
-	var $encoding_id3v1           = 'ISO-8859-1';     // Should always be 'ISO-8859-1', but some tags may be written
-	                                                  // in other encodings such as 'EUC-CN'
+	var $encoding_id3v1  = 'ISO-8859-1';   // Should always be 'ISO-8859-1', but some tags may be written in other encodings such as 'EUC-CN'
 
+	var $tempdir         = '*';            // default '*' should use system temp dir
+
 	// public: Optional tag checks - disable for speed.
-	var $option_tag_id3v1         = true;             // Read and process ID3v1 tags
-	var $option_tag_id3v2         = true;             // Read and process ID3v2 tags
-	var $option_tag_lyrics3       = true;             // Read and process Lyrics3 tags
-	var $option_tag_apetag        = true;             // Read and process APE tags
-	var $option_tags_process      = true;             // Copy tags to root key 'tags' and encode to $this->encoding
-	var $option_tags_html         = true;             // Copy tags to root key 'tags_html' properly translated from various encodings to HTML entities
+	var $option_tag_id3v1         = true;  // Read and process ID3v1 tags
+	var $option_tag_id3v2         = true;  // Read and process ID3v2 tags
+	var $option_tag_lyrics3       = true;  // Read and process Lyrics3 tags
+	var $option_tag_apetag        = true;  // Read and process APE tags
+	var $option_tags_process      = true;  // Copy tags to root key 'tags' and encode to $this->encoding
+	var $option_tags_html         = true;  // Copy tags to root key 'tags_html' properly translated from various encodings to HTML entities
 
 	// public: Optional tag/comment calucations
-	var $option_extra_info        = true;             // Calculate additional info such as bitrate, channelmode etc
+	var $option_extra_info        = true;  // Calculate additional info such as bitrate, channelmode etc
 
 	// public: Optional calculations
-	var $option_md5_data          = false;            // Get MD5 sum of data part - slow
-	var $option_md5_data_source   = false;            // Use MD5 of source file if availble - only FLAC and OptimFROG
-	var $option_sha1_data         = false;            // Get SHA1 sum of data part - slow
-	var $option_max_2gb_check     = true;             // Check whether file is larger than 2 Gb and thus not supported by PHP
+	var $option_md5_data          = false; // Get MD5 sum of data part - slow
+	var $option_md5_data_source   = false; // Use MD5 of source file if availble - only FLAC and OptimFROG
+	var $option_sha1_data         = false; // Get SHA1 sum of data part - slow
+	var $option_max_2gb_check     = true;  // Check whether file is larger than 2 Gb and thus not supported by PHP
 
 	// private
 	var $filename;
@@ -88,11 +89,9 @@
 
 		// Get base path of getID3() - ONCE
 		if (!defined('GETID3_INCLUDEPATH')) {
-			define('GETID3_OS_DIRSLASH', (GETID3_OS_ISWINDOWS ? '\\' : '/'));
-
 			foreach (get_included_files() as $key => $val) {
 				if (basename($val) == 'getid3.php') {
-					define('GETID3_INCLUDEPATH', dirname($val).GETID3_OS_DIRSLASH);
+					define('GETID3_INCLUDEPATH', dirname($val).DIRECTORY_SEPARATOR);
 					break;
 				}
 			}
@@ -111,32 +110,60 @@
 		//   ie for "C:/Program Files/Apache/" put "C:/PROGRA~1/APACHE/"
 		// IMPORTANT: This path must include the trailing slash
 		if (GETID3_OS_ISWINDOWS && !defined('GETID3_HELPERAPPSDIR')) {
+			$helperappsdir = GETID3_INCLUDEPATH.'..'.DIRECTORY_SEPARATOR.'helperapps'; // must not have any space in this path
 
-			$helperappsdir = GETID3_INCLUDEPATH.'..'.GETID3_OS_DIRSLASH.'helperapps'; // must not have any space in this path
+			if (!is_dir($helperappsdir)) {
 
-			if (!is_dir($helperappsdir)) {
 				$this->startup_error .= '"'.$helperappsdir.'" cannot be defined as GETID3_HELPERAPPSDIR because it does not exist';
+
 			} elseif (strpos(realpath($helperappsdir), ' ') !== false) {
-				$DirPieces = explode(GETID3_OS_DIRSLASH, realpath($helperappsdir));
-				foreach ($DirPieces as $key => $value) {
-					if ((strpos($value, '.') !== false) && (strpos($value, ' ') === false)) {
-						if (strpos($value, '.') > 8) {
-							$value = substr($value, 0, 6).'~1';
-						}
-					} elseif ((strpos($value, ' ') !== false) || strlen($value) > 8) {
-						$value = substr($value, 0, 6).'~1';
-					}
-					$DirPieces[$key] = strtoupper($value);
+
+				$DirPieces = explode(DIRECTORY_SEPARATOR, realpath($helperappsdir));
+				$DirPieces8 = $DirPieces;
+
+				$CLIdir = $DirPieces[0].' && cd \\';
+				for ($i = 1; $i < count($DirPieces); $i++) {
+				  if (strpos($DirPieces[$i], ' ') === false) {
+				    $CLIdir .= ' && cd '.$DirPieces[$i];
+				  } else {
+				    ob_start();
+				    system($CLIdir.' && dir /ad /x');
+				    $subdirsraw = explode("\n", ob_get_contents());
+				    ob_end_clean();
+				    foreach ($subdirsraw as $dummy => $line) {
+				      if (eregi('^[0-9]{4}/[0-9]{2}/[0-9]{2}  [0-9]{2}:[0-9]{2} [AP]M    <DIR>          ([^ ]{8})     '.preg_quote($DirPieces[$i]).'$', trim($line), $matches)) {
+				        $CLIdir .= ' && cd '.$matches[1];
+				        break;
+				      }
+				    }
+				    $DirPieces8[$i] = $matches[1];
+				  }
 				}
-				$this->startup_error .= 'GETID3_HELPERAPPSDIR must not have any spaces in it - use 8dot3 naming convention if neccesary (on this server that would be something like "'.implode(GETID3_OS_DIRSLASH, $DirPieces).'" - NOTE: this may or may not be the actual 8.3 equivalent of "'.$helperappsdir.'", please double-check). You can run "dir /x" from the commandline to see the correct 8.3-style names. You need to edit the file "'.GETID3_INCLUDEPATH.'/getid3.php" around line '.(__LINE__ - 16);
+				$helperappsdir = implode(DIRECTORY_SEPARATOR, $DirPieces8);
+
 			}
-			define('GETID3_HELPERAPPSDIR', realpath($helperappsdir).GETID3_OS_DIRSLASH);
+			define('GETID3_HELPERAPPSDIR', realpath($helperappsdir).DIRECTORY_SEPARATOR);
+
 		}
 
 	}
 
 
+	// public: setOption
+	function setOption($optArray) {
+		if (!is_array($optArray) || empty($optArray)) {
+			return false;
+		}
+		foreach ($optArray as $opt => $val) {
+			if (isset($this, $opt) === false) {
+				continue;
+			}
+			$this->$opt = $val;
+		}
+		return true;
+	}
 
+
 	// public: analyze file - replaces GetAllFileInfo() and GetTagOnly()
 	function analyze($filename) {
 
@@ -392,7 +419,7 @@
 
 		// remove possible empty keys
 		$AVpossibleEmptyKeys = array('dataformat', 'bits_per_sample', 'encoder_options', 'streams');
-		foreach ($AVpossibleEmptyKeys as $key) {
+		foreach ($AVpossibleEmptyKeys as $dummy => $key) {
 			if (empty($this->info['audio'][$key]) && isset($this->info['audio'][$key])) {
 				unset($this->info['audio'][$key]);
 			}
@@ -436,7 +463,7 @@
 							'group'     => 'audio',
 							'module'    => 'ac3',
 							'mime_type' => 'audio/ac3',
-						  ),
+						),
 
 				// AAC  - audio       - Advanced Audio Coding (AAC) - ADIF format
 				'adif' => array(
@@ -446,7 +473,7 @@
 							'option'    => 'adif',
 							'mime_type' => 'application/octet-stream',
 							'fail_ape'  => 'WARNING',
-						  ),
+						),
 
 
 				// AAC  - audio       - Advanced Audio Coding (AAC) - ADTS format (very similar to MP3)
@@ -457,7 +484,7 @@
 							'option'    => 'adts',
 							'mime_type' => 'application/octet-stream',
 							'fail_ape'  => 'WARNING',
-						  ),
+						),
 
 
 				// AU   - audio       - NeXT/Sun AUdio (AU)
@@ -466,7 +493,7 @@
 							'group'     => 'audio',
 							'module'    => 'au',
 							'mime_type' => 'audio/basic',
-						  ),
+						),
 
 				// AVR  - audio       - Audio Visual Research
 				'avr'  => array(
@@ -474,7 +501,7 @@
 							'group'     => 'audio',
 							'module'    => 'avr',
 							'mime_type' => 'application/octet-stream',
-						  ),
+						),
 
 				// BONK - audio       - Bonk v0.9+
 				'bonk' => array(
@@ -482,7 +509,7 @@
 							'group'     => 'audio',
 							'module'    => 'bonk',
 							'mime_type' => 'audio/xmms-bonk',
-						  ),
+						),
 
 				// FLAC - audio       - Free Lossless Audio Codec
 				'flac' => array(
@@ -490,7 +517,7 @@
 							'group'     => 'audio',
 							'module'    => 'flac',
 							'mime_type' => 'audio/x-flac',
-						  ),
+						),
 
 				// LA   - audio       - Lossless Audio (LA)
 				'la'   => array(
@@ -498,7 +525,7 @@
 							'group'     => 'audio',
 							'module'    => 'la',
 							'mime_type' => 'application/octet-stream',
-						  ),
+						),
 
 				// LPAC - audio       - Lossless Predictive Audio Compression (LPAC)
 				'lpac' => array(
@@ -506,7 +533,7 @@
 							'group'     => 'audio',
 							'module'    => 'lpac',
 							'mime_type' => 'application/octet-stream',
-						  ),
+						),
 
 				// MIDI - audio       - MIDI (Musical Instrument Digital Interface)
 				'midi' => array(
@@ -514,7 +541,7 @@
 							'group'     => 'audio',
 							'module'    => 'midi',
 							'mime_type' => 'audio/midi',
-						  ),
+						),
 
 				// MAC  - audio       - Monkey's Audio Compressor
 				'mac'  => array(
@@ -522,7 +549,7 @@
 							'group'     => 'audio',
 							'module'    => 'monkey',
 							'mime_type' => 'application/octet-stream',
-						  ),
+						),
 
 				// MOD  - audio       - MODule (assorted sub-formats)
 				'mod'  => array(
@@ -531,7 +558,7 @@
 							'module'    => 'mod',
 							'option'    => 'mod',
 							'mime_type' => 'audio/mod',
-						  ),
+						),
 
 				// MOD  - audio       - MODule (Impulse Tracker)
 				'it'   => array(
@@ -540,7 +567,7 @@
 							'module'    => 'mod',
 							'option'    => 'it',
 							'mime_type' => 'audio/it',
-						  ),
+						),
 
 				// MOD  - audio       - MODule (eXtended Module, various sub-formats)
 				'xm'   => array(
@@ -549,7 +576,7 @@
 							'module'    => 'mod',
 							'option'    => 'xm',
 							'mime_type' => 'audio/xm',
-						  ),
+						),
 
 				// MOD  - audio       - MODule (ScreamTracker)
 				's3m'  => array(
@@ -558,7 +585,7 @@
 							'module'    => 'mod',
 							'option'    => 's3m',
 							'mime_type' => 'audio/s3m',
-						  ),
+						),
 
 				// MPC  - audio       - Musepack / MPEGplus
 				'mpc'  => array(
@@ -566,7 +593,7 @@
 							'group'     => 'audio',
 							'module'    => 'mpc',
 							'mime_type' => 'application/octet-stream',
-						  ),
+						),
 
 				// MP3  - audio       - MPEG-audio Layer 3 (very similar to AAC-ADTS)
 				'mp3'  => array(
@@ -574,7 +601,7 @@
 							'group'     => 'audio',
 							'module'    => 'mp3',
 							'mime_type' => 'audio/mpeg',
-						  ),
+						),
 
 				// OFR  - audio       - OptimFROG
 				'ofr'  => array(
@@ -582,7 +609,7 @@
 							'group'     => 'audio',
 							'module'    => 'optimfrog',
 							'mime_type' => 'application/octet-stream',
-						  ),
+						),
 
 				// RKAU - audio       - RKive AUdio compressor
 				'rkau' => array(
@@ -590,7 +617,7 @@
 							'group'     => 'audio',
 							'module'    => 'rkau',
 							'mime_type' => 'application/octet-stream',
-						  ),
+						),
 
 				// SHN  - audio       - Shorten
 				'shn'  => array(
@@ -600,7 +627,7 @@
 							'mime_type' => 'audio/xmms-shn',
 							'fail_id3'  => 'ERROR',
 							'fail_ape'  => 'ERROR',
-						  ),
+						),
 
 				// TTA  - audio       - TTA Lossless Audio Compressor (http://tta.corecodec.org)
 				'tta'  => array(
@@ -608,7 +635,7 @@
 							'group'     => 'audio',
 							'module'    => 'tta',
 							'mime_type' => 'application/octet-stream',
-						  ),
+						),
 
 				// VOC  - audio       - Creative Voice (VOC)
 				'voc'  => array(
@@ -616,7 +643,7 @@
 							'group'     => 'audio',
 							'module'    => 'voc',
 							'mime_type' => 'audio/voc',
-						  ),
+						),
 
 				// VQF  - audio       - transform-domain weighted interleave Vector Quantization Format (VQF)
 				'vqf'  => array(
@@ -624,7 +651,7 @@
 							'group'     => 'audio',
 							'module'    => 'vqf',
 							'mime_type' => 'application/octet-stream',
-						  ),
+						),
 
 				// WV  - audio        - WavPack (v4.0+)
 				'wv'   => array(
@@ -632,7 +659,7 @@
 							'group'     => 'audio',
 							'module'    => 'wavpack',
 							'mime_type' => 'application/octet-stream',
-						  ),
+						),
 
 
 				// Audio-Video formats
@@ -644,23 +671,31 @@
 							'module'    => 'asf',
 							'mime_type' => 'video/x-ms-asf',
 							'iconv_req' => false,
-						  ),
+						),
 
-				// BINK  - audio/video - Bink / Smacker
+				// BINK - audio/video - Bink / Smacker
 				'bink' => array(
 							'pattern'   => '^(BIK|SMK)',
 							'group'     => 'audio-video',
 							'module'    => 'bink',
 							'mime_type' => 'application/octet-stream',
-						  ),
+						),
 
+				// FLV  - audio/video - FLash Video
+				'flv' => array(
+							'pattern'   => '^FLV\x01',
+							'group'     => 'audio-video',
+							'module'    => 'flv',
+							'mime_type' => 'video/x-flv',
+						),
+
 				// MKAV - audio/video - Mastroka
 				'matroska' => array(
 							'pattern'   => '^\x1A\x45\xDF\xA3',
 							'group'     => 'audio-video',
 							'module'    => 'matroska',
 							'mime_type' => 'application/octet-stream',
-						  ),
+						),
 
 				// MPEG - audio/video - MPEG (Moving Pictures Experts Group)
 				'mpeg' => array(
@@ -668,7 +703,7 @@
 							'group'     => 'audio-video',
 							'module'    => 'mpeg',
 							'mime_type' => 'video/mpeg',
-						  ),
+						),
 
 				// NSV  - audio/video - Nullsoft Streaming Video (NSV)
 				'nsv'  => array(
@@ -676,7 +711,7 @@
 							'group'     => 'audio-video',
 							'module'    => 'nsv',
 							'mime_type' => 'application/octet-stream',
-						  ),
+						),
 
 				// Ogg  - audio/video - Ogg (Ogg-Vorbis, Ogg-FLAC, Speex, Ogg-Theora(*), Ogg-Tarkin(*))
 				'ogg'  => array(
@@ -686,7 +721,7 @@
 							'mime_type' => 'application/ogg',
 							'fail_id3'  => 'WARNING',
 							'fail_ape'  => 'WARNING',
-						  ),
+						),
 
 				// QT   - audio/video - Quicktime
 				'quicktime' => array(
@@ -694,7 +729,7 @@
 							'group'     => 'audio-video',
 							'module'    => 'quicktime',
 							'mime_type' => 'video/quicktime',
-						  ),
+						),
 
 				// RIFF - audio/video - Resource Interchange File Format (RIFF) / WAV / AVI / CD-audio / SDSS = renamed variant used by SmartSound QuickTracks (www.smartsound.com) / FORM = Audio Interchange File Format (AIFF)
 				'riff' => array(
@@ -703,7 +738,7 @@
 							'module'    => 'riff',
 							'mime_type' => 'audio/x-wave',
 							'fail_ape'  => 'WARNING',
-						  ),
+						),
 
 				// Real - audio/video - RealAudio, RealVideo
 				'real' => array(
@@ -711,7 +746,7 @@
 							'group'     => 'audio-video',
 							'module'    => 'real',
 							'mime_type' => 'audio/x-realaudio',
-						  ),
+						),
 
 				// SWF - audio/video - ShockWave Flash
 				'swf' => array(
@@ -719,7 +754,7 @@
 							'group'     => 'audio-video',
 							'module'    => 'swf',
 							'mime_type' => 'application/x-shockwave-flash',
-						  ),
+						),
 
 
 				// Still-Image formats
@@ -732,7 +767,7 @@
 							'mime_type' => 'image/bmp',
 							'fail_id3'  => 'ERROR',
 							'fail_ape'  => 'ERROR',
-						  ),
+						),
 
 				// GIF  - still image - Graphics Interchange Format
 				'gif'  => array(
@@ -742,7 +777,7 @@
 							'mime_type' => 'image/gif',
 							'fail_id3'  => 'ERROR',
 							'fail_ape'  => 'ERROR',
-						  ),
+						),
 
 				// JPEG - still image - Joint Photographic Experts Group (JPEG)
 				'jpg'  => array(
@@ -752,7 +787,7 @@
 							'mime_type' => 'image/jpeg',
 							'fail_id3'  => 'ERROR',
 							'fail_ape'  => 'ERROR',
-						  ),
+						),
 
 				// PCD  - still image - Kodak Photo CD
 				'pcd'  => array(
@@ -762,7 +797,7 @@
 							'mime_type' => 'image/x-photo-cd',
 							'fail_id3'  => 'ERROR',
 							'fail_ape'  => 'ERROR',
-						  ),
+						),
 
 
 				// PNG  - still image - Portable Network Graphics (PNG)
@@ -773,7 +808,7 @@
 							'mime_type' => 'image/png',
 							'fail_id3'  => 'ERROR',
 							'fail_ape'  => 'ERROR',
-						  ),
+						),
 
 
 				// TIFF  - still image - Tagged Information File Format (TIFF)
@@ -784,7 +819,7 @@
 							'mime_type' => 'image/tiff',
 							'fail_id3'  => 'ERROR',
 							'fail_ape'  => 'ERROR',
-						  ),
+						),
 
 
 				// Data formats
@@ -798,7 +833,7 @@
 							'fail_id3'  => 'ERROR',
 							'fail_ape'  => 'ERROR',
 							'iconv_req' => false,
-						  ),
+						),
 
 				// RAR  - data        - RAR compressed data
 				'rar'  => array(
@@ -808,9 +843,9 @@
 							'mime_type' => 'application/octet-stream',
 							'fail_id3'  => 'ERROR',
 							'fail_ape'  => 'ERROR',
-						  ),
+						),
 
-				// SZIP - audio       - SZIP compressed data
+				// SZIP - audio/data  - SZIP compressed data
 				'szip' => array(
 							'pattern'   => '^SZ\x0A\x04',
 							'group'     => 'archive',
@@ -818,7 +853,7 @@
 							'mime_type' => 'application/octet-stream',
 							'fail_id3'  => 'ERROR',
 							'fail_ape'  => 'ERROR',
-						  ),
+						),
 
 				// TAR  - data        - TAR compressed data
 				'tar'  => array(
@@ -828,7 +863,7 @@
 							'mime_type' => 'application/x-tar',
 							'fail_id3'  => 'ERROR',
 							'fail_ape'  => 'ERROR',
-						  ),
+						),
 
 				// GZIP  - data        - GZIP compressed data
 				'gz'  => array(
@@ -838,9 +873,9 @@
 							'mime_type' => 'application/x-gzip',
 							'fail_id3'  => 'ERROR',
 							'fail_ape'  => 'ERROR',
-						  ),
+						),
 
-				// ZIP  - data        - ZIP compressed data
+				// ZIP  - data         - ZIP compressed data
 				'zip'  => array(
 							'pattern'   => '^PK\x03\x04',
 							'group'     => 'archive',
@@ -848,7 +883,30 @@
 							'mime_type' => 'application/zip',
 							'fail_id3'  => 'ERROR',
 							'fail_ape'  => 'ERROR',
-						  )
+						),
+
+
+				// Misc other formats
+
+				// PDF  - data         - ZIP compressed data
+				'pdf'  => array(
+							'pattern'   => '^\x25PDF',
+							'group'     => 'misc',
+							'module'    => 'pdf',
+							'mime_type' => 'application/pdf',
+							'fail_id3'  => 'ERROR',
+							'fail_ape'  => 'ERROR',
+						),
+
+				// MSOFFICE  - data         - ZIP compressed data
+				'msoffice' => array(
+							'pattern'   => '^\xD0\xCF\x11\xE0', // D0CF11E == DOCFILE == Microsoft Office Document
+							'group'     => 'misc',
+							'module'    => 'msoffice',
+							'mime_type' => 'application/octet-stream',
+							'fail_id3'  => 'ERROR',
+							'fail_ape'  => 'ERROR',
+						),
 			);
 		}
 
@@ -876,29 +934,12 @@
 
 
 		if (preg_match('/\.mp[123a]$/i', $filename)) {
-
 			// Too many mp3 encoders on the market put gabage in front of mpeg files
 			// use assume format on these if format detection failed
 			$GetFileFormatArray = $this->GetFileFormatArray();
 			$info = $GetFileFormatArray['mp3'];
 			$info['include'] = 'module.'.$info['group'].'.'.$info['module'].'.php';
 			return $info;
-
-		//} elseif (preg_match('/\.tar$/i', $filename)) {
-        //
-		//	// TAR files don't have any useful header to work from
-		//	// TAR  - data        - TAR compressed data
-		//	$info = array(
-		//		'pattern'   => '^.{512}',
-		//		'group'     => 'archive',
-		//		'module'    => 'tar',
-		//		'mime_type' => 'application/octet-stream',
-		//		'fail_id3'  => 'ERROR',
-		//		'fail_ape'  => 'ERROR',
-		//	);
-		//	$info['include'] = 'module.'.$info['group'].'.'.$info['module'].'.php';
-		//	return $info;
-
 		}
 
 		return false;
@@ -1065,6 +1106,7 @@
 				} else {
 
 					$commandline = 'vorbiscomment -w -c "'.$empty.'" "'.$file.'" "'.$temp.'" 2>&1';
+					$commandline = 'vorbiscomment -w -c '.escapeshellarg($empty).' '.escapeshellarg($file).' '.escapeshellarg($temp).' 2>&1';
 					$VorbisCommentError = `$commandline`;
 
 				}
@@ -1254,6 +1296,10 @@
 		return true;
 	}
 
+	function getid3_tempnam() {
+		return tempnam($this->tempdir, 'gI3');
+	}
+
 }
 
 ?>
\ No newline at end of file

Modified: plog/trunk/class/gallery/getid3/module.archive.gzip.php
===================================================================
--- plog/trunk/class/gallery/getid3/module.archive.gzip.php	2006-07-24 16:40:16 UTC (rev 3789)
+++ plog/trunk/class/gallery/getid3/module.archive.gzip.php	2006-07-24 16:56:08 UTC (rev 3790)
@@ -17,16 +17,11 @@
 class getid3_gzip {
 
 	// public: Optional file list - disable for speed.
-	var $option_gzip_parse_contents = true; // decode gzipped files, if possible, and parse recursively (.tar.gz for example)
+	var $option_gzip_parse_contents = false; // decode gzipped files, if possible, and parse recursively (.tar.gz for example)
 
 	function getid3_gzip(&$fd, &$ThisFileInfo) {
 		$ThisFileInfo['fileformat'] = 'gzip';
-		return $this->read_gzip($fd, $ThisFileInfo);
-	}
 
-	// Reads the gzip-file
-	function read_gzip($fd, &$ThisFileInfo) {
-
 		$start_length = 10;
 		$unpack_header = 'a1id1/a1id2/a1cmethod/a1flags/a4mtime/a1xflags/a1os';
 		//+---+---+---+---+---+---+---+---+---+---+

Modified: plog/trunk/class/gallery/getid3/module.archive.tar.php
===================================================================
--- plog/trunk/class/gallery/getid3/module.archive.tar.php	2006-07-24 16:40:16 UTC (rev 3789)
+++ plog/trunk/class/gallery/getid3/module.archive.tar.php	2006-07-24 16:56:08 UTC (rev 3790)
@@ -18,35 +18,25 @@
 
 	function getid3_tar(&$fd, &$ThisFileInfo) {
 		$ThisFileInfo['fileformat'] = 'tar';
+		$ThisFileInfo['tar']['files'] = array();
 
-		@fseek($fd, 0);
-		$filebuffer = @fread($fd, $ThisFileInfo['filesize']);
-		return $this->read_tar($filebuffer, $ThisFileInfo);
-	}
-
-	// Reads the tar-file
-	function read_tar(&$filebuffer, &$ThisFileInfo) {
-
-		$header_length = 512;
 		$unpack_header = 'a100fname/a8mode/a8uid/a8gid/a12size/a12mtime/a8chksum/a1typflag/a100lnkname/a6magic/a2ver/a32uname/a32gname/a8devmaj/a8devmin/a155/prefix';
-
 		$null_512k = str_repeat("\0", 512); // end-of-file marker
 
-		$ThisFileInfo['tar']['files'] = array();
+		@fseek($fd, 0);
+        while (!feof($fd)) {
+            $buffer = fread($fd, 512);
 
-		while(strlen($filebuffer) != 0) {
-			$buffer = substr($filebuffer, 0, $header_length);
-			$filebuffer = substr($filebuffer, strlen($buffer));
 			// check the block
 			$checksum = 0;
 			for ($i = 0; $i < 148; $i++) {
-				$checksum += ord(substr($buffer, $i, 1));
+				$checksum += ord($buffer{$i});
 			}
 			for ($i = 148; $i < 156; $i++) {
 				$checksum += ord(' ');
 			}
 			for ($i = 156; $i < 512; $i++) {
-				$checksum += ord(substr($buffer, $i, 1));
+				$checksum += ord($buffer{$i});
 			}
 			$attr    = unpack($unpack_header, $buffer);
 			$name    =        trim(@$attr['fname']);
@@ -65,8 +55,8 @@
 			$devmaj  = octdec(trim(@$attr['devmaj']));
 			$devmin  = octdec(trim(@$attr['devmin']));
 			$prefix  =        trim(@$attr['prefix']);
-			// EOF Found
 			if (($checksum == 256) && ($chksum == 0)) {
+				// EOF Found
 				break;
 			}
 			if ($prefix) {
@@ -75,22 +65,18 @@
 			if ((preg_match('#/$#', $name)) && !$name) {
 				$typeflag = 5;
 			}
-			// If it's the end of the tar-file...
 			if ($buffer == $null_512k) {
+				// it's the end of the tar-file...
 				break;
 			}
-			// Read the next chunk
-			$data = substr($filebuffer, 0, $size);
-			$filebuffer = substr($filebuffer, strlen($data));
-			if (strlen($data) != $size) {
-				$ThisFileInfo['error'][] = 'Read error on tar file';
-				return false;
-			}
+
+			// Read to the next chunk
+			fseek($fd, $size, SEEK_CUR);
+
 			$diff = $size % 512;
 			if ($diff != 0) {
 				// Padding, throw away
-				$buff = substr($filebuffer, 0, (512 - $diff));
-				$filebuffer = substr($filebuffer, strlen($buff));
+				fseek($fd, (512 - $diff), SEEK_CUR);
 			}
 			// Protect against tar-files with garbage at the end
 			if ($name == '') {
@@ -122,7 +108,7 @@
 	// Parses the file mode to file permissions
 	function display_perms($mode) {
 		// Determine Type
-		if ($mode & 0x1000)     $type='p'; // FIFO pipe
+		if     ($mode & 0x1000) $type='p'; // FIFO pipe
 		elseif ($mode & 0x2000) $type='c'; // Character special
 		elseif ($mode & 0x4000) $type='d'; // Directory
 		elseif ($mode & 0x6000) $type='b'; // Block special

Modified: plog/trunk/class/gallery/getid3/module.archive.zip.php
===================================================================
--- plog/trunk/class/gallery/getid3/module.archive.zip.php	2006-07-24 16:40:16 UTC (rev 3789)
+++ plog/trunk/class/gallery/getid3/module.archive.zip.php	2006-07-24 16:56:08 UTC (rev 3790)
@@ -406,7 +406,7 @@
 		$UNIXminute = (($DOStime & 0x07E0) >> 5);
 		$UNIXhour   = (($DOStime & 0xF800) >> 11);
 
-		return mktime($UNIXhour, $UNIXminute, $UNIXsecond, $UNIXmonth, $UNIXday, $UNIXyear);
+		return gmmktime($UNIXhour, $UNIXminute, $UNIXsecond, $UNIXmonth, $UNIXday, $UNIXyear);
 	}
 
 }

Modified: plog/trunk/class/gallery/getid3/module.audio-video.asf.php
===================================================================
--- plog/trunk/class/gallery/getid3/module.audio-video.asf.php	2006-07-24 16:40:16 UTC (rev 3789)
+++ plog/trunk/class/gallery/getid3/module.audio-video.asf.php	2006-07-24 16:56:08 UTC (rev 3790)
@@ -146,7 +146,8 @@
 					$offset += 4;
 					$thisfile_asf_filepropertiesobject['max_bitrate']        = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 4));
 					$offset += 4;
-					$ThisFileInfo['bitrate']                                 = $thisfile_asf_filepropertiesobject['max_bitrate'];
+					//$ThisFileInfo['bitrate']                                 = $thisfile_asf_filepropertiesobject['max_bitrate'];
+					$ThisFileInfo['bitrate']                                 = ($thisfile_asf_filepropertiesobject['filesize'] * 8) / $ThisFileInfo['playtime_seconds'];
 					break;
 
 				case GETID3_ASF_Stream_Properties_Object:
@@ -331,45 +332,56 @@
 							if (!isset($thisfile_audio['bitrate']) && strstr($AudioCodecBitrate, 'kbps')) {
 								$thisfile_audio['bitrate'] = (int) (trim(str_replace('kbps', '', $AudioCodecBitrate)) * 1000);
 							}
-							if (!isset($thisfile_video['bitrate']) && isset($thisfile_audio['bitrate']) && isset($thisfile_asf['file_properties_object']['max_bitrate']) && ($thisfile_asf_codeclistobject['codec_entries_count'] > 1)) {
-								$thisfile_video['bitrate'] = $thisfile_asf['file_properties_object']['max_bitrate'] - $thisfile_audio['bitrate'];
+							//if (!isset($thisfile_video['bitrate']) && isset($thisfile_audio['bitrate']) && isset($thisfile_asf['file_properties_object']['max_bitrate']) && ($thisfile_asf_codeclistobject['codec_entries_count'] > 1)) {
+							if (!@$thisfile_video['bitrate'] && @$thisfile_audio['bitrate'] && @$ThisFileInfo['bitrate']) {
+								//$thisfile_video['bitrate'] = $thisfile_asf['file_properties_object']['max_bitrate'] - $thisfile_audio['bitrate'];
+								$thisfile_video['bitrate'] = $ThisFileInfo['bitrate'] - $thisfile_audio['bitrate'];
 							}
 
 							$AudioCodecFrequency = (int) trim(str_replace('kHz', '', $AudioCodecFrequency));
 							switch ($AudioCodecFrequency) {
 								case 8:
+								case 8000:
 									$thisfile_audio['sample_rate'] = 8000;
 									break;
 
 								case 11:
+								case 11025:
 									$thisfile_audio['sample_rate'] = 11025;
 									break;
 
 								case 12:
+								case 12000:
 									$thisfile_audio['sample_rate'] = 12000;
 									break;
 
 								case 16:
+								case 16000:
 									$thisfile_audio['sample_rate'] = 16000;
 									break;
 
 								case 22:
+								case 22050:
 									$thisfile_audio['sample_rate'] = 22050;
 									break;
 
 								case 24:
+								case 24000:
 									$thisfile_audio['sample_rate'] = 24000;
 									break;
 
 								case 32:
+								case 32000:
 									$thisfile_audio['sample_rate'] = 32000;
 									break;
 
 								case 44:
+								case 441000:
 									$thisfile_audio['sample_rate'] = 44100;
 									break;
 
 								case 48:
+								case 48000:
 									$thisfile_audio['sample_rate'] = 48000;
 									break;
 
@@ -754,6 +766,11 @@
 								$thisfile_asf_comments['year'] = array( $this->TrimTerm($thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['value']));
 								break;
 
+							case 'wm/lyrics':
+							case 'lyrics':
+								$thisfile_asf_comments['lyrics'] = array($this->TrimTerm($thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['value']));
+								break;
+
 							case 'isvbr':
 								if ($thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['value']) {
 									$thisfile_audio['bitrate_mode'] = 'vbr';
@@ -769,12 +786,12 @@
 									$tempThisfileInfo = array('encoding'=>$ThisFileInfo['encoding']);
 									fwrite($tempfilehandle, $thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['value']);
 									fclose($tempfilehandle);
-									
+
 									$tempfilehandle = fopen($tempfile, "rb");
 									$id3 = new getid3_id3v2($tempfilehandle, $tempThisfileInfo);
 									fclose($tempfilehandle);
 									unlink($tempfile);
-									
+
 									$ThisFileInfo['id3v2'] = $tempThisfileInfo['id3v2'];
 								}
 								break;
@@ -853,16 +870,16 @@
 					$thisfile_asf['stream_bitrate_properties_object'] = array();
 					$thisfile_asf_streambitratepropertiesobject       = &$thisfile_asf['stream_bitrate_properties_object'];
 
-					$thisfile_asf_streambitrateproperties['objectid']                  = $NextObjectGUID;
-					$thisfile_asf_streambitrateproperties['objectid_guid']             = $NextObjectGUIDtext;
-					$thisfile_asf_streambitrateproperties['objectsize']                = $NextObjectSize;
-					$thisfile_asf_streambitrateproperties['bitrate_records_count']     = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 2));
+					$thisfile_asf_streambitratepropertiesobject['objectid']                  = $NextObjectGUID;
+					$thisfile_asf_streambitratepropertiesobject['objectid_guid']             = $NextObjectGUIDtext;
+					$thisfile_asf_streambitratepropertiesobject['objectsize']                = $NextObjectSize;
+					$thisfile_asf_streambitratepropertiesobject['bitrate_records_count']     = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 2));
 					$offset += 2;
-					for ($BitrateRecordsCounter = 0; $BitrateRecordsCounter < $thisfile_asf_streambitrateproperties['bitrate_records_count']; $BitrateRecordsCounter++) {
-						$thisfile_asf_streambitrateproperties['bitrate_records'][$BitrateRecordsCounter]['flags_raw'] = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 2));
+					for ($BitrateRecordsCounter = 0; $BitrateRecordsCounter < $thisfile_asf_streambitratepropertiesobject['bitrate_records_count']; $BitrateRecordsCounter++) {
+						$thisfile_asf_streambitratepropertiesobject['bitrate_records'][$BitrateRecordsCounter]['flags_raw'] = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 2));
 						$offset += 2;
-						$thisfile_asf_streambitrateproperties['bitrate_records'][$BitrateRecordsCounter]['flags']['stream_number'] = $thisfile_asf_streambitrateproperties['bitrate_records'][$BitrateRecordsCounter]['flags_raw'] & 0x007F;
-						$thisfile_asf_streambitrateproperties['bitrate_records'][$BitrateRecordsCounter]['bitrate'] = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 4));
+						$thisfile_asf_streambitratepropertiesobject['bitrate_records'][$BitrateRecordsCounter]['flags']['stream_number'] = $thisfile_asf_streambitratepropertiesobject['bitrate_records'][$BitrateRecordsCounter]['flags_raw'] & 0x007F;
+						$thisfile_asf_streambitratepropertiesobject['bitrate_records'][$BitrateRecordsCounter]['bitrate'] = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 4));
 						$offset += 4;
 					}
 					break;
@@ -923,14 +940,17 @@
 				}
 			}
 			if ($ASFbitrateAudio > 0) {
-				$thisfile_audio['bitrate']     = $ASFbitrateAudio;
+				$thisfile_audio['bitrate'] = $ASFbitrateAudio;
 			}
 			if ($ASFbitrateVideo > 0) {
-				$thisfile_video['bitrate']     = $ASFbitrateVideo;
+				$thisfile_video['bitrate'] = $ASFbitrateVideo;
 			}
 		}
 		if (isset($thisfile_asf['stream_properties_object']) && is_array($thisfile_asf['stream_properties_object'])) {
 
+			$thisfile_audio['bitrate'] = 0;
+			$thisfile_video['bitrate'] = 0;
+
 			foreach ($thisfile_asf['stream_properties_object'] as $streamnumber => $streamdata) {
 
 				switch ($streamdata['stream_type']) {
@@ -962,8 +982,20 @@
 								break;
 						}
 
-						if (!isset($thisfile_audio['bitrate'])) {
-							$thisfile_audio['bitrate'] = $thisfile_asf_audiomedia_currentstream['bytes_sec'] * 8;
+						if (!empty($thisfile_asf['stream_bitrate_properties_object']['bitrate_records'])) {
+							foreach ($thisfile_asf['stream_bitrate_properties_object']['bitrate_records'] as $dummy => $dataarray) {
+								if (@$dataarray['flags']['stream_number'] == $streamnumber) {
+									$thisfile_asf_audiomedia_currentstream['bitrate'] = $dataarray['bitrate'];
+									$thisfile_audio['bitrate'] += $dataarray['bitrate'];
+									break;
+								}
+							}
+						} else {
+							if (@$thisfile_asf_audiomedia_currentstream['bytes_sec']) {
+								$thisfile_audio['bitrate'] += $thisfile_asf_audiomedia_currentstream['bytes_sec'] * 8;
+							} elseif (@$thisfile_asf_audiomedia_currentstream['bitrate']) {
+								$thisfile_audio['bitrate'] += $thisfile_asf_audiomedia_currentstream['bitrate'];
+							}
 						}
 						$thisfile_audio['streams'][$streamnumber]                = $thisfile_asf_audiomedia_currentstream;
 						$thisfile_audio['streams'][$streamnumber]['wformattag']  = $thisfile_asf_audiomedia_currentstream['raw']['wFormatTag'];
@@ -1035,14 +1067,24 @@
 						$videomediaoffset += 4;
 						$thisfile_asf_videomedia_currentstream['format_data']['codec_data']       = substr($streamdata['type_specific_data'], $videomediaoffset);
 
+						if (!empty($thisfile_asf['stream_bitrate_properties_object']['bitrate_records'])) {
+							foreach ($thisfile_asf['stream_bitrate_properties_object']['bitrate_records'] as $dummy => $dataarray) {
+								if (@$dataarray['flags']['stream_number'] == $streamnumber) {
+									$thisfile_asf_videomedia_currentstream['bitrate'] = $dataarray['bitrate'];
+									$thisfile_video['streams'][$streamnumber]['bitrate'] = $dataarray['bitrate']; 
+									$thisfile_video['bitrate'] += $dataarray['bitrate'];
+									break;
+								}
+							}
+						}
 
 						$thisfile_asf_videomedia_currentstream['format_data']['codec'] = getid3_riff::RIFFfourccLookup($thisfile_asf_videomedia_currentstream['format_data']['codec_fourcc']);
 
-						$thisfile_video['fourcc']          = $thisfile_asf_videomedia_currentstream['format_data']['codec_fourcc'];
-						$thisfile_video['codec']           = $thisfile_asf_videomedia_currentstream['format_data']['codec'];
-						$thisfile_video['resolution_x']    = $thisfile_asf_videomedia_currentstream['image_width'];
-						$thisfile_video['resolution_y']    = $thisfile_asf_videomedia_currentstream['image_height'];
-						$thisfile_video['bits_per_sample'] = $thisfile_asf_videomedia_currentstream['format_data']['bits_per_pixel'];
+						$thisfile_video['streams'][$streamnumber]['fourcc']          = $thisfile_asf_videomedia_currentstream['format_data']['codec_fourcc'];
+						$thisfile_video['streams'][$streamnumber]['codec']           = $thisfile_asf_videomedia_currentstream['format_data']['codec'];
+						$thisfile_video['streams'][$streamnumber]['resolution_x']    = $thisfile_asf_videomedia_currentstream['image_width'];
+						$thisfile_video['streams'][$streamnumber]['resolution_y']    = $thisfile_asf_videomedia_currentstream['image_height'];
+						$thisfile_video['streams'][$streamnumber]['bits_per_sample'] = $thisfile_asf_videomedia_currentstream['format_data']['bits_per_pixel'];
 						break;
 
 					default:
@@ -1278,7 +1320,7 @@
 			}
 		}
 
-		switch ($thisfile_audio['codec']) {
+		switch (@$thisfile_audio['codec']) {
 			case 'MPEG Layer-3':
 				$thisfile_audio['dataformat'] = 'mp3';
 				break;
@@ -1321,7 +1363,7 @@
 			$thisfile_video['pixel_aspect_ratio'] = (isset($thisfile_audio['pixel_aspect_ratio']) ? $thisfile_audio['pixel_aspect_ratio'] : (float) 1);
 			$thisfile_video['dataformat']         = (!empty($thisfile_video['dataformat'])        ? $thisfile_video['dataformat']         : 'asf');
 		}
-
+		$ThisFileInfo['bitrate'] = @$thisfile_audio['bitrate'] + @$thisfile_video['bitrate'];
 		return true;
 	}
 

Added: plog/trunk/class/gallery/getid3/module.audio-video.flv.php
===================================================================
--- plog/trunk/class/gallery/getid3/module.audio-video.flv.php	2006-07-24 16:40:16 UTC (rev 3789)
+++ plog/trunk/class/gallery/getid3/module.audio-video.flv.php	2006-07-24 16:56:08 UTC (rev 3790)
@@ -0,0 +1,497 @@
+<?php
+/////////////////////////////////////////////////////////////////
+/// getID3() by James Heinrich <info at getid3.org>               //
+//  available at http://getid3.sourceforge.net                 //
+//            or http://www.getid3.org                         //
+//                                                             //
+//  FLV module by Seth Kaufman <seth at whirl-i-gig.com>          //
+//                                                             //
+//  * version 0.1 (26 June 2005)                               //
+//                                                             //
+//  minor modifications by James Heinrich <info at getid3.org>    //
+//  * version 0.1.1 (15 July 2005)                             //
+//                                                             //
+//  Support for On2 VP6 codec and meta information by          //
+//  Steve Webster <steve.webster at featurecreep.com>             //
+//  * version 0.2 (22 February 2006)                           //
+//                                                             //
+//  Modified to not read entire file into memory               //
+//  by James Heinrich <info at getid3.org>                        //
+//  * version 0.3 (15 June 2006)                               //
+//                                                             //
+/////////////////////////////////////////////////////////////////
+//                                                             //
+// module.audio-video.flv.php                                  //
+// module for analyzing Shockwave Flash Video files            //
+// dependencies: NONE                                          //
+//                                                            ///
+/////////////////////////////////////////////////////////////////
+
+define('GETID3_FLV_TAG_AUDIO', 8);
+define('GETID3_FLV_TAG_VIDEO', 9);
+define('GETID3_FLV_TAG_META', 18);
+
+define('GETID3_FLV_VIDEO_H263',   2);
+define('GETID3_FLV_VIDEO_SCREEN', 3);
+define('GETID3_FLV_VIDEO_VP6',    4);
+
+class getid3_flv
+{
+
+	function getid3_flv(&$fd, &$ThisFileInfo, $ReturnAllTagData=false) {
+		fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET);
+
+		$FLVdataLength = $ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset'];
+		$FLVheader = fread($fd, 5);
+
+		$ThisFileInfo['fileformat'] = 'flv';
+		$ThisFileInfo['flv']['header']['signature'] =                           substr($FLVheader, 0, 3);
+		$ThisFileInfo['flv']['header']['version']   = getid3_lib::BigEndian2Int(substr($FLVheader, 3, 1));
+		$TypeFlags                                  = getid3_lib::BigEndian2Int(substr($FLVheader, 4, 1));
+
+		if ($ThisFileInfo['flv']['header']['signature'] != 'FLV') {
+			$ThisFileInfo['error'][] = 'Expecting "FLV" at offset '.$ThisFileInfo['avdataoffset'].', found "'.$ThisFileInfo['flv']['header']['signature'].'"';
+			unset($ThisFileInfo['flv']);
+			unset($ThisFileInfo['fileformat']);
+			return false;
+		}
+
+		$ThisFileInfo['flv']['header']['hasAudio'] = (bool) ($TypeFlags & 0x04);
+		$ThisFileInfo['flv']['header']['hasVideo'] = (bool) ($TypeFlags & 0x01);
+
+		$FrameSizeDataLength = getid3_lib::BigEndian2Int(fread($fd, 4));
+		$FLVheaderFrameLength = 9;
+		if ($FrameSizeDataLength > $FLVheaderFrameLength) {
+			fseek($fd, $FrameSizeDataLength - $FLVheaderFrameLength, SEEK_CUR);
+		}
+
+		$Duration = 0;
+		while ((ftell($fd) + 1) < $ThisFileInfo['avdataend']) {
+			//if (!$ThisFileInfo['flv']['header']['hasAudio'] || isset($ThisFileInfo['flv']['audio']['audioFormat'])) {
+			//	if (!$ThisFileInfo['flv']['header']['hasVideo'] || isset($ThisFileInfo['flv']['video']['videoCodec'])) {
+			//		break;
+			//	}
+			//}
+
+			$ThisTagHeader = fread($fd, 16);
+
+			$PreviousTagLength = getid3_lib::BigEndian2Int(substr($ThisTagHeader,  0, 4));
+			$TagType           = getid3_lib::BigEndian2Int(substr($ThisTagHeader,  4, 1));
+			$DataLength        = getid3_lib::BigEndian2Int(substr($ThisTagHeader,  5, 3));
+			$Timestamp         = getid3_lib::BigEndian2Int(substr($ThisTagHeader,  8, 3));
+			$LastHeaderByte    = getid3_lib::BigEndian2Int(substr($ThisTagHeader, 15, 1));
+			$NextOffset = ftell($fd) - 1 + $DataLength;
+
+			switch ($TagType) {
+				case GETID3_FLV_TAG_AUDIO:
+					if (!isset($ThisFileInfo['flv']['audio']['audioFormat'])) {
+						$ThisFileInfo['flv']['audio']['audioFormat']     =  $LastHeaderByte & 0x07;
+						$ThisFileInfo['flv']['audio']['audioRate']       = ($LastHeaderByte & 0x30) / 0x10;
+						$ThisFileInfo['flv']['audio']['audioSampleSize'] = ($LastHeaderByte & 0x40) / 0x40;
+						$ThisFileInfo['flv']['audio']['audioType']       = ($LastHeaderByte & 0x80) / 0x80;
+					}
+					break;
+
+				case GETID3_FLV_TAG_VIDEO:
+					if (!isset($ThisFileInfo['flv']['video']['videoCodec'])) {
+						$ThisFileInfo['flv']['video']['videoCodec'] = $LastHeaderByte & 0x07;
+
+						$FLVvideoHeader = fread($fd, 11);
+
+						if ($ThisFileInfo['flv']['video']['videoCodec'] != GETID3_FLV_VIDEO_VP6) {
+
+							$PictureSizeType = (getid3_lib::BigEndian2Int(substr($FLVvideoHeader, 3, 2))) >> 7;
+							$PictureSizeType = $PictureSizeType & 0x0007;
+							$ThisFileInfo['flv']['header']['videoSizeType'] = $PictureSizeType;
+							switch ($PictureSizeType) {
+								case 0:
+									$PictureSizeEnc = getid3_lib::BigEndian2Int(substr($FLVvideoHeader, 5, 2));
+									$PictureSizeEnc <<= 1;
+									$ThisFileInfo['video']['resolution_x'] = ($PictureSizeEnc & 0xFF00) >> 8;
+									$PictureSizeEnc = getid3_lib::BigEndian2Int(substr($FLVvideoHeader, 6, 2));
+									$PictureSizeEnc <<= 1;
+									$ThisFileInfo['video']['resolution_y'] = ($PictureSizeEnc & 0xFF00) >> 8;
+									break;
+
+								case 1:
+									$PictureSizeEnc = getid3_lib::BigEndian2Int(substr($FLVvideoHeader, 5, 4));
+									$PictureSizeEnc <<= 1;
+									$ThisFileInfo['video']['resolution_x'] = ($PictureSizeEnc & 0xFFFF0000) >> 16;
+
+									$PictureSizeEnc = getid3_lib::BigEndian2Int(substr($FLVvideoHeader, 7, 4));
+									$PictureSizeEnc <<= 1;
+									$ThisFileInfo['video']['resolution_y'] = ($PictureSizeEnc & 0xFFFF0000) >> 16;
+									break;
+
+								case 2:
+									$ThisFileInfo['video']['resolution_x'] = 352;
+									$ThisFileInfo['video']['resolution_y'] = 288;
+									break;
+
+								case 3:
+									$ThisFileInfo['video']['resolution_x'] = 176;
+									$ThisFileInfo['video']['resolution_y'] = 144;
+									break;
+
+								case 4:
+									$ThisFileInfo['video']['resolution_x'] = 128;
+									$ThisFileInfo['video']['resolution_y'] = 96;
+									break;
+
+								case 5:
+									$ThisFileInfo['video']['resolution_x'] = 320;
+									$ThisFileInfo['video']['resolution_y'] = 240;
+									break;
+
+								case 6:
+									$ThisFileInfo['video']['resolution_x'] = 160;
+									$ThisFileInfo['video']['resolution_y'] = 120;
+									break;
+
+								default:
+									$ThisFileInfo['video']['resolution_x'] = 0;
+									$ThisFileInfo['video']['resolution_y'] = 0;
+									break;
+
+							}
+						}
+					}
+					break;
+
+				// Meta tag
+				case GETID3_FLV_TAG_META:
+
+					fseek($fd, -1, SEEK_CUR);
+					$reader = new AMFReader(new AMFStream(fread($fd, $DataLength)));
+					$eventName = $reader->readData();
+					$ThisFileInfo['meta'][$eventName] = $reader->readData();
+					unset($reader);
+
+					$ThisFileInfo['video']['frame_rate']   = $ThisFileInfo['meta']['onMetaData']['framerate'];
+					$ThisFileInfo['video']['resolution_x'] = $ThisFileInfo['meta']['onMetaData']['width'];
+					$ThisFileInfo['video']['resolution_y'] = $ThisFileInfo['meta']['onMetaData']['height'];
+					break;
+
+				default:
+					// noop
+					break;
+			}
+
+			if ($Timestamp > $Duration) {
+				$Duration = $Timestamp;
+			}
+
+			fseek($fd, $NextOffset, SEEK_SET);
+		}
+
+		$ThisFileInfo['playtime_seconds'] = $Duration / 1000;
+		$ThisFileInfo['bitrate'] = ($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) / $ThisFileInfo['playtime_seconds'];
+
+		if ($ThisFileInfo['flv']['header']['hasAudio']) {
+			$ThisFileInfo['audio']['codec']           =   $this->FLVaudioFormat($ThisFileInfo['flv']['audio']['audioFormat']);
+			$ThisFileInfo['audio']['sample_rate']     =     $this->FLVaudioRate($ThisFileInfo['flv']['audio']['audioRate']);
+			$ThisFileInfo['audio']['bits_per_sample'] = $this->FLVaudioBitDepth($ThisFileInfo['flv']['audio']['audioSampleSize']);
+
+			$ThisFileInfo['audio']['channels']   = $ThisFileInfo['flv']['audio']['audioType'] + 1; // 0=mono,1=stereo
+			$ThisFileInfo['audio']['lossless']   = ($ThisFileInfo['flv']['audio']['audioFormat'] ? false : true); // 0=uncompressed
+			$ThisFileInfo['audio']['dataformat'] = 'flv';
+		}
+		if (@$ThisFileInfo['flv']['header']['hasVideo']) {
+			$ThisFileInfo['video']['codec']      = $this->FLVvideoCodec($ThisFileInfo['flv']['video']['videoCodec']);
+			$ThisFileInfo['video']['dataformat'] = 'flv';
+			$ThisFileInfo['video']['lossless']   = false;
+		}
+
+		return true;
+	}
+
+
+	function FLVaudioFormat($id) {
+		$FLVaudioFormat = array(
+			0 => 'uncompressed',
+			1 => 'ADPCM',
+			2 => 'mp3',
+			5 => 'Nellymoser 8kHz mono',
+			6 => 'Nellymoser',
+		);
+		return (@$FLVaudioFormat[$id] ? @$FLVaudioFormat[$id] : false);
+	}
+
+	function FLVaudioRate($id) {
+		$FLVaudioRate = array(
+			0 =>  5500,
+			1 => 11025,
+			2 => 22050,
+			3 => 44100,
+		);
+		return (@$FLVaudioRate[$id] ? @$FLVaudioRate[$id] : false);
+	}
+
+	function FLVaudioBitDepth($id) {
+		$FLVaudioBitDepth = array(
+			0 =>  8,
+			1 => 16,
+		);
+		return (@$FLVaudioBitDepth[$id] ? @$FLVaudioBitDepth[$id] : false);
+	}
+
+	function FLVvideoCodec($id) {
+		$FLVvideoCodec = array(
+			GETID3_FLV_VIDEO_H263   => 'Sorenson H.263',
+			GETID3_FLV_VIDEO_SCREEN => 'Screen video',
+			GETID3_FLV_VIDEO_VP6    => 'On2 VP6',
+		);
+		return (@$FLVvideoCodec[$id] ? @$FLVvideoCodec[$id] : false);
+	}
+}
+
+class AMFStream {
+	var $bytes;
+	var $pos;
+
+	function AMFStream(&$bytes) {
+		$this->bytes =& $bytes;
+		$this->pos = 0;
+	}
+
+	function readByte() {
+		return getid3_lib::BigEndian2Int(substr($this->bytes, $this->pos++, 1));
+	}
+
+	function readInt() {
+		return ($this->readByte() << 8) + $this->readByte();
+	}
+
+	function readLong() {
+		return ($this->readByte() << 24) + ($this->readByte() << 16) + ($this->readByte() << 8) + $this->readByte();
+	}
+
+	function readDouble() {
+		return getid3_lib::BigEndian2Float($this->read(8));
+	}
+
+	function readUTF() {
+		$length = $this->readInt();
+		return $this->read($length);
+	}
+
+	function readLongUTF() {
+		$length = $this->readLong();
+		return $this->read($length);
+	}
+
+	function read($length) {
+		$val = substr($this->bytes, $this->pos, $length);
+		$this->pos += $length;
+		return $val;
+	}
+
+	function peekByte() {
+		$pos = $this->pos;
+		$val = $this->readByte();
+		$this->pos = $pos;
+		return $val;
+	}
+
+	function peekInt() {
+		$pos = $this->pos;
+		$val = $this->readInt();
+		$this->pos = $pos;
+		return $val;
+	}
+
+	function peekLong() {
+		$pos = $this->pos;
+		$val = $this->readLong();
+		$this->pos = $pos;
+		return $val;
+	}
+
+	function peekDouble() {
+		$pos = $this->pos;
+		$val = $this->readDouble();
+		$this->pos = $pos;
+		return $val;
+	}
+
+	function peekUTF() {
+		$pos = $this->pos;
+		$val = $this->readUTF();
+		$this->pos = $pos;
+		return $val;
+	}
+
+	function peekLongUTF() {
+		$pos = $this->pos;
+		$val = $this->readLongUTF();
+		$this->pos = $pos;
+		return $val;
+	}
+}
+
+class AMFReader {
+	var $stream;
+
+	function AMFReader(&$stream) {
+		$this->stream =& $stream;
+	}
+
+	function readData() {
+		$value = null;
+
+		$type = $this->stream->readByte();
+
+		switch($type) {
+			// Double
+			case 0:
+				$value = $this->readDouble();
+			break;
+
+			// Boolean
+			case 1:
+				$value = $this->readBoolean();
+				break;
+
+			// String
+			case 2:
+				$value = $this->readString();
+				break;
+
+			// Object
+			case 3:
+				$value = $this->readObject();
+				break;
+
+			// null
+			case 6:
+				return null;
+				break;
+
+			// Mixed array
+			case 8:
+				$value = $this->readMixedArray();
+				break;
+
+			// Array
+			case 10:
+				$value = $this->readArray();
+				break;
+
+			// Date
+			case 11:
+				$value = $this->readDate();
+				break;
+
+			// Long string
+			case 13:
+				$value = $this->readLongString();
+				break;
+
+			// XML (handled as string)
+			case 15:
+				$value = $this->readXML();
+				break;
+
+			// Typed object (handled as object)
+			case 16:
+				$value = $this->readTypedObject();
+				break;
+
+			// Long string
+			default:
+				$value = '(unknown or unsupported data type)';
+			break;
+		}
+
+		return $value;
+	}
+
+	function readDouble() {
+		return $this->stream->readDouble();
+	}
+
+	function readBoolean() {
+		return $this->stream->readByte() == 1;
+	}
+
+	function readString() {
+		return $this->stream->readUTF();
+	}
+
+	function readObject() {
+		// Get highest numerical index - ignored
+		$highestIndex = $this->stream->readLong();
+
+		$data = array();
+
+		while ($key = $this->stream->readUTF()) {
+			// Mixed array record ends with empty string (0x00 0x00) and 0x09
+			if (($key == '') && ($this->stream->peekByte() == 0x09)) {
+				// Consume byte
+				$this->stream->readByte();
+				break;
+			}
+
+			$data[$key] = $this->readData();
+		}
+
+		return $data;
+	}
+
+	function readMixedArray() {
+		// Get highest numerical index - ignored
+		$highestIndex = $this->stream->readLong();
+
+		$data = array();
+
+		while ($key = $this->stream->readUTF()) {
+			// Mixed array record ends with empty string (0x00 0x00) and 0x09
+			if (($key == '') && ($this->stream->peekByte() == 0x09)) {
+				// Consume byte
+				$this->stream->readByte();
+				break;
+			}
+
+			if (is_numeric($key)) {
+				$key = (float) $key;
+			}
+
+			$data[$key] = $this->readData();
+		}
+
+		return $data;
+	}
+
+	function readArray() {
+		$length = $this->stream->readLong();
+
+		$data = array();
+
+		for ($i = 0; $i < count($length); $i++) {
+			$data[] = $this->readData();
+		}
+
+		return $data;
+	}
+
+	function readDate() {
+		$timestamp = $this->stream->readDouble();
+		$timezone = $this->stream->readInt();
+		return $timestamp;
+	}
+
+	function readLongString() {
+		return $this->stream->readLongUTF();
+	}
+
+	function readXML() {
+		return $this->stream->readLongUTF();
+	}
+
+	function readTypedObject() {
+		$className = $this->stream->readUTF();
+		return $this->readObject();
+	}
+}
+
+?>
\ No newline at end of file

Modified: plog/trunk/class/gallery/getid3/module.audio-video.quicktime.php
===================================================================
--- plog/trunk/class/gallery/getid3/module.audio-video.quicktime.php	2006-07-24 16:40:16 UTC (rev 3789)
+++ plog/trunk/class/gallery/getid3/module.audio-video.quicktime.php	2006-07-24 16:56:08 UTC (rev 3790)
@@ -520,7 +520,7 @@
 						$atomstructure['time_to_sample_table'][$i]['sample_duration'] = getid3_lib::BigEndian2Int(substr($atomdata, $sttsEntriesDataOffset, 4));
 						$sttsEntriesDataOffset += 4;
 
-						if (!empty($ThisFileInfo['quicktime']['time_scale'])) {
+						if (!empty($ThisFileInfo['quicktime']['time_scale']) && (@$atomstructure['time_to_sample_table'][$i]['sample_duration'] > 0)) {
 							$stts_new_framerate = $ThisFileInfo['quicktime']['time_scale'] / $atomstructure['time_to_sample_table'][$i]['sample_duration'];
 							if ($stts_new_framerate <= 60) {
 								// some atoms have durations of "1" giving a very large framerate, which probably is not right
@@ -853,8 +853,10 @@
 					$ThisFileInfo['video']['resolution_x'] = $atomstructure['width'];
 					$ThisFileInfo['video']['resolution_y'] = $atomstructure['height'];
 				}
-				$ThisFileInfo['video']['resolution_x'] = max($ThisFileInfo['video']['resolution_x'], $atomstructure['width']);
-				$ThisFileInfo['video']['resolution_y'] = max($ThisFileInfo['video']['resolution_y'], $atomstructure['height']);
+				if ($atomstructure['flags']['enabled'] == 1) {
+					$ThisFileInfo['video']['resolution_x'] = max($ThisFileInfo['video']['resolution_x'], $atomstructure['width']);
+					$ThisFileInfo['video']['resolution_y'] = max($ThisFileInfo['video']['resolution_y'], $atomstructure['height']);
+				}
 				if (!empty($ThisFileInfo['video']['resolution_x']) && !empty($ThisFileInfo['video']['resolution_y'])) {
 					$ThisFileInfo['quicktime']['video']['resolution_x'] = $ThisFileInfo['video']['resolution_x'];
 					$ThisFileInfo['quicktime']['video']['resolution_y'] = $ThisFileInfo['video']['resolution_y'];

Modified: plog/trunk/class/gallery/getid3/module.audio-video.riff.php
===================================================================
--- plog/trunk/class/gallery/getid3/module.audio-video.riff.php	2006-07-24 16:40:16 UTC (rev 3789)
+++ plog/trunk/class/gallery/getid3/module.audio-video.riff.php	2006-07-24 16:56:08 UTC (rev 3790)
@@ -201,7 +201,7 @@
 					$thisfile_riff_WAVE_bext_0['reserved']       = getid3_lib::LittleEndian2Int(substr($thisfile_riff_WAVE_bext_0['data'], 347, 254));
 					$thisfile_riff_WAVE_bext_0['coding_history'] =         explode("\r\n", trim(substr($thisfile_riff_WAVE_bext_0['data'], 601)));
 
-					$thisfile_riff_WAVE_bext_0['origin_date_unix'] = mktime(
+					$thisfile_riff_WAVE_bext_0['origin_date_unix'] = gmmktime(
 																				substr($thisfile_riff_WAVE_bext_0['origin_time'], 0, 2),
 																				substr($thisfile_riff_WAVE_bext_0['origin_time'], 3, 2),
 																				substr($thisfile_riff_WAVE_bext_0['origin_time'], 6, 2),
@@ -1005,7 +1005,7 @@
 	}
 
 
-	function RIFFcommentsParse(&$RIFFinfoArray, &$CommentsTargetArray) {
+	function RIFFcommentsParse(&$RIFFinfoArray, &$CommentsTargetArray) {
 		$RIFFinfoKeyLookup = array(
 			'IARL'=>'archivallocation',
 			'IART'=>'artist',
@@ -1055,9 +1055,9 @@
 					}
 				}
 			}
-		}
+		}
 		return true;
-	}
+	}
 
 	function ParseRIFF(&$fd, $startoffset, $maxoffset, &$ThisFileInfo) {
 
@@ -1953,8 +1953,6 @@
 			XMPG	Xing MPEG (I-Frame only)
 			XVID	XviD MPEG-4 (www.xvid.org)
 			XXAN	?XXAN?
-			Y422	ADS Technologies Copy of UYVY used in Pyro WebCam firewire camera
-			Y800	Simple, single Y plane for monochrome images
 			YU92	Intel YUV (YU92)
 			YUNV	Nvidia Uncompressed YUV 4:2:2
 			YUVP	Extended PAL format YUV palette (www.riff.org)
@@ -1965,6 +1963,8 @@
 			Y41T	Brooktree PC1 YUV 4:1:1 with transparency
 			Y42B	Weitek YUV 4:2:2 Planar
 			Y42T	Brooktree UYUV 4:2:2 with transparency
+			Y422	ADS Technologies Copy of UYVY used in Pyro WebCam firewire camera
+			Y800	Simple, single Y plane for monochrome images
 			Y8  	Grayscale video
 			YC12	Intel YUV 12 codec
 			YUV8	Winnov Caviar YUV8

Modified: plog/trunk/class/gallery/getid3/module.audio.aac.php
===================================================================
--- plog/trunk/class/gallery/getid3/module.audio.aac.php	2006-07-24 16:40:16 UTC (rev 3789)
+++ plog/trunk/class/gallery/getid3/module.audio.aac.php	2006-07-24 16:56:08 UTC (rev 3790)
@@ -299,7 +299,7 @@
 		}
 
 		// used to calculate bitrate below
-		static $BitrateCache = array();
+		$BitrateCache = array();
 
 
 		while (true) {

Modified: plog/trunk/class/gallery/getid3/module.audio.flac.php
===================================================================
--- plog/trunk/class/gallery/getid3/module.audio.flac.php	2006-07-24 16:40:16 UTC (rev 3789)
+++ plog/trunk/class/gallery/getid3/module.audio.flac.php	2006-07-24 16:56:08 UTC (rev 3790)
@@ -47,7 +47,7 @@
 			$METAdataBlockLength          = getid3_lib::BigEndian2Int(substr($METAdataBlockHeader, 1, 3));
 			$METAdataBlockTypeText        = getid3_flac::FLACmetaBlockTypeLookup($METAdataBlockType);
 
-			if ($METAdataBlockLength <= 0) {
+			if ($METAdataBlockLength < 0) {
 				$ThisFileInfo['error'][] = 'corrupt or invalid METADATA_BLOCK_HEADER.BLOCK_TYPE ('.$METAdataBlockType.') at offset '.$METAdataBlockOffset;
 				break;
 			}
@@ -60,7 +60,7 @@
 			$ThisFileInfo_flac_METAdataBlockTypeText_raw['block_type']      = $METAdataBlockType;
 			$ThisFileInfo_flac_METAdataBlockTypeText_raw['block_type_text'] = $METAdataBlockTypeText;
 			$ThisFileInfo_flac_METAdataBlockTypeText_raw['block_length']    = $METAdataBlockLength;
-			$ThisFileInfo_flac_METAdataBlockTypeText_raw['block_data']      = fread($fd, $METAdataBlockLength);
+			$ThisFileInfo_flac_METAdataBlockTypeText_raw['block_data']      = @fread($fd, $METAdataBlockLength);
 			$ThisFileInfo['avdataoffset'] = ftell($fd);
 
 			switch ($METAdataBlockTypeText) {

Modified: plog/trunk/class/gallery/getid3/module.audio.midi.php
===================================================================
--- plog/trunk/class/gallery/getid3/module.audio.midi.php	2006-07-24 16:40:16 UTC (rev 3789)
+++ plog/trunk/class/gallery/getid3/module.audio.midi.php	2006-07-24 16:56:08 UTC (rev 3790)
@@ -273,10 +273,7 @@
 			}
 			$previoustickoffset = null;
 
-            if ($MicroSecondsPerQuarterNoteAfter != null)
-            {
-			     ksort($MicroSecondsPerQuarterNoteAfter);
-			}
+			ksort($MicroSecondsPerQuarterNoteAfter);
 			foreach ($MicroSecondsPerQuarterNoteAfter as $tickoffset => $microsecondsperbeat) {
 				if (is_null($previoustickoffset)) {
 					$prevmicrosecondsperbeat = $microsecondsperbeat;

Modified: plog/trunk/class/gallery/getid3/module.audio.mp3.php
===================================================================
--- plog/trunk/class/gallery/getid3/module.audio.mp3.php	2006-07-24 16:40:16 UTC (rev 3789)
+++ plog/trunk/class/gallery/getid3/module.audio.mp3.php	2006-07-24 16:56:08 UTC (rev 3790)
@@ -320,8 +320,9 @@
 									'fast standard|19000' => 19000,
 									'r3mix|19500'         => 19500,  // 3.90,   3.90.1, 3.92
 									'r3mix|19600'         => 19600,  // 3.90.2, 3.90.3, 3.91
-									'r3mix|18000'         => 18000); // 3.94,   3.95
-							if (!isset($ExpectedLowpass[$ExplodedOptions[1].'|'.$thisfile_mpeg_audio_lame['lowpass_frequency']])) {
+									'r3mix|18000'         => 18000,  // 3.94,   3.95
+								);
+							if (!isset($ExpectedLowpass[$ExplodedOptions[1].'|'.$thisfile_mpeg_audio_lame['lowpass_frequency']]) && ($thisfile_mpeg_audio_lame['lowpass_frequency'] < 22050) && (round($thisfile_mpeg_audio_lame['lowpass_frequency'] / 1000) < round($thisfile_mpeg_audio['sample_rate'] / 2000))) {
 								$encoder_options .= ' --lowpass '.$thisfile_mpeg_audio_lame['lowpass_frequency'];
 							}
 							break;
@@ -1636,23 +1637,17 @@
 		if (isset($MPEGaudioVersionLookup[$rawarray['version']])) {
 			$decodedVersion = $MPEGaudioVersionLookup[$rawarray['version']];
 		} else {
-			if ($echoerrors) {
-				echo "\n".'invalid Version ('.$rawarray['version'].')';
-			}
+			echo ($echoerrors ? "\n".'invalid Version ('.$rawarray['version'].')' : '');
 			return false;
 		}
 		if (isset($MPEGaudioLayerLookup[$rawarray['layer']])) {
 			$decodedLayer = $MPEGaudioLayerLookup[$rawarray['layer']];
 		} else {
-			if ($echoerrors) {
-				echo "\n".'invalid Layer ('.$rawarray['layer'].')';
-			}
+			echo ($echoerrors ? "\n".'invalid Layer ('.$rawarray['layer'].')' : '');
 			return false;
 		}
 		if (!isset($MPEGaudioBitrateLookup[$decodedVersion][$decodedLayer][$rawarray['bitrate']])) {
-			if ($echoerrors) {
-				echo "\n".'invalid Bitrate ('.$rawarray['bitrate'].')';
-			}
+			echo ($echoerrors ? "\n".'invalid Bitrate ('.$rawarray['bitrate'].')' : '');
 			if ($rawarray['bitrate'] == 15) {
 				// known issue in LAME 3.90 - 3.93.1 where free-format has bitrate ID of 15 instead of 0
 				// let it go through here otherwise file will not be identified
@@ -1664,27 +1659,19 @@
 			}
 		}
 		if (!isset($MPEGaudioFrequencyLookup[$decodedVersion][$rawarray['sample_rate']])) {
-			if ($echoerrors) {
-				echo "\n".'invalid Frequency ('.$rawarray['sample_rate'].')';
-			}
+			echo ($echoerrors ? "\n".'invalid Frequency ('.$rawarray['sample_rate'].')' : '');
 			return false;
 		}
 		if (!isset($MPEGaudioChannelModeLookup[$rawarray['channelmode']])) {
-			if ($echoerrors) {
-				echo "\n".'invalid ChannelMode ('.$rawarray['channelmode'].')';
-			}
+			echo ($echoerrors ? "\n".'invalid ChannelMode ('.$rawarray['channelmode'].')' : '');
 			return false;
 		}
 		if (!isset($MPEGaudioModeExtensionLookup[$decodedLayer][$rawarray['modeextension']])) {
-			if ($echoerrors) {
-				echo "\n".'invalid Mode Extension ('.$rawarray['modeextension'].')';
-			}
+			echo ($echoerrors ? "\n".'invalid Mode Extension ('.$rawarray['modeextension'].')' : '');
 			return false;
 		}
 		if (!isset($MPEGaudioEmphasisLookup[$rawarray['emphasis']])) {
-			if ($echoerrors) {
-				echo "\n".'invalid Emphasis ('.$rawarray['emphasis'].')';
-			}
+			echo ($echoerrors ? "\n".'invalid Emphasis ('.$rawarray['emphasis'].')' : '');
 			return false;
 		}
 		// These are just either set or not set, you can't mess that up :)
@@ -1893,52 +1880,58 @@
 	}
 
 	function LAMEpresetUsedLookup($LAMEtag) {
-		if ($LAMEtag['preset_used_id'] == 0) {
-			// no preset used (LAME >=3.93)
-			// no preset recorded (LAME <3.93)
-			return '';
-		}
-		static $LAMEpresetUsedLookup = array();
-		if (empty($LAMEpresetUsedLookup)) {
-			for ($i = 8; $i <= 320; $i++) {
-				switch ($LAMEtag['vbr_method']) {
-					case 'cbr':
-						$LAMEpresetUsedLookup[$i] = '--alt-preset '.$LAMEtag['vbr_method'].' '.$i;
-						break;
-					case 'abr':
-					default: // other VBR modes shouldn't be here(?)
-						$LAMEpresetUsedLookup[$i] = '--alt-preset '.$i;
-						break;
-				}
-			}
 
-			// named old-style presets (studio, phone, voice, etc) are handled in GuessEncoderOptions()
+        if ($LAMEtag['preset_used_id'] == 0) {
+            // no preset used (LAME >=3.93)
+            // no preset recorded (LAME <3.93)
+            return '';
+        }
+        $LAMEpresetUsedLookup = array();
 
-			// named alt-presets
-			$LAMEpresetUsedLookup[1000] = '--r3mix';
-			$LAMEpresetUsedLookup[1001] = '--alt-preset standard';
-			$LAMEpresetUsedLookup[1002] = '--alt-preset extreme';
-			$LAMEpresetUsedLookup[1003] = '--alt-preset insane';
-			$LAMEpresetUsedLookup[1004] = '--alt-preset fast standard';
-			$LAMEpresetUsedLookup[1005] = '--alt-preset fast extreme';
-			$LAMEpresetUsedLookup[1006] = '--alt-preset medium';
-			$LAMEpresetUsedLookup[1007] = '--alt-preset fast medium';
+        /////  THIS PART CANNOT BE STATIC .
+        for ($i = 8; $i <= 320; $i++) {
+            switch ($LAMEtag['vbr_method']) {
+                case 'cbr':
+                    $LAMEpresetUsedLookup[$i] = '--alt-preset '.$LAMEtag['vbr_method'].' '.$i;
+                    break;
+                case 'abr':
+                default: // other VBR modes shouldn't be here(?)
+                    $LAMEpresetUsedLookup[$i] = '--alt-preset '.$i;
+                    break;
+            }
+        }
 
-			// LAME 3.94 additions/changes
-			$LAMEpresetUsedLookup[1010] = '--preset portable';                                                           // 3.94a15 Oct 21 2003
-			$LAMEpresetUsedLookup[1015] = '--preset radio';                                                              // 3.94a15 Oct 21 2003
+        // named old-style presets (studio, phone, voice, etc) are handled in GuessEncoderOptions()
 
-			$LAMEpresetUsedLookup[320]  = '--preset insane';                                                             // 3.94a15 Nov 12 2003
-			$LAMEpresetUsedLookup[430]  = '--preset radio';                                                              // 3.94a15 Nov 12 2003
-			$LAMEpresetUsedLookup[450]  = '--preset '.(($LAMEtag['raw']['vbr_method'] == 4) ? 'fast ' : '').'portable';  // 3.94a15 Nov 12 2003
-			$LAMEpresetUsedLookup[460]  = '--preset '.(($LAMEtag['raw']['vbr_method'] == 4) ? 'fast ' : '').'medium';    // 3.94a15 Nov 12 2003
-			$LAMEpresetUsedLookup[470]  = '--r3mix';                                                                     // 3.94b1  Dec 18 2003
-			$LAMEpresetUsedLookup[480]  = '--preset '.(($LAMEtag['raw']['vbr_method'] == 4) ? 'fast ' : '').'standard';  // 3.94a15 Nov 12 2003
-			$LAMEpresetUsedLookup[500]  = '--preset '.(($LAMEtag['raw']['vbr_method'] == 4) ? 'fast ' : '').'extreme';   // 3.94a15 Nov 12 2003
-		}
-		return (isset($LAMEpresetUsedLookup[$LAMEtag['preset_used_id']]) ? $LAMEpresetUsedLookup[$LAMEtag['preset_used_id']] : 'new/unknown preset: '.$LAMEtag['preset_used_id'].' - report to info at getid3.org');
-	}
+        // named alt-presets
+        $LAMEpresetUsedLookup[1000] = '--r3mix';
+        $LAMEpresetUsedLookup[1001] = '--alt-preset standard';
+        $LAMEpresetUsedLookup[1002] = '--alt-preset extreme';
+        $LAMEpresetUsedLookup[1003] = '--alt-preset insane';
+        $LAMEpresetUsedLookup[1004] = '--alt-preset fast standard';
+        $LAMEpresetUsedLookup[1005] = '--alt-preset fast extreme';
+        $LAMEpresetUsedLookup[1006] = '--alt-preset medium';
+        $LAMEpresetUsedLookup[1007] = '--alt-preset fast medium';
 
+        // LAME 3.94 additions/changes
+        $LAMEpresetUsedLookup[1010] = '--preset portable';                                                           // 3.94a15 Oct 21 2003
+        $LAMEpresetUsedLookup[1015] = '--preset radio';                                                              // 3.94a15 Oct 21 2003
+
+        $LAMEpresetUsedLookup[320]  = '--preset insane';                                                             // 3.94a15 Nov 12 2003
+        $LAMEpresetUsedLookup[410]  = '-V9';
+        $LAMEpresetUsedLookup[420]  = '-V8';
+        $LAMEpresetUsedLookup[440]  = '-V6';
+        $LAMEpresetUsedLookup[430]  = '--preset radio';                                                              // 3.94a15 Nov 12 2003
+        $LAMEpresetUsedLookup[450]  = '--preset '.(($LAMEtag['raw']['vbr_method'] == 4) ? 'fast ' : '').'portable';  // 3.94a15 Nov 12 2003
+        $LAMEpresetUsedLookup[460]  = '--preset '.(($LAMEtag['raw']['vbr_method'] == 4) ? 'fast ' : '').'medium';    // 3.94a15 Nov 12 2003
+        $LAMEpresetUsedLookup[470]  = '--r3mix';                                                                     // 3.94b1  Dec 18 2003
+        $LAMEpresetUsedLookup[480]  = '--preset '.(($LAMEtag['raw']['vbr_method'] == 4) ? 'fast ' : '').'standard';  // 3.94a15 Nov 12 2003
+        $LAMEpresetUsedLookup[490]  = '-V1';
+        $LAMEpresetUsedLookup[500]  = '--preset '.(($LAMEtag['raw']['vbr_method'] == 4) ? 'fast ' : '').'extreme';   // 3.94a15 Nov 12 2003
+        
+        return (isset($LAMEpresetUsedLookup[$LAMEtag['preset_used_id']]) ? $LAMEpresetUsedLookup[$LAMEtag['preset_used_id']] : 'new/unknown preset: '.$LAMEtag['preset_used_id'].' - report to info at getid3.org');
+    }
+
 }
 
 ?>
\ No newline at end of file

Modified: plog/trunk/class/gallery/getid3/module.audio.shorten.php
===================================================================
--- plog/trunk/class/gallery/getid3/module.audio.shorten.php	2006-07-24 16:40:16 UTC (rev 3789)
+++ plog/trunk/class/gallery/getid3/module.audio.shorten.php	2006-07-24 16:56:08 UTC (rev 3790)
@@ -124,6 +124,7 @@
 				}
 			}
 			$commandline = GETID3_HELPERAPPSDIR.'shorten.exe -x "'.$ThisFileInfo['filenamepath'].'" - | '.GETID3_HELPERAPPSDIR.'head.exe -c 44';
+			$commandline = str_replace('/', '\\', $commandline);
 
 		} else {
 
@@ -135,7 +136,7 @@
                 $ThisFileInfo['error'][] = 'shorten binary was not found in path or /usr/local/bin';
                 return false;
             }
-            $commandline = (file_exists('/usr/local/bin/shorten') ? '/usr/local/bin/' : '' ) . 'shorten -x "'.$ThisFileInfo['filenamepath'].'" - | head -c 44';
+            $commandline = (file_exists('/usr/local/bin/shorten') ? '/usr/local/bin/' : '' ) . 'shorten -x '.escapeshellarg($ThisFileInfo['filenamepath']).' - | head -c 44';
 
 		}
 

Added: plog/trunk/class/gallery/getid3/module.graphic.svg.php
===================================================================
--- plog/trunk/class/gallery/getid3/module.graphic.svg.php	2006-07-24 16:40:16 UTC (rev 3789)
+++ plog/trunk/class/gallery/getid3/module.graphic.svg.php	2006-07-24 16:56:08 UTC (rev 3790)
@@ -0,0 +1,52 @@
+<?php
+/////////////////////////////////////////////////////////////////
+/// getID3() by James Heinrich <info at getid3.org>               //
+//  available at http://getid3.sourceforge.net                 //
+//            or http://www.getid3.org                         //
+/////////////////////////////////////////////////////////////////
+// See readme.txt for more details                             //
+/////////////////////////////////////////////////////////////////
+//                                                             //
+// module.graphic.svg.php                                      //
+// module for analyzing SVG Image files                        //
+// dependencies: NONE                                          //
+// author: Bryce Harrington <bryceØbryceharrington*org>        //
+//                                                            ///
+/////////////////////////////////////////////////////////////////
+
+
+class getid3_svg
+{
+
+
+	function getid3_svg(&$fd, &$ThisFileInfo) {
+		fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET);
+
+		// I'm making this up, please modify as appropriate
+		$SVGheader = fread($fd, 32);
+		$ThisFileInfo['svg']['magic']  = substr($SVGheader, 0, 4);
+		if ($ThisFileInfo['svg']['magic'] == 'aBcD') {
+
+			$ThisFileInfo['fileformat']                  = 'svg';
+			$ThisFileInfo['video']['dataformat']         = 'svg';
+			$ThisFileInfo['video']['lossless']           = true;
+			$ThisFileInfo['video']['bits_per_sample']    = 24;
+			$ThisFileInfo['video']['pixel_aspect_ratio'] = (float) 1;
+
+			$ThisFileInfo['svg']['width']  = getid3_lib::LittleEndian2Int(substr($fileData, 4, 4));
+			$ThisFileInfo['svg']['height'] = getid3_lib::LittleEndian2Int(substr($fileData, 8, 4));
+
+			$ThisFileInfo['video']['resolution_x'] = $ThisFileInfo['svg']['width'];
+			$ThisFileInfo['video']['resolution_y'] = $ThisFileInfo['svg']['height'];
+
+			return true;
+		}
+		$ThisFileInfo['error'][] = 'Did not find SVG magic bytes "aBcD" at '.$ThisFileInfo['avdataoffset'];
+		unset($ThisFileInfo['fileformat']);
+		return false;
+	}
+
+}
+
+
+?>
\ No newline at end of file

Modified: plog/trunk/class/gallery/getid3/module.misc.iso.php
===================================================================
--- plog/trunk/class/gallery/getid3/module.misc.iso.php	2006-07-24 16:40:16 UTC (rev 3789)
+++ plog/trunk/class/gallery/getid3/module.misc.iso.php	2006-07-24 16:56:08 UTC (rev 3790)
@@ -338,7 +338,7 @@
 		if (!$UNIXyear) {
 			return false;
 		}
-		return mktime($UNIXhour, $UNIXminute, $UNIXsecond, $UNIXmonth, $UNIXday, $UNIXyear);
+		return gmmktime($UNIXhour, $UNIXminute, $UNIXsecond, $UNIXmonth, $UNIXday, $UNIXyear);
 	}
 
 	function ISOtime2UNIXtime($ISOtime) {
@@ -359,7 +359,7 @@
 		$UNIXsecond = ord($ISOtime{5});
 		$GMToffset  = $this->TwosCompliment2Decimal(ord($ISOtime{5}));
 
-		return mktime($UNIXhour, $UNIXminute, $UNIXsecond, $UNIXmonth, $UNIXday, $UNIXyear);
+		return gmmktime($UNIXhour, $UNIXminute, $UNIXsecond, $UNIXmonth, $UNIXday, $UNIXyear);
 	}
 
 	function TwosCompliment2Decimal($BinaryValue) {

Modified: plog/trunk/class/gallery/getid3/module.tag.id3v2.php
===================================================================
--- plog/trunk/class/gallery/getid3/module.tag.id3v2.php	2006-07-24 16:40:16 UTC (rev 3789)
+++ plog/trunk/class/gallery/getid3/module.tag.id3v2.php	2006-07-24 16:56:08 UTC (rev 3790)
@@ -101,47 +101,51 @@
 		$thisfile_id3v2['tag_offset_start'] = $StartingOffset;
 		$thisfile_id3v2['tag_offset_end']   = $thisfile_id3v2['tag_offset_start'] + $thisfile_id3v2['headerlength'];
 
-	//    Extended Header
+		//    Extended Header
 		if (isset($thisfile_id3v2_flags['exthead']) && $thisfile_id3v2_flags['exthead']) {
-	//            Extended header size   4 * %0xxxxxxx
-	//            Number of flag bytes       $01
-	//            Extended Flags             $xx
-	//            Where the 'Extended header size' is the size of the whole extended header, stored as a 32 bit synchsafe integer.
-			$extheader = fread ($fd, 4);
-			$thisfile_id3v2['extheaderlength'] = getid3_lib::BigEndian2Int($extheader, 1);
+			// Extended header size   4 * %0xxxxxxx
+			// Number of flag bytes       $01
+			// Extended Flags             $xx
+			// Where the 'Extended header size' is the size of the whole extended header, stored as a 32 bit synchsafe integer.
+			$thisfile_id3v2['exthead_length'] = getid3_lib::BigEndian2Int(fread($fd, 4), 1);
 
-	//            The extended flags field, with its size described by 'number of flag  bytes', is defined as:
-	//                %0bcd0000
-	//            b - Tag is an update
-	//                Flag data length       $00
-	//            c - CRC data present
-	//                Flag data length       $05
-	//                Total frame CRC    5 * %0xxxxxxx
-	//            d - Tag restrictions
-	//                Flag data length       $01
-			$extheaderflagbytes = fread ($fd, 1);
-			$extheaderflags     = fread ($fd, $extheaderflagbytes);
-			$id3_exthead_flags = getid3_lib::BigEndian2Bin(substr($header, 5, 1));
-			$thisfile_id3v2['exthead_flags']['update']       = substr($id3_exthead_flags, 1, 1);
-			$thisfile_id3v2['exthead_flags']['CRC']          = substr($id3_exthead_flags, 2, 1);
-			if ($thisfile_id3v2['exthead_flags']['CRC']) {
-				$extheaderrawCRC = fread ($fd, 5);
-				$thisfile_id3v2['exthead_flags']['CRC'] = getid3_lib::BigEndian2Int($extheaderrawCRC, 1);
+			$thisfile_id3v2['exthead_flag_bytes'] = ord(fread($fd, 1));
+			if ($thisfile_id3v2['exthead_flag_bytes'] == 1) {
+				// The extended flags field, with its size described by 'number of flag  bytes', is defined as:
+				//     %0bcd0000
+				// b - Tag is an update
+				//     Flag data length       $00
+				// c - CRC data present
+				//     Flag data length       $05
+				//     Total frame CRC    5 * %0xxxxxxx
+				// d - Tag restrictions
+				//     Flag data length       $01
+				$extheaderflags    = fread($fd, $thisfile_id3v2['exthead_flag_bytes']);
+				$id3_exthead_flags = getid3_lib::BigEndian2Bin(substr($header, 5, 1));
+				$thisfile_id3v2['exthead_flags']['update']       = substr($id3_exthead_flags, 1, 1);
+				$thisfile_id3v2['exthead_flags']['CRC']          = substr($id3_exthead_flags, 2, 1);
+				if ($thisfile_id3v2['exthead_flags']['CRC']) {
+					$extheaderrawCRC = fread($fd, 5);
+					$thisfile_id3v2['exthead_flags']['CRC'] = getid3_lib::BigEndian2Int($extheaderrawCRC, 1);
+				}
+				$thisfile_id3v2['exthead_flags']['restrictions'] = substr($id3_exthead_flags, 3, 1);
+				if ($thisfile_id3v2['exthead_flags']['restrictions']) {
+					// Restrictions           %ppqrrstt
+					$extheaderrawrestrictions = fread($fd, 1);
+					$thisfile_id3v2['exthead_flags']['restrictions_tagsize']  = (bindec('11000000') & ord($extheaderrawrestrictions)) >> 6; // p - Tag size restrictions
+					$thisfile_id3v2['exthead_flags']['restrictions_textenc']  = (bindec('00100000') & ord($extheaderrawrestrictions)) >> 5; // q - Text encoding restrictions
+					$thisfile_id3v2['exthead_flags']['restrictions_textsize'] = (bindec('00011000') & ord($extheaderrawrestrictions)) >> 3; // r - Text fields size restrictions
+					$thisfile_id3v2['exthead_flags']['restrictions_imgenc']   = (bindec('00000100') & ord($extheaderrawrestrictions)) >> 2; // s - Image encoding restrictions
+					$thisfile_id3v2['exthead_flags']['restrictions_imgsize']  = (bindec('00000011') & ord($extheaderrawrestrictions)) >> 0; // t - Image size restrictions
+				}
+			} else {
+				$ThisFileInfo['warning'][] = '$thisfile_id3v2[exthead_flag_bytes] = "'.$thisfile_id3v2['exthead_flag_bytes'].'" (expecting "1")';
+				fseek($fd, $thisfile_id3v2['exthead_length'] - 1, SEEK_CUR);
+				//return false;
 			}
-			$thisfile_id3v2['exthead_flags']['restrictions'] = substr($id3_exthead_flags, 3, 1);
-			if ($thisfile_id3v2['exthead_flags']['restrictions']) {
-				// Restrictions           %ppqrrstt
-				$extheaderrawrestrictions = fread ($fd, 1);
-				$thisfile_id3v2['exthead_flags']['restrictions_tagsize']  = (bindec('11000000') & ord($extheaderrawrestrictions)) >> 6; // p - Tag size restrictions
-				$thisfile_id3v2['exthead_flags']['restrictions_textenc']  = (bindec('00100000') & ord($extheaderrawrestrictions)) >> 5; // q - Text encoding restrictions
-				$thisfile_id3v2['exthead_flags']['restrictions_textsize'] = (bindec('00011000') & ord($extheaderrawrestrictions)) >> 3; // r - Text fields size restrictions
-				$thisfile_id3v2['exthead_flags']['restrictions_imgenc']   = (bindec('00000100') & ord($extheaderrawrestrictions)) >> 2; // s - Image encoding restrictions
-				$thisfile_id3v2['exthead_flags']['restrictions_imgsize']  = (bindec('00000011') & ord($extheaderrawrestrictions)) >> 0; // t - Image size restrictions
-			}
 		} // end extended header
 
 
-
 		// create 'encoding' key - used by getid3::HandleAllTags()
 		// in ID3v2 every field can have it's own encoding type
 		// so force everything to UTF-8 so it can be handled consistantly
@@ -159,10 +163,10 @@
 	//        Flags         $xx xx
 
 		$sizeofframes = $thisfile_id3v2['headerlength'] - 10; // not including 10-byte initial header
-		if (isset($thisfile_id3v2['extheaderlength'])) {
-			$sizeofframes -= $thisfile_id3v2['extheaderlength'];
+		if (@$thisfile_id3v2['exthead_length']) {
+			$sizeofframes -= ($thisfile_id3v2['exthead_length'] + 4);
 		}
-		if (isset($thisfile_id3v2_flags['isfooter']) && $thisfile_id3v2_flags['isfooter']) {
+		if (@$thisfile_id3v2_flags['isfooter']) {
 			$sizeofframes -= 10; // footer takes last 10 bytes of ID3v2 header, after frame data, before audio
 		}
 		if ($sizeofframes > 0) {
@@ -170,7 +174,7 @@
 			$framedata = fread($fd, $sizeofframes); // read all frames from file into $framedata variable
 
 			//    if entire frame data is unsynched, de-unsynch it now (ID3v2.3.x)
-			if (isset($thisfile_id3v2_flags['unsynch']) && $thisfile_id3v2_flags['unsynch'] && ($id3v2_majorversion <= 3)) {
+			if (@$thisfile_id3v2_flags['unsynch'] && ($id3v2_majorversion <= 3)) {
 				$framedata = $this->DeUnsynchronise($framedata);
 			}
 			//        [in ID3v2.4.0] Unsynchronisation [S:6.1] is done on frame level, instead
@@ -179,7 +183,7 @@
 			//        there exists an unsynchronised frame, while the new unsynchronisation flag in
 			//        the frame header [S:4.1.2] indicates unsynchronisation.
 
-			$framedataoffset = 10; // how many bytes into the stream - start from after the 10-byte header
+			$framedataoffset = 10 + (@$thisfile_id3v2['exthead_length'] ? $thisfile_id3v2['exthead_length'] + 4 : 0); // how many bytes into the stream - start from after the 10-byte header (and extended header length+4, if present)
 			while (isset($framedata) && (strlen($framedata) > 0)) { // cycle through until no more frame data is left to parse
 				if (strlen($framedata) <= $this->ID3v2HeaderLength($id3v2_majorversion)) {
 					// insufficient room left in ID3v2 header for actual data - must be padding
@@ -230,7 +234,7 @@
 						} elseif (($frame_name == "\x00".'MP3') || ($frame_name == "\x00\x00".'MP') || ($frame_name == ' MP3') || ($frame_name == 'MP3e')) {
 							// MP3ext known broken frames - "ok" for the purposes of this test
 						} elseif (($id3v2_majorversion == 4) && ($this->IsValidID3v2FrameName(substr($framedata, getid3_lib::BigEndian2Int(substr($frame_header, 4, 4), 0), 4), 3))) {
-							$ThisFileInfo['warning'][] = 'ID3v2 tag written as ID3v2.4, but with non-synchsafe integers (ID3v2.3 style). Older versions of Helium2 (www.helium2.com) is a known culprit of this. Tag has been parsed as ID3v2.3';
+							$ThisFileInfo['warning'][] = 'ID3v2 tag written as ID3v2.4, but with non-synchsafe integers (ID3v2.3 style). Older versions of (Helium2; iTunes) are known culprits of this. Tag has been parsed as ID3v2.3';
 							$id3v2_majorversion = 3;
 							$frame_size = getid3_lib::BigEndian2Int(substr($frame_header, 4, 4), 0); // 32-bit integer
 						}
@@ -288,7 +292,8 @@
 						} else {
 
 							// next frame is invalid too, abort processing
-							unset($framedata);
+							//unset($framedata);
+							$framedata = null;
 							$ThisFileInfo['error'][] = 'Next ID3v2 frame is also invalid, aborting processing.';
 
 						}
@@ -301,7 +306,8 @@
 					} else {
 
 						// next frame is invalid too, abort processing
-						unset($framedata);
+						//unset($framedata);
+						$framedata = null;
 						$ThisFileInfo['warning'][] = 'Invalid ID3v2 frame size, aborting.';
 
 					}
@@ -350,7 +356,7 @@
 	//        ID3v2 size             4 * %0xxxxxxx
 
 		if (isset($thisfile_id3v2_flags['isfooter']) && $thisfile_id3v2_flags['isfooter']) {
-			$footer = fread ($fd, 10);
+			$footer = fread($fd, 10);
 			if (substr($footer, 0, 3) == '3DI') {
 				$thisfile_id3v2['footer'] = true;
 				$thisfile_id3v2['majorversion_footer'] = ord($footer{3});
@@ -382,7 +388,11 @@
 			}
 		}
 
+		if (!isset($thisfile_id3v2['comments']['year']) && ereg('^([0-9]{4})', trim(@$thisfile_id3v2['comments']['recording_time'][0]), $matches)) {
+			$thisfile_id3v2['comments']['year'] = array($matches[1]);
+		}
 
+
 		// Set avdataoffset
 		$ThisFileInfo['avdataoffset'] = $thisfile_id3v2['headerlength'];
 		if (isset($thisfile_id3v2['footer'])) {
@@ -2869,89 +2879,169 @@
 
 		$begin = __LINE__;
 
-		/** This is not a comment!
+		/** This is not a comment!		
+				
+			AENC	audio_encryption
+			APIC	attached_picture
+			ASPI	audio_seek_point_index
+			BUF	recommended_buffer_size
+			CNT	play_counter
+			COM	comments
+			COMM	comments
+			COMR	commercial_frame
+			CRA	audio_encryption
+			CRM	encrypted_meta_frame
+			ENCR	encryption_method_registration
+			EQU	equalisation
+			EQU2	equalisation
+			EQUA	equalisation
+			ETC	event_timing_codes
+			ETCO	event_timing_codes
+			GEO	general_encapsulated_object
+			GEOB	general_encapsulated_object
+			GRID	group_identification_registration
+			IPL	involved_people_list
+			IPLS	involved_people_list
+			LINK	linked_information
+			LNK	linked_information
+			MCDI	music_cd_identifier
+			MCI	music_cd_identifier
+			MLL	mpeg_location_lookup_table
+			MLLT	mpeg_location_lookup_table
+			OWNE	ownership_frame
+			PCNT	play_counter
+			PIC	attached_picture
+			POP	popularimeter
+			POPM	popularimeter
+			POSS	position_synchronisation_frame
+			PRIV	private_frame
+			RBUF	recommended_buffer_size
+			REV	reverb
+			RVA	relative_volume_adjustment
+			RVA2	relative_volume_adjustment
+			RVAD	relative_volume_adjustment
+			RVRB	reverb
+			SEEK	seek_frame
+			SIGN	signature_frame
+			SLT	synchronised_lyric
+			STC	synced_tempo_codes
+			SYLT	synchronised_lyric
+			SYTC	synchronised_tempo_codes
+			TAL	album
+			TALB	album
+			TBP	bpm
+			TBPM	bpm
+			TCM	composer
+			TCO	content_type
+			TCOM	composer
+			TCON	content_type
+			TCOP	copyright_message
+			TCR	copyright_message
+			TDA	date
+			TDAT	date
+			TDEN	encoding_time
+			TDLY	playlist_delay
+			TDOR	original_release_time
+			TDRC	recording_time
+			TDRL	release_time
+			TDTG	tagging_time
+			TDY	playlist_delay
+			TEN	encoded_by
+			TENC	encoded_by
+			TEXT	lyricist
+			TFLT	file_type
+			TFT	file_type
+			TIM	time
+			TIME	time
+			TIPL	involved_people_list
+			TIT1	content_group_description
+			TIT2	title
+			TIT3	subtitle
+			TKE	initial_key
+			TKEY	initial_key
+			TLA	language
+			TLAN	language
+			TLE	length
+			TLEN	length
+			TMCL	musician_credits_list
+			TMED	media_type
+			TMOO	mood
+			TMT	media_type
+			TOA	original_artist
+			TOAL	original_album
+			TOF	original_filename
+			TOFN	original_filename
+			TOL	original_lyricist
+			TOLY	original_lyricist
+			TOPE	original_artist
+			TOR	original_year
+			TORY	original_year
+			TOT	original_album
+			TOWN	file_owner
+			TP1	artist
+			TP2	band
+			TP3	conductor
+			TP4	remixer
+			TPA	part_of_a_set
+			TPB	publisher
+			TPE1	artist
+			TPE2	band
+			TPE3	conductor
+			TPE4	remixer
+			TPOS	part_of_a_set
+			TPRO	produced_notice
+			TPUB	publisher
+			TRC	isrc
+			TRCK	track_number
+			TRD	recording_dates
+			TRDA	recording_dates
+			TRK	track_number
+			TRSN	internet_radio_station_name
+			TRSO	internet_radio_station_owner
+			TSI	size
+			TSIZ	size
+			TSOA	album_sort_order
+			TSOP	performer_sort_order
+			TSOT	title_sort_order
+			TSRC	isrc
+			TSS	encoder_settings
+			TSSE	encoder_settings
+			TSST	set_subtitle
+			TT1	description
+			TT2	title
+			TT3	subtitle
+			TXT	lyricist
+			TXX	text
+			TXXX	text
+			TYE	year
+			TYER	year
+			UFI	unique_file_identifier
+			UFID	unique_file_identifier
+			ULT	unsychronised_lyric
+			USER	terms_of_use
+			USLT	unsynchronised_lyric
+			WAF	url_file
+			WAR	url_artist
+			WAS	url_source
+			WCM	commercial_information
+			WCOM	commercial_information
+			WCOP	copyright
+			WCP	copyright
+			WOAF	url_file
+			WOAR	url_artist
+			WOAS	url_source
+			WORS	url_station
+			WPAY	url_payment
+			WPB	url_publisher
+			WPUB	url_publisher
+			WXX	url_user
+			WXXX	url_user
+			TFEA	featured_artist
+			TSTU	recording_studio
+			rgad	replay_gain_adjustment
+				
+		*/		
 
-			COM	comment
-			COMM	comment
-			TAL	album
-			TALB	album
-			TBP	bpm
-			TBPM	bpm
-			TCM	composer
-			TCO	genre
-			TCOM	composer
-			TCON	genre
-			TCOP	copyright
-			TCR	copyright
-			TEN	encoded_by
-			TENC	encoded_by
-			TEXT	lyricist
-			TIT1	description
-			TIT2	title
-			TIT3	subtitle
-			TLA	language
-			TLAN	language
-			TLE	length
-			TLEN	length
-			TMOO	mood
-			TOA	original_artist
-			TOAL	original_album
-			TOF	original_filename
-			TOFN	original_filename
-			TOL	original_lyricist
-			TOLY	original_lyricist
-			TOPE	original_artist
-			TOT	original_album
-			TP1	artist
-			TP2	band
-			TP3	conductor
-			TP4	remixer
-			TPB	publisher
-			TPE1	artist
-			TPE2	band
-			TPE3	conductor
-			TPE4	remixer
-			TPUB	publisher
-			TRC	isrc
-			TRCK	track
-			TRK	track
-			TSI	size
-			TSIZ	size
-			TSRC	isrc
-			TSS	encoder_settings
-			TSSE	encoder_settings
-			TSST	subtitle
-			TT1	description
-			TT2	title
-			TT3	subtitle
-			TXT	lyricist
-			TXX	text
-			TXXX	text
-			TYE	year
-			TYER	year
-			UFI	unique_file_identifier
-			UFID	unique_file_identifier
-			ULT	unsychronised_lyric
-			USER	terms_of_use
-			USLT	unsynchronised lyric
-			WAF	url_file
-			WAR	url_artist
-			WAS	url_source
-			WCOP	copyright
-			WCP	copyright
-			WOAF	url_file
-			WOAR	url_artist
-			WOAS	url_source
-			WORS	url_station
-			WPB	url_publisher
-			WPUB	url_publisher
-			WXX	url_user
-			WXXX	url_user
-			TFEA	featured_artist
-			TSTU	studio
-
-		*/
-
 		return getid3_lib::EmbeddedLookup($framename, $begin, __LINE__, __FILE__, 'id3v2-framename_short');
 	}
 

Added: plog/trunk/class/gallery/getid3/write.apetag.php
===================================================================
--- plog/trunk/class/gallery/getid3/write.apetag.php	2006-07-24 16:40:16 UTC (rev 3789)
+++ plog/trunk/class/gallery/getid3/write.apetag.php	2006-07-24 16:56:08 UTC (rev 3790)
@@ -0,0 +1,228 @@
+<?php
+/////////////////////////////////////////////////////////////////
+/// getID3() by James Heinrich <info at getid3.org>               //
+//  available at http://getid3.sourceforge.net                 //
+//            or http://www.getid3.org                         //
+/////////////////////////////////////////////////////////////////
+// See readme.txt for more details                             //
+/////////////////////////////////////////////////////////////////
+//                                                             //
+// write.apetag.php                                            //
+// module for writing APE tags                                 //
+// dependencies: module.tag.apetag.php                         //
+//                                                            ///
+/////////////////////////////////////////////////////////////////
+
+
+getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.tag.apetag.php', __FILE__, true);
+
+class getid3_write_apetag
+{
+
+	var $filename;
+	var $tag_data;
+	var $always_preserve_replaygain = true;  // ReplayGain / MP3gain tags will be copied from old tag even if not passed in data
+	var $warnings = array();                 // any non-critical errors will be stored here
+	var $errors   = array();                 // any critical errors will be stored here
+
+	function getid3_write_apetag() {
+		return true;
+	}
+
+	function WriteAPEtag() {
+		// NOTE: All data passed to this function must be UTF-8 format
+
+		$getID3 = new getID3;
+		$ThisFileInfo = $getID3->analyze($this->filename);
+
+		if (isset($ThisFileInfo['ape']['tag_offset_start']) && isset($ThisFileInfo['lyrics3']['tag_offset_end'])) {
+			if ($ThisFileInfo['ape']['tag_offset_start'] >= $ThisFileInfo['lyrics3']['tag_offset_end']) {
+				// Current APE tag between Lyrics3 and ID3v1/EOF
+				// This break Lyrics3 functionality
+				if (!$this->DeleteAPEtag()) {
+					return false;
+				}
+				$ThisFileInfo = $getID3->analyze($this->filename);
+			}
+		}
+
+		if ($this->always_preserve_replaygain) {
+			$ReplayGainTagsToPreserve = array('mp3gain_minmax', 'mp3gain_album_minmax', 'mp3gain_undo', 'replaygain_track_peak', 'replaygain_track_gain', 'replaygain_album_peak', 'replaygain_album_gain');
+			foreach ($ReplayGainTagsToPreserve as $rg_key) {
+				if (isset($ThisFileInfo['ape']['items'][strtolower($rg_key)]['data'][0]) && !isset($this->tag_data[strtoupper($rg_key)][0])) {
+					$this->tag_data[strtoupper($rg_key)][0] = $ThisFileInfo['ape']['items'][strtolower($rg_key)]['data'][0];
+				}
+			}
+		}
+
+		if ($APEtag = $this->GenerateAPEtag()) {
+			if ($fp = @fopen($this->filename, 'a+b')) {
+				$oldignoreuserabort = ignore_user_abort(true);
+				flock($fp, LOCK_EX);
+
+				$PostAPEdataOffset = $ThisFileInfo['avdataend'];
+				if (isset($ThisFileInfo['ape']['tag_offset_end'])) {
+					$PostAPEdataOffset = max($PostAPEdataOffset, $ThisFileInfo['ape']['tag_offset_end']);
+				}
+				if (isset($ThisFileInfo['lyrics3']['tag_offset_start'])) {
+					$PostAPEdataOffset = max($PostAPEdataOffset, $ThisFileInfo['lyrics3']['tag_offset_start']);
+				}
+				fseek($fp, $PostAPEdataOffset, SEEK_SET);
+				$PostAPEdata = '';
+				if ($ThisFileInfo['filesize'] > $PostAPEdataOffset) {
+					$PostAPEdata = fread($fp, $ThisFileInfo['filesize'] - $PostAPEdataOffset);
+				}
+
+				fseek($fp, $PostAPEdataOffset, SEEK_SET);
+				if (isset($ThisFileInfo['ape']['tag_offset_start'])) {
+					fseek($fp, $ThisFileInfo['ape']['tag_offset_start'], SEEK_SET);
+				}
+				ftruncate($fp, ftell($fp));
+				fwrite($fp, $APEtag, strlen($APEtag));
+				if (!empty($PostAPEdata)) {
+					fwrite($fp, $PostAPEdata, strlen($PostAPEdata));
+				}
+				flock($fp, LOCK_UN);
+				fclose($fp);
+				ignore_user_abort($oldignoreuserabort);
+				return true;
+
+			}
+			return false;
+		}
+		return false;
+	}
+
+	function DeleteAPEtag() {
+		$getID3 = new getID3;
+		$ThisFileInfo = $getID3->analyze($this->filename);
+		if (isset($ThisFileInfo['ape']['tag_offset_start']) && isset($ThisFileInfo['ape']['tag_offset_end'])) {
+			if ($fp = @fopen($this->filename, 'a+b')) {
+
+				flock($fp, LOCK_EX);
+				$oldignoreuserabort = ignore_user_abort(true);
+
+				fseek($fp, $ThisFileInfo['ape']['tag_offset_end'], SEEK_SET);
+				$DataAfterAPE = '';
+				if ($ThisFileInfo['filesize'] > $ThisFileInfo['ape']['tag_offset_end']) {
+					$DataAfterAPE = fread($fp, $ThisFileInfo['filesize'] - $ThisFileInfo['ape']['tag_offset_end']);
+				}
+
+				ftruncate($fp, $ThisFileInfo['ape']['tag_offset_start']);
+				fseek($fp, $ThisFileInfo['ape']['tag_offset_start'], SEEK_SET);
+
+				if (!empty($DataAfterAPE)) {
+					fwrite($fp, $DataAfterAPE, strlen($DataAfterAPE));
+				}
+
+				flock($fp, LOCK_UN);
+				fclose($fp);
+				ignore_user_abort($oldignoreuserabort);
+
+				return true;
+
+			}
+			return false;
+		}
+		return true;
+	}
+
+
+	function GenerateAPEtag() {
+		// NOTE: All data passed to this function must be UTF-8 format
+
+		$items = array();
+		if (!is_array($this->tag_data)) {
+			return false;
+		}
+		foreach ($this->tag_data as $key => $arrayofvalues) {
+			if (!is_array($arrayofvalues)) {
+				return false;
+			}
+
+			$valuestring = '';
+			foreach ($arrayofvalues as $value) {
+				$valuestring .= str_replace("\x00", '', $value)."\x00";
+			}
+			$valuestring = rtrim($valuestring, "\x00");
+
+			// Length of the assigned value in bytes
+			$tagitem  = getid3_lib::LittleEndian2String(strlen($valuestring), 4);
+
+			//$tagitem .= $this->GenerateAPEtagFlags(true, true, false, 0, false);
+			$tagitem .= "\x00\x00\x00\x00";
+
+			$tagitem .= $this->CleanAPEtagItemKey($key)."\x00";
+			$tagitem .= $valuestring;
+
+			$items[] = $tagitem;
+
+		}
+
+		return $this->GenerateAPEtagHeaderFooter($items, true).implode('', $items).$this->GenerateAPEtagHeaderFooter($items, false);
+	}
+
+	function GenerateAPEtagHeaderFooter(&$items, $isheader=false) {
+		$tagdatalength = 0;
+		foreach ($items as $itemdata) {
+			$tagdatalength += strlen($itemdata);
+		}
+
+		$APEheader  = 'APETAGEX';
+		$APEheader .= getid3_lib::LittleEndian2String(2000, 4);
+		$APEheader .= getid3_lib::LittleEndian2String(32 + $tagdatalength, 4);
+		$APEheader .= getid3_lib::LittleEndian2String(count($items), 4);
+		$APEheader .= $this->GenerateAPEtagFlags(true, true, $isheader, 0, false);
+		$APEheader .= str_repeat("\x00", 8);
+
+		return $APEheader;
+	}
+
+	function GenerateAPEtagFlags($header=true, $footer=true, $isheader=false, $encodingid=0, $readonly=false) {
+		$APEtagFlags = array_fill(0, 4, 0);
+		if ($header) {
+			$APEtagFlags[0] |= 0x80; // Tag contains a header
+		}
+		if (!$footer) {
+			$APEtagFlags[0] |= 0x40; // Tag contains no footer
+		}
+		if ($isheader) {
+			$APEtagFlags[0] |= 0x20; // This is the header, not the footer
+		}
+
+		// 0: Item contains text information coded in UTF-8
+		// 1: Item contains binary information °)
+		// 2: Item is a locator of external stored information °°)
+		// 3: reserved
+		$APEtagFlags[3] |= ($encodingid << 1);
+
+		if ($readonly) {
+			$APEtagFlags[3] |= 0x01; // Tag or Item is Read Only
+		}
+
+		return chr($APEtagFlags[3]).chr($APEtagFlags[2]).chr($APEtagFlags[1]).chr($APEtagFlags[0]);
+	}
+
+	function CleanAPEtagItemKey($itemkey) {
+		$itemkey = eregi_replace("[^\x20-\x7E]", '', $itemkey);
+
+		// http://www.personal.uni-jena.de/~pfk/mpp/sv8/apekey.html
+		switch (strtoupper($itemkey)) {
+			case 'EAN/UPC':
+			case 'ISBN':
+			case 'LC':
+			case 'ISRC':
+				$itemkey = strtoupper($itemkey);
+				break;
+
+			default:
+				$itemkey = ucwords($itemkey);
+				break;
+		}
+		return $itemkey;
+
+	}
+
+}
+
+?>
\ No newline at end of file

Added: plog/trunk/class/gallery/getid3/write.id3v1.php
===================================================================
--- plog/trunk/class/gallery/getid3/write.id3v1.php	2006-07-24 16:40:16 UTC (rev 3789)
+++ plog/trunk/class/gallery/getid3/write.id3v1.php	2006-07-24 16:56:08 UTC (rev 3790)
@@ -0,0 +1,104 @@
+<?php
+/////////////////////////////////////////////////////////////////
+/// getID3() by James Heinrich <info at getid3.org>               //
+//  available at http://getid3.sourceforge.net                 //
+//            or http://www.getid3.org                         //
+/////////////////////////////////////////////////////////////////
+// See readme.txt for more details                             //
+/////////////////////////////////////////////////////////////////
+//                                                             //
+// write.id3v1.php                                             //
+// module for writing ID3v1 tags                               //
+// dependencies: module.tag.id3v1.php                          //
+//                                                            ///
+/////////////////////////////////////////////////////////////////
+
+getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.tag.id3v1.php', __FILE__, true);
+
+class getid3_write_id3v1
+{
+	var $filename;
+	var $tag_data;
+	var $warnings = array(); // any non-critical errors will be stored here
+	var $errors   = array(); // any critical errors will be stored here
+
+	function getid3_write_id3v1() {
+		return true;
+	}
+
+	function WriteID3v1() {
+		// File MUST be writeable - CHMOD(646) at least
+		if (is_writeable($this->filename)) {
+			if ($fp_source = @fopen($this->filename, 'r+b')) {
+
+				fseek($fp_source, -128, SEEK_END);
+				if (fread($fp_source, 3) == 'TAG') {
+					fseek($fp_source, -128, SEEK_END); // overwrite existing ID3v1 tag
+				} else {
+					fseek($fp_source, 0, SEEK_END);    // append new ID3v1 tag
+				}
+
+				$new_id3v1_tag_data = getid3_id3v1::GenerateID3v1Tag(
+														@$this->tag_data['title'],
+														@$this->tag_data['artist'],
+														@$this->tag_data['album'],
+														@$this->tag_data['year'],
+														@$this->tag_data['genreid'],
+														@$this->tag_data['comment'],
+														@$this->tag_data['track']);
+				fwrite($fp_source, $new_id3v1_tag_data, 128);
+				fclose($fp_source);
+				return true;
+
+			} else {
+				$this->errors[] = 'Could not open '.$this->filename.' mode "r+b"';
+				return false;
+			}
+		}
+		$this->errors[] = 'File is not writeable: '.$this->filename;
+		return false;
+	}
+
+	function FixID3v1Padding() {
+		// ID3v1 data is supposed to be padded with NULL characters, but some taggers incorrectly use spaces
+		// This function rewrites the ID3v1 tag with correct padding
+
+		// Initialize getID3 engine
+		$getID3 = new getID3;
+		$ThisFileInfo = $getID3->analyze($this->filename);
+		if (isset($ThisFileInfo['tags']['id3v1'])) {
+			foreach ($ThisFileInfo['tags']['id3v1'] as $key => $value) {
+				$id3v1data[$key] = implode(',', $value);
+			}
+			$this->tag_data = $id3v1data;
+			return $this->WriteID3v1();
+		}
+		return false;
+	}
+
+	function RemoveID3v1() {
+		// File MUST be writeable - CHMOD(646) at least
+		if (is_writeable($this->filename)) {
+			if ($fp_source = @fopen($this->filename, 'r+b')) {
+
+				fseek($fp_source, -128, SEEK_END);
+				if (fread($fp_source, 3) == 'TAG') {
+					ftruncate($fp_source, filesize($this->filename) - 128);
+				} else {
+					// no ID3v1 tag to begin with - do nothing
+				}
+				fclose($fp_source);
+				return true;
+
+			} else {
+				$this->errors[] = 'Could not open '.$this->filename.' mode "r+b"';
+			}
+		} else {
+			$this->errors[] = $this->filename.' is not writeable';
+		}
+		return false;
+	}
+
+}
+
+?>
\ No newline at end of file

Added: plog/trunk/class/gallery/getid3/write.id3v2.php
===================================================================
--- plog/trunk/class/gallery/getid3/write.id3v2.php	2006-07-24 16:40:16 UTC (rev 3789)
+++ plog/trunk/class/gallery/getid3/write.id3v2.php	2006-07-24 16:56:08 UTC (rev 3790)
@@ -0,0 +1,2038 @@
+<?php
+/////////////////////////////////////////////////////////////////
+/// getID3() by James Heinrich <info at getid3.org>               //
+//  available at http://getid3.sourceforge.net                 //
+//            or http://www.getid3.org                         //
+/////////////////////////////////////////////////////////////////
+// See readme.txt for more details                             //
+/////////////////////////////////////////////////////////////////
+///                                                            //
+// write.id3v2.php                                             //
+// module for writing ID3v2 tags                               //
+// dependencies: module.tag.id3v2.php                          //
+//                                                            ///
+/////////////////////////////////////////////////////////////////
+
+getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.tag.id3v2.php', __FILE__, true);
+
+class getid3_write_id3v2
+{
+	var $filename;
+	var $tag_data;
+	var $paddedlength                = 4096;     // minimum length of ID3v2 tag in bytes
+	var $majorversion                = 3;        // ID3v2 major version (2, 3 (recommended), 4)
+	var $minorversion                = 0;        // ID3v2 minor version - always 0
+	var $merge_existing_data         = false;    // if true, merge new data with existing tags; if false, delete old tag data and only write new tags
+	var $id3v2_default_encodingid    = 0;        // default text encoding (ISO-8859-1) if not explicitly passed
+	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.
+	var $warnings                    = array();  // any non-critical errors will be stored here
+	var $errors                      = array();  // any critical errors will be stored here
+
+	function getid3_write_id3v2() {
+		return true;
+	}
+
+	function WriteID3v2() {
+		// File MUST be writeable - CHMOD(646) at least. It's best if the
+		// directory is also writeable, because that method is both faster and less susceptible to errors.
+
+		if (is_writeable($this->filename) || (!file_exists($this->filename) && is_writeable(dirname($this->filename)))) {
+			// Initialize getID3 engine
+			$getID3 = new getID3;
+			$OldThisFileInfo = $getID3->analyze($this->filename);
+			if ($this->merge_existing_data) {
+				// merge with existing data
+				if (!empty($OldThisFileInfo['id3v2'])) {
+					$this->tag_data = $this->array_join_merge($OldThisFileInfo['id3v2'], $this->tag_data);
+				}
+			}
+			$this->paddedlength = max(@$OldThisFileInfo['id3v2']['headerlength'], $this->paddedlength);
+
+			if ($NewID3v2Tag = $this->GenerateID3v2Tag()) {
+
+				if (file_exists($this->filename) && is_writeable($this->filename) && isset($OldThisFileInfo['id3v2']['headerlength']) && ($OldThisFileInfo['id3v2']['headerlength'] == strlen($NewID3v2Tag))) {
+
+					// best and fastest method - insert-overwrite existing tag (padded to length of old tag if neccesary)
+					if (file_exists($this->filename)) {
+
+						ob_start();
+						if ($fp = fopen($this->filename, 'r+b')) {
+							rewind($fp);
+							fwrite($fp, $NewID3v2Tag, strlen($NewID3v2Tag));
+							fclose($fp);
+						} else {
+							$this->errors[] = 'Could not open '.$this->filename.' mode "r+b" - '.strip_tags(ob_get_contents());
+						}
+						ob_end_clean();
+
+					} else {
+
+						ob_start();
+						if ($fp = fopen($this->filename, 'wb')) {
+							rewind($fp);
+							fwrite($fp, $NewID3v2Tag, strlen($NewID3v2Tag));
+							fclose($fp);
+						} else {
+							$this->errors[] = 'Could not open '.$this->filename.' mode "wb" - '.strip_tags(ob_get_contents());
+						}
+						ob_end_clean();
+
+					}
+
+				} else {
+
+					if ($tempfilename = tempnam('*', 'getID3')) {
+						ob_start();
+						if ($fp_source = fopen($this->filename, 'rb')) {
+							if ($fp_temp = fopen($tempfilename, 'wb')) {
+
+								fwrite($fp_temp, $NewID3v2Tag, strlen($NewID3v2Tag));
+
+								rewind($fp_source);
+								if (!empty($OldThisFileInfo['avdataoffset'])) {
+									fseek($fp_source, $OldThisFileInfo['avdataoffset'], SEEK_SET);
+								}
+
+								while ($buffer = fread($fp_source, GETID3_FREAD_BUFFER_SIZE)) {
+									fwrite($fp_temp, $buffer, strlen($buffer));
+								}
+
+								fclose($fp_temp);
+								fclose($fp_source);
+								copy($tempfilename, $this->filename);
+								unlink($tempfilename);
+								ob_end_clean();
+								return true;
+
+							} else {
+
+								$this->errors[] = 'Could not open '.$tempfilename.' mode "wb" - '.strip_tags(ob_get_contents());
+
+							}
+							fclose($fp_source);
+
+						} else {
+
+							$this->errors[] = 'Could not open '.$this->filename.' mode "rb" - '.strip_tags(ob_get_contents());
+
+						}
+						ob_end_clean();
+					}
+					return false;
+
+				}
+
+			} else {
+
+				$this->errors[] = '$this->GenerateID3v2Tag() failed';
+
+			}
+
+			if (!empty($this->errors)) {
+				return false;
+			}
+			return true;
+		} else {
+			$this->errors[] = '!is_writeable('.$this->filename.')';
+		}
+		return false;
+	}
+
+	function RemoveID3v2() {
+
+		// File MUST be writeable - CHMOD(646) at least. It's best if the
+		// directory is also writeable, because that method is both faster and less susceptible to errors.
+		if (is_writeable(dirname($this->filename))) {
+
+			// preferred method - only one copying operation, minimal chance of corrupting
+			// original file if script is interrupted, but required directory to be writeable
+			if ($fp_source = @fopen($this->filename, 'rb')) {
+				// Initialize getID3 engine
+				$getID3 = new getID3;
+				$OldThisFileInfo = $getID3->analyze($this->filename);
+				rewind($fp_source);
+				if ($OldThisFileInfo['avdataoffset'] !== false) {
+					fseek($fp_source, $OldThisFileInfo['avdataoffset'], SEEK_SET);
+				}
+				if ($fp_temp = @fopen($this->filename.'getid3tmp', 'w+b')) {
+					while ($buffer = fread($fp_source, GETID3_FREAD_BUFFER_SIZE)) {
+						fwrite($fp_temp, $buffer, strlen($buffer));
+					}
+					fclose($fp_temp);
+				} else {
+					$this->errors[] = 'Could not open '.$this->filename.'getid3tmp mode "w+b"';
+				}
+				fclose($fp_source);
+			} else {
+				$this->errors[] = 'Could not open '.$this->filename.' mode "rb"';
+			}
+			if (file_exists($this->filename)) {
+				unlink($this->filename);
+			}
+			rename($this->filename.'getid3tmp', $this->filename);
+
+		} elseif (is_writable($this->filename)) {
+
+			// less desirable alternate method - double-copies the file, overwrites original file
+			// and could corrupt source file if the script is interrupted or an error occurs.
+			if ($fp_source = @fopen($this->filename, 'rb')) {
+				// Initialize getID3 engine
+				$getID3 = new getID3;
+				$OldThisFileInfo = $getID3->analyze($this->filename);
+				rewind($fp_source);
+				if ($OldThisFileInfo['avdataoffset'] !== false) {
+					fseek($fp_source, $OldThisFileInfo['avdataoffset'], SEEK_SET);
+				}
+				if ($fp_temp = tmpfile()) {
+					while ($buffer = fread($fp_source, GETID3_FREAD_BUFFER_SIZE)) {
+						fwrite($fp_temp, $buffer, strlen($buffer));
+					}
+					fclose($fp_source);
+					if ($fp_source = @fopen($this->filename, 'wb')) {
+						rewind($fp_temp);
+						while ($buffer = fread($fp_temp, GETID3_FREAD_BUFFER_SIZE)) {
+							fwrite($fp_source, $buffer, strlen($buffer));
+						}
+						fseek($fp_temp, -128, SEEK_END);
+						fclose($fp_source);
+					} else {
+						$this->errors[] = 'Could not open '.$this->filename.' mode "wb"';
+					}
+					fclose($fp_temp);
+				} else {
+					$this->errors[] = 'Could not create tmpfile()';
+				}
+			} else {
+				$this->errors[] = 'Could not open '.$this->filename.' mode "rb"';
+			}
+
+		} else {
+
+			$this->errors[] = 'Directory and file both not writeable';
+
+		}
+
+		if (!empty($this->errors)) {
+			return false;
+		}
+		return true;
+	}
+
+
+	function GenerateID3v2TagFlags($flags) {
+		switch ($this->majorversion) {
+			case 4:
+				// %abcd0000
+				$flag  = (@$flags['unsynchronisation'] ? '1' : '0'); // a - Unsynchronisation
+				$flag .= (@$flags['extendedheader']    ? '1' : '0'); // b - Extended header
+				$flag .= (@$flags['experimental']      ? '1' : '0'); // c - Experimental indicator
+				$flag .= (@$flags['footer']            ? '1' : '0'); // d - Footer present
+				$flag .= '0000';
+				break;
+
+			case 3:
+				// %abc00000
+				$flag  = (@$flags['unsynchronisation'] ? '1' : '0'); // a - Unsynchronisation
+				$flag .= (@$flags['extendedheader']    ? '1' : '0'); // b - Extended header
+				$flag .= (@$flags['experimental']      ? '1' : '0'); // c - Experimental indicator
+				$flag .= '00000';
+				break;
+
+			case 2:
+				// %ab000000
+				$flag  = (@$flags['unsynchronisation'] ? '1' : '0'); // a - Unsynchronisation
+				$flag .= (@$flags['compression']       ? '1' : '0'); // b - Compression
+				$flag .= '000000';
+				break;
+
+			default:
+				return false;
+				break;
+		}
+		return chr(bindec($flag));
+	}
+
+
+	function GenerateID3v2FrameFlags($TagAlter=false, $FileAlter=false, $ReadOnly=false, $Compression=false, $Encryption=false, $GroupingIdentity=false, $Unsynchronisation=false, $DataLengthIndicator=false) {
+		switch ($this->majorversion) {
+			case 4:
+				// %0abc0000 %0h00kmnp
+				$flag1  = '0';
+				$flag1 .= $TagAlter  ? '1' : '0'; // a - Tag alter preservation (true == discard)
+				$flag1 .= $FileAlter ? '1' : '0'; // b - File alter preservation (true == discard)
+				$flag1 .= $ReadOnly  ? '1' : '0'; // c - Read only (true == read only)
+				$flag1 .= '0000';
+
+				$flag2  = '0';
+				$flag2 .= $GroupingIdentity    ? '1' : '0'; // h - Grouping identity (true == contains group information)
+				$flag2 .= '00';
+				$flag2 .= $Compression         ? '1' : '0'; // k - Compression (true == compressed)
+				$flag2 .= $Encryption          ? '1' : '0'; // m - Encryption (true == encrypted)
+				$flag2 .= $Unsynchronisation   ? '1' : '0'; // n - Unsynchronisation (true == unsynchronised)
+				$flag2 .= $DataLengthIndicator ? '1' : '0'; // p - Data length indicator (true == data length indicator added)
+				break;
+
+			case 3:
+				// %abc00000 %ijk00000
+				$flag1  = $TagAlter  ? '1' : '0';  // a - Tag alter preservation (true == discard)
+				$flag1 .= $FileAlter ? '1' : '0';  // b - File alter preservation (true == discard)
+				$flag1 .= $ReadOnly  ? '1' : '0';  // c - Read only (true == read only)
+				$flag1 .= '00000';
+
+				$flag2  = $Compression      ? '1' : '0';      // i - Compression (true == compressed)
+				$flag2 .= $Encryption       ? '1' : '0';      // j - Encryption (true == encrypted)
+				$flag2 .= $GroupingIdentity ? '1' : '0';      // k - Grouping identity (true == contains group information)
+				$flag2 .= '00000';
+				break;
+
+			default:
+				return false;
+				break;
+
+		}
+		return chr(bindec($flag1)).chr(bindec($flag2));
+	}
+
+	function GenerateID3v2FrameData($frame_name, $source_data_array) {
+		if (!getid3_id3v2::IsValidID3v2FrameName($frame_name, $this->majorversion)) {
+			return false;
+		}
+		$framedata = '';
+
+		if (($this->majorversion < 3) || ($this->majorversion > 4)) {
+
+			$this->errors[] = 'Only ID3v2.3 and ID3v2.4 are supported in GenerateID3v2FrameData()';
+
+		} else { // $this->majorversion 3 or 4
+
+			switch ($frame_name) {
+				case 'UFID':
+					// 4.1   UFID Unique file identifier
+					// Owner identifier        <text string> $00
+					// Identifier              <up to 64 bytes binary data>
+					if (strlen($source_data_array['data']) > 64) {
+						$this->errors[] = 'Identifier not allowed to be longer than 64 bytes in '.$frame_name.' (supplied data was '.strlen($source_data_array['data']).' bytes long)';
+					} else {
+						$framedata .= str_replace("\x00", '', $source_data_array['ownerid'])."\x00";
+						$framedata .= substr($source_data_array['data'], 0, 64); // max 64 bytes - truncate anything longer
+					}
+					break;
+
+				case 'TXXX':
+					// 4.2.2 TXXX User defined text information frame
+					// Text encoding     $xx
+					// Description       <text string according to encoding> $00 (00)
+					// Value             <text string according to encoding>
+					$source_data_array['encodingid'] = (isset($source_data_array['encodingid']) ? $source_data_array['encodingid'] : $this->id3v2_default_encodingid);
+					if (!$this->ID3v2IsValidTextEncoding($source_data_array['encodingid'], $this->majorversion)) {
+						$this->errors[] = 'Invalid Text Encoding in '.$frame_name.' ('.$source_data_array['encodingid'].') for ID3v2.'.$this->majorversion;
+					} else {
+						$framedata .= chr($source_data_array['encodingid']);
+						$framedata .= $source_data_array['description'].getid3_id3v2::TextEncodingTerminatorLookup($source_data_array['encodingid']);
+						$framedata .= $source_data_array['data'];
+					}
+					break;
+
+				case 'WXXX':
+					// 4.3.2 WXXX User defined URL link frame
+					// Text encoding     $xx
+					// Description       <text string according to encoding> $00 (00)
+					// URL               <text string>
+					$source_data_array['encodingid'] = (isset($source_data_array['encodingid']) ? $source_data_array['encodingid'] : $this->id3v2_default_encodingid);
+					if (!$this->ID3v2IsValidTextEncoding($source_data_array['encodingid'], $this->majorversion)) {
+						$this->errors[] = 'Invalid Text Encoding in '.$frame_name.' ('.$source_data_array['encodingid'].') for ID3v2.'.$this->majorversion;
+					} elseif (!isset($source_data_array['data']) || !$this->IsValidURL($source_data_array['data'], false, false)) {
+						//$this->errors[] = 'Invalid URL in '.$frame_name.' ('.$source_data_array['data'].')';
+						// probably should be an error, need to rewrite IsValidURL() to handle other encodings
+						$this->warnings[] = 'Invalid URL in '.$frame_name.' ('.$source_data_array['data'].')';
+					} else {
+						$framedata .= chr($source_data_array['encodingid']);
+						$framedata .= $source_data_array['description'].getid3_id3v2::TextEncodingTerminatorLookup($source_data_array['encodingid']);
+						$framedata .= $source_data_array['data'];
+					}
+					break;
+
+				case 'IPLS':
+					// 4.4  IPLS Involved people list (ID3v2.3 only)
+					// Text encoding     $xx
+					// People list strings    <textstrings>
+					$source_data_array['encodingid'] = (isset($source_data_array['encodingid']) ? $source_data_array['encodingid'] : $this->id3v2_default_encodingid);
+					if (!$this->ID3v2IsValidTextEncoding($source_data_array['encodingid'], $this->majorversion)) {
+						$this->errors[] = 'Invalid Text Encoding in '.$frame_name.' ('.$source_data_array['encodingid'].') for ID3v2.'.$this->majorversion;
+					} else {
+						$framedata .= chr($source_data_array['encodingid']);
+						$framedata .= $source_data_array['data'];
+					}
+					break;
+
+				case 'MCDI':
+					// 4.4   MCDI Music CD identifier
+					// CD TOC                <binary data>
+					$framedata .= $source_data_array['data'];
+					break;
+
+				case 'ETCO':
+					// 4.5   ETCO Event timing codes
+					// Time stamp format    $xx
+					//   Where time stamp format is:
+					// $01  (32-bit value) MPEG frames from beginning of file
+					// $02  (32-bit value) milliseconds from beginning of file
+					//   Followed by a list of key events in the following format:
+					// Type of event   $xx
+					// Time stamp      $xx (xx ...)
+					//   The 'Time stamp' is set to zero if directly at the beginning of the sound
+					//   or after the previous event. All events MUST be sorted in chronological order.
+					if (($source_data_array['timestampformat'] > 2) || ($source_data_array['timestampformat'] < 1)) {
+						$this->errors[] = 'Invalid Time Stamp Format byte in '.$frame_name.' ('.$source_data_array['timestampformat'].')';
+					} else {
+						$framedata .= chr($source_data_array['timestampformat']);
+						foreach ($source_data_array as $key => $val) {
+							if (!$this->ID3v2IsValidETCOevent($val['typeid'])) {
+								$this->errors[] = 'Invalid Event Type byte in '.$frame_name.' ('.$val['typeid'].')';
+							} elseif (($key != 'timestampformat') && ($key != 'flags')) {
+								if (($val['timestamp'] > 0) && ($previousETCOtimestamp >= $val['timestamp'])) {
+									//   The 'Time stamp' is set to zero if directly at the beginning of the sound
+									//   or after the previous event. All events MUST be sorted in chronological order.
+									$this->errors[] = 'Out-of-order timestamp in '.$frame_name.' ('.$val['timestamp'].') for Event Type ('.$val['typeid'].')';
+								} else {
+									$framedata .= chr($val['typeid']);
+									$framedata .= getid3_lib::BigEndian2String($val['timestamp'], 4, false);
+								}
+							}
+						}
+					}
+					break;
+
+				case 'MLLT':
+					// 4.6   MLLT MPEG location lookup table
+					// MPEG frames between reference  $xx xx
+					// Bytes between reference        $xx xx xx
+					// Milliseconds between reference $xx xx xx
+					// Bits for bytes deviation       $xx
+					// Bits for milliseconds dev.     $xx
+					//   Then for every reference the following data is included;
+					// Deviation in bytes         %xxx....
+					// Deviation in milliseconds  %xxx....
+					if (($source_data_array['framesbetweenreferences'] > 0) && ($source_data_array['framesbetweenreferences'] <= 65535)) {
+						$framedata .= getid3_lib::BigEndian2String($source_data_array['framesbetweenreferences'], 2, false);
+					} else {
+						$this->errors[] = 'Invalid MPEG Frames Between References in '.$frame_name.' ('.$source_data_array['framesbetweenreferences'].')';
+					}
+					if (($source_data_array['bytesbetweenreferences'] > 0) && ($source_data_array['bytesbetweenreferences'] <= 16777215)) {
+						$framedata .= getid3_lib::BigEndian2String($source_data_array['bytesbetweenreferences'], 3, false);
+					} else {
+						$this->errors[] = 'Invalid bytes Between References in '.$frame_name.' ('.$source_data_array['bytesbetweenreferences'].')';
+					}
+					if (($source_data_array['msbetweenreferences'] > 0) && ($source_data_array['msbetweenreferences'] <= 16777215)) {
+						$framedata .= getid3_lib::BigEndian2String($source_data_array['msbetweenreferences'], 3, false);
+					} else {
+						$this->errors[] = 'Invalid Milliseconds Between References in '.$frame_name.' ('.$source_data_array['msbetweenreferences'].')';
+					}
+					if (!$this->IsWithinBitRange($source_data_array['bitsforbytesdeviation'], 8, false)) {
+						if (($source_data_array['bitsforbytesdeviation'] % 4) == 0) {
+							$framedata .= chr($source_data_array['bitsforbytesdeviation']);
+						} else {
+							$this->errors[] = 'Bits For Bytes Deviation in '.$frame_name.' ('.$source_data_array['bitsforbytesdeviation'].') must be a multiple of 4.';
+						}
+					} else {
+						$this->errors[] = 'Invalid Bits For Bytes Deviation in '.$frame_name.' ('.$source_data_array['bitsforbytesdeviation'].')';
+					}
+					if (!$this->IsWithinBitRange($source_data_array['bitsformsdeviation'], 8, false)) {
+						if (($source_data_array['bitsformsdeviation'] % 4) == 0) {
+							$framedata .= chr($source_data_array['bitsformsdeviation']);
+						} else {
+							$this->errors[] = 'Bits For Milliseconds Deviation in '.$frame_name.' ('.$source_data_array['bitsforbytesdeviation'].') must be a multiple of 4.';
+						}
+					} else {
+						$this->errors[] = 'Invalid Bits For Milliseconds Deviation in '.$frame_name.' ('.$source_data_array['bitsformsdeviation'].')';
+					}
+					foreach ($source_data_array as $key => $val) {
+						if (($key != 'framesbetweenreferences') && ($key != 'bytesbetweenreferences') && ($key != 'msbetweenreferences') && ($key != 'bitsforbytesdeviation') && ($key != 'bitsformsdeviation') && ($key != 'flags')) {
+							$unwrittenbitstream .= str_pad(getid3_lib::Dec2Bin($val['bytedeviation']), $source_data_array['bitsforbytesdeviation'], '0', STR_PAD_LEFT);
+							$unwrittenbitstream .= str_pad(getid3_lib::Dec2Bin($val['msdeviation']),   $source_data_array['bitsformsdeviation'],    '0', STR_PAD_LEFT);
+						}
+					}
+					for ($i = 0; $i < strlen($unwrittenbitstream); $i += 8) {
+						$highnibble = bindec(substr($unwrittenbitstream, $i, 4)) << 4;
+						$lownibble  = bindec(substr($unwrittenbitstream, $i + 4, 4));
+						$framedata .= chr($highnibble & $lownibble);
+					}
+					break;
+
+				case 'SYTC':
+					// 4.7   SYTC Synchronised tempo codes
+					// Time stamp format   $xx
+					// Tempo data          <binary data>
+					//   Where time stamp format is:
+					// $01  (32-bit value) MPEG frames from beginning of file
+					// $02  (32-bit value) milliseconds from beginning of file
+					if (($source_data_array['timestampformat'] > 2) || ($source_data_array['timestampformat'] < 1)) {
+						$this->errors[] = 'Invalid Time Stamp Format byte in '.$frame_name.' ('.$source_data_array['timestampformat'].')';
+					} else {
+						$framedata .= chr($source_data_array['timestampformat']);
+						foreach ($source_data_array as $key => $val) {
+							if (!$this->ID3v2IsValidETCOevent($val['typeid'])) {
+								$this->errors[] = 'Invalid Event Type byte in '.$frame_name.' ('.$val['typeid'].')';
+							} elseif (($key != 'timestampformat') && ($key != 'flags')) {
+								if (($val['tempo'] < 0) || ($val['tempo'] > 510)) {
+									$this->errors[] = 'Invalid Tempo (max = 510) in '.$frame_name.' ('.$val['tempo'].') at timestamp ('.$val['timestamp'].')';
+								} else {
+									if ($val['tempo'] > 255) {
+										$framedata .= chr(255);
+										$val['tempo'] -= 255;
+									}
+									$framedata .= chr($val['tempo']);
+									$framedata .= getid3_lib::BigEndian2String($val['timestamp'], 4, false);
+								}
+							}
+						}
+					}
+					break;
+
+				case 'USLT':
+					// 4.8   USLT Unsynchronised lyric/text transcription
+					// Text encoding        $xx
+					// Language             $xx xx xx
+					// Content descriptor   <text string according to encoding> $00 (00)
+					// Lyrics/text          <full text string according to encoding>
+					$source_data_array['encodingid'] = (isset($source_data_array['encodingid']) ? $source_data_array['encodingid'] : $this->id3v2_default_encodingid);
+					if (!$this->ID3v2IsValidTextEncoding($source_data_array['encodingid'])) {
+						$this->errors[] = 'Invalid Text Encoding in '.$frame_name.' ('.$source_data_array['encodingid'].') for ID3v2.'.$this->majorversion;
+					} elseif (getid3_id3v2::LanguageLookup($source_data_array['language'], true) == '') {
+						$this->errors[] = 'Invalid Language in '.$frame_name.' ('.$source_data_array['language'].')';
+					} else {
+						$framedata .= chr($source_data_array['encodingid']);
+						$framedata .= strtolower($source_data_array['language']);
+						$framedata .= $source_data_array['description'].getid3_id3v2::TextEncodingTerminatorLookup($source_data_array['encodingid']);
+						$framedata .= $source_data_array['data'];
+					}
+					break;
+
+				case 'SYLT':
+					// 4.9   SYLT Synchronised lyric/text
+					// Text encoding        $xx
+					// Language             $xx xx xx
+					// Time stamp format    $xx
+					//   $01  (32-bit value) MPEG frames from beginning of file
+					//   $02  (32-bit value) milliseconds from beginning of file
+					// Content type         $xx
+					// Content descriptor   <text string according to encoding> $00 (00)
+					//   Terminated text to be synced (typically a syllable)
+					//   Sync identifier (terminator to above string)   $00 (00)
+					//   Time stamp                                     $xx (xx ...)
+					$source_data_array['encodingid'] = (isset($source_data_array['encodingid']) ? $source_data_array['encodingid'] : $this->id3v2_default_encodingid);
+					if (!$this->ID3v2IsValidTextEncoding($source_data_array['encodingid'])) {
+						$this->errors[] = 'Invalid Text Encoding in '.$frame_name.' ('.$source_data_array['encodingid'].') for ID3v2.'.$this->majorversion;
+					} elseif (getid3_id3v2::LanguageLookup($source_data_array['language'], true) == '') {
+						$this->errors[] = 'Invalid Language in '.$frame_name.' ('.$source_data_array['language'].')';
+					} elseif (($source_data_array['timestampformat'] > 2) || ($source_data_array['timestampformat'] < 1)) {
+						$this->errors[] = 'Invalid Time Stamp Format byte in '.$frame_name.' ('.$source_data_array['timestampformat'].')';
+					} elseif (!$this->ID3v2IsValidSYLTtype($source_data_array['contenttypeid'])) {
+						$this->errors[] = 'Invalid Content Type byte in '.$frame_name.' ('.$source_data_array['contenttypeid'].')';
+					} elseif (!is_array($source_data_array['data'])) {
+						$this->errors[] = 'Invalid Lyric/Timestamp data in '.$frame_name.' (must be an array)';
+					} else {
+						$framedata .= chr($source_data_array['encodingid']);
+						$framedata .= strtolower($source_data_array['language']);
+						$framedata .= chr($source_data_array['timestampformat']);
+						$framedata .= chr($source_data_array['contenttypeid']);
+						$framedata .= $source_data_array['description'].getid3_id3v2::TextEncodingTerminatorLookup($source_data_array['encodingid']);
+						ksort($source_data_array['data']);
+						foreach ($source_data_array['data'] as $key => $val) {
+							$framedata .= $val['data'].getid3_id3v2::TextEncodingTerminatorLookup($source_data_array['encodingid']);
+							$framedata .= getid3_lib::BigEndian2String($val['timestamp'], 4, false);
+						}
+					}
+					break;
+
+				case 'COMM':
+					// 4.10  COMM Comments
+					// Text encoding          $xx
+					// Language               $xx xx xx
+					// Short content descrip. <text string according to encoding> $00 (00)
+					// The actual text        <full text string according to encoding>
+					$source_data_array['encodingid'] = (isset($source_data_array['encodingid']) ? $source_data_array['encodingid'] : $this->id3v2_default_encodingid);
+					if (!$this->ID3v2IsValidTextEncoding($source_data_array['encodingid'])) {
+						$this->errors[] = 'Invalid Text Encoding in '.$frame_name.' ('.$source_data_array['encodingid'].') for ID3v2.'.$this->majorversion;
+					} elseif (getid3_id3v2::LanguageLookup($source_data_array['language'], true) == '') {
+						$this->errors[] = 'Invalid Language in '.$frame_name.' ('.$source_data_array['language'].')';
+					} else {
+						$framedata .= chr($source_data_array['encodingid']);
+						$framedata .= strtolower($source_data_array['language']);
+						$framedata .= $source_data_array['description'].getid3_id3v2::TextEncodingTerminatorLookup($source_data_array['encodingid']);
+						$framedata .= $source_data_array['data'];
+					}
+					break;
+
+				case 'RVA2':
+					// 4.11  RVA2 Relative volume adjustment (2) (ID3v2.4+ only)
+					// Identification          <text string> $00
+					//   The 'identification' string is used to identify the situation and/or
+					//   device where this adjustment should apply. The following is then
+					//   repeated for every channel:
+					// Type of channel         $xx
+					// Volume adjustment       $xx xx
+					// Bits representing peak  $xx
+					// Peak volume             $xx (xx ...)
+					$framedata .= str_replace("\x00", '', $source_data_array['description'])."\x00";
+					foreach ($source_data_array as $key => $val) {
+						if ($key != 'description') {
+							$framedata .= chr($val['channeltypeid']);
+							$framedata .= getid3_lib::BigEndian2String($val['volumeadjust'], 2, false, true); // signed 16-bit
+							if (!$this->IsWithinBitRange($source_data_array['bitspeakvolume'], 8, false)) {
+								$framedata .= chr($val['bitspeakvolume']);
+								if ($val['bitspeakvolume'] > 0) {
+									$framedata .= getid3_lib::BigEndian2String($val['peakvolume'], ceil($val['bitspeakvolume'] / 8), false, false);
+								}
+							} else {
+								$this->errors[] = 'Invalid Bits Representing Peak Volume in '.$frame_name.' ('.$val['bitspeakvolume'].') (range = 0 to 255)';
+							}
+						}
+					}
+					break;
+
+				case 'RVAD':
+					// 4.12  RVAD Relative volume adjustment (ID3v2.3 only)
+					// Increment/decrement     %00fedcba
+					// Bits used for volume descr.        $xx
+					// Relative volume change, right      $xx xx (xx ...) // a
+					// Relative volume change, left       $xx xx (xx ...) // b
+					// Peak volume right                  $xx xx (xx ...)
+					// Peak volume left                   $xx xx (xx ...)
+					// Relative volume change, right back $xx xx (xx ...) // c
+					// Relative volume change, left back  $xx xx (xx ...) // d
+					// Peak volume right back             $xx xx (xx ...)
+					// Peak volume left back              $xx xx (xx ...)
+					// Relative volume change, center     $xx xx (xx ...) // e
+					// Peak volume center                 $xx xx (xx ...)
+					// Relative volume change, bass       $xx xx (xx ...) // f
+					// Peak volume bass                   $xx xx (xx ...)
+					if (!$this->IsWithinBitRange($source_data_array['bitsvolume'], 8, false)) {
+						$this->errors[] = 'Invalid Bits For Volume Description byte in '.$frame_name.' ('.$source_data_array['bitsvolume'].') (range = 1 to 255)';
+					} else {
+						$incdecflag .= '00';
+						$incdecflag .= $source_data_array['incdec']['right']     ? '1' : '0';     // a - Relative volume change, right
+						$incdecflag .= $source_data_array['incdec']['left']      ? '1' : '0';      // b - Relative volume change, left
+						$incdecflag .= $source_data_array['incdec']['rightrear'] ? '1' : '0'; // c - Relative volume change, right back
+						$incdecflag .= $source_data_array['incdec']['leftrear']  ? '1' : '0';  // d - Relative volume change, left back
+						$incdecflag .= $source_data_array['incdec']['center']    ? '1' : '0';    // e - Relative volume change, center
+						$incdecflag .= $source_data_array['incdec']['bass']      ? '1' : '0';      // f - Relative volume change, bass
+						$framedata .= chr(bindec($incdecflag));
+						$framedata .= chr($source_data_array['bitsvolume']);
+						$framedata .= getid3_lib::BigEndian2String($source_data_array['volumechange']['right'], ceil($source_data_array['bitsvolume'] / 8), false);
+						$framedata .= getid3_lib::BigEndian2String($source_data_array['volumechange']['left'],  ceil($source_data_array['bitsvolume'] / 8), false);
+						$framedata .= getid3_lib::BigEndian2String($source_data_array['peakvolume']['right'], ceil($source_data_array['bitsvolume'] / 8), false);
+						$framedata .= getid3_lib::BigEndian2String($source_data_array['peakvolume']['left'],  ceil($source_data_array['bitsvolume'] / 8), false);
+						if ($source_data_array['volumechange']['rightrear'] || $source_data_array['volumechange']['leftrear'] ||
+							$source_data_array['peakvolume']['rightrear'] || $source_data_array['peakvolume']['leftrear'] ||
+							$source_data_array['volumechange']['center'] || $source_data_array['peakvolume']['center'] ||
+							$source_data_array['volumechange']['bass'] || $source_data_array['peakvolume']['bass']) {
+								$framedata .= getid3_lib::BigEndian2String($source_data_array['volumechange']['rightrear'], ceil($source_data_array['bitsvolume']/8), false);
+								$framedata .= getid3_lib::BigEndian2String($source_data_array['volumechange']['leftrear'],  ceil($source_data_array['bitsvolume']/8), false);
+								$framedata .= getid3_lib::BigEndian2String($source_data_array['peakvolume']['rightrear'], ceil($source_data_array['bitsvolume']/8), false);
+								$framedata .= getid3_lib::BigEndian2String($source_data_array['peakvolume']['leftrear'],  ceil($source_data_array['bitsvolume']/8), false);
+						}
+						if ($source_data_array['volumechange']['center'] || $source_data_array['peakvolume']['center'] ||
+							$source_data_array['volumechange']['bass'] || $source_data_array['peakvolume']['bass']) {
+								$framedata .= getid3_lib::BigEndian2String($source_data_array['volumechange']['center'], ceil($source_data_array['bitsvolume']/8), false);
+								$framedata .= getid3_lib::BigEndian2String($source_data_array['peakvolume']['center'], ceil($source_data_array['bitsvolume']/8), false);
+						}
+						if ($source_data_array['volumechange']['bass'] || $source_data_array['peakvolume']['bass']) {
+								$framedata .= getid3_lib::BigEndian2String($source_data_array['volumechange']['bass'], ceil($source_data_array['bitsvolume']/8), false);
+								$framedata .= getid3_lib::BigEndian2String($source_data_array['peakvolume']['bass'], ceil($source_data_array['bitsvolume']/8), false);
+						}
+					}
+					break;
+
+				case 'EQU2':
+					// 4.12  EQU2 Equalisation (2) (ID3v2.4+ only)
+					// Interpolation method  $xx
+					//   $00  Band
+					//   $01  Linear
+					// Identification        <text string> $00
+					//   The following is then repeated for every adjustment point
+					// Frequency          $xx xx
+					// Volume adjustment  $xx xx
+					if (($source_data_array['interpolationmethod'] < 0) || ($source_data_array['interpolationmethod'] > 1)) {
+						$this->errors[] = 'Invalid Interpolation Method byte in '.$frame_name.' ('.$source_data_array['interpolationmethod'].') (valid = 0 or 1)';
+					} else {
+						$framedata .= chr($source_data_array['interpolationmethod']);
+						$framedata .= str_replace("\x00", '', $source_data_array['description'])."\x00";
+						foreach ($source_data_array['data'] as $key => $val) {
+							$framedata .= getid3_lib::BigEndian2String(intval(round($key * 2)), 2, false);
+							$framedata .= getid3_lib::BigEndian2String($val, 2, false, true); // signed 16-bit
+						}
+					}
+					break;
+
+				case 'EQUA':
+					// 4.12  EQUA Equalisation (ID3v2.3 only)
+					// Adjustment bits    $xx
+					//   This is followed by 2 bytes + ('adjustment bits' rounded up to the
+					//   nearest byte) for every equalisation band in the following format,
+					//   giving a frequency range of 0 - 32767Hz:
+					// Increment/decrement   %x (MSB of the Frequency)
+					// Frequency             (lower 15 bits)
+					// Adjustment            $xx (xx ...)
+					if (!$this->IsWithinBitRange($source_data_array['bitsvolume'], 8, false)) {
+						$this->errors[] = 'Invalid Adjustment Bits byte in '.$frame_name.' ('.$source_data_array['bitsvolume'].') (range = 1 to 255)';
+					} else {
+						$framedata .= chr($source_data_array['adjustmentbits']);
+						foreach ($source_data_array as $key => $val) {
+							if ($key != 'bitsvolume') {
+								if (($key > 32767) || ($key < 0)) {
+									$this->errors[] = 'Invalid Frequency in '.$frame_name.' ('.$key.') (range = 0 to 32767)';
+								} else {
+									if ($val >= 0) {
+										// put MSB of frequency to 1 if increment, 0 if decrement
+										$key |= 0x8000;
+									}
+									$framedata .= getid3_lib::BigEndian2String($key, 2, false);
+									$framedata .= getid3_lib::BigEndian2String($val, ceil($source_data_array['adjustmentbits'] / 8), false);
+								}
+							}
+						}
+					}
+					break;
+
+				case 'RVRB':
+					// 4.13  RVRB Reverb
+					// Reverb left (ms)                 $xx xx
+					// Reverb right (ms)                $xx xx
+					// Reverb bounces, left             $xx
+					// Reverb bounces, right            $xx
+					// Reverb feedback, left to left    $xx
+					// Reverb feedback, left to right   $xx
+					// Reverb feedback, right to right  $xx
+					// Reverb feedback, right to left   $xx
+					// Premix left to right             $xx
+					// Premix right to left             $xx
+					if (!$this->IsWithinBitRange($source_data_array['left'], 16, false)) {
+						$this->errors[] = 'Invalid Reverb Left in '.$frame_name.' ('.$source_data_array['left'].') (range = 0 to 65535)';
+					} elseif (!$this->IsWithinBitRange($source_data_array['right'], 16, false)) {
+						$this->errors[] = 'Invalid Reverb Left in '.$frame_name.' ('.$source_data_array['right'].') (range = 0 to 65535)';
+					} elseif (!$this->IsWithinBitRange($source_data_array['bouncesL'], 8, false)) {
+						$this->errors[] = 'Invalid Reverb Bounces, Left in '.$frame_name.' ('.$source_data_array['bouncesL'].') (range = 0 to 255)';
+					} elseif (!$this->IsWithinBitRange($source_data_array['bouncesR'], 8, false)) {
+						$this->errors[] = 'Invalid Reverb Bounces, Right in '.$frame_name.' ('.$source_data_array['bouncesR'].') (range = 0 to 255)';
+					} elseif (!$this->IsWithinBitRange($source_data_array['feedbackLL'], 8, false)) {
+						$this->errors[] = 'Invalid Reverb Feedback, Left-To-Left in '.$frame_name.' ('.$source_data_array['feedbackLL'].') (range = 0 to 255)';
+					} elseif (!$this->IsWithinBitRange($source_data_array['feedbackLR'], 8, false)) {
+						$this->errors[] = 'Invalid Reverb Feedback, Left-To-Right in '.$frame_name.' ('.$source_data_array['feedbackLR'].') (range = 0 to 255)';
+					} elseif (!$this->IsWithinBitRange($source_data_array['feedbackRR'], 8, false)) {
+						$this->errors[] = 'Invalid Reverb Feedback, Right-To-Right in '.$frame_name.' ('.$source_data_array['feedbackRR'].') (range = 0 to 255)';
+					} elseif (!$this->IsWithinBitRange($source_data_array['feedbackRL'], 8, false)) {
+						$this->errors[] = 'Invalid Reverb Feedback, Right-To-Left in '.$frame_name.' ('.$source_data_array['feedbackRL'].') (range = 0 to 255)';
+					} elseif (!$this->IsWithinBitRange($source_data_array['premixLR'], 8, false)) {
+						$this->errors[] = 'Invalid Premix, Left-To-Right in '.$frame_name.' ('.$source_data_array['premixLR'].') (range = 0 to 255)';
+					} elseif (!$this->IsWithinBitRange($source_data_array['premixRL'], 8, false)) {
+						$this->errors[] = 'Invalid Premix, Right-To-Left in '.$frame_name.' ('.$source_data_array['premixRL'].') (range = 0 to 255)';
+					} else {
+						$framedata .= getid3_lib::BigEndian2String($source_data_array['left'], 2, false);
+						$framedata .= getid3_lib::BigEndian2String($source_data_array['right'], 2, false);
+						$framedata .= chr($source_data_array['bouncesL']);
+						$framedata .= chr($source_data_array['bouncesR']);
+						$framedata .= chr($source_data_array['feedbackLL']);
+						$framedata .= chr($source_data_array['feedbackLR']);
+						$framedata .= chr($source_data_array['feedbackRR']);
+						$framedata .= chr($source_data_array['feedbackRL']);
+						$framedata .= chr($source_data_array['premixLR']);
+						$framedata .= chr($source_data_array['premixRL']);
+					}
+					break;
+
+				case 'APIC':
+					// 4.14  APIC Attached picture
+					// Text encoding      $xx
+					// MIME type          <text string> $00
+					// Picture type       $xx
+					// Description        <text string according to encoding> $00 (00)
+					// Picture data       <binary data>
+					$source_data_array['encodingid'] = (isset($source_data_array['encodingid']) ? $source_data_array['encodingid'] : $this->id3v2_default_encodingid);
+					if (!$this->ID3v2IsValidTextEncoding($source_data_array['encodingid'])) {
+						$this->errors[] = 'Invalid Text Encoding in '.$frame_name.' ('.$source_data_array['encodingid'].') for ID3v2.'.$this->majorversion;
+					} elseif (!$this->ID3v2IsValidAPICpicturetype($source_data_array['picturetypeid'])) {
+						$this->errors[] = 'Invalid Picture Type byte in '.$frame_name.' ('.$source_data_array['picturetypeid'].') for ID3v2.'.$this->majorversion;
+					} elseif (($this->majorversion >= 3) && (!$this->ID3v2IsValidAPICimageformat($source_data_array['mime']))) {
+						$this->errors[] = 'Invalid MIME Type in '.$frame_name.' ('.$source_data_array['mime'].') for ID3v2.'.$this->majorversion;
+					} elseif (($source_data_array['mime'] == '-->') && (!$this->IsValidURL($source_data_array['data'], false, false))) {
+						//$this->errors[] = 'Invalid URL in '.$frame_name.' ('.$source_data_array['data'].')';
+						// probably should be an error, need to rewrite IsValidURL() to handle other encodings
+						$this->warnings[] = 'Invalid URL in '.$frame_name.' ('.$source_data_array['data'].')';
+					} else {
+						$framedata .= chr($source_data_array['encodingid']);
+						$framedata .= str_replace("\x00", '', $source_data_array['mime'])."\x00";
+						$framedata .= chr($source_data_array['picturetypeid']);
+						$framedata .= @$source_data_array['description'].getid3_id3v2::TextEncodingTerminatorLookup($source_data_array['encodingid']);
+						$framedata .= $source_data_array['data'];
+					}
+					break;
+
+				case 'GEOB':
+					// 4.15  GEOB General encapsulated object
+					// Text encoding          $xx
+					// MIME type              <text string> $00
+					// Filename               <text string according to encoding> $00 (00)
+					// Content description    <text string according to encoding> $00 (00)
+					// Encapsulated object    <binary data>
+					$source_data_array['encodingid'] = (isset($source_data_array['encodingid']) ? $source_data_array['encodingid'] : $this->id3v2_default_encodingid);
+					if (!$this->ID3v2IsValidTextEncoding($source_data_array['encodingid'])) {
+						$this->errors[] = 'Invalid Text Encoding in '.$frame_name.' ('.$source_data_array['encodingid'].') for ID3v2.'.$this->majorversion;
+					} elseif (!$this->IsValidMIMEstring($source_data_array['mime'])) {
+						$this->errors[] = 'Invalid MIME Type in '.$frame_name.' ('.$source_data_array['mime'].')';
+					} elseif (!$source_data_array['description']) {
+						$this->errors[] = 'Missing Description in '.$frame_name;
+					} else {
+						$framedata .= chr($source_data_array['encodingid']);
+						$framedata .= str_replace("\x00", '', $source_data_array['mime'])."\x00";
+						$framedata .= $source_data_array['filename'].getid3_id3v2::TextEncodingTerminatorLookup($source_data_array['encodingid']);
+						$framedata .= $source_data_array['description'].getid3_id3v2::TextEncodingTerminatorLookup($source_data_array['encodingid']);
+						$framedata .= $source_data_array['data'];
+					}
+					break;
+
+				case 'PCNT':
+					// 4.16  PCNT Play counter
+					//   When the counter reaches all one's, one byte is inserted in
+					//   front of the counter thus making the counter eight bits bigger
+					// Counter        $xx xx xx xx (xx ...)
+					$framedata .= getid3_lib::BigEndian2String($source_data_array['data'], 4, false);
+					break;
+
+				case 'POPM':
+					// 4.17  POPM Popularimeter
+					//   When the counter reaches all one's, one byte is inserted in
+					//   front of the counter thus making the counter eight bits bigger
+					// Email to user   <text string> $00
+					// Rating          $xx
+					// Counter         $xx xx xx xx (xx ...)
+					if (!$this->IsWithinBitRange($source_data_array['rating'], 8, false)) {
+						$this->errors[] = 'Invalid Rating byte in '.$frame_name.' ('.$source_data_array['rating'].') (range = 0 to 255)';
+					} elseif (!IsValidEmail($source_data_array['email'])) {
+						$this->errors[] = 'Invalid Email in '.$frame_name.' ('.$source_data_array['email'].')';
+					} else {
+						$framedata .= str_replace("\x00", '', $source_data_array['email'])."\x00";
+						$framedata .= chr($source_data_array['rating']);
+						$framedata .= getid3_lib::BigEndian2String($source_data_array['data'], 4, false);
+					}
+					break;
+
+				case 'RBUF':
+					// 4.18  RBUF Recommended buffer size
+					// Buffer size               $xx xx xx
+					// Embedded info flag        %0000000x
+					// Offset to next tag        $xx xx xx xx
+					if (!$this->IsWithinBitRange($source_data_array['buffersize'], 24, false)) {
+						$this->errors[] = 'Invalid Buffer Size in '.$frame_name;
+					} elseif (!$this->IsWithinBitRange($source_data_array['nexttagoffset'], 32, false)) {
+						$this->errors[] = 'Invalid Offset To Next Tag in '.$frame_name;
+					} else {
+						$framedata .= getid3_lib::BigEndian2String($source_data_array['buffersize'], 3, false);
+						$flag .= '0000000';
+						$flag .= $source_data_array['flags']['embededinfo'] ? '1' : '0';
+						$framedata .= chr(bindec($flag));
+						$framedata .= getid3_lib::BigEndian2String($source_data_array['nexttagoffset'], 4, false);
+					}
+					break;
+
+				case 'AENC':
+					// 4.19  AENC Audio encryption
+					// Owner identifier   <text string> $00
+					// Preview start      $xx xx
+					// Preview length     $xx xx
+					// Encryption info    <binary data>
+					if (!$this->IsWithinBitRange($source_data_array['previewstart'], 16, false)) {
+						$this->errors[] = 'Invalid Preview Start in '.$frame_name.' ('.$source_data_array['previewstart'].')';
+					} elseif (!$this->IsWithinBitRange($source_data_array['previewlength'], 16, false)) {
+						$this->errors[] = 'Invalid Preview Length in '.$frame_name.' ('.$source_data_array['previewlength'].')';
+					} else {
+						$framedata .= str_replace("\x00", '', $source_data_array['ownerid'])."\x00";
+						$framedata .= getid3_lib::BigEndian2String($source_data_array['previewstart'], 2, false);
+						$framedata .= getid3_lib::BigEndian2String($source_data_array['previewlength'], 2, false);
+						$framedata .= $source_data_array['encryptioninfo'];
+					}
+					break;
+
+				case 'LINK':
+					// 4.20  LINK Linked information
+					// Frame identifier               $xx xx xx xx
+					// URL                            <text string> $00
+					// ID and additional data         <text string(s)>
+					if (!getid3_id3v2::IsValidID3v2FrameName($source_data_array['frameid'], $this->majorversion)) {
+						$this->errors[] = 'Invalid Frame Identifier in '.$frame_name.' ('.$source_data_array['frameid'].')';
+					} elseif (!$this->IsValidURL($source_data_array['data'], true, false)) {
+						//$this->errors[] = 'Invalid URL in '.$frame_name.' ('.$source_data_array['data'].')';
+						// probably should be an error, need to rewrite IsValidURL() to handle other encodings
+						$this->warnings[] = 'Invalid URL in '.$frame_name.' ('.$source_data_array['data'].')';
+					} 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'] == '')) {
+						$this->errors[] = 'Content Descriptor must be specified as additional data for Frame Identifier of '.$source_data_array['frameid'].' in '.$frame_name;
+					} elseif (($source_data_array['frameid'] == 'USER') && (getid3_id3v2::LanguageLookup($source_data_array['additionaldata'], true) == '')) {
+						$this->errors[] = 'Language must be specified as additional data for Frame Identifier of '.$source_data_array['frameid'].' in '.$frame_name;
+					} elseif (($source_data_array['frameid'] == 'PRIV') && ($source_data_array['additionaldata'] == '')) {
+						$this->errors[] = 'Owner Identifier must be specified as additional data for Frame Identifier of '.$source_data_array['frameid'].' in '.$frame_name;
+					} 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) == ''))) {
+						$this->errors[] = 'Language followed by Content Descriptor must be specified as additional data for Frame Identifier of '.$source_data_array['frameid'].' in '.$frame_name;
+					} else {
+						$framedata .= $source_data_array['frameid'];
+						$framedata .= str_replace("\x00", '', $source_data_array['data'])."\x00";
+						switch ($source_data_array['frameid']) {
+							case 'COMM':
+							case 'SYLT':
+							case 'USLT':
+							case 'PRIV':
+							case 'USER':
+							case 'AENC':
+							case 'APIC':
+							case 'GEOB':
+							case 'TXXX':
+								$framedata .= $source_data_array['additionaldata'];
+								break;
+							case 'ASPI':
+							case 'ETCO':
+							case 'EQU2':
+							case 'MCID':
+							case 'MLLT':
+							case 'OWNE':
+							case 'RVA2':
+							case 'RVRB':
+							case 'SYTC':
+							case 'IPLS':
+							case 'RVAD':
+							case 'EQUA':
+								// no additional data required
+								break;
+							case 'RBUF':
+								if ($this->majorversion == 3) {
+									// no additional data required
+								} else {
+									$this->errors[] = $source_data_array['frameid'].' is not a valid Frame Identifier in '.$frame_name.' (in ID3v2.'.$this->majorversion.')';
+								}
+
+							default:
+								if ((substr($source_data_array['frameid'], 0, 1) == 'T') || (substr($source_data_array['frameid'], 0, 1) == 'W')) {
+									// no additional data required
+								} else {
+									$this->errors[] = $source_data_array['frameid'].' is not a valid Frame Identifier in '.$frame_name.' (in ID3v2.'.$this->majorversion.')';
+								}
+								break;
+						}
+					}
+					break;
+
+				case 'POSS':
+					// 4.21  POSS Position synchronisation frame (ID3v2.3+ only)
+					// Time stamp format         $xx
+					// Position                  $xx (xx ...)
+					if (($source_data_array['timestampformat'] < 1) || ($source_data_array['timestampformat'] > 2)) {
+						$this->errors[] = 'Invalid Time Stamp Format in '.$frame_name.' ('.$source_data_array['timestampformat'].') (valid = 1 or 2)';
+					} elseif (!$this->IsWithinBitRange($source_data_array['position'], 32, false)) {
+						$this->errors[] = 'Invalid Position in '.$frame_name.' ('.$source_data_array['position'].') (range = 0 to 4294967295)';
+					} else {
+						$framedata .= chr($source_data_array['timestampformat']);
+						$framedata .= getid3_lib::BigEndian2String($source_data_array['position'], 4, false);
+					}
+					break;
+
+				case 'USER':
+					// 4.22  USER Terms of use (ID3v2.3+ only)
+					// Text encoding        $xx
+					// Language             $xx xx xx
+					// The actual text      <text string according to encoding>
+					$source_data_array['encodingid'] = (isset($source_data_array['encodingid']) ? $source_data_array['encodingid'] : $this->id3v2_default_encodingid);
+					if (!$this->ID3v2IsValidTextEncoding($source_data_array['encodingid'])) {
+						$this->errors[] = 'Invalid Text Encoding in '.$frame_name.' ('.$source_data_array['encodingid'].')';
+					} elseif (getid3_id3v2::LanguageLookup($source_data_array['language'], true) == '') {
+						$this->errors[] = 'Invalid Language in '.$frame_name.' ('.$source_data_array['language'].')';
+					} else {
+						$framedata .= chr($source_data_array['encodingid']);
+						$framedata .= strtolower($source_data_array['language']);
+						$framedata .= $source_data_array['data'];
+					}
+					break;
+
+				case 'OWNE':
+					// 4.23  OWNE Ownership frame (ID3v2.3+ only)
+					// Text encoding     $xx
+					// Price paid        <text string> $00
+					// Date of purch.    <text string>
+					// Seller            <text string according to encoding>
+					$source_data_array['encodingid'] = (isset($source_data_array['encodingid']) ? $source_data_array['encodingid'] : $this->id3v2_default_encodingid);
+					if (!$this->ID3v2IsValidTextEncoding($source_data_array['encodingid'])) {
+						$this->errors[] = 'Invalid Text Encoding in '.$frame_name.' ('.$source_data_array['encodingid'].')';
+					} elseif (!$this->IsANumber($source_data_array['pricepaid']['value'], false)) {
+						$this->errors[] = 'Invalid Price Paid in '.$frame_name.' ('.$source_data_array['pricepaid']['value'].')';
+					} elseif (!$this->IsValidDateStampString($source_data_array['purchasedate'])) {
+						$this->errors[] = 'Invalid Date Of Purchase in '.$frame_name.' ('.$source_data_array['purchasedate'].') (format = YYYYMMDD)';
+					} else {
+						$framedata .= chr($source_data_array['encodingid']);
+						$framedata .= str_replace("\x00", '', $source_data_array['pricepaid']['value'])."\x00";
+						$framedata .= $source_data_array['purchasedate'];
+						$framedata .= $source_data_array['seller'];
+					}
+					break;
+
+				case 'COMR':
+					// 4.24  COMR Commercial frame (ID3v2.3+ only)
+					// Text encoding      $xx
+					// Price string       <text string> $00
+					// Valid until        <text string>
+					// Contact URL        <text string> $00
+					// Received as        $xx
+					// Name of seller     <text string according to encoding> $00 (00)
+					// Description        <text string according to encoding> $00 (00)
+					// Picture MIME type  <string> $00
+					// Seller logo        <binary data>
+					$source_data_array['encodingid'] = (isset($source_data_array['encodingid']) ? $source_data_array['encodingid'] : $this->id3v2_default_encodingid);
+					if (!$this->ID3v2IsValidTextEncoding($source_data_array['encodingid'])) {
+						$this->errors[] = 'Invalid Text Encoding in '.$frame_name.' ('.$source_data_array['encodingid'].')';
+					} elseif (!$this->IsValidDateStampString($source_data_array['pricevaliduntil'])) {
+						$this->errors[] = 'Invalid Valid Until date in '.$frame_name.' ('.$source_data_array['pricevaliduntil'].') (format = YYYYMMDD)';
+					} elseif (!$this->IsValidURL($source_data_array['contacturl'], false, true)) {
+						$this->errors[] = 'Invalid Contact URL in '.$frame_name.' ('.$source_data_array['contacturl'].') (allowed schemes: http, https, ftp, mailto)';
+					} elseif (!$this->ID3v2IsValidCOMRreceivedAs($source_data_array['receivedasid'])) {
+						$this->errors[] = 'Invalid Received As byte in '.$frame_name.' ('.$source_data_array['contacturl'].') (range = 0 to 8)';
+					} elseif (!$this->IsValidMIMEstring($source_data_array['mime'])) {
+						$this->errors[] = 'Invalid MIME Type in '.$frame_name.' ('.$source_data_array['mime'].')';
+					} else {
+						$framedata .= chr($source_data_array['encodingid']);
+						unset($pricestring);
+						foreach ($source_data_array['price'] as $key => $val) {
+							if ($this->ID3v2IsValidPriceString($key.$val['value'])) {
+								$pricestrings[] = $key.$val['value'];
+							} else {
+								$this->errors[] = 'Invalid Price String in '.$frame_name.' ('.$key.$val['value'].')';
+							}
+						}
+						$framedata .= implode('/', $pricestrings);
+						$framedata .= $source_data_array['pricevaliduntil'];
+						$framedata .= str_replace("\x00", '', $source_data_array['contacturl'])."\x00";
+						$framedata .= chr($source_data_array['receivedasid']);
+						$framedata .= $source_data_array['sellername'].getid3_id3v2::TextEncodingTerminatorLookup($source_data_array['encodingid']);
+						$framedata .= $source_data_array['description'].getid3_id3v2::TextEncodingTerminatorLookup($source_data_array['encodingid']);
+						$framedata .= $source_data_array['mime']."\x00";
+						$framedata .= $source_data_array['logo'];
+					}
+					break;
+
+				case 'ENCR':
+					// 4.25  ENCR Encryption method registration (ID3v2.3+ only)
+					// Owner identifier    <text string> $00
+					// Method symbol       $xx
+					// Encryption data     <binary data>
+					if (!$this->IsWithinBitRange($source_data_array['methodsymbol'], 8, false)) {
+						$this->errors[] = 'Invalid Group Symbol in '.$frame_name.' ('.$source_data_array['methodsymbol'].') (range = 0 to 255)';
+					} else {
+						$framedata .= str_replace("\x00", '', $source_data_array['ownerid'])."\x00";
+						$framedata .= ord($source_data_array['methodsymbol']);
+						$framedata .= $source_data_array['data'];
+					}
+					break;
+
+				case 'GRID':
+					// 4.26  GRID Group identification registration (ID3v2.3+ only)
+					// Owner identifier      <text string> $00
+					// Group symbol          $xx
+					// Group dependent data  <binary data>
+					if (!$this->IsWithinBitRange($source_data_array['groupsymbol'], 8, false)) {
+						$this->errors[] = 'Invalid Group Symbol in '.$frame_name.' ('.$source_data_array['groupsymbol'].') (range = 0 to 255)';
+					} else {
+						$framedata .= str_replace("\x00", '', $source_data_array['ownerid'])."\x00";
+						$framedata .= ord($source_data_array['groupsymbol']);
+						$framedata .= $source_data_array['data'];
+					}
+					break;
+
+				case 'PRIV':
+					// 4.27  PRIV Private frame (ID3v2.3+ only)
+					// Owner identifier      <text string> $00
+					// The private data      <binary data>
+					$framedata .= str_replace("\x00", '', $source_data_array['ownerid'])."\x00";
+					$framedata .= $source_data_array['data'];
+					break;
+
+				case 'SIGN':
+					// 4.28  SIGN Signature frame (ID3v2.4+ only)
+					// Group symbol      $xx
+					// Signature         <binary data>
+					if (!$this->IsWithinBitRange($source_data_array['groupsymbol'], 8, false)) {
+						$this->errors[] = 'Invalid Group Symbol in '.$frame_name.' ('.$source_data_array['groupsymbol'].') (range = 0 to 255)';
+					} else {
+						$framedata .= ord($source_data_array['groupsymbol']);
+						$framedata .= $source_data_array['data'];
+					}
+					break;
+
+				case 'SEEK':
+					// 4.29  SEEK Seek frame (ID3v2.4+ only)
+					// Minimum offset to next tag       $xx xx xx xx
+					if (!$this->IsWithinBitRange($source_data_array['data'], 32, false)) {
+						$this->errors[] = 'Invalid Minimum Offset in '.$frame_name.' ('.$source_data_array['data'].') (range = 0 to 4294967295)';
+					} else {
+						$framedata .= getid3_lib::BigEndian2String($source_data_array['data'], 4, false);
+					}
+					break;
+
+				case 'ASPI':
+					// 4.30  ASPI Audio seek point index (ID3v2.4+ only)
+					// Indexed data start (S)         $xx xx xx xx
+					// Indexed data length (L)        $xx xx xx xx
+					// Number of index points (N)     $xx xx
+					// Bits per index point (b)       $xx
+					//   Then for every index point the following data is included:
+					// Fraction at index (Fi)          $xx (xx)
+					if (!$this->IsWithinBitRange($source_data_array['datastart'], 32, false)) {
+						$this->errors[] = 'Invalid Indexed Data Start in '.$frame_name.' ('.$source_data_array['datastart'].') (range = 0 to 4294967295)';
+					} elseif (!$this->IsWithinBitRange($source_data_array['datalength'], 32, false)) {
+						$this->errors[] = 'Invalid Indexed Data Length in '.$frame_name.' ('.$source_data_array['datalength'].') (range = 0 to 4294967295)';
+					} elseif (!$this->IsWithinBitRange($source_data_array['indexpoints'], 16, false)) {
+						$this->errors[] = 'Invalid Number Of Index Points in '.$frame_name.' ('.$source_data_array['indexpoints'].') (range = 0 to 65535)';
+					} elseif (!$this->IsWithinBitRange($source_data_array['bitsperpoint'], 8, false)) {
+						$this->errors[] = 'Invalid Bits Per Index Point in '.$frame_name.' ('.$source_data_array['bitsperpoint'].') (range = 0 to 255)';
+					} elseif ($source_data_array['indexpoints'] != count($source_data_array['indexes'])) {
+						$this->errors[] = 'Number Of Index Points does not match actual supplied data in '.$frame_name;
+					} else {
+						$framedata .= getid3_lib::BigEndian2String($source_data_array['datastart'], 4, false);
+						$framedata .= getid3_lib::BigEndian2String($source_data_array['datalength'], 4, false);
+						$framedata .= getid3_lib::BigEndian2String($source_data_array['indexpoints'], 2, false);
+						$framedata .= getid3_lib::BigEndian2String($source_data_array['bitsperpoint'], 1, false);
+						foreach ($source_data_array['indexes'] as $key => $val) {
+							$framedata .= getid3_lib::BigEndian2String($val, ceil($source_data_array['bitsperpoint'] / 8), false);
+						}
+					}
+					break;
+
+				case 'RGAD':
+					//   RGAD Replay Gain Adjustment
+					//   http://privatewww.essex.ac.uk/~djmrob/replaygain/
+					// Peak Amplitude                     $xx $xx $xx $xx
+					// Radio Replay Gain Adjustment        %aaabbbcd %dddddddd
+					// Audiophile Replay Gain Adjustment   %aaabbbcd %dddddddd
+					//   a - name code
+					//   b - originator code
+					//   c - sign bit
+					//   d - replay gain adjustment
+
+					if (($source_data_array['track_adjustment'] > 51) || ($source_data_array['track_adjustment'] < -51)) {
+						$this->errors[] = 'Invalid Track Adjustment in '.$frame_name.' ('.$source_data_array['track_adjustment'].') (range = -51.0 to +51.0)';
+					} elseif (($source_data_array['album_adjustment'] > 51) || ($source_data_array['album_adjustment'] < -51)) {
+						$this->errors[] = 'Invalid Album Adjustment in '.$frame_name.' ('.$source_data_array['album_adjustment'].') (range = -51.0 to +51.0)';
+					} elseif (!$this->ID3v2IsValidRGADname($source_data_array['raw']['track_name'])) {
+						$this->errors[] = 'Invalid Track Name Code in '.$frame_name.' ('.$source_data_array['raw']['track_name'].') (range = 0 to 2)';
+					} elseif (!$this->ID3v2IsValidRGADname($source_data_array['raw']['album_name'])) {
+						$this->errors[] = 'Invalid Album Name Code in '.$frame_name.' ('.$source_data_array['raw']['album_name'].') (range = 0 to 2)';
+					} elseif (!$this->ID3v2IsValidRGADoriginator($source_data_array['raw']['track_originator'])) {
+						$this->errors[] = 'Invalid Track Originator Code in '.$frame_name.' ('.$source_data_array['raw']['track_originator'].') (range = 0 to 3)';
+					} elseif (!$this->ID3v2IsValidRGADoriginator($source_data_array['raw']['album_originator'])) {
+						$this->errors[] = 'Invalid Album Originator Code in '.$frame_name.' ('.$source_data_array['raw']['album_originator'].') (range = 0 to 3)';
+					} else {
+						$framedata .= getid3_lib::Float2String($source_data_array['peakamplitude'], 32);
+						$framedata .= getid3_lib::RGADgainString($source_data_array['raw']['track_name'], $source_data_array['raw']['track_originator'], $source_data_array['track_adjustment']);
+						$framedata .= getid3_lib::RGADgainString($source_data_array['raw']['album_name'], $source_data_array['raw']['album_originator'], $source_data_array['album_adjustment']);
+					}
+					break;
+
+				default:
+					if ($frame_name{0} == 'T') {
+						// 4.2. T???  Text information frames
+						// Text encoding                $xx
+						// Information                  <text string(s) according to encoding>
+						$source_data_array['encodingid'] = (isset($source_data_array['encodingid']) ? $source_data_array['encodingid'] : $this->id3v2_default_encodingid);
+						if (!$this->ID3v2IsValidTextEncoding($source_data_array['encodingid'])) {
+							$this->errors[] = 'Invalid Text Encoding in '.$frame_name.' ('.$source_data_array['encodingid'].') for ID3v2.'.$this->majorversion;
+						} else {
+							$framedata .= chr($source_data_array['encodingid']);
+							$framedata .= $source_data_array['data'];
+						}
+					} elseif ($frame_name{0} == 'W') {
+						// 4.3. W???  URL link frames
+						// URL              <text string>
+						if (!$this->IsValidURL($source_data_array['data'], false, false)) {
+							//$this->errors[] = 'Invalid URL in '.$frame_name.' ('.$source_data_array['data'].')';
+							// probably should be an error, need to rewrite IsValidURL() to handle other encodings
+							$this->warnings[] = 'Invalid URL in '.$frame_name.' ('.$source_data_array['data'].')';
+						} else {
+							$framedata .= $source_data_array['data'];
+						}
+					} else {
+						$this->errors[] = $frame_name.' not yet supported in $this->GenerateID3v2FrameData()';
+					}
+					break;
+			}
+		}
+		if (!empty($this->errors)) {
+			return false;
+		}
+		return $framedata;
+	}
+
+	function ID3v2FrameIsAllowed($frame_name, $source_data_array) {
+		static $PreviousFrames = array();
+
+		if ($frame_name === null) {
+			// if the writing functions are called multiple times, the static array needs to be
+			// cleared - this can be done by calling $this->ID3v2FrameIsAllowed(null, '')
+			$PreviousFrames = array();
+			return true;
+		}
+
+		if ($this->majorversion == 4) {
+			switch ($frame_name) {
+				case 'UFID':
+				case 'AENC':
+				case 'ENCR':
+				case 'GRID':
+					if (!isset($source_data_array['ownerid'])) {
+						$this->errors[] = '[ownerid] not specified for '.$frame_name;
+					} elseif (in_array($frame_name.$source_data_array['ownerid'], $PreviousFrames)) {
+						$this->errors[] = 'Only one '.$frame_name.' tag allowed with the same OwnerID ('.$source_data_array['ownerid'].')';
+					} else {
+						$PreviousFrames[] = $frame_name.$source_data_array['ownerid'];
+					}
+					break;
+
+				case 'TXXX':
+				case 'WXXX':
+				case 'RVA2':
+				case 'EQU2':
+				case 'APIC':
+				case 'GEOB':
+					if (!isset($source_data_array['description'])) {
+						$this->errors[] = '[description] not specified for '.$frame_name;
+					} elseif (in_array($frame_name.$source_data_array['description'], $PreviousFrames)) {
+						$this->errors[] = 'Only one '.$frame_name.' tag allowed with the same Description ('.$source_data_array['description'].')';
+					} else {
+						$PreviousFrames[] = $frame_name.$source_data_array['description'];
+					}
+					break;
+
+				case 'USER':
+					if (!isset($source_data_array['language'])) {
+						$this->errors[] = '[language] not specified for '.$frame_name;
+					} elseif (in_array($frame_name.$source_data_array['language'], $PreviousFrames)) {
+						$this->errors[] = 'Only one '.$frame_name.' tag allowed with the same Language ('.$source_data_array['language'].')';
+					} else {
+						$PreviousFrames[] = $frame_name.$source_data_array['language'];
+					}
+					break;
+
+				case 'USLT':
+				case 'SYLT':
+				case 'COMM':
+					if (!isset($source_data_array['language'])) {
+						$this->errors[] = '[language] not specified for '.$frame_name;
+					} elseif (!isset($source_data_array['description'])) {
+						$this->errors[] = '[description] not specified for '.$frame_name;
+					} elseif (in_array($frame_name.$source_data_array['language'].$source_data_array['description'], $PreviousFrames)) {
+						$this->errors[] = 'Only one '.$frame_name.' tag allowed with the same Language + Description ('.$source_data_array['language'].' + '.$source_data_array['description'].')';
+					} else {
+						$PreviousFrames[] = $frame_name.$source_data_array['language'].$source_data_array['description'];
+					}
+					break;
+
+				case 'POPM':
+					if (!isset($source_data_array['email'])) {
+						$this->errors[] = '[email] not specified for '.$frame_name;
+					} elseif (in_array($frame_name.$source_data_array['email'], $PreviousFrames)) {
+						$this->errors[] = 'Only one '.$frame_name.' tag allowed with the same Email ('.$source_data_array['email'].')';
+					} else {
+						$PreviousFrames[] = $frame_name.$source_data_array['email'];
+					}
+					break;
+
+				case 'IPLS':
+				case 'MCDI':
+				case 'ETCO':
+				case 'MLLT':
+				case 'SYTC':
+				case 'RVRB':
+				case 'PCNT':
+				case 'RBUF':
+				case 'POSS':
+				case 'OWNE':
+				case 'SEEK':
+				case 'ASPI':
+				case 'RGAD':
+					if (in_array($frame_name, $PreviousFrames)) {
+						$this->errors[] = 'Only one '.$frame_name.' tag allowed';
+					} else {
+						$PreviousFrames[] = $frame_name;
+					}
+					break;
+
+				case 'LINK':
+					// this isn't implemented quite right (yet) - it should check the target frame data for compliance
+					// but right now it just allows one linked frame of each type, to be safe.
+					if (!isset($source_data_array['frameid'])) {
+						$this->errors[] = '[frameid] not specified for '.$frame_name;
+					} elseif (in_array($frame_name.$source_data_array['frameid'], $PreviousFrames)) {
+						$this->errors[] = 'Only one '.$frame_name.' tag allowed with the same FrameID ('.$source_data_array['frameid'].')';
+					} elseif (in_array($source_data_array['frameid'], $PreviousFrames)) {
+						// no links to singleton tags
+						$this->errors[] = 'Cannot specify a '.$frame_name.' tag to a singleton tag that already exists ('.$source_data_array['frameid'].')';
+					} else {
+						$PreviousFrames[] = $frame_name.$source_data_array['frameid']; // only one linked tag of this type
+						$PreviousFrames[] = $source_data_array['frameid'];             // no non-linked singleton tags of this type
+					}
+					break;
+
+				case 'COMR':
+					//   There may be more than one 'commercial frame' in a tag, but no two may be identical
+					// Checking isn't implemented at all (yet) - just assumes that it's OK.
+					break;
+
+				case 'PRIV':
+				case 'SIGN':
+					if (!isset($source_data_array['ownerid'])) {
+						$this->errors[] = '[ownerid] not specified for '.$frame_name;
+					} elseif (!isset($source_data_array['data'])) {
+						$this->errors[] = '[data] not specified for '.$frame_name;
+					} elseif (in_array($frame_name.$source_data_array['ownerid'].$source_data_array['data'], $PreviousFrames)) {
+						$this->errors[] = 'Only one '.$frame_name.' tag allowed with the same OwnerID + Data ('.$source_data_array['ownerid'].' + '.$source_data_array['data'].')';
+					} else {
+						$PreviousFrames[] = $frame_name.$source_data_array['ownerid'].$source_data_array['data'];
+					}
+					break;
+
+				default:
+					if (($frame_name{0} != 'T') && ($frame_name{0} != 'W')) {
+						$this->errors[] = 'Frame not allowed in ID3v2.'.$this->majorversion.': '.$frame_name;
+					}
+					break;
+			}
+
+		} elseif ($this->majorversion == 3) {
+
+			switch ($frame_name) {
+				case 'UFID':
+				case 'AENC':
+				case 'ENCR':
+				case 'GRID':
+					if (!isset($source_data_array['ownerid'])) {
+						$this->errors[] = '[ownerid] not specified for '.$frame_name;
+					} elseif (in_array($frame_name.$source_data_array['ownerid'], $PreviousFrames)) {
+						$this->errors[] = 'Only one '.$frame_name.' tag allowed with the same OwnerID ('.$source_data_array['ownerid'].')';
+					} else {
+						$PreviousFrames[] = $frame_name.$source_data_array['ownerid'];
+					}
+					break;
+
+				case 'TXXX':
+				case 'WXXX':
+				case 'APIC':
+				case 'GEOB':
+					if (!isset($source_data_array['description'])) {
+						$this->errors[] = '[description] not specified for '.$frame_name;
+					} elseif (in_array($frame_name.$source_data_array['description'], $PreviousFrames)) {
+						$this->errors[] = 'Only one '.$frame_name.' tag allowed with the same Description ('.$source_data_array['description'].')';
+					} else {
+						$PreviousFrames[] = $frame_name.$source_data_array['description'];
+					}
+					break;
+
+				case 'USER':
+					if (!isset($source_data_array['language'])) {
+						$this->errors[] = '[language] not specified for '.$frame_name;
+					} elseif (in_array($frame_name.$source_data_array['language'], $PreviousFrames)) {
+						$this->errors[] = 'Only one '.$frame_name.' tag allowed with the same Language ('.$source_data_array['language'].')';
+					} else {
+						$PreviousFrames[] = $frame_name.$source_data_array['language'];
+					}
+					break;
+
+				case 'USLT':
+				case 'SYLT':
+				case 'COMM':
+					if (!isset($source_data_array['language'])) {
+						$this->errors[] = '[language] not specified for '.$frame_name;
+					} elseif (!isset($source_data_array['description'])) {
+						$this->errors[] = '[description] not specified for '.$frame_name;
+					} elseif (in_array($frame_name.$source_data_array['language'].$source_data_array['description'], $PreviousFrames)) {
+						$this->errors[] = 'Only one '.$frame_name.' tag allowed with the same Language + Description ('.$source_data_array['language'].' + '.$source_data_array['description'].')';
+					} else {
+						$PreviousFrames[] = $frame_name.$source_data_array['language'].$source_data_array['description'];
+					}
+					break;
+
+				case 'POPM':
+					if (!isset($source_data_array['email'])) {
+						$this->errors[] = '[email] not specified for '.$frame_name;
+					} elseif (in_array($frame_name.$source_data_array['email'], $PreviousFrames)) {
+						$this->errors[] = 'Only one '.$frame_name.' tag allowed with the same Email ('.$source_data_array['email'].')';
+					} else {
+						$PreviousFrames[] = $frame_name.$source_data_array['email'];
+					}
+					break;
+
+				case 'IPLS':
+				case 'MCDI':
+				case 'ETCO':
+				case 'MLLT':
+				case 'SYTC':
+				case 'RVAD':
+				case 'EQUA':
+				case 'RVRB':
+				case 'PCNT':
+				case 'RBUF':
+				case 'POSS':
+				case 'OWNE':
+				case 'RGAD':
+					if (in_array($frame_name, $PreviousFrames)) {
+						$this->errors[] = 'Only one '.$frame_name.' tag allowed';
+					} else {
+						$PreviousFrames[] = $frame_name;
+					}
+					break;
+
+				case 'LINK':
+					// this isn't implemented quite right (yet) - it should check the target frame data for compliance
+					// but right now it just allows one linked frame of each type, to be safe.
+					if (!isset($source_data_array['frameid'])) {
+						$this->errors[] = '[frameid] not specified for '.$frame_name;
+					} elseif (in_array($frame_name.$source_data_array['frameid'], $PreviousFrames)) {
+						$this->errors[] = 'Only one '.$frame_name.' tag allowed with the same FrameID ('.$source_data_array['frameid'].')';
+					} elseif (in_array($source_data_array['frameid'], $PreviousFrames)) {
+						// no links to singleton tags
+						$this->errors[] = 'Cannot specify a '.$frame_name.' tag to a singleton tag that already exists ('.$source_data_array['frameid'].')';
+					} else {
+						$PreviousFrames[] = $frame_name.$source_data_array['frameid']; // only one linked tag of this type
+						$PreviousFrames[] = $source_data_array['frameid'];             // no non-linked singleton tags of this type
+					}
+					break;
+
+				case 'COMR':
+					//   There may be more than one 'commercial frame' in a tag, but no two may be identical
+					// Checking isn't implemented at all (yet) - just assumes that it's OK.
+					break;
+
+				case 'PRIV':
+					if (!isset($source_data_array['ownerid'])) {
+						$this->errors[] = '[ownerid] not specified for '.$frame_name;
+					} elseif (!isset($source_data_array['data'])) {
+						$this->errors[] = '[data] not specified for '.$frame_name;
+					} elseif (in_array($frame_name.$source_data_array['ownerid'].$source_data_array['data'], $PreviousFrames)) {
+						$this->errors[] = 'Only one '.$frame_name.' tag allowed with the same OwnerID + Data ('.$source_data_array['ownerid'].' + '.$source_data_array['data'].')';
+					} else {
+						$PreviousFrames[] = $frame_name.$source_data_array['ownerid'].$source_data_array['data'];
+					}
+					break;
+
+				default:
+					if (($frame_name{0} != 'T') && ($frame_name{0} != 'W')) {
+						$this->errors[] = 'Frame not allowed in ID3v2.'.$this->majorversion.': '.$frame_name;
+					}
+					break;
+			}
+
+		} elseif ($this->majorversion == 2) {
+
+			switch ($frame_name) {
+				case 'UFI':
+				case 'CRM':
+				case 'CRA':
+					if (!isset($source_data_array['ownerid'])) {
+						$this->errors[] = '[ownerid] not specified for '.$frame_name;
+					} elseif (in_array($frame_name.$source_data_array['ownerid'], $PreviousFrames)) {
+						$this->errors[] = 'Only one '.$frame_name.' tag allowed with the same OwnerID ('.$source_data_array['ownerid'].')';
+					} else {
+						$PreviousFrames[] = $frame_name.$source_data_array['ownerid'];
+					}
+					break;
+
+				case 'TXX':
+				case 'WXX':
+				case 'PIC':
+				case 'GEO':
+					if (!isset($source_data_array['description'])) {
+						$this->errors[] = '[description] not specified for '.$frame_name;
+					} elseif (in_array($frame_name.$source_data_array['description'], $PreviousFrames)) {
+						$this->errors[] = 'Only one '.$frame_name.' tag allowed with the same Description ('.$source_data_array['description'].')';
+					} else {
+						$PreviousFrames[] = $frame_name.$source_data_array['description'];
+					}
+					break;
+
+				case 'ULT':
+				case 'SLT':
+				case 'COM':
+					if (!isset($source_data_array['language'])) {
+						$this->errors[] = '[language] not specified for '.$frame_name;
+					} elseif (!isset($source_data_array['description'])) {
+						$this->errors[] = '[description] not specified for '.$frame_name;
+					} elseif (in_array($frame_name.$source_data_array['language'].$source_data_array['description'], $PreviousFrames)) {
+						$this->errors[] = 'Only one '.$frame_name.' tag allowed with the same Language + Description ('.$source_data_array['language'].' + '.$source_data_array['description'].')';
+					} else {
+						$PreviousFrames[] = $frame_name.$source_data_array['language'].$source_data_array['description'];
+					}
+					break;
+
+				case 'POP':
+					if (!isset($source_data_array['email'])) {
+						$this->errors[] = '[email] not specified for '.$frame_name;
+					} elseif (in_array($frame_name.$source_data_array['email'], $PreviousFrames)) {
+						$this->errors[] = 'Only one '.$frame_name.' tag allowed with the same Email ('.$source_data_array['email'].')';
+					} else {
+						$PreviousFrames[] = $frame_name.$source_data_array['email'];
+					}
+					break;
+
+				case 'IPL':
+				case 'MCI':
+				case 'ETC':
+				case 'MLL':
+				case 'STC':
+				case 'RVA':
+				case 'EQU':
+				case 'REV':
+				case 'CNT':
+				case 'BUF':
+					if (in_array($frame_name, $PreviousFrames)) {
+						$this->errors[] = 'Only one '.$frame_name.' tag allowed';
+					} else {
+						$PreviousFrames[] = $frame_name;
+					}
+					break;
+
+				case 'LNK':
+					// this isn't implemented quite right (yet) - it should check the target frame data for compliance
+					// but right now it just allows one linked frame of each type, to be safe.
+					if (!isset($source_data_array['frameid'])) {
+						$this->errors[] = '[frameid] not specified for '.$frame_name;
+					} elseif (in_array($frame_name.$source_data_array['frameid'], $PreviousFrames)) {
+						$this->errors[] = 'Only one '.$frame_name.' tag allowed with the same FrameID ('.$source_data_array['frameid'].')';
+					} elseif (in_array($source_data_array['frameid'], $PreviousFrames)) {
+						// no links to singleton tags
+						$this->errors[] = 'Cannot specify a '.$frame_name.' tag to a singleton tag that already exists ('.$source_data_array['frameid'].')';
+					} else {
+						$PreviousFrames[] = $frame_name.$source_data_array['frameid']; // only one linked tag of this type
+						$PreviousFrames[] = $source_data_array['frameid'];             // no non-linked singleton tags of this type
+					}
+					break;
+
+				default:
+					if (($frame_name{0} != 'T') && ($frame_name{0} != 'W')) {
+						$this->errors[] = 'Frame not allowed in ID3v2.'.$this->majorversion.': '.$frame_name;
+					}
+					break;
+			}
+		}
+
+		if (!empty($this->errors)) {
+			return false;
+		}
+		return true;
+	}
+
+	function GenerateID3v2Tag($noerrorsonly=true) {
+		$this->ID3v2FrameIsAllowed(null, ''); // clear static array in case this isn't the first call to $this->GenerateID3v2Tag()
+
+		$tagstring = '';
+		if (is_array($this->tag_data)) {
+			foreach ($this->tag_data as $frame_name => $frame_rawinputdata) {
+				foreach ($frame_rawinputdata as $irrelevantindex => $source_data_array) {
+					if (getid3_id3v2::IsValidID3v2FrameName($frame_name, $this->majorversion)) {
+						unset($frame_length);
+						unset($frame_flags);
+						$frame_data = false;
+						if ($this->ID3v2FrameIsAllowed($frame_name, $source_data_array)) {
+							if ($frame_data = $this->GenerateID3v2FrameData($frame_name, $source_data_array)) {
+								$FrameUnsynchronisation = false;
+								if ($this->majorversion >= 4) {
+									// frame-level unsynchronisation
+									$unsynchdata = $frame_data;
+									if ($this->id3v2_use_unsynchronisation) {
+										$unsynchdata = $this->Unsynchronise($frame_data);
+									}
+									if (strlen($unsynchdata) != strlen($frame_data)) {
+										// unsynchronisation needed
+										$FrameUnsynchronisation = true;
+										$frame_data = $unsynchdata;
+										if (isset($TagUnsynchronisation) && $TagUnsynchronisation === false) {
+											// only set to true if ALL frames are unsynchronised
+										} else {
+											$TagUnsynchronisation = true;
+										}
+									} else {
+										if (isset($TagUnsynchronisation)) {
+											$TagUnsynchronisation = false;
+										}
+									}
+									unset($unsynchdata);
+
+									$frame_length = getid3_lib::BigEndian2String(strlen($frame_data), 4, true);
+								} else {
+									$frame_length = getid3_lib::BigEndian2String(strlen($frame_data), 4, false);
+								}
+								$frame_flags  = $this->GenerateID3v2FrameFlags($this->ID3v2FrameFlagsLookupTagAlter($frame_name), $this->ID3v2FrameFlagsLookupFileAlter($frame_name), false, false, false, false, $FrameUnsynchronisation, false);
+							}
+						} else {
+							$this->errors[] = 'Frame "'.$frame_name.'" is NOT allowed';
+						}
+						if ($frame_data === false) {
+							$this->errors[] = '$this->GenerateID3v2FrameData() failed for "'.$frame_name.'"';
+							if ($noerrorsonly) {
+								return false;
+							} else {
+								unset($frame_name);
+							}
+						}
+					} else {
+						// ignore any invalid frame names, including 'title', 'header', etc
+						$this->warnings[] = 'Ignoring invalid ID3v2 frame type: "'.$frame_name.'"';
+						unset($frame_name);
+						unset($frame_length);
+						unset($frame_flags);
+						unset($frame_data);
+					}
+					if (isset($frame_name) && isset($frame_length) && isset($frame_flags) && isset($frame_data)) {
+						$tagstring .= $frame_name.$frame_length.$frame_flags.$frame_data;
+					}
+				}
+			}
+
+			if (!isset($TagUnsynchronisation)) {
+				$TagUnsynchronisation = false;
+			}
+			if (($this->majorversion <= 3) && $this->id3v2_use_unsynchronisation) {
+				// tag-level unsynchronisation
+				$unsynchdata = $this->Unsynchronise($tagstring);
+				if (strlen($unsynchdata) != strlen($tagstring)) {
+					// unsynchronisation needed
+					$TagUnsynchronisation = true;
+					$tagstring = $unsynchdata;
+				}
+			}
+
+			while ($this->paddedlength < (strlen($tagstring) + getid3_id3v2::ID3v2HeaderLength($this->majorversion))) {
+				$this->paddedlength += 1024;
+			}
+
+			$footer = false; // ID3v2 footers not yet supported in getID3()
+			if (!$footer && ($this->paddedlength > (strlen($tagstring) + getid3_id3v2::ID3v2HeaderLength($this->majorversion)))) {
+				// pad up to $paddedlength bytes if unpadded tag is shorter than $paddedlength
+				// "Furthermore it MUST NOT have any padding when a tag footer is added to the tag."
+				$tagstring .= @str_repeat("\x00", $this->paddedlength - strlen($tagstring) - getid3_id3v2::ID3v2HeaderLength($this->majorversion));
+			}
+			if ($this->id3v2_use_unsynchronisation && (substr($tagstring, strlen($tagstring) - 1, 1) == "\xFF")) {
+				// special unsynchronisation case:
+				// if last byte == $FF then appended a $00
+				$TagUnsynchronisation = true;
+				$tagstring .= "\x00";
+			}
+
+			$tagheader  = 'ID3';
+			$tagheader .= chr($this->majorversion);
+			$tagheader .= chr($this->minorversion);
+			$tagheader .= $this->GenerateID3v2TagFlags(array('unsynchronisation'=>$TagUnsynchronisation));
+			$tagheader .= getid3_lib::BigEndian2String(strlen($tagstring), 4, true);
+
+			return $tagheader.$tagstring;
+		}
+		$this->errors[] = 'tag_data is not an array in GenerateID3v2Tag()';
+		return false;
+	}
+
+	function ID3v2IsValidPriceString($pricestring) {
+		if (getid3_id3v2::LanguageLookup(substr($pricestring, 0, 3), true) == '') {
+			return false;
+		} elseif (!$this->IsANumber(substr($pricestring, 3), true)) {
+			return false;
+		}
+		return true;
+	}
+
+	function ID3v2FrameFlagsLookupTagAlter($framename) {
+		// unfinished
+		switch ($framename) {
+			case 'RGAD':
+				$allow = true;
+			default:
+				$allow = false;
+				break;
+		}
+		return $allow;
+	}
+
+	function ID3v2FrameFlagsLookupFileAlter($framename) {
+		// unfinished
+		switch ($framename) {
+			case 'RGAD':
+				return false;
+				break;
+
+			default:
+				return false;
+				break;
+		}
+	}
+
+	function ID3v2IsValidETCOevent($eventid) {
+		if (($eventid < 0) || ($eventid > 0xFF)) {
+			// outside range of 1 byte
+			return false;
+		} elseif (($eventid >= 0xF0) && ($eventid <= 0xFC)) {
+			// reserved for future use
+			return false;
+		} elseif (($eventid >= 0x17) && ($eventid <= 0xDF)) {
+			// reserved for future use
+			return false;
+		} elseif (($eventid >= 0x0E) && ($eventid <= 0x16) && ($this->majorversion == 2)) {
+			// not defined in ID3v2.2
+			return false;
+		} elseif (($eventid >= 0x15) && ($eventid <= 0x16) && ($this->majorversion == 3)) {
+			// not defined in ID3v2.3
+			return false;
+		}
+		return true;
+	}
+
+	function ID3v2IsValidSYLTtype($contenttype) {
+		if (($contenttype >= 0) && ($contenttype <= 8) && ($this->majorversion == 4)) {
+			return true;
+		} elseif (($contenttype >= 0) && ($contenttype <= 6) && ($this->majorversion == 3)) {
+			return true;
+		}
+		return false;
+	}
+
+	function ID3v2IsValidRVA2channeltype($channeltype) {
+		if (($channeltype >= 0) && ($channeltype <= 8) && ($this->majorversion == 4)) {
+			return true;
+		}
+		return false;
+	}
+
+	function ID3v2IsValidAPICpicturetype($picturetype) {
+		if (($picturetype >= 0) && ($picturetype <= 0x14) && ($this->majorversion >= 2) && ($this->majorversion <= 4)) {
+			return true;
+		}
+		return false;
+	}
+
+	function ID3v2IsValidAPICimageformat($imageformat) {
+		if ($imageformat == '-->') {
+			return true;
+		} elseif ($this->majorversion == 2) {
+			if ((strlen($imageformat) == 3) && ($imageformat == strtoupper($imageformat))) {
+				return true;
+			}
+		} elseif (($this->majorversion == 3) || ($this->majorversion == 4)) {
+			if ($this->IsValidMIMEstring($imageformat)) {
+				return true;
+			}
+		}
+		return false;
+	}
+
+	function ID3v2IsValidCOMRreceivedAs($receivedas) {
+		if (($this->majorversion >= 3) && ($receivedas >= 0) && ($receivedas <= 8)) {
+			return true;
+		}
+		return false;
+	}
+
+	function ID3v2IsValidRGADname($RGADname) {
+		if (($RGADname >= 0) && ($RGADname <= 2)) {
+			return true;
+		}
+		return false;
+	}
+
+	function ID3v2IsValidRGADoriginator($RGADoriginator) {
+		if (($RGADoriginator >= 0) && ($RGADoriginator <= 3)) {
+			return true;
+		}
+		return false;
+	}
+
+	function ID3v2IsValidTextEncoding($textencodingbyte) {
+		static $ID3v2IsValidTextEncoding_cache = array(
+			2 => array(true, true),
+			3 => array(true, true),
+			4 => array(true, true, true, true));
+		return isset($ID3v2IsValidTextEncoding_cache[$this->majorversion][$textencodingbyte]);
+	}
+
+	function Unsynchronise($data) {
+		// Whenever a false synchronisation is found within the tag, one zeroed
+		// byte is inserted after the first false synchronisation byte. The
+		// format of a correct sync that should be altered by ID3 encoders is as
+		// follows:
+		//      %11111111 111xxxxx
+		// And should be replaced with:
+		//      %11111111 00000000 111xxxxx
+		// This has the side effect that all $FF 00 combinations have to be
+		// altered, so they won't be affected by the decoding process. Therefore
+		// all the $FF 00 combinations have to be replaced with the $FF 00 00
+		// combination during the unsynchronisation.
+
+		$data = str_replace("\xFF\x00", "\xFF\x00\x00", $data);
+		$unsyncheddata = '';
+		$datalength = strlen($data);
+		for ($i = 0; $i < $datalength; $i++) {
+			$thischar = $data{$i};
+			$unsyncheddata .= $thischar;
+			if ($thischar == "\xFF") {
+				$nextchar = ord($data{$i + 1});
+				if (($nextchar & 0xE0) == 0xE0) {
+					// previous byte = 11111111, this byte = 111?????
+					$unsyncheddata .= "\x00";
+				}
+			}
+		}
+		return $unsyncheddata;
+	}
+
+	function is_hash($var) {
+		// written by dev-nullØchristophe*vg
+		// taken from http://www.php.net/manual/en/function.array-merge-recursive.php
+		if (is_array($var)) {
+			$keys = array_keys($var);
+			$all_num = true;
+			for ($i = 0; $i < count($keys); $i++) {
+				if (is_string($keys[$i])) {
+					return true;
+				}
+			}
+		}
+		return false;
+	}
+
+	function array_join_merge($arr1, $arr2) {
+		// written by dev-nullØchristophe*vg
+		// taken from http://www.php.net/manual/en/function.array-merge-recursive.php
+		if (is_array($arr1) && is_array($arr2)) {
+			// the same -> merge
+			$new_array = array();
+
+			if ($this->is_hash($arr1) && $this->is_hash($arr2)) {
+				// hashes -> merge based on keys
+				$keys = array_merge(array_keys($arr1), array_keys($arr2));
+				foreach ($keys as $key) {
+					$new_array[$key] = $this->array_join_merge(@$arr1[$key], @$arr2[$key]);
+				}
+			} else {
+				// two real arrays -> merge
+				$new_array = array_reverse(array_unique(array_reverse(array_merge($arr1, $arr2))));
+			}
+			return $new_array;
+		} else {
+			// not the same ... take new one if defined, else the old one stays
+			return $arr2 ? $arr2 : $arr1;
+		}
+	}
+
+	function IsValidMIMEstring($mimestring) {
+		if ((strlen($mimestring) >= 3) && (strpos($mimestring, '/') > 0) && (strpos($mimestring, '/') < (strlen($mimestring) - 1))) {
+			return true;
+		}
+		return false;
+	}
+
+	function IsWithinBitRange($number, $maxbits, $signed=false) {
+		if ($signed) {
+			if (($number > (0 - pow(2, $maxbits - 1))) && ($number <= pow(2, $maxbits - 1))) {
+				return true;
+			}
+		} else {
+			if (($number >= 0) && ($number <= pow(2, $maxbits))) {
+				return true;
+			}
+		}
+		return false;
+	}
+
+	function safe_parse_url($url) {
+		$parts = @parse_url($url);
+		$parts['scheme'] = (isset($parts['scheme']) ? $parts['scheme'] : '');
+		$parts['host']   = (isset($parts['host'])   ? $parts['host']   : '');
+		$parts['user']   = (isset($parts['user'])   ? $parts['user']   : '');
+		$parts['pass']   = (isset($parts['pass'])   ? $parts['pass']   : '');
+		$parts['path']   = (isset($parts['path'])   ? $parts['path']   : '');
+		$parts['query']  = (isset($parts['query'])  ? $parts['query']  : '');
+		return $parts;
+	}
+
+	function IsValidURL($url, $allowUserPass=false) {
+		if ($url == '') {
+			return false;
+		}
+		if ($allowUserPass !== true) {
+			if (strstr($url, '@')) {
+				// in the format http://user:pass@example.com  or http://user@example.com
+				// but could easily be somebody incorrectly entering an email address in place of a URL
+				return false;
+			}
+		}
+		if ($parts = $this->safe_parse_url($url)) {
+			if (($parts['scheme'] != 'http') && ($parts['scheme'] != 'https') && ($parts['scheme'] != 'ftp') && ($parts['scheme'] != 'gopher')) {
+				return false;
+			} elseif (!eregi("^[[:alnum:]]([-.]?[0-9a-z])*\.[a-z]{2,3}$", $parts['host'], $regs) && !IsValidDottedIP($parts['host'])) {
+				return false;
+			} elseif (!eregi("^([[:alnum:]-]|[\_])*$", $parts['user'], $regs)) {
+				return false;
+			} elseif (!eregi("^([[:alnum:]-]|[\_])*$", $parts['pass'], $regs)) {
+				return false;
+			} elseif (!eregi("^[[:alnum:]/_\.@~-]*$", $parts['path'], $regs)) {
+				return false;
+			} elseif (!eregi("^[[:alnum:]?&=+:;_()%#/,\.-]*$", $parts['query'], $regs)) {
+				return false;
+			} else {
+				return true;
+			}
+		}
+		return false;
+	}
+
+	function ID3v2ShortFrameNameLookup($majorversion, $long_description) {
+		$long_description = str_replace(' ', '_', strtolower(trim($long_description)));
+		static $ID3v2ShortFrameNameLookup = array();
+		if (empty($ID3v2ShortFrameNameLookup)) {
+
+			// The following are unique to ID3v2.2
+			$ID3v2ShortFrameNameLookup[2]['comment']                                          = 'COM';
+			$ID3v2ShortFrameNameLookup[2]['album']                                            = 'TAL';
+			$ID3v2ShortFrameNameLookup[2]['beats_per_minute']                                 = 'TBP';
+			$ID3v2ShortFrameNameLookup[2]['composer']                                         = 'TCM';
+			$ID3v2ShortFrameNameLookup[2]['genre']                                            = 'TCO';
+			$ID3v2ShortFrameNameLookup[2]['copyright']                                        = 'TCR';
+			$ID3v2ShortFrameNameLookup[2]['encoded_by']                                       = 'TEN';
+			$ID3v2ShortFrameNameLookup[2]['language']                                         = 'TLA';
+			$ID3v2ShortFrameNameLookup[2]['length']                                           = 'TLE';
+			$ID3v2ShortFrameNameLookup[2]['original_artist']                                  = 'TOA';
+			$ID3v2ShortFrameNameLookup[2]['original_filename']                                = 'TOF';
+			$ID3v2ShortFrameNameLookup[2]['original_lyricist']                                = 'TOL';
+			$ID3v2ShortFrameNameLookup[2]['original_album_title']                             = 'TOT';
+			$ID3v2ShortFrameNameLookup[2]['artist']                                           = 'TP1';
+			$ID3v2ShortFrameNameLookup[2]['band']                                             = 'TP2';
+			$ID3v2ShortFrameNameLookup[2]['conductor']                                        = 'TP3';
+			$ID3v2ShortFrameNameLookup[2]['remixer']                                          = 'TP4';
+			$ID3v2ShortFrameNameLookup[2]['publisher']                                        = 'TPB';
+			$ID3v2ShortFrameNameLookup[2]['isrc']                                             = 'TRC';
+			$ID3v2ShortFrameNameLookup[2]['tracknumber']                                      = 'TRK';
+			$ID3v2ShortFrameNameLookup[2]['size']                                             = 'TSI';
+			$ID3v2ShortFrameNameLookup[2]['encoder_settings']                                 = 'TSS';
+			$ID3v2ShortFrameNameLookup[2]['description']                                      = 'TT1';
+			$ID3v2ShortFrameNameLookup[2]['title']                                            = 'TT2';
+			$ID3v2ShortFrameNameLookup[2]['subtitle']                                         = 'TT3';
+			$ID3v2ShortFrameNameLookup[2]['lyricist']                                         = 'TXT';
+			$ID3v2ShortFrameNameLookup[2]['user_text']                                        = 'TXX';
+			$ID3v2ShortFrameNameLookup[2]['year']                                             = 'TYE';
+			$ID3v2ShortFrameNameLookup[2]['unique_file_identifier']                           = 'UFI';
+			$ID3v2ShortFrameNameLookup[2]['unsynchronised_lyrics']                            = 'ULT';
+			$ID3v2ShortFrameNameLookup[2]['url_file']                                         = 'WAF';
+			$ID3v2ShortFrameNameLookup[2]['url_artist']                                       = 'WAR';
+			$ID3v2ShortFrameNameLookup[2]['url_source']                                       = 'WAS';
+			$ID3v2ShortFrameNameLookup[2]['copyright_information']                            = 'WCP';
+			$ID3v2ShortFrameNameLookup[2]['url_publisher']                                    = 'WPB';
+			$ID3v2ShortFrameNameLookup[2]['url_user']                                         = 'WXX';
+
+			// The following are common to ID3v2.3 and ID3v2.4
+			$ID3v2ShortFrameNameLookup[3]['audio_encryption']                                 = 'AENC';
+			$ID3v2ShortFrameNameLookup[3]['attached_picture']                                 = 'APIC';
+			$ID3v2ShortFrameNameLookup[3]['comment']                                          = 'COMM';
+			$ID3v2ShortFrameNameLookup[3]['commercial']                                       = 'COMR';
+			$ID3v2ShortFrameNameLookup[3]['encryption_method_registration']                   = 'ENCR';
+			$ID3v2ShortFrameNameLookup[3]['event_timing_codes']                               = 'ETCO';
+			$ID3v2ShortFrameNameLookup[3]['general_encapsulated_object']                      = 'GEOB';
+			$ID3v2ShortFrameNameLookup[3]['group_identification_registration']                = 'GRID';
+			$ID3v2ShortFrameNameLookup[3]['linked_information']                               = 'LINK';
+			$ID3v2ShortFrameNameLookup[3]['music_cd_identifier']                              = 'MCDI';
+			$ID3v2ShortFrameNameLookup[3]['mpeg_location_lookup_table']                       = 'MLLT';
+			$ID3v2ShortFrameNameLookup[3]['ownership']                                        = 'OWNE';
+			$ID3v2ShortFrameNameLookup[3]['play_counter']                                     = 'PCNT';
+			$ID3v2ShortFrameNameLookup[3]['popularimeter']                                    = 'POPM';
+			$ID3v2ShortFrameNameLookup[3]['position_synchronisation']                         = 'POSS';
+			$ID3v2ShortFrameNameLookup[3]['private']                                          = 'PRIV';
+			$ID3v2ShortFrameNameLookup[3]['recommended_buffer_size']                          = 'RBUF';
+			$ID3v2ShortFrameNameLookup[3]['reverb']                                           = 'RVRB';
+			$ID3v2ShortFrameNameLookup[3]['synchronised_lyrics']                              = 'SYLT';
+			$ID3v2ShortFrameNameLookup[3]['synchronised_tempo_codes']                         = 'SYTC';
+			$ID3v2ShortFrameNameLookup[3]['album']                                            = 'TALB';
+			$ID3v2ShortFrameNameLookup[3]['beats_per_minute']                                 = 'TBPM';
+			$ID3v2ShortFrameNameLookup[3]['composer']                                         = 'TCOM';
+			$ID3v2ShortFrameNameLookup[3]['genre']                                            = 'TCON';
+			$ID3v2ShortFrameNameLookup[3]['copyright']                                        = 'TCOP';
+			$ID3v2ShortFrameNameLookup[3]['playlist_delay']                                   = 'TDLY';
+			$ID3v2ShortFrameNameLookup[3]['encoded_by']                                       = 'TENC';
+			$ID3v2ShortFrameNameLookup[3]['lyricist']                                         = 'TEXT';
+			$ID3v2ShortFrameNameLookup[3]['file_type']                                        = 'TFLT';
+			$ID3v2ShortFrameNameLookup[3]['content_group_description']                        = 'TIT1';
+			$ID3v2ShortFrameNameLookup[3]['title']                                            = 'TIT2';
+			$ID3v2ShortFrameNameLookup[3]['subtitle']                                         = 'TIT3';
+			$ID3v2ShortFrameNameLookup[3]['initial_key']                                      = 'TKEY';
+			$ID3v2ShortFrameNameLookup[3]['language']                                         = 'TLAN';
+			$ID3v2ShortFrameNameLookup[3]['length']                                           = 'TLEN';
+			$ID3v2ShortFrameNameLookup[3]['media_type']                                       = 'TMED';
+			$ID3v2ShortFrameNameLookup[3]['original_album_title']                             = 'TOAL';
+			$ID3v2ShortFrameNameLookup[3]['original_filename']                                = 'TOFN';
+			$ID3v2ShortFrameNameLookup[3]['original_lyricist']                                = 'TOLY';
+			$ID3v2ShortFrameNameLookup[3]['original_artist']                                  = 'TOPE';
+			$ID3v2ShortFrameNameLookup[3]['file_owner']                                       = 'TOWN';
+			$ID3v2ShortFrameNameLookup[3]['artist']                                           = 'TPE1';
+			$ID3v2ShortFrameNameLookup[3]['band']                                             = 'TPE2';
+			$ID3v2ShortFrameNameLookup[3]['conductor']                                        = 'TPE3';
+			$ID3v2ShortFrameNameLookup[3]['remixer']                                          = 'TPE4';
+			$ID3v2ShortFrameNameLookup[3]['part_of_set']                                      = 'TPOS';
+			$ID3v2ShortFrameNameLookup[3]['publisher']                                        = 'TPUB';
+			$ID3v2ShortFrameNameLookup[3]['tracknumber']                                      = 'TRCK';
+			$ID3v2ShortFrameNameLookup[3]['internet_radio_station_name']                      = 'TRSN';
+			$ID3v2ShortFrameNameLookup[3]['internet_radio_station_owner']                     = 'TRSO';
+			$ID3v2ShortFrameNameLookup[3]['isrc']                                             = 'TSRC';
+			$ID3v2ShortFrameNameLookup[3]['encoder_settings']                                 = 'TSSE';
+			$ID3v2ShortFrameNameLookup[3]['user_text']                                        = 'TXXX';
+			$ID3v2ShortFrameNameLookup[3]['unique_file_identifier']                           = 'UFID';
+			$ID3v2ShortFrameNameLookup[3]['terms_of_use']                                     = 'USER';
+			$ID3v2ShortFrameNameLookup[3]['unsynchronised_lyrics']                            = 'USLT';
+			$ID3v2ShortFrameNameLookup[3]['commercial']                                       = 'WCOM';
+			$ID3v2ShortFrameNameLookup[3]['copyright_information']                            = 'WCOP';
+			$ID3v2ShortFrameNameLookup[3]['url_file']                                         = 'WOAF';
+			$ID3v2ShortFrameNameLookup[3]['url_artist']                                       = 'WOAR';
+			$ID3v2ShortFrameNameLookup[3]['url_source']                                       = 'WOAS';
+			$ID3v2ShortFrameNameLookup[3]['url_station']                                      = 'WORS';
+			$ID3v2ShortFrameNameLookup[3]['payment']                                          = 'WPAY';
+			$ID3v2ShortFrameNameLookup[3]['url_publisher']                                    = 'WPUB';
+			$ID3v2ShortFrameNameLookup[3]['url_user']                                         = 'WXXX';
+
+			// The above are common to ID3v2.3 and ID3v2.4
+			// so copy them to ID3v2.4 before adding specifics for 2.3 and 2.4
+			$ID3v2ShortFrameNameLookup[4] = $ID3v2ShortFrameNameLookup[3];
+
+			// The following are unique to ID3v2.3
+			$ID3v2ShortFrameNameLookup[3]['equalisation']                                     = 'EQUA';
+			$ID3v2ShortFrameNameLookup[3]['involved_people_list']                             = 'IPLS';
+			$ID3v2ShortFrameNameLookup[3]['relative_volume_adjustment']                       = 'RVAD';
+			$ID3v2ShortFrameNameLookup[3]['date']                                             = 'TDAT';
+			$ID3v2ShortFrameNameLookup[3]['time']                                             = 'TIME';
+			$ID3v2ShortFrameNameLookup[3]['original_release_year']                            = 'TORY';
+			$ID3v2ShortFrameNameLookup[3]['recording_dates']                                  = 'TRDA';
+			$ID3v2ShortFrameNameLookup[3]['size']                                             = 'TSIZ';
+			$ID3v2ShortFrameNameLookup[3]['year']                                             = 'TYER';
+
+
+			// The following are unique to ID3v2.4
+			$ID3v2ShortFrameNameLookup[4]['audio_seek_point_index']                           = 'ASPI';
+			$ID3v2ShortFrameNameLookup[4]['equalisation']                                     = 'EQU2';
+			$ID3v2ShortFrameNameLookup[4]['relative_volume_adjustment']                       = 'RVA2';
+			$ID3v2ShortFrameNameLookup[4]['seek']                                             = 'SEEK';
+			$ID3v2ShortFrameNameLookup[4]['signature']                                        = 'SIGN';
+			$ID3v2ShortFrameNameLookup[4]['encoding_time']                                    = 'TDEN';
+			$ID3v2ShortFrameNameLookup[4]['original_release_time']                            = 'TDOR';
+			$ID3v2ShortFrameNameLookup[4]['recording_time']                                   = 'TDRC';
+			$ID3v2ShortFrameNameLookup[4]['release_time']                                     = 'TDRL';
+			$ID3v2ShortFrameNameLookup[4]['tagging_time']                                     = 'TDTG';
+			$ID3v2ShortFrameNameLookup[4]['involved_people_list']                             = 'TIPL';
+			$ID3v2ShortFrameNameLookup[4]['musician_credits_list']                            = 'TMCL';
+			$ID3v2ShortFrameNameLookup[4]['mood']                                             = 'TMOO';
+			$ID3v2ShortFrameNameLookup[4]['produced_notice']                                  = 'TPRO';
+			$ID3v2ShortFrameNameLookup[4]['album_sort_order']                                 = 'TSOA';
+			$ID3v2ShortFrameNameLookup[4]['performer_sort_order']                             = 'TSOP';
+			$ID3v2ShortFrameNameLookup[4]['title_sort_order']                                 = 'TSOT';
+			$ID3v2ShortFrameNameLookup[4]['set_subtitle']                                     = 'TSST';
+		}
+		return @$ID3v2ShortFrameNameLookup[$majorversion][strtolower($long_description)];
+
+	}
+
+}
+
+?>

Added: plog/trunk/class/gallery/getid3/write.lyrics3.php
===================================================================
--- plog/trunk/class/gallery/getid3/write.lyrics3.php	2006-07-24 16:40:16 UTC (rev 3789)
+++ plog/trunk/class/gallery/getid3/write.lyrics3.php	2006-07-24 16:56:08 UTC (rev 3790)
@@ -0,0 +1,78 @@
+<?php
+/////////////////////////////////////////////////////////////////
+/// getID3() by James Heinrich <info at getid3.org>               //
+//  available at http://getid3.sourceforge.net                 //
+//            or http://www.getid3.org                         //
+/////////////////////////////////////////////////////////////////
+// See readme.txt for more details                             //
+/////////////////////////////////////////////////////////////////
+//                                                             //
+// write.lyrics3.php                                           //
+// module for writing Lyrics3 tags                             //
+// dependencies: module.tag.lyrics3.php                        //
+//                                                            ///
+/////////////////////////////////////////////////////////////////
+
+
+class getid3_write_lyrics3
+{
+	var $filename;
+	var $tag_data;
+	//var $lyrics3_version = 2;       // 1 or 2
+	var $warnings        = array(); // any non-critical errors will be stored here
+	var $errors          = array(); // any critical errors will be stored here
+
+	function getid3_write_lyrics3() {
+		return true;
+	}
+
+	function WriteLyrics3() {
+		$this->errors[] = 'WriteLyrics3() not yet functional - cannot write Lyrics3';
+		return false;
+	}
+
+	function DeleteLyrics3() {
+		// Initialize getID3 engine
+		$getID3 = new getID3;
+		$ThisFileInfo = $getID3->analyze($this->filename);
+		if (isset($ThisFileInfo['lyrics3']['tag_offset_start']) && isset($ThisFileInfo['lyrics3']['tag_offset_end'])) {
+			if ($fp = @fopen($this->filename, 'a+b')) {
+
+				flock($fp, LOCK_EX);
+				$oldignoreuserabort = ignore_user_abort(true);
+
+				fseek($fp, $ThisFileInfo['lyrics3']['tag_offset_end'], SEEK_SET);
+				$DataAfterLyrics3 = '';
+				if ($ThisFileInfo['filesize'] > $ThisFileInfo['lyrics3']['tag_offset_end']) {
+					$DataAfterLyrics3 = fread($fp, $ThisFileInfo['filesize'] - $ThisFileInfo['lyrics3']['tag_offset_end']);
+				}
+
+				ftruncate($fp, $ThisFileInfo['lyrics3']['tag_offset_start']);
+
+				if (!empty($DataAfterLyrics3)) {
+					fseek($fp, $ThisFileInfo['lyrics3']['tag_offset_start'], SEEK_SET);
+					fwrite($fp, $DataAfterLyrics3, strlen($DataAfterLyrics3));
+				}
+
+				flock($fp, LOCK_UN);
+				fclose($fp);
+				ignore_user_abort($oldignoreuserabort);
+
+				return true;
+
+			} else {
+
+				$this->errors[] = 'Cannot open "'.$this->filename.'" in "a+b" mode';
+				return false;
+
+			}
+		}
+		// no Lyrics3 present
+		return true;
+	}
+
+
+
+}
+
+?>
\ No newline at end of file

Added: plog/trunk/class/gallery/getid3/write.metaflac.php
===================================================================
--- plog/trunk/class/gallery/getid3/write.metaflac.php	2006-07-24 16:40:16 UTC (rev 3789)
+++ plog/trunk/class/gallery/getid3/write.metaflac.php	2006-07-24 16:56:08 UTC (rev 3790)
@@ -0,0 +1,167 @@
+<?php
+/////////////////////////////////////////////////////////////////
+/// getID3() by James Heinrich <info at getid3.org>               //
+//  available at http://getid3.sourceforge.net                 //
+//            or http://www.getid3.org                         //
+/////////////////////////////////////////////////////////////////
+// See readme.txt for more details                             //
+/////////////////////////////////////////////////////////////////
+//                                                             //
+// write.metaflac.php                                          //
+// module for writing metaflac tags                            //
+// dependencies: /helperapps/metaflac.exe                      //
+//                                                            ///
+/////////////////////////////////////////////////////////////////
+
+
+class getid3_write_metaflac
+{
+
+	var $filename;
+	var $tag_data;
+	var $warnings = array(); // any non-critical errors will be stored here
+	var $errors   = array(); // any critical errors will be stored here
+
+	function getid3_write_metaflac() {
+		return true;
+	}
+
+	function WriteMetaFLAC() {
+
+		if (!ini_get('safe_mode')) {
+
+			// Create file with new comments
+			$tempcommentsfilename = tempnam('*', 'getID3');
+			if ($fpcomments = @fopen($tempcommentsfilename, 'wb')) {
+
+				foreach ($this->tag_data as $key => $value) {
+					foreach ($value as $commentdata) {
+						fwrite($fpcomments, $this->CleanmetaflacName($key).'='.$commentdata."\n");
+					}
+				}
+				fclose($fpcomments);
+
+			} else {
+
+				$this->errors[] = 'failed to open temporary tags file "'.$tempcommentsfilename.'", tags not written';
+				return false;
+
+			}
+
+			$oldignoreuserabort = ignore_user_abort(true);
+			if (GETID3_OS_ISWINDOWS) {
+
+				if (file_exists(GETID3_HELPERAPPSDIR.'metaflac.exe')) {
+					//$commandline = '"'.GETID3_HELPERAPPSDIR.'metaflac.exe" --no-utf8-convert --remove-vc-all --import-vc-from="'.$tempcommentsfilename.'" "'.str_replace('/', '\\', $this->filename).'"';
+					//  metaflac works fine if you copy-paste the above commandline into a command prompt,
+					//  but refuses to work with `backtick` if there are "doublequotes" present around BOTH
+					//  the metaflac pathname and the target filename. For whatever reason...??
+					//  The solution is simply ensure that the metaflac pathname has no spaces,
+					//  and therefore does not need to be quoted
+
+					// On top of that, if error messages are not always captured properly under Windows
+					// To at least see if there was a problem, compare file modification timestamps before and after writing
+					clearstatcache();
+					$timestampbeforewriting = filemtime($this->filename);
+
+					$commandline = GETID3_HELPERAPPSDIR.'metaflac.exe --no-utf8-convert --remove-vc-all --import-vc-from="'.$tempcommentsfilename.'" "'.$this->filename.'" 2>&1';
+					$metaflacError = `$commandline`;
+
+					if (empty($metaflacError)) {
+						clearstatcache();
+						if ($timestampbeforewriting == filemtime($this->filename)) {
+							$metaflacError = 'File modification timestamp has not changed - it looks like the tags were not written';
+						}
+					}
+				} else {
+					$metaflacError = 'metaflac.exe not found in '.GETID3_HELPERAPPSDIR;
+				}
+
+			} else {
+
+				// It's simpler on *nix
+				$commandline = 'metaflac --no-utf8-convert --remove-vc-all --import-vc-from='.$tempcommentsfilename.' "'.$this->filename.'" 2>&1';
+				$metaflacError = `$commandline`;
+
+			}
+
+			// Remove temporary comments file
+			unlink($tempcommentsfilename);
+			ignore_user_abort($oldignoreuserabort);
+
+			if (!empty($metaflacError)) {
+
+				$this->errors[] = 'System call to metaflac failed with this message returned: '."\n\n".$metaflacError;
+				return false;
+
+			}
+
+			return true;
+		}
+
+		$this->errors[] = 'PHP running in Safe Mode (backtick operator not available) - cannot call metaflac, tags not written';
+		return false;
+	}
+
+
+	function DeleteMetaFLAC() {
+
+		if (!ini_get('safe_mode')) {
+
+			$oldignoreuserabort = ignore_user_abort(true);
+			if (GETID3_OS_ISWINDOWS) {
+
+				if (file_exists(GETID3_HELPERAPPSDIR.'metaflac.exe')) {
+					// To at least see if there was a problem, compare file modification timestamps before and after writing
+					clearstatcache();
+					$timestampbeforewriting = filemtime($this->filename);
+
+					$commandline = GETID3_HELPERAPPSDIR.'metaflac.exe --remove-vc-all "'.$this->filename.'" 2>&1';
+					$metaflacError = `$commandline`;
+
+					if (empty($metaflacError)) {
+						clearstatcache();
+						if ($timestampbeforewriting == filemtime($this->filename)) {
+							$metaflacError = 'File modification timestamp has not changed - it looks like the tags were not deleted';
+						}
+					}
+				} else {
+					$metaflacError = 'metaflac.exe not found in '.GETID3_HELPERAPPSDIR;
+				}
+
+			} else {
+
+				// It's simpler on *nix
+				$commandline = 'metaflac --remove-vc-all "'.$this->filename.'" 2>&1';
+				$metaflacError = `$commandline`;
+
+			}
+
+			ignore_user_abort($oldignoreuserabort);
+
+			if (!empty($metaflacError)) {
+				$this->errors[] = 'System call to metaflac failed with this message returned: '."\n\n".$metaflacError;
+				return false;
+			}
+			return true;
+		}
+		$this->errors[] = 'PHP running in Safe Mode (backtick operator not available) - cannot call metaflac, tags not deleted';
+		return false;
+	}
+
+
+	function CleanmetaflacName($originalcommentname) {
+		// A case-insensitive field name that may consist of ASCII 0x20 through 0x7D, 0x3D ('=') excluded.
+		// ASCII 0x41 through 0x5A inclusive (A-Z) is to be considered equivalent to ASCII 0x61 through
+		// 0x7A inclusive (a-z).
+
+		// replace invalid chars with a space, return uppercase text
+		// Thanks Chris Bolt <chris-getid3Øbolt*cx> for improving this function
+		// note: ereg_replace() replaces nulls with empty string (not space)
+		return strtoupper(ereg_replace('[^ -<>-}]', ' ', str_replace("\x00", ' ', $originalcommentname)));
+
+	}
+
+}
+
+?>
\ No newline at end of file

Added: plog/trunk/class/gallery/getid3/write.php
===================================================================
--- plog/trunk/class/gallery/getid3/write.php	2006-07-24 16:40:16 UTC (rev 3789)
+++ plog/trunk/class/gallery/getid3/write.php	2006-07-24 16:56:08 UTC (rev 3790)
@@ -0,0 +1,592 @@
+<?php
+/////////////////////////////////////////////////////////////////
+/// getID3() by James Heinrich <info at getid3.org>               //
+//  available at http://getid3.sourceforge.net                 //
+//            or http://www.getid3.org                         //
+/////////////////////////////////////////////////////////////////
+// See readme.txt for more details                             //
+/////////////////////////////////////////////////////////////////
+///                                                            //
+// write.php                                                   //
+// module for writing tags (APEv2, ID3v1, ID3v2)               //
+// dependencies: getid3.lib.php                                //
+//               write.apetag.php (optional)                   //
+//               write.id3v1.php (optional)                    //
+//               write.id3v2.php (optional)                    //
+//               write.vorbiscomment.php (optional)            //
+//               write.metaflac.php (optional)                 //
+//               write.lyrics3.php (optional)                  //
+//                                                            ///
+/////////////////////////////////////////////////////////////////
+
+if (!defined('GETID3_INCLUDEPATH')) {
+	die('getid3.php MUST be included before calling getid3_writetags');
+}
+if (!include_once(GETID3_INCLUDEPATH.'getid3.lib.php')) {
+	die('write.php depends on getid3.lib.php, which is missing.');
+}
+
+
+// NOTES:
+//
+// You should pass data here with standard field names as follows:
+// * TITLE
+// * ARTIST
+// * ALBUM
+// * TRACKNUMBER
+// * COMMENT
+// * GENRE
+// * YEAR
+// * ATTACHED_PICTURE (ID3v2 only)
+//
+// http://www.personal.uni-jena.de/~pfk/mpp/sv8/apekey.html
+// The APEv2 Tag Items Keys definition says "TRACK" is correct but foobar2000 uses "TRACKNUMBER" instead
+// Pass data here as "TRACKNUMBER" for compatability with all formats
+
+
+class getid3_writetags
+{
+	// public
+	var $filename;                            // absolute filename of file to write tags to
+	var $tagformats         = array();        // array of tag formats to write ('id3v1', 'id3v2.2', 'id2v2.3', 'id3v2.4', 'ape', 'vorbiscomment', 'metaflac', 'real')
+	var $tag_data           = array(array()); // 2-dimensional array of tag data (ex: $data['ARTIST'][0] = 'Elvis')
+	var $tag_encoding       = 'ISO-8859-1';   // text encoding used for tag data ('ISO-8859-1', 'UTF-8', 'UTF-16', 'UTF-16LE', 'UTF-16BE', )
+	var $overwrite_tags     = true;           // if true will erase existing tag data and write only passed data; if false will merge passed data with existing tag data
+	var $remove_other_tags  = false;          // if true will erase remove all existing tags and only write those passed in $tagformats; if false will ignore any tags not mentioned in $tagformats
+
+	var $id3v2_tag_language = 'eng';          // ISO-639-2 3-character language code needed for some ID3v2 frames (http://www.id3.org/iso639-2.html)
+	var $id3v2_paddedlength = 4096;           // minimum length of ID3v2 tags (will be padded to this length if tag data is shorter)
+
+	var $warnings           = array();        // any non-critical errors will be stored here
+	var $errors             = array();        // any critical errors will be stored here
+
+	// private
+	var $ThisFileInfo; // analysis of file before writing
+
+	function getid3_writetags() {
+		return true;
+	}
+
+
+	function WriteTags() {
+
+		if (empty($this->filename)) {
+			$this->errors[] = 'filename is undefined in getid3_writetags';
+			return false;
+		} elseif (!file_exists($this->filename)) {
+			$this->errors[] = 'filename set to non-existant file "'.$this->filename.'" in getid3_writetags';
+			return false;
+		}
+
+		if (!is_array($this->tagformats)) {
+			$this->errors[] = 'tagformats must be an array in getid3_writetags';
+			return false;
+		}
+
+		$TagFormatsToRemove = array();
+		if (filesize($this->filename) == 0) {
+
+			// empty file special case - allow any tag format, don't check existing format
+			// could be useful if you want to generate tag data for a non-existant file
+			$this->ThisFileInfo = array('fileformat'=>'');
+			$AllowedTagFormats = array('id3v1', 'id3v2.2', 'id3v2.3', 'id3v2.4', 'ape', 'lyrics3');
+
+		} else {
+
+			$getID3 = new getID3;
+			$getID3->encoding = $this->tag_encoding;
+			$this->ThisFileInfo = $getID3->analyze($this->filename);
+
+			// check for what file types are allowed on this fileformat
+			switch (@$this->ThisFileInfo['fileformat']) {
+				case 'mp3':
+				case 'mp2':
+				case 'mp1':
+				case 'riff': // maybe not officially, but people do it anyway
+					$AllowedTagFormats = array('id3v1', 'id3v2.2', 'id3v2.3', 'id3v2.4', 'ape', 'lyrics3');
+					break;
+
+				case 'mpc':
+					$AllowedTagFormats = array('ape');
+					break;
+
+				case 'flac':
+					$AllowedTagFormats = array('metaflac');
+					break;
+
+				case 'real':
+					$AllowedTagFormats = array('real');
+					break;
+
+				case 'ogg':
+					switch (@$this->ThisFileInfo['audio']['dataformat']) {
+						case 'flac':
+							//$AllowedTagFormats = array('metaflac');
+							$this->errors[] = 'metaflac is not (yet) compatible with OggFLAC files';
+							return false;
+							break;
+						case 'vorbis':
+							$AllowedTagFormats = array('vorbiscomment');
+							break;
+						default:
+							$this->errors[] = 'metaflac is not (yet) compatible with Ogg files other than OggVorbis';
+							return false;
+							break;
+					}
+					break;
+
+				default:
+					$AllowedTagFormats = array();
+					break;
+			}
+			foreach ($this->tagformats as $requested_tag_format) {
+				if (!in_array($requested_tag_format, $AllowedTagFormats)) {
+					$errormessage = 'Tag format "'.$requested_tag_format.'" is not allowed on "'.@$this->ThisFileInfo['fileformat'];
+					if (@$this->ThisFileInfo['fileformat'] != @$this->ThisFileInfo['audio']['dataformat']) {
+						$errormessage .= '.'.@$this->ThisFileInfo['audio']['dataformat'];
+					}
+					$errormessage .= '" files';
+					$this->errors[] = $errormessage;
+					return false;
+				}
+			}
+
+			// List of other tag formats, removed if requested
+			if ($this->remove_other_tags) {
+				foreach ($AllowedTagFormats as $AllowedTagFormat) {
+					switch ($AllowedTagFormat) {
+						case 'id3v2.2':
+						case 'id3v2.3':
+						case 'id3v2.4':
+							if (!in_array('id3v2', $TagFormatsToRemove) && !in_array('id3v2.2', $this->tagformats) && !in_array('id3v2.3', $this->tagformats) && !in_array('id3v2.4', $this->tagformats)) {
+								$TagFormatsToRemove[] = 'id3v2';
+							}
+							break;
+
+						default:
+							if (!in_array($AllowedTagFormat, $this->tagformats)) {
+								$TagFormatsToRemove[] = $AllowedTagFormat;
+							}
+							break;
+					}
+				}
+			}
+		}
+
+		$WritingFilesToInclude = array_merge($this->tagformats, $TagFormatsToRemove);
+
+		// Check for required include files and include them
+		foreach ($WritingFilesToInclude as $tagformat) {
+			switch ($tagformat) {
+				case 'ape':
+					$GETID3_ERRORARRAY = &$this->errors;
+					if (!getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'write.apetag.php', __FILE__, false)) {
+						return false;
+					}
+					break;
+
+				case 'id3v1':
+				case 'lyrics3':
+				case 'vorbiscomment':
+				case 'metaflac':
+				case 'real':
+					$GETID3_ERRORARRAY = &$this->errors;
+					if (!getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'write.'.$tagformat.'.php', __FILE__, false)) {
+						return false;
+					}
+					break;
+
+				case 'id3v2.2':
+				case 'id3v2.3':
+				case 'id3v2.4':
+				case 'id3v2':
+					$GETID3_ERRORARRAY = &$this->errors;
+					if (!getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'write.id3v2.php', __FILE__, false)) {
+						return false;
+					}
+					break;
+
+				default:
+					$this->errors[] = 'unknown tag format "'.$tagformat.'" in $tagformats in WriteTags()';
+					return false;
+					break;
+			}
+
+		}
+
+		// Validation of supplied data
+		if (!is_array($this->tag_data)) {
+			$this->errors[] = '$tag_data is not an array in WriteTags()';
+			return false;
+		}
+		// convert supplied data array keys to upper case, if they're not already
+		foreach ($this->tag_data as $tag_key => $tag_array) {
+			if (strtoupper($tag_key) !== $tag_key) {
+				$this->tag_data[strtoupper($tag_key)] = $this->tag_data[$tag_key];
+				unset($this->tag_data[$tag_key]);
+			}
+		}
+		// convert source data array keys to upper case, if they're not already
+		if (!empty($this->ThisFileInfo['tags'])) {
+			foreach ($this->ThisFileInfo['tags'] as $tag_format => $tag_data_array) {
+				foreach ($tag_data_array as $tag_key => $tag_array) {
+					if (strtoupper($tag_key) !== $tag_key) {
+						$this->ThisFileInfo['tags'][$tag_format][strtoupper($tag_key)] = $this->ThisFileInfo['tags'][$tag_format][$tag_key];
+						unset($this->ThisFileInfo['tags'][$tag_format][$tag_key]);
+					}
+				}
+			}
+		}
+
+		// Convert "TRACK" to "TRACKNUMBER" (if needed) for compatability with all formats
+		if (isset($this->tag_data['TRACK']) && !isset($this->tag_data['TRACKNUMBER'])) {
+			$this->tag_data['TRACKNUMBER'] = $this->tag_data['TRACK'];
+			unset($this->tag_data['TRACK']);
+		}
+
+		// Remove all other tag formats, if requested
+		if ($this->remove_other_tags) {
+			$this->DeleteTags($TagFormatsToRemove);
+		}
+
+		// Write data for each tag format
+		foreach ($this->tagformats as $tagformat) {
+			$success = false; // overridden if tag writing is successful
+			switch ($tagformat) {
+				case 'ape':
+					$ape_writer = new getid3_write_apetag;
+					if (($ape_writer->tag_data = $this->FormatDataForAPE()) !== false) {
+						$ape_writer->filename = $this->filename;
+						if (($success = $ape_writer->WriteAPEtag()) === false) {
+							$this->errors[] = 'WriteAPEtag() failed with message(s):<PRE><UL><LI>'.trim(implode('</LI><LI>', $ape_writer->errors)).'</LI></UL></PRE>';
+						}
+					} else {
+						$this->errors[] = 'FormatDataForAPE() failed';
+					}
+					break;
+
+				case 'id3v1':
+					$id3v1_writer = new getid3_write_id3v1;
+					if (($id3v1_writer->tag_data = $this->FormatDataForID3v1()) !== false) {
+						$id3v1_writer->filename = $this->filename;
+						if (($success = $id3v1_writer->WriteID3v1()) === false) {
+							$this->errors[] = 'WriteID3v1() failed with message(s):<PRE><UL><LI>'.trim(implode('</LI><LI>', $id3v1_writer->errors)).'</LI></UL></PRE>';
+						}
+					} else {
+						$this->errors[] = 'FormatDataForID3v1() failed';
+					}
+					break;
+
+				case 'id3v2.2':
+				case 'id3v2.3':
+				case 'id3v2.4':
+					$id3v2_writer = new getid3_write_id3v2;
+					$id3v2_writer->majorversion = intval(substr($tagformat, -1));
+					$id3v2_writer->paddedlength = $this->id3v2_paddedlength;
+					if (($id3v2_writer->tag_data = $this->FormatDataForID3v2($id3v2_writer->majorversion)) !== false) {
+						$id3v2_writer->filename = $this->filename;
+						if (($success = $id3v2_writer->WriteID3v2()) === false) {
+							$this->errors[] = 'WriteID3v2() failed with message(s):<PRE><UL><LI>'.trim(implode('</LI><LI>', $id3v2_writer->errors)).'</LI></UL></PRE>';
+						}
+					} else {
+						$this->errors[] = 'FormatDataForID3v2() failed';
+					}
+					break;
+
+				case 'vorbiscomment':
+					$vorbiscomment_writer = new getid3_write_vorbiscomment;
+					if (($vorbiscomment_writer->tag_data = $this->FormatDataForVorbisComment()) !== false) {
+						$vorbiscomment_writer->filename = $this->filename;
+						if (($success = $vorbiscomment_writer->WriteVorbisComment()) === false) {
+							$this->errors[] = 'WriteVorbisComment() failed with message(s):<PRE><UL><LI>'.trim(implode('</LI><LI>', $vorbiscomment_writer->errors)).'</LI></UL></PRE>';
+						}
+					} else {
+						$this->errors[] = 'FormatDataForVorbisComment() failed';
+					}
+					break;
+
+				case 'metaflac':
+					$metaflac_writer = new getid3_write_metaflac;
+					if (($metaflac_writer->tag_data = $this->FormatDataForMetaFLAC()) !== false) {
+						$metaflac_writer->filename = $this->filename;
+						if (($success = $metaflac_writer->WriteMetaFLAC()) === false) {
+							$this->errors[] = 'WriteMetaFLAC() failed with message(s):<PRE><UL><LI>'.trim(implode('</LI><LI>', $metaflac_writer->errors)).'</LI></UL></PRE>';
+						}
+					} else {
+						$this->errors[] = 'FormatDataForMetaFLAC() failed';
+					}
+					break;
+
+				case 'real':
+					$real_writer = new getid3_write_real;
+					if (($real_writer->tag_data = $this->FormatDataForReal()) !== false) {
+						$real_writer->filename = $this->filename;
+						if (($success = $real_writer->WriteReal()) === false) {
+							$this->errors[] = 'WriteReal() failed with message(s):<PRE><UL><LI>'.trim(implode('</LI><LI>', $real_writer->errors)).'</LI></UL></PRE>';
+						}
+					} else {
+						$this->errors[] = 'FormatDataForReal() failed';
+					}
+					break;
+
+				default:
+					$this->errors[] = 'Invalid tag format to write: "'.$tagformat.'"';
+					return false;
+					break;
+			}
+			if (!$success) {
+				return false;
+			}
+		}
+		return true;
+
+	}
+
+
+	function DeleteTags($TagFormatsToDelete) {
+		foreach ($TagFormatsToDelete as $DeleteTagFormat) {
+			$success = false; // overridden if tag deletion is successful
+			switch ($DeleteTagFormat) {
+				case 'id3v1':
+					$id3v1_writer = new getid3_write_id3v1;
+					$id3v1_writer->filename = $this->filename;
+					if (($success = $id3v1_writer->RemoveID3v1()) === false) {
+						$this->errors[] = 'RemoveID3v1() failed with message(s):<PRE><UL><LI>'.trim(implode('</LI><LI>', $id3v1_writer->errors)).'</LI></UL></PRE>';
+					}
+					break;
+
+				case 'id3v2':
+					$id3v2_writer = new getid3_write_id3v2;
+					$id3v2_writer->filename = $this->filename;
+					if (($success = $id3v2_writer->RemoveID3v2()) === false) {
+						$this->errors[] = 'RemoveID3v2() failed with message(s):<PRE><UL><LI>'.trim(implode('</LI><LI>', $id3v2_writer->errors)).'</LI></UL></PRE>';
+					}
+					break;
+
+				case 'ape':
+					$ape_writer = new getid3_write_apetag;
+					$ape_writer->filename = $this->filename;
+					if (($success = $ape_writer->DeleteAPEtag()) === false) {
+						$this->errors[] = 'DeleteAPEtag() failed with message(s):<PRE><UL><LI>'.trim(implode('</LI><LI>', $ape_writer->errors)).'</LI></UL></PRE>';
+					}
+					break;
+
+				case 'vorbiscomment':
+					$vorbiscomment_writer = new getid3_write_vorbiscomment;
+					$vorbiscomment_writer->filename = $this->filename;
+					if (($success = $vorbiscomment_writer->DeleteVorbisComment()) === false) {
+						$this->errors[] = 'DeleteVorbisComment() failed with message(s):<PRE><UL><LI>'.trim(implode('</LI><LI>', $vorbiscomment_writer->errors)).'</LI></UL></PRE>';
+					}
+					break;
+
+				case 'metaflac':
+					$metaflac_writer = new getid3_write_metaflac;
+					$metaflac_writer->filename = $this->filename;
+					if (($success = $metaflac_writer->DeleteMetaFLAC()) === false) {
+						$this->errors[] = 'DeleteMetaFLAC() failed with message(s):<PRE><UL><LI>'.trim(implode('</LI><LI>', $metaflac_writer->errors)).'</LI></UL></PRE>';
+					}
+					break;
+
+				case 'lyrics3':
+					$lyrics3_writer = new getid3_write_lyrics3;
+					$lyrics3_writer->filename = $this->filename;
+					if (($success = $lyrics3_writer->DeleteLyrics3()) === false) {
+						$this->errors[] = 'DeleteLyrics3() failed with message(s):<PRE><UL><LI>'.trim(implode('</LI><LI>', $lyrics3_writer->errors)).'</LI></UL></PRE>';
+					}
+					break;
+
+				case 'real':
+					$real_writer = new getid3_write_real;
+					$real_writer->filename = $this->filename;
+					if (($success = $real_writer->RemoveReal()) === false) {
+						$this->errors[] = 'RemoveReal() failed with message(s):<PRE><UL><LI>'.trim(implode('</LI><LI>', $real_writer->errors)).'</LI></UL></PRE>';
+					}
+					break;
+
+				default:
+					$this->errors[] = 'Invalid tag format to delete: "'.$tagformat.'"';
+					return false;
+					break;
+			}
+			if (!$success) {
+				return false;
+			}
+		}
+		return true;
+	}
+
+
+	function MergeExistingTagData($TagFormat, &$tag_data) {
+		// Merge supplied data with existing data, if requested
+		if ($this->overwrite_tags) {
+			// do nothing - ignore previous data
+		} else {
+			if (!isset($this->ThisFileInfo['tags'][$TagFormat])) {
+				return false;
+			}
+			$tag_data = array_merge_recursive($tag_data, $this->ThisFileInfo['tags'][$TagFormat]);
+		}
+		return true;
+	}
+
+	function FormatDataForAPE() {
+		$ape_tag_data = array();
+		foreach ($this->tag_data as $tag_key => $valuearray) {
+			switch ($tag_key) {
+				case 'ATTACHED_PICTURE':
+					// ATTACHED_PICTURE is ID3v2 only - ignore
+					$this->warnings[] = '$data['.$tag_key.'] is assumed to be ID3v2 APIC data - NOT written to APE tag';
+					break;
+
+				default:
+					foreach ($valuearray as $key => $value) {
+						if (is_string($value) || is_numeric($value)) {
+							$ape_tag_data[$tag_key][$key] = getid3_lib::iconv_fallback($this->tag_encoding, 'UTF-8', $value);
+						} else {
+							$this->warnings[] = '$data['.$tag_key.']['.$key.'] is not a string value - all of $data['.$tag_key.'] NOT written to APE tag';
+							unset($ape_tag_data[$tag_key]);
+							break;
+						}
+					}
+					break;
+			}
+		}
+		$this->MergeExistingTagData('ape', $ape_tag_data);
+		return $ape_tag_data;
+	}
+
+
+	function FormatDataForID3v1() {
+		$tag_data_id3v1['genreid'] = 255;
+		if (!empty($this->tag_data['GENRE'])) {
+			foreach ($this->tag_data['GENRE'] as $key => $value) {
+				if (getid3_id3v1::LookupGenreID($value) !== false) {
+					$tag_data_id3v1['genreid'] = getid3_id3v1::LookupGenreID($value);
+					break;
+				}
+			}
+		}
+
+		$tag_data_id3v1['title']   = getid3_lib::iconv_fallback($this->tag_encoding, 'ISO-8859-1', @implode(' ', @$this->tag_data['TITLE']));
+		$tag_data_id3v1['artist']  = getid3_lib::iconv_fallback($this->tag_encoding, 'ISO-8859-1', @implode(' ', @$this->tag_data['ARTIST']));
+		$tag_data_id3v1['album']   = getid3_lib::iconv_fallback($this->tag_encoding, 'ISO-8859-1', @implode(' ', @$this->tag_data['ALBUM']));
+		$tag_data_id3v1['year']    = getid3_lib::iconv_fallback($this->tag_encoding, 'ISO-8859-1', @implode(' ', @$this->tag_data['YEAR']));
+		$tag_data_id3v1['comment'] = getid3_lib::iconv_fallback($this->tag_encoding, 'ISO-8859-1', @implode(' ', @$this->tag_data['COMMENT']));
+
+		$tag_data_id3v1['track']   = intval(getid3_lib::iconv_fallback($this->tag_encoding, 'ISO-8859-1', @implode(' ', @$this->tag_data['TRACKNUMBER'])));
+		if ($tag_data_id3v1['track'] <= 0) {
+			$tag_data_id3v1['track'] = '';
+		}
+
+		$this->MergeExistingTagData('id3v1', $tag_data_id3v1);
+		return $tag_data_id3v1;
+	}
+
+	function FormatDataForID3v2($id3v2_majorversion) {
+		$tag_data_id3v2 = array();
+
+		$ID3v2_text_encoding_lookup[2] = array('ISO-8859-1'=>0, 'UTF-16'=>1);
+		$ID3v2_text_encoding_lookup[3] = array('ISO-8859-1'=>0, 'UTF-16'=>1);
+		$ID3v2_text_encoding_lookup[4] = array('ISO-8859-1'=>0, 'UTF-16'=>1, 'UTF-16BE'=>2, 'UTF-8'=>3);
+		foreach ($this->tag_data as $tag_key => $valuearray) {
+			$ID3v2_framename = getid3_write_id3v2::ID3v2ShortFrameNameLookup($id3v2_majorversion, $tag_key);
+			switch ($ID3v2_framename) {
+				case 'APIC':
+					foreach ($valuearray as $key => $apic_data_array) {
+						if (isset($apic_data_array['data']) &&
+							isset($apic_data_array['picturetypeid']) &&
+							isset($apic_data_array['description']) &&
+							isset($apic_data_array['mime'])) {
+								$tag_data_id3v2['APIC'][] = $apic_data_array;
+						} else {
+							$this->errors[] = 'ID3v2 APIC data is not properly structured';
+							return false;
+						}
+					}
+					break;
+
+				case '':
+					$this->errors[] = 'ID3v2: Skipping "'.$tag_key.'" because cannot match it to a known ID3v2 frame type';
+					// some other data type, don't know how to handle it, ignore it
+					break;
+
+				default:
+					// most other (text) frames can be copied over as-is
+					foreach ($valuearray as $key => $value) {
+						if (isset($ID3v2_text_encoding_lookup[$id3v2_majorversion][$this->tag_encoding])) {
+							// source encoding is valid in ID3v2 - use it with no conversion
+							$tag_data_id3v2[$ID3v2_framename][$key]['encodingid'] = $ID3v2_text_encoding_lookup[$id3v2_majorversion][$this->tag_encoding];
+							$tag_data_id3v2[$ID3v2_framename][$key]['data']       = $value;
+						} else {
+							// source encoding is NOT valid in ID3v2 - convert it to an ID3v2-valid encoding first
+							if ($id3v2_majorversion < 4) {
+								// convert data from other encoding to UTF-16
+								$tag_data_id3v2[$ID3v2_framename][$key]['encodingid'] = 1;
+								$tag_data_id3v2[$ID3v2_framename][$key]['data']       = getid3_lib::iconv_fallback($this->tag_encoding, 'UTF-16', $value);
+
+							} else {
+								// convert data from other encoding to UTF-8
+								$tag_data_id3v2[$ID3v2_framename][$key]['encodingid'] = 3;
+								$tag_data_id3v2[$ID3v2_framename][$key]['data']       = getid3_lib::iconv_fallback($this->tag_encoding, 'UTF-8', $value);
+							}
+						}
+
+						// These values are not needed for all frame types, but if they're not used no matter
+						$tag_data_id3v2[$ID3v2_framename][$key]['description'] = '';
+						$tag_data_id3v2[$ID3v2_framename][$key]['language']    = $this->id3v2_tag_language;
+					}
+					break;
+			}
+		}
+		$this->MergeExistingTagData('id3v2', $tag_data_id3v2);
+		return $tag_data_id3v2;
+	}
+
+	function FormatDataForVorbisComment() {
+		$tag_data_vorbiscomment = $this->tag_data;
+
+		// check for multi-line comment values - split out to multiple comments if neccesary
+		// and convert data to UTF-8 strings
+		foreach ($tag_data_vorbiscomment as $tag_key => $valuearray) {
+			foreach ($valuearray as $key => $value) {
+				str_replace("\r", "\n", $value);
+				if (strstr($value, "\n")) {
+					unset($tag_data_vorbiscomment[$tag_key][$key]);
+					$multilineexploded = explode("\n", $value);
+					foreach ($multilineexploded as $newcomment) {
+						if (strlen(trim($newcomment)) > 0) {
+							$tag_data_vorbiscomment[$tag_key][] = getid3_lib::iconv_fallback($this->tag_encoding, 'UTF-8', $newcomment);
+						}
+					}
+				} elseif (is_string($value) || is_numeric($value)) {
+					$tag_data_vorbiscomment[$tag_key][$key] = getid3_lib::iconv_fallback($this->tag_encoding, 'UTF-8', $value);
+				} else {
+					$this->warnings[] = '$data['.$tag_key.']['.$key.'] is not a string value - all of $data['.$tag_key.'] NOT written to VorbisComment tag';
+					unset($tag_data_vorbiscomment[$tag_key]);
+					break;
+				}
+			}
+		}
+		$this->MergeExistingTagData('vorbiscomment', $tag_data_vorbiscomment);
+		return $tag_data_vorbiscomment;
+	}
+
+	function FormatDataForMetaFLAC() {
+		// FLAC & OggFLAC use VorbisComments same as OggVorbis
+		// but require metaflac to do the writing rather than vorbiscomment
+		return $this->FormatDataForVorbisComment();
+	}
+
+	function FormatDataForReal() {
+		$tag_data_real['title']     = getid3_lib::iconv_fallback($this->tag_encoding, 'ISO-8859-1', @implode(' ', @$this->tag_data['TITLE']));
+		$tag_data_real['artist']    = getid3_lib::iconv_fallback($this->tag_encoding, 'ISO-8859-1', @implode(' ', @$this->tag_data['ARTIST']));
+		$tag_data_real['copyright'] = getid3_lib::iconv_fallback($this->tag_encoding, 'ISO-8859-1', @implode(' ', @$this->tag_data['COPYRIGHT']));
+		$tag_data_real['comment']   = getid3_lib::iconv_fallback($this->tag_encoding, 'ISO-8859-1', @implode(' ', @$this->tag_data['COMMENT']));
+
+		$this->MergeExistingTagData('real', $tag_data_real);
+		return $tag_data_real;
+	}
+
+}
+
+?>
\ No newline at end of file

Added: plog/trunk/class/gallery/getid3/write.real.php
===================================================================
--- plog/trunk/class/gallery/getid3/write.real.php	2006-07-24 16:40:16 UTC (rev 3789)
+++ plog/trunk/class/gallery/getid3/write.real.php	2006-07-24 16:56:08 UTC (rev 3790)
@@ -0,0 +1,295 @@
+<?php
+/////////////////////////////////////////////////////////////////
+/// getID3() by James Heinrich <info at getid3.org>               //
+//  available at http://getid3.sourceforge.net                 //
+//            or http://www.getid3.org                         //
+/////////////////////////////////////////////////////////////////
+// See readme.txt for more details                             //
+/////////////////////////////////////////////////////////////////
+//                                                             //
+// write.real.php                                              //
+// module for writing RealAudio/RealVideo tags                 //
+// dependencies: module.tag.real.php                           //
+//                                                            ///
+/////////////////////////////////////////////////////////////////
+
+class getid3_write_real
+{
+	var $filename;
+	var $tag_data     = array();
+	var $warnings     = array(); // any non-critical errors will be stored here
+	var $errors       = array(); // any critical errors will be stored here
+	var $paddedlength = 512;     // minimum length of CONT tag in bytes
+
+	function getid3_write_real() {
+		return true;
+	}
+
+	function WriteReal() {
+		// File MUST be writeable - CHMOD(646) at least
+		if (is_writeable($this->filename)) {
+			if ($fp_source = @fopen($this->filename, 'r+b')) {
+
+				// Initialize getID3 engine
+				$getID3 = new getID3;
+				$OldThisFileInfo = $getID3->analyze($this->filename);
+				if (empty($OldThisFileInfo['real']['chunks']) && !empty($OldThisFileInfo['real']['old_ra_header'])) {
+					$this->errors[] = 'Cannot write Real tags on old-style file format';
+					fclose($fp_source);
+					return false;
+				}
+
+				if (empty($OldThisFileInfo['real']['chunks'])) {
+					$this->errors[] = 'Cannot write Real tags because cannot find DATA chunk in file';
+					fclose($fp_source);
+					return false;
+				}
+				foreach ($OldThisFileInfo['real']['chunks'] as $chunknumber => $chunkarray) {
+					$oldChunkInfo[$chunkarray['name']] = $chunkarray;
+				}
+				if (!empty($oldChunkInfo['CONT']['length'])) {
+					$this->paddedlength = max($oldChunkInfo['CONT']['length'], $this->paddedlength);
+				}
+
+				$new_CONT_tag_data = $this->GenerateCONTchunk();
+				$new_PROP_tag_data = $this->GeneratePROPchunk($OldThisFileInfo['real']['chunks'], $new_CONT_tag_data);
+				$new__RMF_tag_data = $this->GenerateRMFchunk($OldThisFileInfo['real']['chunks']);
+
+				if (@$oldChunkInfo['.RMF']['length'] == strlen($new__RMF_tag_data)) {
+					fseek($fp_source, $oldChunkInfo['.RMF']['offset'], SEEK_SET);
+					fwrite($fp_source, $new__RMF_tag_data);
+				} else {
+					$this->errors[] = 'new .RMF tag ('.strlen($new__RMF_tag_data).' bytes) different length than old .RMF tag ('.$oldChunkInfo['.RMF']['length'].' bytes)';
+					fclose($fp_source);
+					return false;
+				}
+
+				if (@$oldChunkInfo['PROP']['length'] == strlen($new_PROP_tag_data)) {
+					fseek($fp_source, $oldChunkInfo['PROP']['offset'], SEEK_SET);
+					fwrite($fp_source, $new_PROP_tag_data);
+				} else {
+					$this->errors[] = 'new PROP tag ('.strlen($new_PROP_tag_data).' bytes) different length than old PROP tag ('.$oldChunkInfo['PROP']['length'].' bytes)';
+					fclose($fp_source);
+					return false;
+				}
+
+				if (@$oldChunkInfo['CONT']['length'] == strlen($new_CONT_tag_data)) {
+
+					// new data length is same as old data length - just overwrite
+					fseek($fp_source, $oldChunkInfo['CONT']['offset'], SEEK_SET);
+					fwrite($fp_source, $new_CONT_tag_data);
+					fclose($fp_source);
+					return true;
+
+				} else {
+
+					if (empty($oldChunkInfo['CONT'])) {
+						// no existing CONT chunk
+						$BeforeOffset = $oldChunkInfo['DATA']['offset'];
+						$AfterOffset  = $oldChunkInfo['DATA']['offset'];
+					} else {
+						// new data is longer than old data
+						$BeforeOffset = $oldChunkInfo['CONT']['offset'];
+						$AfterOffset  = $oldChunkInfo['CONT']['offset'] + $oldChunkInfo['CONT']['length'];
+					}
+					if ($tempfilename = tempnam('*', 'getID3')) {
+						ob_start();
+						if ($fp_temp = fopen($tempfilename, 'wb')) {
+
+							rewind($fp_source);
+							fwrite($fp_temp, fread($fp_source, $BeforeOffset));
+							fwrite($fp_temp, $new_CONT_tag_data);
+							fseek($fp_source, $AfterOffset, SEEK_SET);
+							while ($buffer = fread($fp_source, GETID3_FREAD_BUFFER_SIZE)) {
+								fwrite($fp_temp, $buffer, strlen($buffer));
+							}
+							fclose($fp_temp);
+
+							if (copy($tempfilename, $this->filename)) {
+								unlink($tempfilename);
+								fclose($fp_source);
+								return true;
+							}
+							unlink($tempfilename);
+							$this->errors[] = 'FAILED: copy('.$tempfilename.', '.$this->filename.') - '.strip_tags(ob_get_contents());
+
+						} else {
+
+							$this->errors[] = 'Could not open '.$tempfilename.' mode "wb" - '.strip_tags(ob_get_contents());
+
+						}
+						ob_end_clean();
+					}
+					fclose($fp_source);
+					return false;
+
+				}
+
+
+			} else {
+				$this->errors[] = 'Could not open '.$this->filename.' mode "r+b"';
+				return false;
+			}
+		}
+		$this->errors[] = 'File is not writeable: '.$this->filename;
+		return false;
+	}
+
+	function GenerateRMFchunk(&$chunks) {
+		$oldCONTexists = false;
+		foreach ($chunks as $key => $chunk) {
+			$chunkNameKeys[$chunk['name']] = $key;
+			if ($chunk['name'] == 'CONT') {
+				$oldCONTexists = true;
+			}
+		}
+		$newHeadersCount = $chunks[$chunkNameKeys['.RMF']]['headers_count'] + ($oldCONTexists ? 0 : 1);
+
+		$RMFchunk  = "\x00\x00"; // object version
+		$RMFchunk .= getid3_lib::BigEndian2String($chunks[$chunkNameKeys['.RMF']]['file_version'], 4);
+		$RMFchunk .= getid3_lib::BigEndian2String($newHeadersCount,                                4);
+
+		$RMFchunk  = '.RMF'.getid3_lib::BigEndian2String(strlen($RMFchunk) + 8, 4).$RMFchunk; // .RMF chunk identifier + chunk length
+		return $RMFchunk;
+	}
+
+	function GeneratePROPchunk(&$chunks, &$new_CONT_tag_data) {
+		$old_CONT_length = 0;
+		$old_DATA_offset = 0;
+		$old_INDX_offset = 0;
+		foreach ($chunks as $key => $chunk) {
+			$chunkNameKeys[$chunk['name']] = $key;
+			if ($chunk['name'] == 'CONT') {
+				$old_CONT_length = $chunk['length'];
+			} elseif ($chunk['name'] == 'DATA') {
+				if (!$old_DATA_offset) {
+					$old_DATA_offset = $chunk['offset'];
+				}
+			} elseif ($chunk['name'] == 'INDX') {
+				if (!$old_INDX_offset) {
+					$old_INDX_offset = $chunk['offset'];
+				}
+			}
+		}
+		$CONTdelta = strlen($new_CONT_tag_data) - $old_CONT_length;
+
+		$PROPchunk  = "\x00\x00"; // object version
+		$PROPchunk .= getid3_lib::BigEndian2String($chunks[$chunkNameKeys['PROP']]['max_bit_rate'],    4);
+		$PROPchunk .= getid3_lib::BigEndian2String($chunks[$chunkNameKeys['PROP']]['avg_bit_rate'],    4);
+		$PROPchunk .= getid3_lib::BigEndian2String($chunks[$chunkNameKeys['PROP']]['max_packet_size'], 4);
+		$PROPchunk .= getid3_lib::BigEndian2String($chunks[$chunkNameKeys['PROP']]['avg_packet_size'], 4);
+		$PROPchunk .= getid3_lib::BigEndian2String($chunks[$chunkNameKeys['PROP']]['num_packets'],     4);
+		$PROPchunk .= getid3_lib::BigEndian2String($chunks[$chunkNameKeys['PROP']]['duration'],        4);
+		$PROPchunk .= getid3_lib::BigEndian2String($chunks[$chunkNameKeys['PROP']]['preroll'],         4);
+		$PROPchunk .= getid3_lib::BigEndian2String(max(0, $old_INDX_offset + $CONTdelta),              4);
+		$PROPchunk .= getid3_lib::BigEndian2String(max(0, $old_DATA_offset + $CONTdelta),              4);
+		$PROPchunk .= getid3_lib::BigEndian2String($chunks[$chunkNameKeys['PROP']]['num_streams'],     2);
+		$PROPchunk .= getid3_lib::BigEndian2String($chunks[$chunkNameKeys['PROP']]['flags_raw'],       2);
+
+		$PROPchunk  = 'PROP'.getid3_lib::BigEndian2String(strlen($PROPchunk) + 8, 4).$PROPchunk; // PROP chunk identifier + chunk length
+		return $PROPchunk;
+	}
+
+	function GenerateCONTchunk() {
+		foreach ($this->tag_data as $key => $value) {
+			// limit each value to 0xFFFF bytes
+			$this->tag_data[$key] = substr($value, 0, 65535);
+		}
+
+		$CONTchunk  = "\x00\x00"; // object version
+
+		$CONTchunk .= getid3_lib::BigEndian2String(strlen(@$this->tag_data['title']), 2);
+		$CONTchunk .= @$this->tag_data['title'];
+
+		$CONTchunk .= getid3_lib::BigEndian2String(strlen(@$this->tag_data['artist']), 2);
+		$CONTchunk .= @$this->tag_data['artist'];
+
+		$CONTchunk .= getid3_lib::BigEndian2String(strlen(@$this->tag_data['copyright']), 2);
+		$CONTchunk .= @$this->tag_data['copyright'];
+
+		$CONTchunk .= getid3_lib::BigEndian2String(strlen(@$this->tag_data['comment']), 2);
+		$CONTchunk .= @$this->tag_data['comment'];
+
+		if ($this->paddedlength > (strlen($CONTchunk) + 8)) {
+			$CONTchunk .= str_repeat("\x00", $this->paddedlength - strlen($CONTchunk) - 8);
+		}
+
+		$CONTchunk  = 'CONT'.getid3_lib::BigEndian2String(strlen($CONTchunk) + 8, 4).$CONTchunk; // CONT chunk identifier + chunk length
+
+		return $CONTchunk;
+	}
+
+	function RemoveReal() {
+		// File MUST be writeable - CHMOD(646) at least
+		if (is_writeable($this->filename)) {
+			if ($fp_source = @fopen($this->filename, 'r+b')) {
+
+				// Initialize getID3 engine
+				$getID3 = new getID3;
+				$OldThisFileInfo = $getID3->analyze($this->filename);
+				if (empty($OldThisFileInfo['real']['chunks']) && !empty($OldThisFileInfo['real']['old_ra_header'])) {
+					$this->errors[] = 'Cannot remove Real tags from old-style file format';
+					fclose($fp_source);
+					return false;
+				}
+
+				if (empty($OldThisFileInfo['real']['chunks'])) {
+					$this->errors[] = 'Cannot remove Real tags because cannot find DATA chunk in file';
+					fclose($fp_source);
+					return false;
+				}
+				foreach ($OldThisFileInfo['real']['chunks'] as $chunknumber => $chunkarray) {
+					$oldChunkInfo[$chunkarray['name']] = $chunkarray;
+				}
+
+				if (empty($oldChunkInfo['CONT'])) {
+					// no existing CONT chunk
+					fclose($fp_source);
+					return true;
+				}
+
+				$BeforeOffset = $oldChunkInfo['CONT']['offset'];
+				$AfterOffset  = $oldChunkInfo['CONT']['offset'] + $oldChunkInfo['CONT']['length'];
+				if ($tempfilename = tempnam('*', 'getID3')) {
+					ob_start();
+					if ($fp_temp = fopen($tempfilename, 'wb')) {
+
+						rewind($fp_source);
+						fwrite($fp_temp, fread($fp_source, $BeforeOffset));
+						fseek($fp_source, $AfterOffset, SEEK_SET);
+						while ($buffer = fread($fp_source, GETID3_FREAD_BUFFER_SIZE)) {
+							fwrite($fp_temp, $buffer, strlen($buffer));
+						}
+						fclose($fp_temp);
+
+						if (copy($tempfilename, $this->filename)) {
+							unlink($tempfilename);
+							fclose($fp_source);
+							return true;
+						}
+						unlink($tempfilename);
+						$this->errors[] = 'FAILED: copy('.$tempfilename.', '.$this->filename.') - '.strip_tags(ob_get_contents());
+
+					} else {
+
+						$this->errors[] = 'Could not open '.$tempfilename.' mode "wb" - '.strip_tags(ob_get_contents());
+
+					}
+					ob_end_clean();
+				}
+				fclose($fp_source);
+				return false;
+
+
+			} else {
+				$this->errors[] = 'Could not open '.$this->filename.' mode "r+b"';
+				return false;
+			}
+		}
+		$this->errors[] = 'File is not writeable: '.$this->filename;
+		return false;
+	}
+
+}
+
+?>
\ No newline at end of file

Added: plog/trunk/class/gallery/getid3/write.vorbiscomment.php
===================================================================
--- plog/trunk/class/gallery/getid3/write.vorbiscomment.php	2006-07-24 16:40:16 UTC (rev 3789)
+++ plog/trunk/class/gallery/getid3/write.vorbiscomment.php	2006-07-24 16:56:08 UTC (rev 3790)
@@ -0,0 +1,124 @@
+<?php
+/////////////////////////////////////////////////////////////////
+/// getID3() by James Heinrich <info at getid3.org>               //
+//  available at http://getid3.sourceforge.net                 //
+//            or http://www.getid3.org                         //
+/////////////////////////////////////////////////////////////////
+// See readme.txt for more details                             //
+/////////////////////////////////////////////////////////////////
+//                                                             //
+// write.vorbiscomment.php                                     //
+// module for writing VorbisComment tags                       //
+// dependencies: /helperapps/vorbiscomment.exe                 //
+//                                                            ///
+/////////////////////////////////////////////////////////////////
+
+
+class getid3_write_vorbiscomment
+{
+
+	var $filename;
+	var $tag_data;
+	var $warnings = array(); // any non-critical errors will be stored here
+	var $errors   = array(); // any critical errors will be stored here
+
+	function getid3_write_vorbiscomment() {
+		return true;
+	}
+
+	function WriteVorbisComment() {
+
+		if (!ini_get('safe_mode')) {
+
+			// Create file with new comments
+			$tempcommentsfilename = tempnam('*', 'getID3');
+			if ($fpcomments = @fopen($tempcommentsfilename, 'wb')) {
+
+				foreach ($this->tag_data as $key => $value) {
+					foreach ($value as $commentdata) {
+						fwrite($fpcomments, $this->CleanVorbisCommentName($key).'='.$commentdata."\n");
+					}
+				}
+				fclose($fpcomments);
+
+			} else {
+
+				$this->errors[] = 'failed to open temporary tags file "'.$tempcommentsfilename.'", tags not written';
+				return false;
+
+			}
+
+			$oldignoreuserabort = ignore_user_abort(true);
+			if (GETID3_OS_ISWINDOWS) {
+
+				if (file_exists(GETID3_HELPERAPPSDIR.'vorbiscomment.exe')) {
+					//$commandline = '"'.GETID3_HELPERAPPSDIR.'vorbiscomment.exe" -w --raw -c "'.$tempcommentsfilename.'" "'.str_replace('/', '\\', $this->filename).'"';
+					//  vorbiscomment works fine if you copy-paste the above commandline into a command prompt,
+					//  but refuses to work with `backtick` if there are "doublequotes" present around BOTH
+					//  the metaflac pathname and the target filename. For whatever reason...??
+					//  The solution is simply ensure that the metaflac pathname has no spaces,
+					//  and therefore does not need to be quoted
+
+					// On top of that, if error messages are not always captured properly under Windows
+					// To at least see if there was a problem, compare file modification timestamps before and after writing
+					clearstatcache();
+					$timestampbeforewriting = filemtime($this->filename);
+
+					$commandline = GETID3_HELPERAPPSDIR.'vorbiscomment.exe -w --raw -c "'.$tempcommentsfilename.'" "'.$this->filename.'" 2>&1';
+					$VorbiscommentError = `$commandline`;
+
+					if (empty($VorbiscommentError)) {
+						clearstatcache();
+						if ($timestampbeforewriting == filemtime($this->filename)) {
+							$VorbiscommentError = 'File modification timestamp has not changed - it looks like the tags were not written';
+						}
+					}
+				} else {
+					$VorbiscommentError = 'vorbiscomment.exe not found in '.GETID3_HELPERAPPSDIR;
+				}
+
+			} else {
+
+				$commandline = 'vorbiscomment -w --raw -c "'.$tempcommentsfilename.'" "'.$this->filename.'" 2>&1';
+				$VorbiscommentError = `$commandline`;
+
+			}
+
+			// Remove temporary comments file
+			unlink($tempcommentsfilename);
+			ignore_user_abort($oldignoreuserabort);
+
+			if (!empty($VorbiscommentError)) {
+
+				$this->errors[] = 'system call to vorbiscomment failed with message: '."\n\n".$VorbiscommentError;
+				return false;
+
+			}
+
+			return true;
+		}
+
+		$this->errors[] = 'PHP running in Safe Mode (backtick operator not available) - cannot call vorbiscomment, tags not written';
+		return false;
+	}
+
+	function DeleteVorbisComment() {
+		$this->tag_data = array(array());
+		return $this->WriteVorbisComment();
+	}
+
+	function CleanVorbisCommentName($originalcommentname) {
+		// A case-insensitive field name that may consist of ASCII 0x20 through 0x7D, 0x3D ('=') excluded.
+		// ASCII 0x41 through 0x5A inclusive (A-Z) is to be considered equivalent to ASCII 0x61 through
+		// 0x7A inclusive (a-z).
+
+		// replace invalid chars with a space, return uppercase text
+		// Thanks Chris Bolt <chris-getid3Øbolt*cx> for improving this function
+		// note: ereg_replace() replaces nulls with empty string (not space)
+		return strtoupper(ereg_replace('[^ -<>-}]', ' ', str_replace("\x00", ' ', $originalcommentname)));
+
+	}
+
+}
+
+?>
\ No newline at end of file



More information about the pLog-svn mailing list