diff --git a/.gitattributes b/.gitattributes
index 2e2514f0d46e05bbfad5c15e08257cc3960bed62..2eebc897798d95ffd2b6146671c3368b9beed4c8 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -142,6 +142,7 @@ lib/getid3/module.archive.gzip.php -text
 lib/getid3/module.archive.rar.php -text
 lib/getid3/module.archive.szip.php -text
 lib/getid3/module.archive.tar.php -text
+lib/getid3/module.archive.xz.php -text
 lib/getid3/module.archive.zip.php -text
 lib/getid3/module.audio-video.asf.php -text
 lib/getid3/module.audio-video.bink.php -text
diff --git a/lib/getid3/extension.cache.mysql.php b/lib/getid3/extension.cache.mysql.php
index 2dd2b12a72118cdab3637d4759f405f259b54daf..e362885a49ca3c35b2766510510520e1a9cf3fc1 100644
--- a/lib/getid3/extension.cache.mysql.php
+++ b/lib/getid3/extension.cache.mysql.php
@@ -167,7 +167,7 @@ class getID3_cached_mysql extends getID3
 	 */
 	public function analyze($filename, $filesize=null, $original_filename='') {
 
-        $filetime = 0;
+		$filetime = 0;
 		if (file_exists($filename)) {
 
 			// Short-hands
diff --git a/lib/getid3/extension.cache.mysqli.php b/lib/getid3/extension.cache.mysqli.php
index 44ea694ee566a8f171e5d7a8fcd131cc6cd953ed..6781e620c34c5507dd04961968a0e29e7a086a4d 100644
--- a/lib/getid3/extension.cache.mysqli.php
+++ b/lib/getid3/extension.cache.mysqli.php
@@ -87,6 +87,11 @@ class getID3_cached_mysqli extends getID3
 	 */
 	private $table;
 
+	/**
+	 * @var bool
+	 */
+	private $db_structure_check;
+
 
 	/**
 	 * constructor - see top of this file for cache type and cache_options
@@ -124,14 +129,15 @@ class getID3_cached_mysqli extends getID3
 		// Create cache table if not exists
 		$this->create_table();
 
+		$this->db_structure_check = true; // set to false if you know your table structure has already been migrated to use `hash` as the primary key to avoid
+		$this->migrate_db_structure();
+
 		// Check version number and clear cache if changed
 		$version = '';
 		$SQLquery  = 'SELECT `value`';
 		$SQLquery .= ' FROM `'.$this->mysqli->real_escape_string($this->table).'`';
 		$SQLquery .= ' WHERE (`filename` = \''.$this->mysqli->real_escape_string(getID3::VERSION).'\')';
-		$SQLquery .= ' AND (`filesize` = -1)';
-		$SQLquery .= ' AND (`filetime` = -1)';
-		$SQLquery .= ' AND (`analyzetime` = -1)';
+		$SQLquery .= ' AND (`hash` = \'getID3::VERSION\')';
 		if ($this->cursor = $this->mysqli->query($SQLquery)) {
 			list($version) = $this->cursor->fetch_array();
 		}
@@ -147,8 +153,39 @@ class getID3_cached_mysqli extends getID3
 	 * clear cache
 	 */
 	public function clear_cache() {
-		$this->mysqli->query('DELETE FROM `'.$this->mysqli->real_escape_string($this->table).'`');
-		$this->mysqli->query('INSERT INTO `'.$this->mysqli->real_escape_string($this->table).'` (`filename`, `filesize`, `filetime`, `analyzetime`, `value`) VALUES (\''.getID3::VERSION.'\', -1, -1, -1, \''.getID3::VERSION.'\')');
+		$this->mysqli->query('TRUNCATE TABLE `'.$this->mysqli->real_escape_string($this->table).'`');
+		$this->mysqli->query('INSERT INTO `'.$this->mysqli->real_escape_string($this->table).'` (`hash`, `filename`, `filesize`, `filetime`, `analyzetime`, `value`) VALUES (\'getID3::VERSION\', \''.getID3::VERSION.'\', -1, -1, -1, \''.getID3::VERSION.'\')');
+	}
+
+
+	/**
+	 * migrate database structure if needed
+	 */
+	public function migrate_db_structure() {
+		// Check for table structure
+		if ($this->db_structure_check) {
+			$SQLquery  = 'SHOW COLUMNS';
+			$SQLquery .= ' FROM `'.$this->mysqli->real_escape_string($this->table).'`';
+			$SQLquery .= ' LIKE \'hash\'';
+			$this->cursor = $this->mysqli->query($SQLquery);
+			if ($this->cursor->num_rows == 0) {
+				// table has not been migrated, add column, add hashes, change index
+				$SQLquery  = 'ALTER TABLE `getid3_cache` DROP PRIMARY KEY, ADD `hash` CHAR(32) NOT NULL DEFAULT \'\' FIRST, ADD PRIMARY KEY(`hash`)';
+				$this->mysqli->query($SQLquery);
+
+				$SQLquery  = 'UPDATE `getid3_cache` SET';
+				$SQLquery .= ' `hash` = MD5(`filename`, `filesize`, `filetime`)';
+				$SQLquery .= ' WHERE (`filesize` > -1)';
+				$this->mysqli->query($SQLquery);
+
+				$SQLquery  = 'UPDATE `getid3_cache` SET';
+				$SQLquery .= ' `hash` = \'getID3::VERSION\'';
+				$SQLquery .= ' WHERE (`filesize` = -1)';
+				$SQLquery .= '   AND (`filetime` = -1)';
+				$SQLquery .= '   AND (`filetime` = -1)';
+				$this->mysqli->query($SQLquery);
+			}
+		}
 	}
 
 
@@ -163,7 +200,7 @@ class getID3_cached_mysqli extends getID3
 	 */
 	public function analyze($filename, $filesize=null, $original_filename='') {
 
-        $filetime = 0;
+		$filetime = 0;
 		if (file_exists($filename)) {
 
 			// Short-hands
@@ -173,9 +210,7 @@ class getID3_cached_mysqli extends getID3
 			// Lookup file
 			$SQLquery  = 'SELECT `value`';
 			$SQLquery .= ' FROM `'.$this->mysqli->real_escape_string($this->table).'`';
-			$SQLquery .= ' WHERE (`filename` = \''.$this->mysqli->real_escape_string($filename).'\')';
-			$SQLquery .= '   AND (`filesize` = \''.$this->mysqli->real_escape_string($filesize).'\')';
-			$SQLquery .= '   AND (`filetime` = \''.$this->mysqli->real_escape_string($filetime).'\')';
+			$SQLquery .= ' WHERE (`hash` = \''.$this->mysqli->real_escape_string(md5($filename.$filesize.$filetime)).'\')';
 			$this->cursor = $this->mysqli->query($SQLquery);
 			if ($this->cursor->num_rows > 0) {
 				// Hit
@@ -189,12 +224,14 @@ class getID3_cached_mysqli extends getID3
 
 		// Save result
 		if (file_exists($filename)) {
-			$SQLquery  = 'INSERT INTO `'.$this->mysqli->real_escape_string($this->table).'` (`filename`, `filesize`, `filetime`, `analyzetime`, `value`) VALUES (';
-			$SQLquery .=   '\''.$this->mysqli->real_escape_string($filename).'\'';
+			$SQLquery  = 'INSERT INTO `'.$this->mysqli->real_escape_string($this->table).'` (`hash`, `filename`, `filesize`, `filetime`, `analyzetime`, `value`) VALUES (';
+			$SQLquery .=   '\''.$this->mysqli->real_escape_string(md5($filename.$filesize.$filetime)).'\'';
+			$SQLquery .= ', \''.$this->mysqli->real_escape_string($filename).'\'';
 			$SQLquery .= ', \''.$this->mysqli->real_escape_string($filesize).'\'';
 			$SQLquery .= ', \''.$this->mysqli->real_escape_string($filetime).'\'';
-			$SQLquery .= ', \''.$this->mysqli->real_escape_string(time()   ).'\'';
-			$SQLquery .= ', \''.$this->mysqli->real_escape_string(base64_encode(serialize($analysis))).'\')';
+			$SQLquery .= ', UNIX_TIMESTAMP()';
+			$SQLquery .= ', \''.$this->mysqli->real_escape_string(base64_encode(serialize($analysis))).'\'';
+			$SQLquery .= ')';
 			$this->cursor = $this->mysqli->query($SQLquery);
 		}
 		return $analysis;
@@ -207,13 +244,18 @@ class getID3_cached_mysqli extends getID3
 	 * @param bool $drop
 	 */
 	private function create_table($drop=false) {
+		if ($drop) {
+			$SQLquery  = 'DROP TABLE IF EXISTS `'.$this->mysqli->real_escape_string($this->table).'`';
+			$this->mysqli->query($SQLquery);
+		}
 		$SQLquery  = 'CREATE TABLE IF NOT EXISTS `'.$this->mysqli->real_escape_string($this->table).'` (';
-		$SQLquery .=   '`filename` VARCHAR(990) NOT NULL DEFAULT \'\'';
+		$SQLquery .=   '`hash` CHAR(32) NOT NULL DEFAULT \'\'';
+		$SQLquery .= ', `filename` VARCHAR(1000) NOT NULL DEFAULT \'\'';
 		$SQLquery .= ', `filesize` INT(11) NOT NULL DEFAULT \'0\'';
 		$SQLquery .= ', `filetime` INT(11) NOT NULL DEFAULT \'0\'';
 		$SQLquery .= ', `analyzetime` INT(11) NOT NULL DEFAULT \'0\'';
 		$SQLquery .= ', `value` LONGTEXT NOT NULL';
-		$SQLquery .= ', PRIMARY KEY (`filename`, `filesize`, `filetime`))';
+		$SQLquery .= ', PRIMARY KEY (`hash`))';
 		$this->cursor = $this->mysqli->query($SQLquery);
 		echo $this->mysqli->error;
 	}
diff --git a/lib/getid3/getid3.lib.php b/lib/getid3/getid3.lib.php
index 5c5a5dfacac2811e50622bc8223faf68c7b811a7..1d51e8c4f3f1037ab07ca5f9a6ac44be621eb00c 100644
--- a/lib/getid3/getid3.lib.php
+++ b/lib/getid3/getid3.lib.php
@@ -26,9 +26,9 @@ class getid3_lib
 		$returnstring = '';
 		for ($i = 0; $i < strlen($string); $i++) {
 			if ($hex) {
-				$returnstring .= str_pad(dechex(ord($string{$i})), 2, '0', STR_PAD_LEFT);
+				$returnstring .= str_pad(dechex(ord($string[$i])), 2, '0', STR_PAD_LEFT);
 			} else {
-				$returnstring .= ' '.(preg_match("#[\x20-\x7E]#", $string{$i}) ? $string{$i} : '¤');
+				$returnstring .= ' '.(preg_match("#[\x20-\x7E]#", $string[$i]) ? $string[$i] : '¤');
 			}
 			if ($spaces) {
 				$returnstring .= ' ';
@@ -152,11 +152,11 @@ class getid3_lib
 	public static function NormalizeBinaryPoint($binarypointnumber, $maxbits=52) {
 		if (strpos($binarypointnumber, '.') === false) {
 			$binarypointnumber = '0.'.$binarypointnumber;
-		} elseif ($binarypointnumber{0} == '.') {
+		} elseif ($binarypointnumber[0] == '.') {
 			$binarypointnumber = '0'.$binarypointnumber;
 		}
 		$exponent = 0;
-		while (($binarypointnumber{0} != '1') || (substr($binarypointnumber, 1, 1) != '.')) {
+		while (($binarypointnumber[0] != '1') || (substr($binarypointnumber, 1, 1) != '.')) {
 			if (substr($binarypointnumber, 1, 1) == '.') {
 				$exponent--;
 				$binarypointnumber = substr($binarypointnumber, 2, 1).'.'.substr($binarypointnumber, 3);
@@ -164,7 +164,7 @@ class getid3_lib
 				$pointpos = strpos($binarypointnumber, '.');
 				$exponent += ($pointpos - 1);
 				$binarypointnumber = str_replace('.', '', $binarypointnumber);
-				$binarypointnumber = $binarypointnumber{0}.'.'.substr($binarypointnumber, 1);
+				$binarypointnumber = $binarypointnumber[0].'.'.substr($binarypointnumber, 1);
 			}
 		}
 		$binarypointnumber = str_pad(substr($binarypointnumber, 0, $maxbits + 2), $maxbits + 2, '0', STR_PAD_RIGHT);
@@ -255,7 +255,7 @@ class getid3_lib
 		if (!$bitword) {
 			return 0;
 		}
-		$signbit = $bitword{0};
+		$signbit = $bitword[0];
 		$floatvalue = 0;
 		$exponentbits = 0;
 		$fractionbits = 0;
@@ -275,7 +275,7 @@ class getid3_lib
 				// 80-bit Apple SANE format
 				// http://www.mactech.com/articles/mactech/Vol.06/06.01/SANENormalized/
 				$exponentstring = substr($bitword, 1, 15);
-				$isnormalized = intval($bitword{16});
+				$isnormalized = intval($bitword[16]);
 				$fractionstring = substr($bitword, 17, 63);
 				$exponent = pow(2, self::Bin2Dec($exponentstring) - 16383);
 				$fraction = $isnormalized + self::DecimalBinary2Float($fractionstring);
@@ -343,9 +343,9 @@ class getid3_lib
 		for ($i = 0; $i < $bytewordlen; $i++) {
 			if ($synchsafe) { // disregard MSB, effectively 7-bit bytes
 				//$intvalue = $intvalue | (ord($byteword{$i}) & 0x7F) << (($bytewordlen - 1 - $i) * 7); // faster, but runs into problems past 2^31 on 32-bit systems
-				$intvalue += (ord($byteword{$i}) & 0x7F) * pow(2, ($bytewordlen - 1 - $i) * 7);
+				$intvalue += (ord($byteword[$i]) & 0x7F) * pow(2, ($bytewordlen - 1 - $i) * 7);
 			} else {
-				$intvalue += ord($byteword{$i}) * pow(256, ($bytewordlen - 1 - $i));
+				$intvalue += ord($byteword[$i]) * pow(256, ($bytewordlen - 1 - $i));
 			}
 		}
 		if ($signed && !$synchsafe) {
@@ -390,7 +390,7 @@ class getid3_lib
 		$binvalue = '';
 		$bytewordlen = strlen($byteword);
 		for ($i = 0; $i < $bytewordlen; $i++) {
-			$binvalue .= str_pad(decbin(ord($byteword{$i})), 8, '0', STR_PAD_LEFT);
+			$binvalue .= str_pad(decbin(ord($byteword[$i])), 8, '0', STR_PAD_LEFT);
 		}
 		return $binvalue;
 	}
@@ -451,7 +451,7 @@ class getid3_lib
 	public static function Bin2Dec($binstring, $signed=false) {
 		$signmult = 1;
 		if ($signed) {
-			if ($binstring{0} == '1') {
+			if ($binstring[0] == '1') {
 				$signmult = -1;
 			}
 			$binstring = substr($binstring, 1);
@@ -751,9 +751,7 @@ class getid3_lib
 	}
 
 	/**
-	 * self::md5_data() - returns md5sum for a file from startuing position to absolute end position
-	 *
-	 * @author Allan Hansen <ahØartemis*dk>
+	 * Returns checksum for a file from starting position to absolute end position.
 	 *
 	 * @param string $file
 	 * @param int    $offset
@@ -761,97 +759,30 @@ class getid3_lib
 	 * @param string $algorithm
 	 *
 	 * @return string|false
-	 * @throws Exception
 	 * @throws getid3_exception
 	 */
 	public static function hash_data($file, $offset, $end, $algorithm) {
-		static $tempdir = '';
-		$windows_call = null;
-		$unix_call = null;
-		$hash_length = null;
-		$hash_function = null;
 		if (!self::intValueSupported($end)) {
 			return false;
 		}
-		switch ($algorithm) {
-			case 'md5':
-				$hash_function = 'md5_file';
-				$unix_call     = 'md5sum';
-				$windows_call  = 'md5sum.exe';
-				$hash_length   = 32;
-				break;
-
-			case 'sha1':
-				$hash_function = 'sha1_file';
-				$unix_call     = 'sha1sum';
-				$windows_call  = 'sha1sum.exe';
-				$hash_length   = 40;
-				break;
-
-			default:
-				throw new Exception('Invalid algorithm ('.$algorithm.') in self::hash_data()');
-				break;
+		if (!in_array($algorithm, array('md5', 'sha1'))) {
+			throw new getid3_exception('Invalid algorithm ('.$algorithm.') in self::hash_data()');
 		}
-		$size = $end - $offset;
-		while (true) {
-			if (GETID3_OS_ISWINDOWS) {
 
-				// It seems that sha1sum.exe for Windows only works on physical files, does not accept piped data
-				// Fall back to create-temp-file method:
-				if ($algorithm == 'sha1') {
-					break;
-				}
-
-				$RequiredFiles = array('cygwin1.dll', 'head.exe', 'tail.exe', $windows_call);
-				foreach ($RequiredFiles as $required_file) {
-					if (!is_readable(GETID3_HELPERAPPSDIR.$required_file)) {
-						// helper apps not available - fall back to old method
-						break 2;
-					}
-				}
-				$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.' '.escapeshellarg($file).' | ';
-				$commandline .= 'tail -c'.$size.' | ';
-				$commandline .= $unix_call;
-
-			}
-			if (preg_match('#(1|ON)#i', ini_get('safe_mode'))) {
-				//throw new Exception('PHP running in Safe Mode - backtick operator not available, using slower non-system-call '.$algorithm.' algorithm');
-				break;
-			}
-			return substr(`$commandline`, 0, $hash_length);
-		}
+		$size = $end - $offset;
 
-		if (empty($tempdir)) {
-			// yes this is ugly, feel free to suggest a better way
-			require_once(dirname(__FILE__).'/getid3.php');
-			$getid3_temp = new getID3();
-			$tempdir = $getid3_temp->tempdir;
-			unset($getid3_temp);
-		}
-		// try to create a temporary file in the system temp directory - invalid dirname should force to system temp dir
-		if (($data_filename = tempnam($tempdir, 'gI3')) === false) {
-			// can't find anywhere to create a temp file, just fail
-			return false;
+		$fp = fopen($file, 'rb');
+		fseek($fp, $offset);
+		$ctx = hash_init($algorithm);
+		while ($size > 0) {
+			$buffer = fread($fp, min($size, getID3::FREAD_BUFFER_SIZE));
+			hash_update($ctx, $buffer);
+			$size -= getID3::FREAD_BUFFER_SIZE;
 		}
+		$hash = hash_final($ctx);
+		fclose($fp);
 
-		// Init
-		$result = false;
-
-		// copy parts of file
-		try {
-			self::CopyFileParts($file, $data_filename, $offset, $end - $offset);
-			$result = $hash_function($data_filename);
-		} catch (Exception $e) {
-			throw new Exception('self::CopyFileParts() failed in getid_lib::hash_data(): '.$e->getMessage());
-		}
-		unlink($data_filename);
-		return $result;
+		return $hash;
 	}
 
 	/**
@@ -862,6 +793,8 @@ class getid3_lib
 	 *
 	 * @return bool
 	 * @throws Exception
+	 *
+	 * @deprecated Unused, may be removed in future versions of getID3
 	 */
 	public static function CopyFileParts($filename_source, $filename_dest, $offset, $length) {
 		if (!self::intValueSupported($offset + $length)) {
@@ -935,7 +868,7 @@ class getid3_lib
 			$newcharstring .= "\xEF\xBB\xBF";
 		}
 		for ($i = 0; $i < strlen($string); $i++) {
-			$charval = ord($string{$i});
+			$charval = ord($string[$i]);
 			$newcharstring .= self::iconv_fallback_int_utf8($charval);
 		}
 		return $newcharstring;
@@ -955,7 +888,7 @@ class getid3_lib
 			$newcharstring .= "\xFE\xFF";
 		}
 		for ($i = 0; $i < strlen($string); $i++) {
-			$newcharstring .= "\x00".$string{$i};
+			$newcharstring .= "\x00".$string[$i];
 		}
 		return $newcharstring;
 	}
@@ -974,7 +907,7 @@ class getid3_lib
 			$newcharstring .= "\xFF\xFE";
 		}
 		for ($i = 0; $i < strlen($string); $i++) {
-			$newcharstring .= $string{$i}."\x00";
+			$newcharstring .= $string[$i]."\x00";
 		}
 		return $newcharstring;
 	}
@@ -1006,27 +939,27 @@ class getid3_lib
 		$offset = 0;
 		$stringlength = strlen($string);
 		while ($offset < $stringlength) {
-			if ((ord($string{$offset}) | 0x07) == 0xF7) {
+			if ((ord($string[$offset]) | 0x07) == 0xF7) {
 				// 11110bbb 10bbbbbb 10bbbbbb 10bbbbbb
-				$charval = ((ord($string{($offset + 0)}) & 0x07) << 18) &
-						   ((ord($string{($offset + 1)}) & 0x3F) << 12) &
-						   ((ord($string{($offset + 2)}) & 0x3F) <<  6) &
-							(ord($string{($offset + 3)}) & 0x3F);
+				$charval = ((ord($string[($offset + 0)]) & 0x07) << 18) &
+						   ((ord($string[($offset + 1)]) & 0x3F) << 12) &
+						   ((ord($string[($offset + 2)]) & 0x3F) <<  6) &
+							(ord($string[($offset + 3)]) & 0x3F);
 				$offset += 4;
-			} elseif ((ord($string{$offset}) | 0x0F) == 0xEF) {
+			} elseif ((ord($string[$offset]) | 0x0F) == 0xEF) {
 				// 1110bbbb 10bbbbbb 10bbbbbb
-				$charval = ((ord($string{($offset + 0)}) & 0x0F) << 12) &
-						   ((ord($string{($offset + 1)}) & 0x3F) <<  6) &
-							(ord($string{($offset + 2)}) & 0x3F);
+				$charval = ((ord($string[($offset + 0)]) & 0x0F) << 12) &
+						   ((ord($string[($offset + 1)]) & 0x3F) <<  6) &
+							(ord($string[($offset + 2)]) & 0x3F);
 				$offset += 3;
-			} elseif ((ord($string{$offset}) | 0x1F) == 0xDF) {
+			} elseif ((ord($string[$offset]) | 0x1F) == 0xDF) {
 				// 110bbbbb 10bbbbbb
-				$charval = ((ord($string{($offset + 0)}) & 0x1F) <<  6) &
-							(ord($string{($offset + 1)}) & 0x3F);
+				$charval = ((ord($string[($offset + 0)]) & 0x1F) <<  6) &
+							(ord($string[($offset + 1)]) & 0x3F);
 				$offset += 2;
-			} elseif ((ord($string{$offset}) | 0x7F) == 0x7F) {
+			} elseif ((ord($string[$offset]) | 0x7F) == 0x7F) {
 				// 0bbbbbbb
-				$charval = ord($string{$offset});
+				$charval = ord($string[$offset]);
 				$offset += 1;
 			} else {
 				// error? throw some kind of warning here?
@@ -1056,27 +989,27 @@ class getid3_lib
 		$offset = 0;
 		$stringlength = strlen($string);
 		while ($offset < $stringlength) {
-			if ((ord($string{$offset}) | 0x07) == 0xF7) {
+			if ((ord($string[$offset]) | 0x07) == 0xF7) {
 				// 11110bbb 10bbbbbb 10bbbbbb 10bbbbbb
-				$charval = ((ord($string{($offset + 0)}) & 0x07) << 18) &
-						   ((ord($string{($offset + 1)}) & 0x3F) << 12) &
-						   ((ord($string{($offset + 2)}) & 0x3F) <<  6) &
-							(ord($string{($offset + 3)}) & 0x3F);
+				$charval = ((ord($string[($offset + 0)]) & 0x07) << 18) &
+						   ((ord($string[($offset + 1)]) & 0x3F) << 12) &
+						   ((ord($string[($offset + 2)]) & 0x3F) <<  6) &
+							(ord($string[($offset + 3)]) & 0x3F);
 				$offset += 4;
-			} elseif ((ord($string{$offset}) | 0x0F) == 0xEF) {
+			} elseif ((ord($string[$offset]) | 0x0F) == 0xEF) {
 				// 1110bbbb 10bbbbbb 10bbbbbb
-				$charval = ((ord($string{($offset + 0)}) & 0x0F) << 12) &
-						   ((ord($string{($offset + 1)}) & 0x3F) <<  6) &
-							(ord($string{($offset + 2)}) & 0x3F);
+				$charval = ((ord($string[($offset + 0)]) & 0x0F) << 12) &
+						   ((ord($string[($offset + 1)]) & 0x3F) <<  6) &
+							(ord($string[($offset + 2)]) & 0x3F);
 				$offset += 3;
-			} elseif ((ord($string{$offset}) | 0x1F) == 0xDF) {
+			} elseif ((ord($string[$offset]) | 0x1F) == 0xDF) {
 				// 110bbbbb 10bbbbbb
-				$charval = ((ord($string{($offset + 0)}) & 0x1F) <<  6) &
-							(ord($string{($offset + 1)}) & 0x3F);
+				$charval = ((ord($string[($offset + 0)]) & 0x1F) <<  6) &
+							(ord($string[($offset + 1)]) & 0x3F);
 				$offset += 2;
-			} elseif ((ord($string{$offset}) | 0x7F) == 0x7F) {
+			} elseif ((ord($string[$offset]) | 0x7F) == 0x7F) {
 				// 0bbbbbbb
-				$charval = ord($string{$offset});
+				$charval = ord($string[$offset]);
 				$offset += 1;
 			} else {
 				// error? throw some kind of warning here?
@@ -1106,27 +1039,27 @@ class getid3_lib
 		$offset = 0;
 		$stringlength = strlen($string);
 		while ($offset < $stringlength) {
-			if ((ord($string{$offset}) | 0x07) == 0xF7) {
+			if ((ord($string[$offset]) | 0x07) == 0xF7) {
 				// 11110bbb 10bbbbbb 10bbbbbb 10bbbbbb
-				$charval = ((ord($string{($offset + 0)}) & 0x07) << 18) &
-						   ((ord($string{($offset + 1)}) & 0x3F) << 12) &
-						   ((ord($string{($offset + 2)}) & 0x3F) <<  6) &
-							(ord($string{($offset + 3)}) & 0x3F);
+				$charval = ((ord($string[($offset + 0)]) & 0x07) << 18) &
+						   ((ord($string[($offset + 1)]) & 0x3F) << 12) &
+						   ((ord($string[($offset + 2)]) & 0x3F) <<  6) &
+							(ord($string[($offset + 3)]) & 0x3F);
 				$offset += 4;
-			} elseif ((ord($string{$offset}) | 0x0F) == 0xEF) {
+			} elseif ((ord($string[$offset]) | 0x0F) == 0xEF) {
 				// 1110bbbb 10bbbbbb 10bbbbbb
-				$charval = ((ord($string{($offset + 0)}) & 0x0F) << 12) &
-						   ((ord($string{($offset + 1)}) & 0x3F) <<  6) &
-							(ord($string{($offset + 2)}) & 0x3F);
+				$charval = ((ord($string[($offset + 0)]) & 0x0F) << 12) &
+						   ((ord($string[($offset + 1)]) & 0x3F) <<  6) &
+							(ord($string[($offset + 2)]) & 0x3F);
 				$offset += 3;
-			} elseif ((ord($string{$offset}) | 0x1F) == 0xDF) {
+			} elseif ((ord($string[$offset]) | 0x1F) == 0xDF) {
 				// 110bbbbb 10bbbbbb
-				$charval = ((ord($string{($offset + 0)}) & 0x1F) <<  6) &
-							(ord($string{($offset + 1)}) & 0x3F);
+				$charval = ((ord($string[($offset + 0)]) & 0x1F) <<  6) &
+							(ord($string[($offset + 1)]) & 0x3F);
 				$offset += 2;
-			} elseif ((ord($string{$offset}) | 0x7F) == 0x7F) {
+			} elseif ((ord($string[$offset]) | 0x7F) == 0x7F) {
 				// 0bbbbbbb
-				$charval = ord($string{$offset});
+				$charval = ord($string[$offset]);
 				$offset += 1;
 			} else {
 				// error? maybe throw some warning here?
@@ -1281,6 +1214,16 @@ class getid3_lib
 
 		// mb_convert_encoding() available
 		if (function_exists('mb_convert_encoding')) {
+			if ((strtoupper($in_charset) == 'UTF-16') && (substr($string, 0, 2) != "\xFE\xFF") && (substr($string, 0, 2) != "\xFF\xFE")) {
+				// if BOM missing, mb_convert_encoding will mishandle the conversion, assume UTF-16BE and prepend appropriate BOM
+				$string = "\xFF\xFE".$string;
+			}
+			if ((strtoupper($in_charset) == 'UTF-16') && (strtoupper($out_charset) == 'UTF-8')) {
+				if (($string == "\xFF\xFE") || ($string == "\xFE\xFF")) {
+					// if string consists of only BOM, mb_convert_encoding will return the BOM unmodified
+					return '';
+				}
+			}
 			if ($converted_string = @mb_convert_encoding($string, $out_charset, $in_charset)) {
 				switch ($out_charset) {
 					case 'ISO-8859-1':
@@ -1290,9 +1233,9 @@ class getid3_lib
 				return $converted_string;
 			}
 			return $string;
-		}
+
 		// iconv() available
-		else if (function_exists('iconv')) {
+		} elseif (function_exists('iconv')) {
 			if ($converted_string = @iconv($in_charset, $out_charset.'//TRANSLIT', $string)) {
 				switch ($out_charset) {
 					case 'ISO-8859-1':
@@ -1397,22 +1340,22 @@ class getid3_lib
 			case 'utf-8':
 				$strlen = strlen($string);
 				for ($i = 0; $i < $strlen; $i++) {
-					$char_ord_val = ord($string{$i});
+					$char_ord_val = ord($string[$i]);
 					$charval = 0;
 					if ($char_ord_val < 0x80) {
 						$charval = $char_ord_val;
 					} elseif ((($char_ord_val & 0xF0) >> 4) == 0x0F  &&  $i+3 < $strlen) {
 						$charval  = (($char_ord_val & 0x07) << 18);
-						$charval += ((ord($string{++$i}) & 0x3F) << 12);
-						$charval += ((ord($string{++$i}) & 0x3F) << 6);
-						$charval +=  (ord($string{++$i}) & 0x3F);
+						$charval += ((ord($string[++$i]) & 0x3F) << 12);
+						$charval += ((ord($string[++$i]) & 0x3F) << 6);
+						$charval +=  (ord($string[++$i]) & 0x3F);
 					} elseif ((($char_ord_val & 0xE0) >> 5) == 0x07  &&  $i+2 < $strlen) {
 						$charval  = (($char_ord_val & 0x0F) << 12);
-						$charval += ((ord($string{++$i}) & 0x3F) << 6);
-						$charval +=  (ord($string{++$i}) & 0x3F);
+						$charval += ((ord($string[++$i]) & 0x3F) << 6);
+						$charval +=  (ord($string[++$i]) & 0x3F);
 					} elseif ((($char_ord_val & 0xC0) >> 6) == 0x03  &&  $i+1 < $strlen) {
 						$charval  = (($char_ord_val & 0x1F) << 6);
-						$charval += (ord($string{++$i}) & 0x3F);
+						$charval += (ord($string[++$i]) & 0x3F);
 					}
 					if (($charval >= 32) && ($charval <= 127)) {
 						$HTMLstring .= htmlentities(chr($charval));
diff --git a/lib/getid3/getid3.php b/lib/getid3/getid3.php
index 90d7c9c2a7a036ed636147364d8f9d8e892c6e12..ec9f4069bf174ed63089e6dc5089608add63a5a1 100644
--- a/lib/getid3/getid3.php
+++ b/lib/getid3/getid3.php
@@ -1,5 +1,4 @@
 <?php
-
 /////////////////////////////////////////////////////////////////
 /// getID3() by James Heinrich <info@getid3.org>               //
 //  available at https://github.com/JamesHeinrich/getID3       //
@@ -251,7 +250,7 @@ class getID3
 	 */
 	protected $startup_warning = '';
 
-	const VERSION           = '1.9.16-201810171314';
+	const VERSION           = '1.9.18-201907240906';
 	const FREAD_BUFFER_SIZE = 32768;
 
 	const ATTACHMENTS_NONE   = false;
@@ -321,7 +320,7 @@ class getID3
 
 		// Needed for Windows only:
 		// Define locations of helper applications for Shorten, VorbisComment, MetaFLAC
-		//   as well as other helper functions such as head, tail, md5sum, etc
+		//   as well as other helper functions such as head, etc
 		// This path cannot contain spaces, but the below code will attempt to get the
 		//   8.3-equivalent path automatically
 		// IMPORTANT: This path must include the trailing slash
@@ -406,7 +405,7 @@ class getID3
 	 *
 	 * @throws getid3_exception
 	 */
-	public function openfile($filename, $filesize=null) {
+	public function openfile($filename, $filesize=null, $fp=null) {
 		try {
 			if (!empty($this->startup_error)) {
 				throw new getid3_exception($this->startup_error);
@@ -433,7 +432,9 @@ class getID3
 
 			// open local file
 			//if (is_readable($filename) && is_file($filename) && ($this->fp = fopen($filename, 'rb'))) { // see https://www.getid3.org/phpBB3/viewtopic.php?t=1720
-			if ((is_readable($filename) || file_exists($filename)) && is_file($filename) && ($this->fp = fopen($filename, 'rb'))) {
+			if (($fp != null) && ((get_resource_type($fp) == 'file') || (get_resource_type($fp) == 'stream'))) {
+				$this->fp = $fp;
+			} elseif ((is_readable($filename) || file_exists($filename)) && is_file($filename) && ($this->fp = fopen($filename, 'rb'))) {
 				// great
 			} else {
 				$errormessagelist = array();
@@ -514,9 +515,9 @@ class getID3
 	 *
 	 * @return array
 	 */
-	public function analyze($filename, $filesize=null, $original_filename='') {
+	public function analyze($filename, $filesize=null, $original_filename='', $fp=null) {
 		try {
-			if (!$this->openfile($filename, $filesize)) {
+			if (!$this->openfile($filename, $filesize, $fp)) {
 				return $this->info;
 			}
 
@@ -550,8 +551,8 @@ class getID3
 				$header = fread($this->fp, 10);
 				if ((substr($header, 0, 3) == 'ID3') && (strlen($header) == 10)) {
 					$this->info['id3v2']['header']        = true;
-					$this->info['id3v2']['majorversion']  = ord($header{3});
-					$this->info['id3v2']['minorversion']  = ord($header{4});
+					$this->info['id3v2']['majorversion']  = ord($header[3]);
+					$this->info['id3v2']['minorversion']  = ord($header[4]);
 					$this->info['avdataoffset']          += getid3_lib::BigEndian2Int(substr($header, 6, 4), 1) + 10; // length of ID3v2 tag in 10-byte header doesn't include 10-byte header length
 				}
 			}
@@ -833,7 +834,7 @@ class getID3
 
 				// DSS  - audio       - Digital Speech Standard
 				'dss'  => array(
-							'pattern'   => '^[\\x02-\\x06]ds[s2]',
+							'pattern'   => '^[\\x02-\\x08]ds[s2]',
 							'group'     => 'audio',
 							'module'    => 'dss',
 							'mime_type' => 'application/octet-stream',
@@ -1252,6 +1253,16 @@ class getID3
 							'fail_ape'  => 'ERROR',
 						),
 
+				// XZ   - data         - XZ compressed data
+				'xz'  => array(
+							'pattern'   => '^\\xFD7zXZ\\x00',
+							'group'     => 'archive',
+							'module'    => 'xz',
+							'mime_type' => 'application/x-xz',
+							'fail_id3'  => 'ERROR',
+							'fail_ape'  => 'ERROR',
+						),
+
 
 				// Misc other formats
 
@@ -1323,7 +1334,7 @@ class getID3
 
 
 		if (preg_match('#\\.mp[123a]$#i', $filename)) {
-			// Too many mp3 encoders on the market put gabage in front of mpeg files
+			// Too many mp3 encoders on the market put garbage in front of mpeg files
 			// use assume format on these if format detection failed
 			$GetFileFormatArray = $this->GetFileFormatArray();
 			$info = $GetFileFormatArray['mp3'];
@@ -1427,6 +1438,7 @@ class getID3
 						}
 					}
 					if ($tag_key == 'picture') {
+						// pictures can take up a lot of space, and we don't need multiple copies of them; let there be a single copy in [comments][picture], and not elsewhere
 						unset($this->info[$comment_name]['comments'][$tag_key]);
 					}
 				}
@@ -1440,6 +1452,11 @@ class getID3
 
 				if ($this->option_tags_html) {
 					foreach ($this->info['tags'][$tag_name] as $tag_key => $valuearray) {
+						if ($tag_key == 'picture') {
+							// Do not to try to convert binary picture data to HTML
+							// https://github.com/JamesHeinrich/getID3/issues/178
+							continue;
+						}
 						$this->info['tags_html'][$tag_name][$tag_key] = getid3_lib::recursiveMultiByteCharString2HTML($valuearray, $this->info[$comment_name]['encoding']);
 					}
 				}
@@ -1448,8 +1465,7 @@ class getID3
 
 		}
 
-		// pictures can take up a lot of space, and we don't need multiple copies of them
-		// let there be a single copy in [comments][picture], and not elsewhere
+		// pictures can take up a lot of space, and we don't need multiple copies of them; let there be a single copy in [comments][picture], and not elsewhere
 		if (!empty($this->info['tags'])) {
 			$unset_keys = array('tags', 'tags_html');
 			foreach ($this->info['tags'] as $tagtype => $tagarray) {
@@ -1824,16 +1840,14 @@ class getID3
 	 *
 	 * @return bool
 	 */
-    public static function is_writable ($filename) {
-        $ret = is_writable($filename);
-
-        if (!$ret) {
-            $perms = fileperms($filename);
-            $ret = ($perms & 0x0080) || ($perms & 0x0010) || ($perms & 0x0002);
-        }
-
-        return $ret;
-    }
+	public static function is_writable ($filename) {
+		$ret = is_writable($filename);
+		if (!$ret) {
+			$perms = fileperms($filename);
+			$ret = ($perms & 0x0080) || ($perms & 0x0010) || ($perms & 0x0002);
+		}
+		return $ret;
+	}
 
 }
 
@@ -1976,7 +1990,8 @@ abstract class getid3_handler
 		*/
 		$contents = '';
 		do {
-			if (($this->getid3->memory_limit > 0) && ($bytes > $this->getid3->memory_limit)) {
+			//if (($this->getid3->memory_limit > 0) && ($bytes > $this->getid3->memory_limit)) {
+			if (($this->getid3->memory_limit > 0) && (($bytes / $this->getid3->memory_limit) > 0.99)) { // enable a more-fuzzy match to prevent close misses generating errors like "PHP Fatal error: Allowed memory size of 33554432 bytes exhausted (tried to allocate 33554464 bytes)"
 				throw new getid3_exception('cannot fread('.$bytes.' from '.$this->ftell().') that is more than available PHP memory ('.$this->getid3->memory_limit.')', 10);
 			}
 			$part = fread($this->getid3->fp, $bytes);
diff --git a/lib/getid3/module.archive.tar.php b/lib/getid3/module.archive.tar.php
index 952a626e722ca0581bbd79f2d5d648519b4832d4..7d97db8000ce8fc4207a7fc2707fe37055189399 100644
--- a/lib/getid3/module.archive.tar.php
+++ b/lib/getid3/module.archive.tar.php
@@ -44,13 +44,13 @@ class getid3_tar extends getid3_handler
 			// check the block
 			$checksum = 0;
 			for ($i = 0; $i < 148; $i++) {
-				$checksum += ord($buffer{$i});
+				$checksum += ord($buffer[$i]);
 			}
 			for ($i = 148; $i < 156; $i++) {
 				$checksum += ord(' ');
 			}
 			for ($i = 156; $i < 512; $i++) {
-				$checksum += ord($buffer{$i});
+				$checksum += ord($buffer[$i]);
 			}
 			$attr    = unpack($unpack_header, $buffer);
 			$name    =       (isset($attr['fname']  ) ? trim($attr['fname']  ) : '');
diff --git a/lib/getid3/module.archive.xz.php b/lib/getid3/module.archive.xz.php
new file mode 100644
index 0000000000000000000000000000000000000000..f405e69d81eac4bc4388f9da12a7d5d19b5fb554
--- /dev/null
+++ b/lib/getid3/module.archive.xz.php
@@ -0,0 +1,42 @@
+<?php
+
+/////////////////////////////////////////////////////////////////
+/// getID3() by James Heinrich <info@getid3.org>               //
+//  available at https://github.com/JamesHeinrich/getID3       //
+//            or https://www.getid3.org                        //
+//            or http://getid3.sourceforge.net                 //
+//  see readme.txt for more details                            //
+/////////////////////////////////////////////////////////////////
+//                                                             //
+// module.archive.xz.php                                       //
+// module for analyzing XZ files                               //
+// dependencies: NONE                                          //
+//                                                            ///
+/////////////////////////////////////////////////////////////////
+
+
+class getid3_xz extends getid3_handler
+{
+
+	/**
+	 * @return bool
+	 */
+	public function Analyze() {
+		$info = &$this->getid3->info;
+
+		$this->fseek($info['avdataoffset']);
+		$xzheader = $this->fread(6);
+
+		// https://tukaani.org/xz/xz-file-format-1.0.4.txt
+		$info['xz']['stream_header']['magic'] = substr($xzheader, 0, 6);
+		if ($info['xz']['stream_header']['magic'] != "\xFD".'7zXZ'."\x00") {
+			$this->error('Invalid XZ stream header magic (expecting FD 37 7A 58 5A 00, found '.getid3_lib::PrintHexBytes($info['xz']['stream_header']['magic']).') at offset '.$info['avdataoffset']);
+			return false;
+		}
+		$info['fileformat'] = 'xz';
+		$this->error('XZ parsing not enabled in this version of getID3() ['.$this->getid3->version().']');
+		return false;
+
+	}
+
+}
diff --git a/lib/getid3/module.archive.zip.php b/lib/getid3/module.archive.zip.php
index 7cb8719d2d82d53e4abd82a88fa32cf90282153d..c096eb32ad150d18f140f8ec54c00954eae72ee4 100644
--- a/lib/getid3/module.archive.zip.php
+++ b/lib/getid3/module.archive.zip.php
@@ -96,15 +96,15 @@ class getid3_zip extends getid3_handler
 					    !empty($info['zip']['files']['_rels']['.rels'])      &&
 					    !empty($info['zip']['files']['docProps']['app.xml']) &&
 					    !empty($info['zip']['files']['docProps']['core.xml'])) {
-						   // http://technet.microsoft.com/en-us/library/cc179224.aspx
-						   $info['fileformat'] = 'zip.msoffice';
-						   if (!empty($ThisFileInfo['zip']['files']['ppt'])) {
-						      $info['mime_type'] = 'application/vnd.openxmlformats-officedocument.presentationml.presentation';
-						   } elseif (!empty($ThisFileInfo['zip']['files']['xl'])) {
-						      $info['mime_type'] = 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet';
-						   } elseif (!empty($ThisFileInfo['zip']['files']['word'])) {
-						      $info['mime_type'] = 'application/vnd.openxmlformats-officedocument.wordprocessingml.document';
-						   }
+							// http://technet.microsoft.com/en-us/library/cc179224.aspx
+							$info['fileformat'] = 'zip.msoffice';
+							if (!empty($ThisFileInfo['zip']['files']['ppt'])) {
+								$info['mime_type'] = 'application/vnd.openxmlformats-officedocument.presentationml.presentation';
+							} elseif (!empty($ThisFileInfo['zip']['files']['xl'])) {
+								$info['mime_type'] = 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet';
+							} elseif (!empty($ThisFileInfo['zip']['files']['word'])) {
+								$info['mime_type'] = 'application/vnd.openxmlformats-officedocument.wordprocessingml.document';
+							}
 					}
 
 					return true;
diff --git a/lib/getid3/module.audio-video.asf.php b/lib/getid3/module.audio-video.asf.php
index d42607e0cd07e5bdcef8212eebcd09451df67553..0a2132dd1dc011910626a9cee3131dc3c6848914 100644
--- a/lib/getid3/module.audio-video.asf.php
+++ b/lib/getid3/module.audio-video.asf.php
@@ -797,17 +797,17 @@ class getid3_asf extends getid3_handler
 							case 'wm/tracknumber':
 							case 'tracknumber':
 								// be careful casting to int: casting unicode strings to int gives unexpected results (stops parsing at first non-numeric character)
-								$thisfile_asf_comments['track'] = array($this->TrimTerm($thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['value']));
-								foreach ($thisfile_asf_comments['track'] as $key => $value) {
+								$thisfile_asf_comments['track_number'] = array($this->TrimTerm($thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['value']));
+								foreach ($thisfile_asf_comments['track_number'] as $key => $value) {
 									if (preg_match('/^[0-9\x00]+$/', $value)) {
-										$thisfile_asf_comments['track'][$key] = intval(str_replace("\x00", '', $value));
+										$thisfile_asf_comments['track_number'][$key] = intval(str_replace("\x00", '', $value));
 									}
 								}
 								break;
 
 							case 'wm/track':
-								if (empty($thisfile_asf_comments['track'])) {
-									$thisfile_asf_comments['track'] = array(1 + $this->TrimConvert($thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['value']));
+								if (empty($thisfile_asf_comments['track_number'])) {
+									$thisfile_asf_comments['track_number'] = array(1 + $this->TrimConvert($thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['value']));
 								}
 								break;
 
@@ -1653,26 +1653,26 @@ class getid3_asf extends getid3_handler
 	 * @return string
 	 */
 	public static function BytestringToGUID($Bytestring) {
-		$GUIDstring  = str_pad(dechex(ord($Bytestring{3})),  2, '0', STR_PAD_LEFT);
-		$GUIDstring .= str_pad(dechex(ord($Bytestring{2})),  2, '0', STR_PAD_LEFT);
-		$GUIDstring .= str_pad(dechex(ord($Bytestring{1})),  2, '0', STR_PAD_LEFT);
-		$GUIDstring .= str_pad(dechex(ord($Bytestring{0})),  2, '0', STR_PAD_LEFT);
+		$GUIDstring  = str_pad(dechex(ord($Bytestring[3])),  2, '0', STR_PAD_LEFT);
+		$GUIDstring .= str_pad(dechex(ord($Bytestring[2])),  2, '0', STR_PAD_LEFT);
+		$GUIDstring .= str_pad(dechex(ord($Bytestring[1])),  2, '0', STR_PAD_LEFT);
+		$GUIDstring .= str_pad(dechex(ord($Bytestring[0])),  2, '0', STR_PAD_LEFT);
 		$GUIDstring .= '-';
-		$GUIDstring .= str_pad(dechex(ord($Bytestring{5})),  2, '0', STR_PAD_LEFT);
-		$GUIDstring .= str_pad(dechex(ord($Bytestring{4})),  2, '0', STR_PAD_LEFT);
+		$GUIDstring .= str_pad(dechex(ord($Bytestring[5])),  2, '0', STR_PAD_LEFT);
+		$GUIDstring .= str_pad(dechex(ord($Bytestring[4])),  2, '0', STR_PAD_LEFT);
 		$GUIDstring .= '-';
-		$GUIDstring .= str_pad(dechex(ord($Bytestring{7})),  2, '0', STR_PAD_LEFT);
-		$GUIDstring .= str_pad(dechex(ord($Bytestring{6})),  2, '0', STR_PAD_LEFT);
+		$GUIDstring .= str_pad(dechex(ord($Bytestring[7])),  2, '0', STR_PAD_LEFT);
+		$GUIDstring .= str_pad(dechex(ord($Bytestring[6])),  2, '0', STR_PAD_LEFT);
 		$GUIDstring .= '-';
-		$GUIDstring .= str_pad(dechex(ord($Bytestring{8})),  2, '0', STR_PAD_LEFT);
-		$GUIDstring .= str_pad(dechex(ord($Bytestring{9})),  2, '0', STR_PAD_LEFT);
+		$GUIDstring .= str_pad(dechex(ord($Bytestring[8])),  2, '0', STR_PAD_LEFT);
+		$GUIDstring .= str_pad(dechex(ord($Bytestring[9])),  2, '0', STR_PAD_LEFT);
 		$GUIDstring .= '-';
-		$GUIDstring .= str_pad(dechex(ord($Bytestring{10})), 2, '0', STR_PAD_LEFT);
-		$GUIDstring .= str_pad(dechex(ord($Bytestring{11})), 2, '0', STR_PAD_LEFT);
-		$GUIDstring .= str_pad(dechex(ord($Bytestring{12})), 2, '0', STR_PAD_LEFT);
-		$GUIDstring .= str_pad(dechex(ord($Bytestring{13})), 2, '0', STR_PAD_LEFT);
-		$GUIDstring .= str_pad(dechex(ord($Bytestring{14})), 2, '0', STR_PAD_LEFT);
-		$GUIDstring .= str_pad(dechex(ord($Bytestring{15})), 2, '0', STR_PAD_LEFT);
+		$GUIDstring .= str_pad(dechex(ord($Bytestring[10])), 2, '0', STR_PAD_LEFT);
+		$GUIDstring .= str_pad(dechex(ord($Bytestring[11])), 2, '0', STR_PAD_LEFT);
+		$GUIDstring .= str_pad(dechex(ord($Bytestring[12])), 2, '0', STR_PAD_LEFT);
+		$GUIDstring .= str_pad(dechex(ord($Bytestring[13])), 2, '0', STR_PAD_LEFT);
+		$GUIDstring .= str_pad(dechex(ord($Bytestring[14])), 2, '0', STR_PAD_LEFT);
+		$GUIDstring .= str_pad(dechex(ord($Bytestring[15])), 2, '0', STR_PAD_LEFT);
 
 		return strtoupper($GUIDstring);
 	}
diff --git a/lib/getid3/module.audio-video.matroska.php b/lib/getid3/module.audio-video.matroska.php
index b2b187b6f174fb9dc40fa2380fb50c5e54d547d8..5999066218ec0ee8193428cc3d605b5003efcf48 100644
--- a/lib/getid3/module.audio-video.matroska.php
+++ b/lib/getid3/module.audio-video.matroska.php
@@ -342,7 +342,7 @@ class getid3_matroska extends getid3_handler
 						switch ($trackarray['CodecID']) {
 							case 'A_PCM/INT/LIT':
 							case 'A_PCM/INT/BIG':
-								$track_info['bitrate'] = $trackarray['SamplingFrequency'] * $trackarray['Channels'] * $trackarray['BitDepth'];
+								$track_info['bitrate'] = $track_info['sample_rate'] * $track_info['channels'] * $trackarray['BitDepth'];
 								break;
 
 							case 'A_AC3':
diff --git a/lib/getid3/module.audio-video.mpeg.php b/lib/getid3/module.audio-video.mpeg.php
index f27cd4b3af88cd89d424858b51dd625fac63178d..751a95d862ba0d57411f859020de3c2373e6cbb8 100644
--- a/lib/getid3/module.audio-video.mpeg.php
+++ b/lib/getid3/module.audio-video.mpeg.php
@@ -90,11 +90,9 @@ class getid3_mpeg extends getid3_handler
 					break;
 
 				case 0xB3: // sequence_header_code
-					/*
-					Note: purposely doing the less-pretty (and probably a bit slower) method of using string of bits rather than bitwise operations.
-					      Mostly because PHP 32-bit doesn't handle unsigned integers well for bitwise operation.
-					      Also the MPEG stream is designed as a bitstream and often doesn't align nicely with byte boundaries.
-					*/
+					// Note: purposely doing the less-pretty (and probably a bit slower) method of using string of bits rather than bitwise operations.
+					// Mostly because PHP 32-bit doesn't handle unsigned integers well for bitwise operation.
+					// Also the MPEG stream is designed as a bitstream and often doesn't align nicely with byte boundaries.
 					$info['video']['codec'] = 'MPEG-1'; // will be updated if extension_start_code found
 
 					$bitstream = getid3_lib::BigEndian2Bin(substr($MPEGstreamData, $StartCodeOffset + 4, 8));
@@ -622,7 +620,7 @@ echo 'average_File_bitrate = '.number_format(array_sum($vbr_bitrates) / count($v
 
 	/**
 	 * @param int $rawaspectratio
-     * @param int $mpeg_version
+	 * @param int $mpeg_version
 	 *
 	 * @return string
 	 */
diff --git a/lib/getid3/module.audio-video.quicktime.php b/lib/getid3/module.audio-video.quicktime.php
index 5c791e32a5b5343a38e7bd8ce39dc008383e7b12..cb1173bdbdbb54f8378b68df0880074f2805fe4d 100644
--- a/lib/getid3/module.audio-video.quicktime.php
+++ b/lib/getid3/module.audio-video.quicktime.php
@@ -38,7 +38,7 @@ class getid3_quicktime extends getid3_handler
 
 		$offset      = 0;
 		$atomcounter = 0;
-		$atom_data_read_buffer_size = max($this->getid3->option_fread_buffer_size * 1024, ($info['php_memory_limit'] ? round($info['php_memory_limit'] / 4) : 1024)); // set read buffer to 25% of PHP memory limit (if one is specified), otherwise use option_fread_buffer_size [default: 32MB]
+		$atom_data_read_buffer_size = $info['php_memory_limit'] ? round($info['php_memory_limit'] / 4) : $this->getid3->option_fread_buffer_size * 1024; // set read buffer to 25% of PHP memory limit (if one is specified), otherwise use option_fread_buffer_size [default: 32MB]
 		while ($offset < $info['avdataend']) {
 			if (!getid3_lib::intValueSupported($offset)) {
 				$this->error('Unable to parse atom at offset '.$offset.' because beyond '.round(PHP_INT_MAX / 1073741824).'GB limit of PHP filesystem functions');
@@ -568,8 +568,8 @@ class getid3_quicktime extends getid3_handler
 							}
 						}
 					}
-						$this->CopyToAppropriateCommentsSection($atomname, $atom_structure['data'], $atom_structure['name']);
-						break;
+					$this->CopyToAppropriateCommentsSection($atomname, $atom_structure['data'], $atom_structure['name']);
+					break;
 
 
 				case 'play': // auto-PLAY atom
@@ -2654,7 +2654,7 @@ class getid3_quicktime extends getid3_handler
 			$handyatomtranslatorarray["\xA9".'src'] = 'source_credit';
 			$handyatomtranslatorarray["\xA9".'swr'] = 'software';
 			$handyatomtranslatorarray["\xA9".'too'] = 'encoding_tool';       // iTunes 4.0
-			$handyatomtranslatorarray["\xA9".'trk'] = 'track';
+			$handyatomtranslatorarray["\xA9".'trk'] = 'track_number';
 			$handyatomtranslatorarray["\xA9".'url'] = 'url';
 			$handyatomtranslatorarray["\xA9".'wrn'] = 'warning';
 			$handyatomtranslatorarray["\xA9".'wrt'] = 'composer';
@@ -2752,38 +2752,35 @@ class getid3_quicktime extends getid3_handler
 	 *
 	 * @return string
 	 */
-    public function LociString($lstring, &$count) {
-            // Loci strings are UTF-8 or UTF-16 and null (x00/x0000) terminated. UTF-16 has a BOM
-            // Also need to return the number of bytes the string occupied so additional fields can be extracted
-            $len = strlen($lstring);
-            if ($len == 0) {
-                $count = 0;
-                return '';
-            }
-            if ($lstring[0] == "\x00") {
-                $count = 1;
-                return '';
-            }
-            //check for BOM
-            if ($len > 2 && (($lstring[0] == "\xFE" && $lstring[1] == "\xFF") || ($lstring[0] == "\xFF" && $lstring[1] == "\xFE"))) {
-                //UTF-16
-                if (preg_match('/(.*)\x00/', $lstring, $lmatches)){
-                     $count = strlen($lmatches[1]) * 2 + 2; //account for 2 byte characters and trailing \x0000
-                    return getid3_lib::iconv_fallback_utf16_utf8($lmatches[1]);
-                } else {
-                    return '';
-                }
-            } else {
-                //UTF-8
-                if (preg_match('/(.*)\x00/', $lstring, $lmatches)){
-                    $count = strlen($lmatches[1]) + 1; //account for trailing \x00
-                    return $lmatches[1];
-                }else {
-                    return '';
-                }
-
-            }
-        }
+	public function LociString($lstring, &$count) {
+		// Loci strings are UTF-8 or UTF-16 and null (x00/x0000) terminated. UTF-16 has a BOM
+		// Also need to return the number of bytes the string occupied so additional fields can be extracted
+		$len = strlen($lstring);
+		if ($len == 0) {
+			$count = 0;
+			return '';
+		}
+		if ($lstring[0] == "\x00") {
+			$count = 1;
+			return '';
+		}
+		// check for BOM
+		if (($len > 2) && ((($lstring[0] == "\xFE") && ($lstring[1] == "\xFF")) || (($lstring[0] == "\xFF") && ($lstring[1] == "\xFE")))) {
+			// UTF-16
+			if (preg_match('/(.*)\x00/', $lstring, $lmatches)) {
+				$count = strlen($lmatches[1]) * 2 + 2; //account for 2 byte characters and trailing \x0000
+				return getid3_lib::iconv_fallback_utf16_utf8($lmatches[1]);
+			} else {
+				return '';
+			}
+		}
+		// UTF-8
+		if (preg_match('/(.*)\x00/', $lstring, $lmatches)) {
+			$count = strlen($lmatches[1]) + 1; //account for trailing \x00
+			return $lmatches[1];
+		}
+		return '';
+	}
 
 	/**
 	 * @param string $nullterminatedstring
diff --git a/lib/getid3/module.audio-video.riff.php b/lib/getid3/module.audio-video.riff.php
index 8992ad7c94e08c2e189f4934bd1b5b6fc05a2bf4..276f131eaa02f38fb035d3b9a633a0df8d93913b 100644
--- a/lib/getid3/module.audio-video.riff.php
+++ b/lib/getid3/module.audio-video.riff.php
@@ -51,7 +51,7 @@ class getid3_riff extends getid3_handler
 		$thisfile_audio_dataformat = &$thisfile_audio['dataformat'];
 		$thisfile_riff_audio       = &$thisfile_riff['audio'];
 		$thisfile_riff_video       = &$thisfile_riff['video'];
-        $thisfile_riff_WAVE        = array();
+		$thisfile_riff_WAVE        = array();
 
 		$Original['avdataoffset'] = $info['avdataoffset'];
 		$Original['avdataend']    = $info['avdataend'];
@@ -703,13 +703,13 @@ class getid3_riff extends getid3_handler
 						'capturedfile' => 0x00010000,
 						'copyrighted'  => 0x00020010,
 					);
-                    foreach ($flags as $flag => $value) {
+					foreach ($flags as $flag => $value) {
 						$thisfile_riff_raw_avih['flags'][$flag] = (bool) ($thisfile_riff_raw_avih['dwFlags'] & $value);
 					}
 
 					// shortcut
 					$thisfile_riff_video[$streamindex] = array();
-                    /** @var array $thisfile_riff_video_current */
+					/** @var array $thisfile_riff_video_current */
 					$thisfile_riff_video_current = &$thisfile_riff_video[$streamindex];
 
 					if ($thisfile_riff_raw_avih['dwWidth'] > 0) {
@@ -923,7 +923,7 @@ class getid3_riff extends getid3_handler
 			// http://en.wikipedia.org/wiki/CD-DA
 			case 'CDDA':
 				$info['fileformat'] = 'cda';
-			    unset($info['mime_type']);
+				unset($info['mime_type']);
 
 				$thisfile_audio_dataformat      = 'cda';
 
@@ -943,7 +943,7 @@ class getid3_riff extends getid3_handler
 
 					$thisfile_riff_CDDA_fmt_0['start_offset_seconds'] = (float) $thisfile_riff_CDDA_fmt_0['start_offset_frame'] / 75;
 					$thisfile_riff_CDDA_fmt_0['playtime_seconds']     = (float) $thisfile_riff_CDDA_fmt_0['playtime_frames'] / 75;
-					$info['comments']['track']                = $thisfile_riff_CDDA_fmt_0['track_num'];
+					$info['comments']['track_number']         = $thisfile_riff_CDDA_fmt_0['track_num'];
 					$info['playtime_seconds']                 = $thisfile_riff_CDDA_fmt_0['playtime_seconds'];
 
 					// hardcoded data for CD-audio
@@ -956,7 +956,7 @@ class getid3_riff extends getid3_handler
 				}
 				break;
 
-            // http://en.wikipedia.org/wiki/AIFF
+			// http://en.wikipedia.org/wiki/AIFF
 			case 'AIFF':
 			case 'AIFC':
 				$info['fileformat'] = 'aiff';
@@ -1066,7 +1066,7 @@ class getid3_riff extends getid3_handler
 				if (isset($thisfile_riff[$RIFFsubtype]['ID3 '])) {
 					getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.tag.id3v2.php', __FILE__, true);
 					$getid3_temp = new getID3();
-					$getid3_temp->openfile($this->getid3->filename);
+					$getid3_temp->openfile($this->getid3->filename, null, $this->getid3->fp);
 					$getid3_id3v2 = new getid3_id3v2($getid3_temp);
 					$getid3_id3v2->StartingOffset = $thisfile_riff[$RIFFsubtype]['ID3 '][0]['offset'] + 8;
 					if ($thisfile_riff[$RIFFsubtype]['ID3 '][0]['valid'] = $getid3_id3v2->Analyze()) {
@@ -1169,7 +1169,7 @@ class getid3_riff extends getid3_handler
 					getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio-video.mpeg.php', __FILE__, true);
 
 					$getid3_temp = new getID3();
-					$getid3_temp->openfile($this->getid3->filename);
+					$getid3_temp->openfile($this->getid3->filename, null, $this->getid3->fp);
 					$getid3_mpeg = new getid3_mpeg($getid3_temp);
 					$getid3_mpeg->Analyze();
 					if (empty($getid3_temp->info['error'])) {
@@ -1255,7 +1255,7 @@ class getid3_riff extends getid3_handler
 					getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.tag.id3v2.php', __FILE__, true);
 
 					$getid3_temp = new getID3();
-					$getid3_temp->openfile($this->getid3->filename);
+					$getid3_temp->openfile($this->getid3->filename, null, $this->getid3->fp);
 					$getid3_id3v2 = new getid3_id3v2($getid3_temp);
 					$getid3_id3v2->StartingOffset = $thisfile_riff[$RIFFsubtype]['id3 '][0]['offset'] + 8;
 					if ($thisfile_riff[$RIFFsubtype]['id3 '][0]['valid'] = $getid3_id3v2->Analyze()) {
@@ -1554,7 +1554,7 @@ class getid3_riff extends getid3_handler
 										// MP3
 										if (getid3_mp3::MPEGaudioHeaderBytesValid($FirstFourBytes)) {
 											$getid3_temp = new getID3();
-											$getid3_temp->openfile($this->getid3->filename);
+											$getid3_temp->openfile($this->getid3->filename, null, $this->getid3->fp);
 											$getid3_temp->info['avdataoffset'] = $this->ftell() - 4;
 											$getid3_temp->info['avdataend']    = $this->ftell() + $AudioChunkSize;
 											$getid3_mp3 = new getid3_mp3($getid3_temp, __CLASS__);
@@ -1576,7 +1576,7 @@ class getid3_riff extends getid3_handler
 
 										// AC3
 										$getid3_temp = new getID3();
-										$getid3_temp->openfile($this->getid3->filename);
+										$getid3_temp->openfile($this->getid3->filename, null, $this->getid3->fp);
 										$getid3_temp->info['avdataoffset'] = $this->ftell() - 4;
 										$getid3_temp->info['avdataend']    = $this->ftell() + $AudioChunkSize;
 										$getid3_ac3 = new getid3_ac3($getid3_temp);
@@ -1637,7 +1637,7 @@ class getid3_riff extends getid3_handler
 									// Probably is MP3 data
 									if (getid3_mp3::MPEGaudioHeaderBytesValid(substr($testData, 0, 4))) {
 										$getid3_temp = new getID3();
-										$getid3_temp->openfile($this->getid3->filename);
+										$getid3_temp->openfile($this->getid3->filename, null, $this->getid3->fp);
 										$getid3_temp->info['avdataoffset'] = $info['avdataoffset'];
 										$getid3_temp->info['avdataend']    = $info['avdataend'];
 										$getid3_mp3 = new getid3_mp3($getid3_temp, __CLASS__);
@@ -1654,7 +1654,7 @@ class getid3_riff extends getid3_handler
 									// This is probably AC-3 data
 									$getid3_temp = new getID3();
 									if ($isRegularAC3) {
-										$getid3_temp->openfile($this->getid3->filename);
+										$getid3_temp->openfile($this->getid3->filename, null, $this->getid3->fp);
 										$getid3_temp->info['avdataoffset'] = $info['avdataoffset'];
 										$getid3_temp->info['avdataend']    = $info['avdataend'];
 									}
@@ -1688,7 +1688,7 @@ class getid3_riff extends getid3_handler
 
 									// This is probably DTS data
 									$getid3_temp = new getID3();
-									$getid3_temp->openfile($this->getid3->filename);
+									$getid3_temp->openfile($this->getid3->filename, null, $this->getid3->fp);
 									$getid3_temp->info['avdataoffset'] = $info['avdataoffset'];
 									$getid3_dts = new getid3_dts($getid3_temp);
 									$getid3_dts->Analyze();
@@ -1756,6 +1756,75 @@ class getid3_riff extends getid3_handler
 							//	$info['divxtag']['comments'] = self::ParseDIVXTAG($this->fread($chunksize));
 							//	break;
 
+							case 'scot':
+								// https://cmsdk.com/node-js/adding-scot-chunk-to-wav-file.html
+								$RIFFchunk[$chunkname][$thisindex]['data'] = $this->fread($chunksize);
+								$RIFFchunk[$chunkname][$thisindex]['parsed']['alter']           =                              substr($RIFFchunk[$chunkname][$thisindex]['data'],   0,   1);
+								$RIFFchunk[$chunkname][$thisindex]['parsed']['attrib']          =                              substr($RIFFchunk[$chunkname][$thisindex]['data'],   1,   1);
+								$RIFFchunk[$chunkname][$thisindex]['parsed']['artnum']          = getid3_lib::LittleEndian2Int(substr($RIFFchunk[$chunkname][$thisindex]['data'],   2,   2));
+								$RIFFchunk[$chunkname][$thisindex]['parsed']['title']           =                              substr($RIFFchunk[$chunkname][$thisindex]['data'],   4,  43);  // "name" in other documentation
+								$RIFFchunk[$chunkname][$thisindex]['parsed']['copy']            =                              substr($RIFFchunk[$chunkname][$thisindex]['data'],  47,   4);
+								$RIFFchunk[$chunkname][$thisindex]['parsed']['padd']            =                              substr($RIFFchunk[$chunkname][$thisindex]['data'],  51,   1);
+								$RIFFchunk[$chunkname][$thisindex]['parsed']['asclen']          =                              substr($RIFFchunk[$chunkname][$thisindex]['data'],  52,   5);
+								$RIFFchunk[$chunkname][$thisindex]['parsed']['startseconds']    = getid3_lib::LittleEndian2Int(substr($RIFFchunk[$chunkname][$thisindex]['data'],  57,   2));
+								$RIFFchunk[$chunkname][$thisindex]['parsed']['starthundredths'] = getid3_lib::LittleEndian2Int(substr($RIFFchunk[$chunkname][$thisindex]['data'],  59,   2));
+								$RIFFchunk[$chunkname][$thisindex]['parsed']['endseconds']      = getid3_lib::LittleEndian2Int(substr($RIFFchunk[$chunkname][$thisindex]['data'],  61,   2));
+								$RIFFchunk[$chunkname][$thisindex]['parsed']['endhundreths']    = getid3_lib::LittleEndian2Int(substr($RIFFchunk[$chunkname][$thisindex]['data'],  63,   2));
+								$RIFFchunk[$chunkname][$thisindex]['parsed']['sdate']           =                              substr($RIFFchunk[$chunkname][$thisindex]['data'],  65,   6);
+								$RIFFchunk[$chunkname][$thisindex]['parsed']['kdate']           =                              substr($RIFFchunk[$chunkname][$thisindex]['data'],  71,   6);
+								$RIFFchunk[$chunkname][$thisindex]['parsed']['start_hr']        =                              substr($RIFFchunk[$chunkname][$thisindex]['data'],  77,   1);
+								$RIFFchunk[$chunkname][$thisindex]['parsed']['kill_hr']         =                              substr($RIFFchunk[$chunkname][$thisindex]['data'],  78,   1);
+								$RIFFchunk[$chunkname][$thisindex]['parsed']['digital']         =                              substr($RIFFchunk[$chunkname][$thisindex]['data'],  79,   1);
+								$RIFFchunk[$chunkname][$thisindex]['parsed']['sample_rate']     = getid3_lib::LittleEndian2Int(substr($RIFFchunk[$chunkname][$thisindex]['data'],  80,   2));
+								$RIFFchunk[$chunkname][$thisindex]['parsed']['stereo']          =                              substr($RIFFchunk[$chunkname][$thisindex]['data'],  82,   1);
+								$RIFFchunk[$chunkname][$thisindex]['parsed']['compress']        =                              substr($RIFFchunk[$chunkname][$thisindex]['data'],  83,   1);
+								$RIFFchunk[$chunkname][$thisindex]['parsed']['eomstrt']         = getid3_lib::LittleEndian2Int(substr($RIFFchunk[$chunkname][$thisindex]['data'],  84,   4));
+								$RIFFchunk[$chunkname][$thisindex]['parsed']['eomlen']          = getid3_lib::LittleEndian2Int(substr($RIFFchunk[$chunkname][$thisindex]['data'],  88,   2));
+								$RIFFchunk[$chunkname][$thisindex]['parsed']['attrib2']         = getid3_lib::LittleEndian2Int(substr($RIFFchunk[$chunkname][$thisindex]['data'],  90,   4));
+								$RIFFchunk[$chunkname][$thisindex]['parsed']['future1']         =                              substr($RIFFchunk[$chunkname][$thisindex]['data'],  94,  12);
+								$RIFFchunk[$chunkname][$thisindex]['parsed']['catfontcolor']    = getid3_lib::LittleEndian2Int(substr($RIFFchunk[$chunkname][$thisindex]['data'], 106,   4));
+								$RIFFchunk[$chunkname][$thisindex]['parsed']['catcolor']        = getid3_lib::LittleEndian2Int(substr($RIFFchunk[$chunkname][$thisindex]['data'], 110,   4));
+								$RIFFchunk[$chunkname][$thisindex]['parsed']['segeompos']       = getid3_lib::LittleEndian2Int(substr($RIFFchunk[$chunkname][$thisindex]['data'], 114,   4));
+								$RIFFchunk[$chunkname][$thisindex]['parsed']['vt_startsecs']    = getid3_lib::LittleEndian2Int(substr($RIFFchunk[$chunkname][$thisindex]['data'], 118,   2));
+								$RIFFchunk[$chunkname][$thisindex]['parsed']['vt_starthunds']   = getid3_lib::LittleEndian2Int(substr($RIFFchunk[$chunkname][$thisindex]['data'], 120,   2));
+								$RIFFchunk[$chunkname][$thisindex]['parsed']['priorcat']        =                              substr($RIFFchunk[$chunkname][$thisindex]['data'], 122,   3);
+								$RIFFchunk[$chunkname][$thisindex]['parsed']['priorcopy']       =                              substr($RIFFchunk[$chunkname][$thisindex]['data'], 125,   4);
+								$RIFFchunk[$chunkname][$thisindex]['parsed']['priorpadd']       =                              substr($RIFFchunk[$chunkname][$thisindex]['data'], 129,   1);
+								$RIFFchunk[$chunkname][$thisindex]['parsed']['postcat']         =                              substr($RIFFchunk[$chunkname][$thisindex]['data'], 130,   3);
+								$RIFFchunk[$chunkname][$thisindex]['parsed']['postcopy']        =                              substr($RIFFchunk[$chunkname][$thisindex]['data'], 133,   4);
+								$RIFFchunk[$chunkname][$thisindex]['parsed']['postpadd']        =                              substr($RIFFchunk[$chunkname][$thisindex]['data'], 137,   1);
+								$RIFFchunk[$chunkname][$thisindex]['parsed']['hrcanplay']       =                              substr($RIFFchunk[$chunkname][$thisindex]['data'], 138,  21);
+								$RIFFchunk[$chunkname][$thisindex]['parsed']['future2']         =                              substr($RIFFchunk[$chunkname][$thisindex]['data'], 159, 108);
+								$RIFFchunk[$chunkname][$thisindex]['parsed']['artist']          =                              substr($RIFFchunk[$chunkname][$thisindex]['data'], 267,  34);
+								$RIFFchunk[$chunkname][$thisindex]['parsed']['comment']         =                              substr($RIFFchunk[$chunkname][$thisindex]['data'], 301,  34); // "trivia" in other documentation
+								$RIFFchunk[$chunkname][$thisindex]['parsed']['intro']           =                              substr($RIFFchunk[$chunkname][$thisindex]['data'], 335,   2);
+								$RIFFchunk[$chunkname][$thisindex]['parsed']['end']             =                              substr($RIFFchunk[$chunkname][$thisindex]['data'], 337,   1);
+								$RIFFchunk[$chunkname][$thisindex]['parsed']['year']            =                              substr($RIFFchunk[$chunkname][$thisindex]['data'], 338,   4);
+								$RIFFchunk[$chunkname][$thisindex]['parsed']['obsolete2']       =                              substr($RIFFchunk[$chunkname][$thisindex]['data'], 342,   1);
+								$RIFFchunk[$chunkname][$thisindex]['parsed']['rec_hr']          =                              substr($RIFFchunk[$chunkname][$thisindex]['data'], 343,   1);
+								$RIFFchunk[$chunkname][$thisindex]['parsed']['rdate']           =                              substr($RIFFchunk[$chunkname][$thisindex]['data'], 344,   6);
+								$RIFFchunk[$chunkname][$thisindex]['parsed']['mpeg_bitrate']    = getid3_lib::LittleEndian2Int(substr($RIFFchunk[$chunkname][$thisindex]['data'], 350,   2));
+								$RIFFchunk[$chunkname][$thisindex]['parsed']['pitch']           = getid3_lib::LittleEndian2Int(substr($RIFFchunk[$chunkname][$thisindex]['data'], 352,   2));
+								$RIFFchunk[$chunkname][$thisindex]['parsed']['playlevel']       = getid3_lib::LittleEndian2Int(substr($RIFFchunk[$chunkname][$thisindex]['data'], 354,   2));
+								$RIFFchunk[$chunkname][$thisindex]['parsed']['lenvalid']        =                              substr($RIFFchunk[$chunkname][$thisindex]['data'], 356,   1);
+								$RIFFchunk[$chunkname][$thisindex]['parsed']['filelength']      = getid3_lib::LittleEndian2Int(substr($RIFFchunk[$chunkname][$thisindex]['data'], 357,   4));
+								$RIFFchunk[$chunkname][$thisindex]['parsed']['newplaylevel']    = getid3_lib::LittleEndian2Int(substr($RIFFchunk[$chunkname][$thisindex]['data'], 361,   2));
+								$RIFFchunk[$chunkname][$thisindex]['parsed']['chopsize']        = getid3_lib::LittleEndian2Int(substr($RIFFchunk[$chunkname][$thisindex]['data'], 363,   4));
+								$RIFFchunk[$chunkname][$thisindex]['parsed']['vteomovr']        = getid3_lib::LittleEndian2Int(substr($RIFFchunk[$chunkname][$thisindex]['data'], 367,   4));
+								$RIFFchunk[$chunkname][$thisindex]['parsed']['desiredlen']      = getid3_lib::LittleEndian2Int(substr($RIFFchunk[$chunkname][$thisindex]['data'], 371,   4));
+								$RIFFchunk[$chunkname][$thisindex]['parsed']['triggers']        = getid3_lib::LittleEndian2Int(substr($RIFFchunk[$chunkname][$thisindex]['data'], 375,   4));
+								$RIFFchunk[$chunkname][$thisindex]['parsed']['fillout']         =                              substr($RIFFchunk[$chunkname][$thisindex]['data'], 379,   33);
+
+								foreach (array('title', 'artist', 'comment') as $key) {
+									if (trim($RIFFchunk[$chunkname][$thisindex]['parsed'][$key])) {
+										$info['riff']['comments'][$key] = array($RIFFchunk[$chunkname][$thisindex]['parsed'][$key]);
+									}
+								}
+								if ($RIFFchunk[$chunkname][$thisindex]['parsed']['filelength'] && !empty($info['filesize']) && ($RIFFchunk[$chunkname][$thisindex]['parsed']['filelength'] != $info['filesize'])) {
+									$this->warning('RIFF.WAVE.scot.filelength ('.$RIFFchunk[$chunkname][$thisindex]['parsed']['filelength'].') different from actual filesize ('.$info['filesize'].')');
+								}
+								break;
+
 							default:
 								if (!empty($LISTchunkParent) && isset($LISTchunkMaxOffset) && (($RIFFchunk[$chunkname][$thisindex]['offset'] + $RIFFchunk[$chunkname][$thisindex]['size']) <= $LISTchunkMaxOffset)) {
 									$RIFFchunk[$LISTchunkParent][$chunkname][$thisindex]['offset'] = $RIFFchunk[$chunkname][$thisindex]['offset'];
diff --git a/lib/getid3/module.audio.ac3.php b/lib/getid3/module.audio.ac3.php
index d4e4e4ba2252754a919687a1cdfa23e9b298d4a5..364681b632cf0acc67c4ab967a44b2e0501a3f50 100644
--- a/lib/getid3/module.audio.ac3.php
+++ b/lib/getid3/module.audio.ac3.php
@@ -20,14 +20,14 @@ class getid3_ac3 extends getid3_handler
 	/**
 	 * @var array
 	 */
-    private $AC3header = array();
+	private $AC3header = array();
 
 	/**
 	 * @var int
 	 */
-    private $BSIoffset = 0;
+	private $BSIoffset = 0;
 
-    const syncword = 0x0B77;
+	const syncword = 0x0B77;
 
 	/**
 	 * @return bool
@@ -422,7 +422,7 @@ class getid3_ac3 extends getid3_handler
 		} else {
 
 			$this->error('Bit stream identification is version '.$thisfile_ac3_raw_bsi['bsid'].', but getID3() only understands up to version 16. Please submit a support ticket with a sample file.');
-		    unset($info['ac3']);
+			unset($info['ac3']);
 			return false;
 
 		}
@@ -687,7 +687,7 @@ class getid3_ac3 extends getid3_handler
 		// -8    -42.14 dB
 
 		$fourbit = str_pad(decbin(($compre & 0xF0) >> 4), 4, '0', STR_PAD_LEFT);
-		if ($fourbit{0} == '1') {
+		if ($fourbit[0] == '1') {
 			$log_gain = -8 + bindec(substr($fourbit, 1));
 		} else {
 			$log_gain = bindec(substr($fourbit, 1));
@@ -758,11 +758,13 @@ class getid3_ac3 extends getid3_handler
 				18 => array(2560, 2786, 3840)   // 640 kbps
 			);
 		}
+		$paddingBytes = 0;
 		if (($fscod == 1) && $padding) {
 			// frame lengths are padded by 1 word (16 bits) at 44100
-			$frameSizeLookup[$frmsizecod] += 2;
+			// (fscode==1) means 44100Hz (see sampleRateCodeLookup)
+			$paddingBytes = 2;
 		}
-		return (isset($frameSizeLookup[$framesizeid][$fscod]) ? $frameSizeLookup[$framesizeid][$fscod] : false);
+		return (isset($frameSizeLookup[$framesizeid][$fscod]) ? $frameSizeLookup[$framesizeid][$fscod] + $paddingBytes : false);
 	}
 
 	/**
diff --git a/lib/getid3/module.audio.bonk.php b/lib/getid3/module.audio.bonk.php
index 53983914ff9a9206bc963464a7aedf88f4331980..c9ee5ea1f2056bed4e5f53cc484700dfc5edc3fd 100644
--- a/lib/getid3/module.audio.bonk.php
+++ b/lib/getid3/module.audio.bonk.php
@@ -44,7 +44,7 @@ class getid3_bonk extends getid3_handler
 				$this->fseek(0 - $BonkTagSize, SEEK_CUR);
 				$BonkTagOffset = $this->ftell();
 				$TagHeaderTest = $this->fread(5);
-				if (($TagHeaderTest{0} != "\x00") || (substr($PossibleBonkTag, 4, 4) != strtolower(substr($PossibleBonkTag, 4, 4)))) {
+				if (($TagHeaderTest[0] != "\x00") || (substr($PossibleBonkTag, 4, 4) != strtolower(substr($PossibleBonkTag, 4, 4)))) {
 					$this->error('Expecting "'.getid3_lib::PrintHexBytes("\x00".strtoupper(substr($PossibleBonkTag, 4, 4))).'" at offset '.$BonkTagOffset.', found "'.getid3_lib::PrintHexBytes($TagHeaderTest).'"');
 					return false;
 				}
diff --git a/lib/getid3/module.audio.dss.php b/lib/getid3/module.audio.dss.php
index 492393a8f77e620cb2d6577befb8737e1fef37e9..b8a0115e28db6414e474b4305ba7c27917c54c5a 100644
--- a/lib/getid3/module.audio.dss.php
+++ b/lib/getid3/module.audio.dss.php
@@ -26,8 +26,8 @@ class getid3_dss extends getid3_handler
 		$this->fseek($info['avdataoffset']);
 		$DSSheader  = $this->fread(1540);
 
-		if (!preg_match('#^[\\x02-\\x06]ds[s2]#', $DSSheader)) {
-			$this->error('Expecting "[02-06] 64 73 [73|32]" at offset '.$info['avdataoffset'].', found "'.getid3_lib::PrintHexBytes(substr($DSSheader, 0, 4)).'"');
+		if (!preg_match('#^[\\x02-\\x08]ds[s2]#', $DSSheader)) {
+			$this->error('Expecting "[02-08] 64 73 [73|32]" at offset '.$info['avdataoffset'].', found "'.getid3_lib::PrintHexBytes(substr($DSSheader, 0, 4)).'"');
 			return false;
 		}
 
diff --git a/lib/getid3/module.audio.dts.php b/lib/getid3/module.audio.dts.php
index 2dd44e9eec55589d0d3aaca46dc2014e54206fbb..9be8f1ba36fb8f7b16046a748cbac778bb5c7577 100644
--- a/lib/getid3/module.audio.dts.php
+++ b/lib/getid3/module.audio.dts.php
@@ -51,18 +51,18 @@ class getid3_dts extends getid3_handler
 
 		// check syncword
 		$sync = substr($DTSheader, 0, 4);
-        if (($encoding = array_search($sync, self::$syncwords)) !== false) {
+		if (($encoding = array_search($sync, self::$syncwords)) !== false) {
 
-        	$info['dts']['raw']['magic'] = $sync;
+			$info['dts']['raw']['magic'] = $sync;
 			$this->readBinDataOffset = 32;
 
-        } elseif ($this->isDependencyFor('matroska')) {
+		} elseif ($this->isDependencyFor('matroska')) {
 
 			// Matroska contains DTS without syncword encoded as raw big-endian format
 			$encoding = 0;
 			$this->readBinDataOffset = 0;
 
-        } else {
+		} else {
 
 			unset($info['fileformat']);
 			return $this->error('Expecting "'.implode('| ', array_map('getid3_lib::PrintHexBytes', self::$syncwords)).'" at offset '.$info['avdataoffset'].', found "'.getid3_lib::PrintHexBytes($sync).'"');
diff --git a/lib/getid3/module.audio.flac.php b/lib/getid3/module.audio.flac.php
index 4edf587df0a2fc52023b708a5eb603e425df25be..a37bae9b8f31886d4159627b587c390be185a234 100644
--- a/lib/getid3/module.audio.flac.php
+++ b/lib/getid3/module.audio.flac.php
@@ -178,7 +178,7 @@ class getid3_flac extends getid3_handler
 		if (isset($info['flac']['STREAMINFO']['audio_signature'])) {
 
 			if ($info['flac']['STREAMINFO']['audio_signature'] === str_repeat("\x00", 16)) {
-                $this->warning('FLAC STREAMINFO.audio_signature is null (known issue with libOggFLAC)');
+				$this->warning('FLAC STREAMINFO.audio_signature is null (known issue with libOggFLAC)');
 			}
 			else {
 				$info['md5_data_source'] = '';
diff --git a/lib/getid3/module.audio.midi.php b/lib/getid3/module.audio.midi.php
index 770481c64043d37c62193471480c3416796b3ab7..8f519316effad1116722f00df786e91a98ec4831 100644
--- a/lib/getid3/module.audio.midi.php
+++ b/lib/getid3/module.audio.midi.php
@@ -243,9 +243,9 @@ class getid3_midi extends getid3_handler
 								break;
 
 							case 0x58: // Time signature
-								$timesig_numerator   = getid3_lib::BigEndian2Int($METAeventData{0});
-								$timesig_denominator = pow(2, getid3_lib::BigEndian2Int($METAeventData{1})); // $02 -> x/4, $03 -> x/8, etc
-								$timesig_32inqnote   = getid3_lib::BigEndian2Int($METAeventData{2});         // number of 32nd notes to the quarter note
+								$timesig_numerator   = getid3_lib::BigEndian2Int($METAeventData[0]);
+								$timesig_denominator = pow(2, getid3_lib::BigEndian2Int($METAeventData[1])); // $02 -> x/4, $03 -> x/8, etc
+								$timesig_32inqnote   = getid3_lib::BigEndian2Int($METAeventData[2]);         // number of 32nd notes to the quarter note
 								//$thisfile_midi_raw['events'][$tracknumber][$eventid]['timesig_32inqnote']   = $timesig_32inqnote;
 								//$thisfile_midi_raw['events'][$tracknumber][$eventid]['timesig_numerator']   = $timesig_numerator;
 								//$thisfile_midi_raw['events'][$tracknumber][$eventid]['timesig_denominator'] = $timesig_denominator;
@@ -254,13 +254,13 @@ class getid3_midi extends getid3_handler
 								break;
 
 							case 0x59: // Keysignature
-								$keysig_sharpsflats = getid3_lib::BigEndian2Int($METAeventData{0});
+								$keysig_sharpsflats = getid3_lib::BigEndian2Int($METAeventData[0]);
 								if ($keysig_sharpsflats & 0x80) {
 									// (-7 -> 7 flats, 0 ->key of C, 7 -> 7 sharps)
 									$keysig_sharpsflats -= 256;
 								}
 
-								$keysig_majorminor  = getid3_lib::BigEndian2Int($METAeventData{1}); // 0 -> major, 1 -> minor
+								$keysig_majorminor  = getid3_lib::BigEndian2Int($METAeventData[1]); // 0 -> major, 1 -> minor
 								$keysigs = array(-7=>'Cb', -6=>'Gb', -5=>'Db', -4=>'Ab', -3=>'Eb', -2=>'Bb', -1=>'F', 0=>'C', 1=>'G', 2=>'D', 3=>'A', 4=>'E', 5=>'B', 6=>'F#', 7=>'C#');
 								//$thisfile_midi_raw['events'][$tracknumber][$eventid]['keysig_sharps'] = (($keysig_sharpsflats > 0) ? abs($keysig_sharpsflats) : 0);
 								//$thisfile_midi_raw['events'][$tracknumber][$eventid]['keysig_flats']  = (($keysig_sharpsflats < 0) ? abs($keysig_sharpsflats) : 0);
diff --git a/lib/getid3/module.audio.monkey.php b/lib/getid3/module.audio.monkey.php
index 3317a93efa83c015386e7ac8411ee58e968a5001..e5d1b0e7132c3302b747813061a46b1de6ab4312 100644
--- a/lib/getid3/module.audio.monkey.php
+++ b/lib/getid3/module.audio.monkey.php
@@ -162,7 +162,7 @@ class getid3_monkey extends getid3_handler
 				$info['md5_data_source'] = '';
 				$md5 = $thisfile_monkeysaudio_raw['cFileMD5'];
 				for ($i = 0; $i < strlen($md5); $i++) {
-					$info['md5_data_source'] .= str_pad(dechex(ord($md5{$i})), 2, '00', STR_PAD_LEFT);
+					$info['md5_data_source'] .= str_pad(dechex(ord($md5[$i])), 2, '00', STR_PAD_LEFT);
 				}
 				if (!preg_match('/^[0-9a-f]{32}$/', $info['md5_data_source'])) {
 					unset($info['md5_data_source']);
diff --git a/lib/getid3/module.audio.mp3.php b/lib/getid3/module.audio.mp3.php
index 4434380455cfc7f4274f5ced2a993c94fa0fc4e9..4bd75a02192b57a03bf52b5e36d4e9c6b4d9ae69 100644
--- a/lib/getid3/module.audio.mp3.php
+++ b/lib/getid3/module.audio.mp3.php
@@ -701,7 +701,7 @@ class getid3_mp3 extends getid3_handler
 				if ($thisfile_mpeg_audio['xing_flags']['toc']) {
 					$LAMEtocData = substr($headerstring, $VBRidOffset + 16, 100);
 					for ($i = 0; $i < 100; $i++) {
-						$thisfile_mpeg_audio['toc'][$i] = ord($LAMEtocData{$i});
+						$thisfile_mpeg_audio['toc'][$i] = ord($LAMEtocData[$i]);
 					}
 				}
 				if ($thisfile_mpeg_audio['xing_flags']['vbr_scale']) {
@@ -1173,9 +1173,9 @@ class getid3_mp3 extends getid3_handler
 
 		$SyncPattern1 = substr($MPEGaudioData, 0, 4);
 		// may be different pattern due to padding
-		$SyncPattern2 = $SyncPattern1{0}.$SyncPattern1{1}.chr(ord($SyncPattern1{2}) | 0x02).$SyncPattern1{3};
+		$SyncPattern2 = $SyncPattern1[0].$SyncPattern1[1].chr(ord($SyncPattern1[2]) | 0x02).$SyncPattern1[3];
 		if ($SyncPattern2 === $SyncPattern1) {
-			$SyncPattern2 = $SyncPattern1{0}.$SyncPattern1{1}.chr(ord($SyncPattern1{2}) & 0xFD).$SyncPattern1{3};
+			$SyncPattern2 = $SyncPattern1[0].$SyncPattern1[1].chr(ord($SyncPattern1[2]) & 0xFD).$SyncPattern1[3];
 		}
 
 		$framelength = false;
@@ -1280,9 +1280,9 @@ class getid3_mp3 extends getid3_handler
 			if (strlen($head4) < 4) {
 				break;
 			}
-			if ($head4{0} != "\xFF") {
+			if ($head4[0] != "\xFF") {
 				for ($i = 1; $i < 4; $i++) {
-					if ($head4{$i} == "\xFF") {
+					if ($head4[$i] == "\xFF") {
 						$this->fseek($i - 4, SEEK_CUR);
 						continue 2;
 					}
@@ -1314,7 +1314,7 @@ class getid3_mp3 extends getid3_handler
 					$WhereWeWere = $this->ftell();
 					$this->fseek($MPEGaudioHeaderLengthCache[$head4] - 4, SEEK_CUR);
 					$next4 = $this->fread(4);
-					if ($next4{0} == "\xFF") {
+					if ($next4[0] == "\xFF") {
 						if (!isset($MPEGaudioHeaderDecodeCache[$next4])) {
 							$MPEGaudioHeaderDecodeCache[$next4] = self::MPEGaudioHeaderDecode($next4);
 						}
@@ -1407,10 +1407,9 @@ class getid3_mp3 extends getid3_handler
 		static $MPEGaudioLayerLookup;
 		static $MPEGaudioBitrateLookup;
 		if (empty($MPEGaudioVersionLookup)) {
-		   $MPEGaudioVersionLookup = self::MPEGaudioVersionArray();
-		   $MPEGaudioLayerLookup   = self::MPEGaudioLayerArray();
-		   $MPEGaudioBitrateLookup = self::MPEGaudioBitrateArray();
-
+			$MPEGaudioVersionLookup = self::MPEGaudioVersionArray();
+			$MPEGaudioLayerLookup   = self::MPEGaudioLayerArray();
+			$MPEGaudioBitrateLookup = self::MPEGaudioBitrateArray();
 		}
 
 		$this->fseek($avdataoffset);
@@ -1460,7 +1459,7 @@ class getid3_mp3 extends getid3_handler
 				return false;
 			}
 
-			if (($header{$SynchSeekOffset} == "\xFF") && ($header{($SynchSeekOffset + 1)} > "\xE0")) { // synch detected
+			if (($header[$SynchSeekOffset] == "\xFF") && ($header[($SynchSeekOffset + 1)] > "\xE0")) { // synch detected
 				$FirstFrameAVDataOffset = null;
 				if (!isset($FirstFrameThisfileInfo) && !isset($info['mpeg']['audio'])) {
 					$FirstFrameThisfileInfo = $info;
@@ -1555,7 +1554,7 @@ class getid3_mp3 extends getid3_handler
 								$this->fseek($scan_start_offset[$current_segment]);
 								$buffer_4k = $this->fread(4096);
 								for ($j = 0; $j < (strlen($buffer_4k) - 4); $j++) {
-									if (($buffer_4k{$j} == "\xFF") && ($buffer_4k{($j + 1)} > "\xE0")) { // synch detected
+									if (($buffer_4k[$j] == "\xFF") && ($buffer_4k[($j + 1)] > "\xE0")) { // synch detected
 										if ($this->decodeMPEGaudioHeader($scan_start_offset[$current_segment] + $j, $dummy, false, false, $FastMode)) {
 											$calculated_next_offset = $scan_start_offset[$current_segment] + $j + $dummy['mpeg']['audio']['framelength'];
 											if ($this->decodeMPEGaudioHeader($calculated_next_offset, $dummy, false, false, $FastMode)) {
@@ -1877,18 +1876,18 @@ class getid3_mp3 extends getid3_handler
 		}
 
 		$MPEGrawHeader['synch']         = (getid3_lib::BigEndian2Int(substr($Header4Bytes, 0, 2)) & 0xFFE0) >> 4;
-		$MPEGrawHeader['version']       = (ord($Header4Bytes{1}) & 0x18) >> 3; //    BB
-		$MPEGrawHeader['layer']         = (ord($Header4Bytes{1}) & 0x06) >> 1; //      CC
-		$MPEGrawHeader['protection']    = (ord($Header4Bytes{1}) & 0x01);      //        D
-		$MPEGrawHeader['bitrate']       = (ord($Header4Bytes{2}) & 0xF0) >> 4; // EEEE
-		$MPEGrawHeader['sample_rate']   = (ord($Header4Bytes{2}) & 0x0C) >> 2; //     FF
-		$MPEGrawHeader['padding']       = (ord($Header4Bytes{2}) & 0x02) >> 1; //       G
-		$MPEGrawHeader['private']       = (ord($Header4Bytes{2}) & 0x01);      //        H
-		$MPEGrawHeader['channelmode']   = (ord($Header4Bytes{3}) & 0xC0) >> 6; // II
-		$MPEGrawHeader['modeextension'] = (ord($Header4Bytes{3}) & 0x30) >> 4; //   JJ
-		$MPEGrawHeader['copyright']     = (ord($Header4Bytes{3}) & 0x08) >> 3; //     K
-		$MPEGrawHeader['original']      = (ord($Header4Bytes{3}) & 0x04) >> 2; //      L
-		$MPEGrawHeader['emphasis']      = (ord($Header4Bytes{3}) & 0x03);      //       MM
+		$MPEGrawHeader['version']       = (ord($Header4Bytes[1]) & 0x18) >> 3; //    BB
+		$MPEGrawHeader['layer']         = (ord($Header4Bytes[1]) & 0x06) >> 1; //      CC
+		$MPEGrawHeader['protection']    = (ord($Header4Bytes[1]) & 0x01);      //        D
+		$MPEGrawHeader['bitrate']       = (ord($Header4Bytes[2]) & 0xF0) >> 4; // EEEE
+		$MPEGrawHeader['sample_rate']   = (ord($Header4Bytes[2]) & 0x0C) >> 2; //     FF
+		$MPEGrawHeader['padding']       = (ord($Header4Bytes[2]) & 0x02) >> 1; //       G
+		$MPEGrawHeader['private']       = (ord($Header4Bytes[2]) & 0x01);      //        H
+		$MPEGrawHeader['channelmode']   = (ord($Header4Bytes[3]) & 0xC0) >> 6; // II
+		$MPEGrawHeader['modeextension'] = (ord($Header4Bytes[3]) & 0x30) >> 4; //   JJ
+		$MPEGrawHeader['copyright']     = (ord($Header4Bytes[3]) & 0x08) >> 3; //     K
+		$MPEGrawHeader['original']      = (ord($Header4Bytes[3]) & 0x04) >> 2; //      L
+		$MPEGrawHeader['emphasis']      = (ord($Header4Bytes[3]) & 0x03);      //       MM
 
 		return $MPEGrawHeader;
 	}
@@ -1997,7 +1996,7 @@ class getid3_mp3 extends getid3_handler
 	public static function XingVBRidOffset($version, $channelmode) {
 		static $XingVBRidOffsetCache = array();
 		if (empty($XingVBRidOffsetCache)) {
-            $XingVBRidOffsetCache = array (
+			$XingVBRidOffsetCache = array (
 				'1'   => array ('mono'          => 0x15, // 4 + 17 = 21
 								'stereo'        => 0x24, // 4 + 32 = 36
 								'joint stereo'  => 0x24,
diff --git a/lib/getid3/module.audio.tta.php b/lib/getid3/module.audio.tta.php
index 13d8fe2c1facbdb76b9f7a9493360c69691f1826..86fe3bfd62da2c011fdc7502623d1bb5ed42b4f1 100644
--- a/lib/getid3/module.audio.tta.php
+++ b/lib/getid3/module.audio.tta.php
@@ -41,7 +41,7 @@ class getid3_tta extends getid3_handler
 			return false;
 		}
 
-		switch ($ttaheader{3}) {
+		switch ($ttaheader[3]) {
 			case "\x01": // TTA v1.x
 			case "\x02": // TTA v1.x
 			case "\x03": // TTA v1.x
@@ -49,7 +49,7 @@ class getid3_tta extends getid3_handler
 				$info['tta']['major_version'] = 1;
 				$info['avdataoffset'] += 16;
 
-				$info['tta']['compression_level']   = ord($ttaheader{3});
+				$info['tta']['compression_level']   = ord($ttaheader[3]);
 				$info['tta']['channels']            = getid3_lib::LittleEndian2Int(substr($ttaheader,  4,  2));
 				$info['tta']['bits_per_sample']     = getid3_lib::LittleEndian2Int(substr($ttaheader,  6,  2));
 				$info['tta']['sample_rate']         = getid3_lib::LittleEndian2Int(substr($ttaheader,  8,  4));
@@ -92,7 +92,7 @@ class getid3_tta extends getid3_handler
 				break;
 
 			default:
-				$this->error('This version of getID3() ['.$this->getid3->version().'] only knows how to handle TTA v1 and v2 - it may not work correctly with this file which appears to be TTA v'.$ttaheader{3});
+				$this->error('This version of getID3() ['.$this->getid3->version().'] only knows how to handle TTA v1 and v2 - it may not work correctly with this file which appears to be TTA v'.$ttaheader[3]);
 				return false;
 				break;
 		}
diff --git a/lib/getid3/module.audio.voc.php b/lib/getid3/module.audio.voc.php
index 448315811bff44f4312c6dad91a30e4ad7a2ad15..9038c060020132e21c90eb586e0dfb56f634c2a1 100644
--- a/lib/getid3/module.audio.voc.php
+++ b/lib/getid3/module.audio.voc.php
@@ -61,7 +61,7 @@ class getid3_voc extends getid3_handler
 
 			$BlockOffset    = $this->ftell();
 			$BlockData      = $this->fread(4);
-			$BlockType      = ord($BlockData{0});
+			$BlockType      = ord($BlockData[0]);
 			$BlockSize      = getid3_lib::LittleEndian2Int(substr($BlockData, 1, 3));
 			$ThisBlock      = array();
 
diff --git a/lib/getid3/module.audio.wavpack.php b/lib/getid3/module.audio.wavpack.php
index a6e57fd93fc3df01ab57f637d173eefda9d5dcb5..0e88f9f387365e843e956ce25ce452e97e9a01a1 100644
--- a/lib/getid3/module.audio.wavpack.php
+++ b/lib/getid3/module.audio.wavpack.php
@@ -102,8 +102,8 @@ class getid3_wavpack extends getid3_handler
 					return false;
 				}
 
-				$info['wavpack']['blockheader']['minor_version'] = ord($wavpackheader{8});
-				$info['wavpack']['blockheader']['major_version'] = ord($wavpackheader{9});
+				$info['wavpack']['blockheader']['minor_version'] = ord($wavpackheader[8]);
+				$info['wavpack']['blockheader']['major_version'] = ord($wavpackheader[9]);
 
 				if (($info['wavpack']['blockheader']['major_version'] != 4) ||
 					(($info['wavpack']['blockheader']['minor_version'] < 4) &&
@@ -122,8 +122,8 @@ class getid3_wavpack extends getid3_handler
 						return false;
 				}
 
-				$info['wavpack']['blockheader']['track_number']  = ord($wavpackheader{10}); // unused
-				$info['wavpack']['blockheader']['index_number']  = ord($wavpackheader{11}); // unused
+				$info['wavpack']['blockheader']['track_number']  = ord($wavpackheader[10]); // unused
+				$info['wavpack']['blockheader']['index_number']  = ord($wavpackheader[11]); // unused
 				$info['wavpack']['blockheader']['total_samples'] = getid3_lib::LittleEndian2Int(substr($wavpackheader, 12,  4));
 				$info['wavpack']['blockheader']['block_index']   = getid3_lib::LittleEndian2Int(substr($wavpackheader, 16,  4));
 				$info['wavpack']['blockheader']['block_samples'] = getid3_lib::LittleEndian2Int(substr($wavpackheader, 20,  4));
@@ -153,7 +153,7 @@ class getid3_wavpack extends getid3_handler
 				if (feof($this->getid3->fp)) {
 					break;
 				}
-				$metablock['id'] = ord($metablockheader{0});
+				$metablock['id'] = ord($metablockheader[0]);
 				$metablock['function_id'] = ($metablock['id'] & 0x3F);
 				$metablock['function_name'] = $this->WavPackMetablockNameLookup($metablock['function_id']);
 
diff --git a/lib/getid3/module.graphic.bmp.php b/lib/getid3/module.graphic.bmp.php
index dc7b30d727e4b434952cfe85388f0dc1f0db5d33..37abd207bf6cb804ca81aed5870e5aacb0137eb6 100644
--- a/lib/getid3/module.graphic.bmp.php
+++ b/lib/getid3/module.graphic.bmp.php
@@ -334,7 +334,7 @@ class getid3_bmp extends getid3_handler
 						case 1:
 							for ($row = ($thisfile_bmp_header_raw['height'] - 1); $row >= 0; $row--) {
 								for ($col = 0; $col < $thisfile_bmp_header_raw['width']; $col = $col) {
-									$paletteindexbyte = ord($BMPpixelData{$pixeldataoffset++});
+									$paletteindexbyte = ord($BMPpixelData[$pixeldataoffset++]);
 									for ($i = 7; $i >= 0; $i--) {
 										$paletteindex = ($paletteindexbyte & (0x01 << $i)) >> $i;
 										$thisfile_bmp['data'][$row][$col] = $thisfile_bmp['palette'][$paletteindex];
@@ -351,7 +351,7 @@ class getid3_bmp extends getid3_handler
 						case 4:
 							for ($row = ($thisfile_bmp_header_raw['height'] - 1); $row >= 0; $row--) {
 								for ($col = 0; $col < $thisfile_bmp_header_raw['width']; $col = $col) {
-									$paletteindexbyte = ord($BMPpixelData{$pixeldataoffset++});
+									$paletteindexbyte = ord($BMPpixelData[$pixeldataoffset++]);
 									for ($i = 1; $i >= 0; $i--) {
 										$paletteindex = ($paletteindexbyte & (0x0F << (4 * $i))) >> (4 * $i);
 										$thisfile_bmp['data'][$row][$col] = $thisfile_bmp['palette'][$paletteindex];
@@ -368,7 +368,7 @@ class getid3_bmp extends getid3_handler
 						case 8:
 							for ($row = ($thisfile_bmp_header_raw['height'] - 1); $row >= 0; $row--) {
 								for ($col = 0; $col < $thisfile_bmp_header_raw['width']; $col++) {
-									$paletteindex = ord($BMPpixelData{$pixeldataoffset++});
+									$paletteindex = ord($BMPpixelData[$pixeldataoffset++]);
 									$thisfile_bmp['data'][$row][$col] = $thisfile_bmp['palette'][$paletteindex];
 								}
 								while (($pixeldataoffset % 4) != 0) {
@@ -381,7 +381,7 @@ class getid3_bmp extends getid3_handler
 						case 24:
 							for ($row = ($thisfile_bmp_header_raw['height'] - 1); $row >= 0; $row--) {
 								for ($col = 0; $col < $thisfile_bmp_header_raw['width']; $col++) {
-									$thisfile_bmp['data'][$row][$col] = (ord($BMPpixelData{$pixeldataoffset+2}) << 16) | (ord($BMPpixelData{$pixeldataoffset+1}) << 8) | ord($BMPpixelData{$pixeldataoffset});
+									$thisfile_bmp['data'][$row][$col] = (ord($BMPpixelData[$pixeldataoffset+2]) << 16) | (ord($BMPpixelData[$pixeldataoffset+1]) << 8) | ord($BMPpixelData[$pixeldataoffset]);
 									$pixeldataoffset += 3;
 								}
 								while (($pixeldataoffset % 4) != 0) {
@@ -394,7 +394,7 @@ class getid3_bmp extends getid3_handler
 						case 32:
 							for ($row = ($thisfile_bmp_header_raw['height'] - 1); $row >= 0; $row--) {
 								for ($col = 0; $col < $thisfile_bmp_header_raw['width']; $col++) {
-									$thisfile_bmp['data'][$row][$col] = (ord($BMPpixelData{$pixeldataoffset+3}) << 24) | (ord($BMPpixelData{$pixeldataoffset+2}) << 16) | (ord($BMPpixelData{$pixeldataoffset+1}) << 8) | ord($BMPpixelData{$pixeldataoffset});
+									$thisfile_bmp['data'][$row][$col] = (ord($BMPpixelData[$pixeldataoffset+3]) << 24) | (ord($BMPpixelData[$pixeldataoffset+2]) << 16) | (ord($BMPpixelData[$pixeldataoffset+1]) << 8) | ord($BMPpixelData[$pixeldataoffset]);
 									$pixeldataoffset += 4;
 								}
 								while (($pixeldataoffset % 4) != 0) {
diff --git a/lib/getid3/module.graphic.gif.php b/lib/getid3/module.graphic.gif.php
index 4fb1247ad2395a70bc75b172841a6c00205601c4..acea218899db755a608332b1716b1c446aea8f26 100644
--- a/lib/getid3/module.graphic.gif.php
+++ b/lib/getid3/module.graphic.gif.php
@@ -46,6 +46,10 @@ class getid3_gif extends getid3_handler
 			return false;
 		}
 
+		//if (!$this->getid3->option_extra_info) {
+		//	$this->warning('GIF Extensions and Global Color Table not returned due to !getid3->option_extra_info');
+		//}
+
 		$info['gif']['header']['raw']['version']               =                              substr($GIFheader, $offset, 3);
 		$offset += 3;
 		$info['gif']['header']['raw']['width']                 = getid3_lib::LittleEndian2Int(substr($GIFheader, $offset, 2));
@@ -85,12 +89,15 @@ class getid3_gif extends getid3_handler
 
 		if ($info['gif']['header']['flags']['global_color_table']) {
 			$GIFcolorTable = $this->fread(3 * $info['gif']['header']['global_color_size']);
-			$offset = 0;
-			for ($i = 0; $i < $info['gif']['header']['global_color_size']; $i++) {
-				$red   = getid3_lib::LittleEndian2Int(substr($GIFcolorTable, $offset++, 1));
-				$green = getid3_lib::LittleEndian2Int(substr($GIFcolorTable, $offset++, 1));
-				$blue  = getid3_lib::LittleEndian2Int(substr($GIFcolorTable, $offset++, 1));
-				$info['gif']['global_color_table'][$i] = (($red << 16) | ($green << 8) | ($blue));
+			if ($this->getid3->option_extra_info) {
+				$offset = 0;
+				for ($i = 0; $i < $info['gif']['header']['global_color_size']; $i++) {
+					$red   = getid3_lib::LittleEndian2Int(substr($GIFcolorTable, $offset++, 1));
+					$green = getid3_lib::LittleEndian2Int(substr($GIFcolorTable, $offset++, 1));
+					$blue  = getid3_lib::LittleEndian2Int(substr($GIFcolorTable, $offset++, 1));
+					$info['gif']['global_color_table'][$i] = (($red << 16) | ($green << 8) | ($blue));
+					$info['gif']['global_color_table_rgb'][$i] = sprintf('%02X%02X%02X', $red, $green, $blue);
+				}
 			}
 		}
 
@@ -166,7 +173,9 @@ class getid3_gif extends getid3_handler
 						}
 					}
 
-					$info['gif']['extension_blocks'][] = $ExtensionBlock;
+					if ($this->getid3->option_extra_info) {
+						$info['gif']['extension_blocks'][] = $ExtensionBlock;
+					}
 					break;
 
 				case ';':
diff --git a/lib/getid3/module.graphic.pcd.php b/lib/getid3/module.graphic.pcd.php
index 882447965819934aafb052f7d2fc28237ca8f9c7..ed4c1b44a3d4db82ba745f3bb933fff9ff86a991 100644
--- a/lib/getid3/module.graphic.pcd.php
+++ b/lib/getid3/module.graphic.pcd.php
@@ -76,11 +76,11 @@ class getid3_pcd extends getid3_handler
 
 				for ($x = 0; $x < $PCD_width; $x++) {
 					if ($PCDisVertical) {
-						$info['pcd']['data'][$PCD_width - $x][$y]     = $this->YCbCr2RGB(ord($PCD_data_Y1{$x}), ord($PCD_data_Cb{(int) floor($x / 2)}), ord($PCD_data_Cr{(int) floor($x / 2)}));
-						$info['pcd']['data'][$PCD_width - $x][$y + 1] = $this->YCbCr2RGB(ord($PCD_data_Y2{$x}), ord($PCD_data_Cb{(int) floor($x / 2)}), ord($PCD_data_Cr{(int) floor($x / 2)}));
+						$info['pcd']['data'][$PCD_width - $x][$y]     = $this->YCbCr2RGB(ord($PCD_data_Y1[$x]), ord($PCD_data_Cb[(int) floor($x / 2)]), ord($PCD_data_Cr[(int) floor($x / 2)]));
+						$info['pcd']['data'][$PCD_width - $x][$y + 1] = $this->YCbCr2RGB(ord($PCD_data_Y2[$x]), ord($PCD_data_Cb[(int) floor($x / 2)]), ord($PCD_data_Cr[(int) floor($x / 2)]));
 					} else {
-						$info['pcd']['data'][$y][$x]                  = $this->YCbCr2RGB(ord($PCD_data_Y1{$x}), ord($PCD_data_Cb{(int) floor($x / 2)}), ord($PCD_data_Cr{(int) floor($x / 2)}));
-						$info['pcd']['data'][$y + 1][$x]              = $this->YCbCr2RGB(ord($PCD_data_Y2{$x}), ord($PCD_data_Cb{(int) floor($x / 2)}), ord($PCD_data_Cr{(int) floor($x / 2)}));
+						$info['pcd']['data'][$y][$x]                  = $this->YCbCr2RGB(ord($PCD_data_Y1[$x]), ord($PCD_data_Cb[(int) floor($x / 2)]), ord($PCD_data_Cr[(int) floor($x / 2)]));
+						$info['pcd']['data'][$y + 1][$x]              = $this->YCbCr2RGB(ord($PCD_data_Y2[$x]), ord($PCD_data_Cb[(int) floor($x / 2)]), ord($PCD_data_Cr[(int) floor($x / 2)]));
 					}
 				}
 			}
diff --git a/lib/getid3/module.graphic.tiff.php b/lib/getid3/module.graphic.tiff.php
index 57d500f55b97eb29757e532830097e18f7725cf7..508411f36739b55ef2d5e842b36612eb908f252d 100644
--- a/lib/getid3/module.graphic.tiff.php
+++ b/lib/getid3/module.graphic.tiff.php
@@ -43,7 +43,7 @@ class getid3_tiff extends getid3_handler
 		$info['video']['dataformat'] = 'tiff';
 		$info['video']['lossless']   = true;
 		$info['tiff']['ifd']         = array();
-		$CurrentIFD                          = array();
+		$CurrentIFD                  = array();
 
 		$FieldTypeByteLength = array(1=>1, 2=>1, 3=>2, 4=>4, 5=>8);
 
@@ -57,47 +57,61 @@ class getid3_tiff extends getid3_handler
 			$CurrentIFD['fieldcount'] = $this->TIFFendian2Int($this->fread(2), $info['tiff']['byte_order']);
 
 			for ($i = 0; $i < $CurrentIFD['fieldcount']; $i++) {
-				$CurrentIFD['fields'][$i]['raw']['tag']    = $this->TIFFendian2Int($this->fread(2), $info['tiff']['byte_order']);
-				$CurrentIFD['fields'][$i]['raw']['type']   = $this->TIFFendian2Int($this->fread(2), $info['tiff']['byte_order']);
-				$CurrentIFD['fields'][$i]['raw']['length'] = $this->TIFFendian2Int($this->fread(4), $info['tiff']['byte_order']);
-				$CurrentIFD['fields'][$i]['raw']['offset'] =                       $this->fread(4);
-
+				$CurrentIFD['fields'][$i]['raw']['tag']      = $this->TIFFendian2Int($this->fread(2), $info['tiff']['byte_order']);
+				$CurrentIFD['fields'][$i]['raw']['type']     = $this->TIFFendian2Int($this->fread(2), $info['tiff']['byte_order']);
+				$CurrentIFD['fields'][$i]['raw']['length']   = $this->TIFFendian2Int($this->fread(4), $info['tiff']['byte_order']);
+				$CurrentIFD['fields'][$i]['raw']['valoff']   =                       $this->fread(4); // To save time and space the Value Offset contains the Value instead of pointing to the Value if and only if the Value fits into 4 bytes. If the Value is shorter than 4 bytes, it is left-justified within the 4-byte Value Offset, i.e., stored in the lowernumbered bytes. Whether the Value fits within 4 bytes is determined by the Type and Count of the field.
 				$CurrentIFD['fields'][$i]['raw']['tag_name'] = $this->TIFFcommentName($CurrentIFD['fields'][$i]['raw']['tag']);
 
 				switch ($CurrentIFD['fields'][$i]['raw']['type']) {
 					case 1: // BYTE  An 8-bit unsigned integer.
 						if ($CurrentIFD['fields'][$i]['raw']['length'] <= 4) {
-							$CurrentIFD['fields'][$i]['value']  = $this->TIFFendian2Int(substr($CurrentIFD['fields'][$i]['raw']['offset'], 0, 1), $info['tiff']['byte_order']);
+							$CurrentIFD['fields'][$i]['value']  = $this->TIFFendian2Int(substr($CurrentIFD['fields'][$i]['raw']['valoff'], 0, 1), $info['tiff']['byte_order']);
 						} else {
-							$CurrentIFD['fields'][$i]['offset'] = $this->TIFFendian2Int($CurrentIFD['fields'][$i]['raw']['offset'], $info['tiff']['byte_order']);
+							$CurrentIFD['fields'][$i]['offset'] = $this->TIFFendian2Int($CurrentIFD['fields'][$i]['raw']['valoff'], $info['tiff']['byte_order']);
 						}
 						break;
 
 					case 2: // ASCII 8-bit bytes  that store ASCII codes; the last byte must be null.
 						if ($CurrentIFD['fields'][$i]['raw']['length'] <= 4) {
-							$CurrentIFD['fields'][$i]['value']  = substr($CurrentIFD['fields'][$i]['raw']['offset'], 3);
+							$CurrentIFD['fields'][$i]['value']  = substr($CurrentIFD['fields'][$i]['raw']['valoff'], 3);
 						} else {
-							$CurrentIFD['fields'][$i]['offset'] = $this->TIFFendian2Int($CurrentIFD['fields'][$i]['raw']['offset'], $info['tiff']['byte_order']);
+							$CurrentIFD['fields'][$i]['offset'] = $this->TIFFendian2Int($CurrentIFD['fields'][$i]['raw']['valoff'], $info['tiff']['byte_order']);
 						}
 						break;
 
 					case 3: // SHORT A 16-bit (2-byte) unsigned integer.
 						if ($CurrentIFD['fields'][$i]['raw']['length'] <= 2) {
-							$CurrentIFD['fields'][$i]['value']  = $this->TIFFendian2Int(substr($CurrentIFD['fields'][$i]['raw']['offset'], 0, 2), $info['tiff']['byte_order']);
+							$CurrentIFD['fields'][$i]['value']  = $this->TIFFendian2Int(substr($CurrentIFD['fields'][$i]['raw']['valoff'], 0, 2), $info['tiff']['byte_order']);
 						} else {
-							$CurrentIFD['fields'][$i]['offset'] = $this->TIFFendian2Int($CurrentIFD['fields'][$i]['raw']['offset'], $info['tiff']['byte_order']);
+							$CurrentIFD['fields'][$i]['offset'] = $this->TIFFendian2Int($CurrentIFD['fields'][$i]['raw']['valoff'], $info['tiff']['byte_order']);
 						}
 						break;
 
 					case 4: // LONG  A 32-bit (4-byte) unsigned integer.
-						if ($CurrentIFD['fields'][$i]['raw']['length'] <= 1) {
-							$CurrentIFD['fields'][$i]['value']  = $this->TIFFendian2Int($CurrentIFD['fields'][$i]['raw']['offset'], $info['tiff']['byte_order']);
+						if ($CurrentIFD['fields'][$i]['raw']['length'] <= 4) {
+							$CurrentIFD['fields'][$i]['value']  = $this->TIFFendian2Int($CurrentIFD['fields'][$i]['raw']['valoff'], $info['tiff']['byte_order']);
 						} else {
-							$CurrentIFD['fields'][$i]['offset'] = $this->TIFFendian2Int($CurrentIFD['fields'][$i]['raw']['offset'], $info['tiff']['byte_order']);
+							$CurrentIFD['fields'][$i]['offset'] = $this->TIFFendian2Int($CurrentIFD['fields'][$i]['raw']['valoff'], $info['tiff']['byte_order']);
 						}
 						break;
 
 					case 5: // RATIONAL   Two LONG_s:  the first represents the numerator of a fraction, the second the denominator.
+					case 7: // UNDEFINED An 8-bit byte that may contain anything, depending on the definition of the field.
+						$CurrentIFD['fields'][$i]['offset'] = $this->TIFFendian2Int($CurrentIFD['fields'][$i]['raw']['valoff'], $info['tiff']['byte_order']);
+						break;
+
+					// Warning: It is possible that other TIFF field types will be added in the future. Readers should skip over fields containing an unexpected field type.
+					// In TIFF 6.0, some new field types have been defined:
+					// These new field types are also governed by the byte order (II or MM) in the TIFF header.
+					case 6: // SBYTE An 8-bit signed (twos-complement) integer.
+					case 8: // SSHORT A 16-bit (2-byte) signed (twos-complement) integer.
+					case 9: // SLONG A 32-bit (4-byte) signed (twos-complement) integer.
+					case 10: // SRATIONAL Two SLONGs: the first represents the numerator of a fraction, the second the denominator.
+					case 11: // FLOAT Single precision (4-byte) IEEE format
+					case 12: // DOUBLE Double precision (8-byte) IEEE format
+					default:
+						$this->warning('unhandled IFD field type '.$CurrentIFD['fields'][$i]['raw']['type'].' for IFD entry '.$i);
 						break;
 				}
 			}
@@ -137,6 +151,16 @@ class getid3_tiff extends getid3_handler
 
 						}
 						break;
+					case 700:
+						$XMPmagic = '<?xpacket';
+						$this->fseek($fieldarray['offset']);
+						$xmpkey = (isset($info['tiff']['XMP']) ? count($info['tiff']['XMP']) : 0);
+						$info['tiff']['XMP'][$xmpkey]['raw'] = $this->fread($fieldarray['raw']['length']);
+						if (substr($info['tiff']['XMP'][$xmpkey]['raw'], 0, strlen($XMPmagic)) != $XMPmagic) {
+							$this->warning('did not find expected XMP data at offset '.$fieldarray['offset']);
+							unset($info['tiff']['XMP'][$xmpkey]['raw']);
+						}
+						break;
 				}
 				switch ($fieldarray['raw']['tag']) {
 					case 256: // ImageWidth
@@ -207,14 +231,33 @@ class getid3_tiff extends getid3_handler
 	 * @return string
 	 */
 	public function TIFFcompressionMethod($id) {
+		// https://en.wikipedia.org/wiki/TIFF#TIFF_Compression_Tag
 		static $TIFFcompressionMethod = array();
 		if (empty($TIFFcompressionMethod)) {
 			$TIFFcompressionMethod = array(
-				1     => 'Uncompressed',
-				2     => 'Huffman',
-				3     => 'Fax - CCITT 3',
-				5     => 'LZW',
-				32773 => 'PackBits',
+				0x0001 => 'Uncompressed',
+				0x0002 => 'Huffman',
+				0x0003 => 'CCITT T.4',
+				0x0004 => 'CCITT T.6',
+				0x0005 => 'LZW',
+				0x0006 => 'JPEG-old',
+				0x0007 => 'JPEG',
+				0x0008 => 'deflate',
+				0x0009 => 'JBIG ITU-T T.85',
+				0x000A => 'JBIG ITU-T T.43',
+				0x7FFE => 'NeXT RLE 2-bit',
+				0x8005 => 'PackBits',
+				0x8029 => 'ThunderScan RLE 4-bit',
+				0x807F => 'RasterPadding',
+				0x8080 => 'RLE-LW',
+				0x8081 => 'RLE-CT',
+				0x8082 => 'RLE-BL',
+				0x80B2 => 'deflate-PK',
+				0x80B3 => 'Kodak-DCS',
+				0x8765 => 'JBIG',
+				0x8798 => 'JPEG2000',
+				0x8799 => 'Nikon NEF',
+				0x879B => 'JBIG2',
 			);
 		}
 		return (isset($TIFFcompressionMethod[$id]) ? $TIFFcompressionMethod[$id] : 'unknown/invalid ('.$id.')');
diff --git a/lib/getid3/module.misc.iso.php b/lib/getid3/module.misc.iso.php
index 231a32a23747fe67db378013e0d2599ea322757b..d0c968505960418a3df742795bbf063ab87abc8e 100644
--- a/lib/getid3/module.misc.iso.php
+++ b/lib/getid3/module.misc.iso.php
@@ -29,7 +29,7 @@ class getid3_iso extends getid3_handler
 			$this->fseek(2048 * $i);
 			$ISOheader = $this->fread(2048);
 			if (substr($ISOheader, 1, 5) == 'CD001') {
-				switch (ord($ISOheader{0})) {
+				switch (ord($ISOheader[0])) {
 					case 1:
 						$info['iso']['primary_volume_descriptor']['offset'] = 2048 * $i;
 						$this->ParsePrimaryVolumeDescriptor($ISOheader);
@@ -296,9 +296,9 @@ class getid3_iso extends getid3_handler
 		$DirectoryRecordData = $this->fread(1);
 		$DirectoryRecord = array();
 
-		while (ord($DirectoryRecordData{0}) > 33) {
+		while (ord($DirectoryRecordData[0]) > 33) {
 
-			$DirectoryRecordData .= $this->fread(ord($DirectoryRecordData{0}) - 1);
+			$DirectoryRecordData .= $this->fread(ord($DirectoryRecordData[0]) - 1);
 
 			$ThisDirectoryRecord = array();
 
@@ -389,13 +389,13 @@ class getid3_iso extends getid3_handler
 		// 6: second of the minute from 0 to 59
 		// 7: Offset from Greenwich Mean Time in number of 15 minute intervals from -48 (West) to +52 (East)
 
-		$UNIXyear   = ord($ISOtime{0}) + 1900;
-		$UNIXmonth  = ord($ISOtime{1});
-		$UNIXday    = ord($ISOtime{2});
-		$UNIXhour   = ord($ISOtime{3});
-		$UNIXminute = ord($ISOtime{4});
-		$UNIXsecond = ord($ISOtime{5});
-		$GMToffset  = $this->TwosCompliment2Decimal(ord($ISOtime{5}));
+		$UNIXyear   = ord($ISOtime[0]) + 1900;
+		$UNIXmonth  = ord($ISOtime[1]);
+		$UNIXday    = ord($ISOtime[2]);
+		$UNIXhour   = ord($ISOtime[3]);
+		$UNIXminute = ord($ISOtime[4]);
+		$UNIXsecond = ord($ISOtime[5]);
+		$GMToffset  = $this->TwosCompliment2Decimal(ord($ISOtime[5]));
 
 		return gmmktime($UNIXhour, $UNIXminute, $UNIXsecond, $UNIXmonth, $UNIXday, $UNIXyear);
 	}
diff --git a/lib/getid3/module.tag.apetag.php b/lib/getid3/module.tag.apetag.php
index 37971a6b99d3b6775074a8b95fe9bbae36f24644..d56fcbac79c7d3b9dfa29944bb43ebde6d5aab25 100644
--- a/lib/getid3/module.tag.apetag.php
+++ b/lib/getid3/module.tag.apetag.php
@@ -162,8 +162,8 @@ class getid3_apetag extends getid3_handler
 			switch (strtolower($item_key)) {
 				// http://wiki.hydrogenaud.io/index.php?title=ReplayGain#MP3Gain
 				case 'replaygain_track_gain':
-					if (preg_match('#^[\\-\\+][0-9\\.,]{8}$#', $thisfile_ape_items_current['data'][0])) {
-						$thisfile_replaygain['track']['adjustment'] = (float) str_replace(',', '.', $thisfile_ape_items_current['data'][0]); // float casting will see "0,95" as zero!
+					if (preg_match('#^([\\-\\+][0-9\\.,]{8})( dB)?$#', $thisfile_ape_items_current['data'][0], $matches)) {
+						$thisfile_replaygain['track']['adjustment'] = (float) str_replace(',', '.', $matches[1]); // float casting will see "0,95" as zero!
 						$thisfile_replaygain['track']['originator'] = 'unspecified';
 					} else {
 						$this->warning('MP3gainTrackGain value in APEtag appears invalid: "'.$thisfile_ape_items_current['data'][0].'"');
@@ -171,8 +171,8 @@ class getid3_apetag extends getid3_handler
 					break;
 
 				case 'replaygain_track_peak':
-					if (preg_match('#^[0-9\\.,]{8}$#', $thisfile_ape_items_current['data'][0])) {
-						$thisfile_replaygain['track']['peak']       = (float) str_replace(',', '.', $thisfile_ape_items_current['data'][0]); // float casting will see "0,95" as zero!
+					if (preg_match('#^([0-9\\.,]{8})$#', $thisfile_ape_items_current['data'][0], $matches)) {
+						$thisfile_replaygain['track']['peak']       = (float) str_replace(',', '.', $matches[1]); // float casting will see "0,95" as zero!
 						$thisfile_replaygain['track']['originator'] = 'unspecified';
 						if ($thisfile_replaygain['track']['peak'] <= 0) {
 							$this->warning('ReplayGain Track peak from APEtag appears invalid: '.$thisfile_replaygain['track']['peak'].' (original value = "'.$thisfile_ape_items_current['data'][0].'")');
@@ -183,8 +183,8 @@ class getid3_apetag extends getid3_handler
 					break;
 
 				case 'replaygain_album_gain':
-					if (preg_match('#^[\\-\\+][0-9\\.,]{8}$#', $thisfile_ape_items_current['data'][0])) {
-						$thisfile_replaygain['album']['adjustment'] = (float) str_replace(',', '.', $thisfile_ape_items_current['data'][0]); // float casting will see "0,95" as zero!
+					if (preg_match('#^([\\-\\+][0-9\\.,]{8})( dB)?$#', $thisfile_ape_items_current['data'][0], $matches)) {
+						$thisfile_replaygain['album']['adjustment'] = (float) str_replace(',', '.', $matches[1]); // float casting will see "0,95" as zero!
 						$thisfile_replaygain['album']['originator'] = 'unspecified';
 					} else {
 						$this->warning('MP3gainAlbumGain value in APEtag appears invalid: "'.$thisfile_ape_items_current['data'][0].'"');
@@ -192,8 +192,8 @@ class getid3_apetag extends getid3_handler
 					break;
 
 				case 'replaygain_album_peak':
-					if (preg_match('#^[0-9\\.,]{8}$#', $thisfile_ape_items_current['data'][0])) {
-						$thisfile_replaygain['album']['peak']       = (float) str_replace(',', '.', $thisfile_ape_items_current['data'][0]); // float casting will see "0,95" as zero!
+					if (preg_match('#^([0-9\\.,]{8})$#', $thisfile_ape_items_current['data'][0], $matches)) {
+						$thisfile_replaygain['album']['peak']       = (float) str_replace(',', '.', $matches[1]); // float casting will see "0,95" as zero!
 						$thisfile_replaygain['album']['originator'] = 'unspecified';
 						if ($thisfile_replaygain['album']['peak'] <= 0) {
 							$this->warning('ReplayGain Album peak from APEtag appears invalid: '.$thisfile_replaygain['album']['peak'].' (original value = "'.$thisfile_ape_items_current['data'][0].'")');
@@ -237,7 +237,7 @@ class getid3_apetag extends getid3_handler
 				case 'tracknumber':
 					if (is_array($thisfile_ape_items_current['data'])) {
 						foreach ($thisfile_ape_items_current['data'] as $comment) {
-							$thisfile_ape['comments']['track'][] = $comment;
+							$thisfile_ape['comments']['track_number'][] = $comment;
 						}
 					}
 					break;
diff --git a/lib/getid3/module.tag.id3v1.php b/lib/getid3/module.tag.id3v1.php
index cafc3fbaf8b8b83c19d5fab42b792a90f710e01a..0084838b634209ca072adeac762e22c60512abfe 100644
--- a/lib/getid3/module.tag.id3v1.php
+++ b/lib/getid3/module.tag.id3v1.php
@@ -45,9 +45,9 @@ class getid3_id3v1 extends getid3_handler
 
 			// If second-last byte of comment field is null and last byte of comment field is non-null
 			// then this is ID3v1.1 and the comment field is 28 bytes long and the 30th byte is the track number
-			if (($id3v1tag{125} === "\x00") && ($id3v1tag{126} !== "\x00")) {
-				$ParsedID3v1['track']   = ord(substr($ParsedID3v1['comment'], 29,  1));
-				$ParsedID3v1['comment'] =     substr($ParsedID3v1['comment'],  0, 28);
+			if (($id3v1tag[125] === "\x00") && ($id3v1tag[126] !== "\x00")) {
+				$ParsedID3v1['track_number'] = ord(substr($ParsedID3v1['comment'], 29,  1));
+				$ParsedID3v1['comment']      =     substr($ParsedID3v1['comment'],  0, 28);
 			}
 			$ParsedID3v1['comment'] = $this->cutfield($ParsedID3v1['comment']);
 
@@ -68,7 +68,7 @@ class getid3_id3v1 extends getid3_handler
 			$ID3v1encoding = 'ISO-8859-1';
 			foreach ($ParsedID3v1['comments'] as $tag_key => $valuearray) {
 				foreach ($valuearray as $key => $value) {
-					if (preg_match('#^[\\x00-\\x40\\xA8\\B8\\x80-\\xFF]+$#', $value)) {
+					if (preg_match('#^[\\x00-\\x40\\xA8\\xB8\\x80-\\xFF]+$#', $value)) {
 						foreach (array('Windows-1251', 'KOI8-R') as $id3v1_bad_encoding) {
 							if (function_exists('mb_convert_encoding') && @mb_convert_encoding($value, $id3v1_bad_encoding, $id3v1_bad_encoding) === $value) {
 								$ID3v1encoding = $id3v1_bad_encoding;
@@ -91,7 +91,7 @@ class getid3_id3v1 extends getid3_handler
 											$ParsedID3v1['year'],
 											(isset($ParsedID3v1['genre']) ? $this->LookupGenreID($ParsedID3v1['genre']) : false),
 											$ParsedID3v1['comment'],
-											(!empty($ParsedID3v1['track']) ? $ParsedID3v1['track'] : ''));
+											(!empty($ParsedID3v1['track_number']) ? $ParsedID3v1['track_number'] : ''));
 			$ParsedID3v1['padding_valid'] = true;
 			if ($id3v1tag !== $GoodFormatID3v1tag) {
 				$ParsedID3v1['padding_valid'] = false;
diff --git a/lib/getid3/module.tag.id3v2.php b/lib/getid3/module.tag.id3v2.php
index c51dedb8018bab93b1c570a93364a8489312af26..0c7c34bf6fb6209d52ae5e719ea3d0135380844f 100644
--- a/lib/getid3/module.tag.id3v2.php
+++ b/lib/getid3/module.tag.id3v2.php
@@ -59,8 +59,8 @@ class getid3_id3v2 extends getid3_handler
 		$header = $this->fread(10);
 		if (substr($header, 0, 3) == 'ID3'  &&  strlen($header) == 10) {
 
-			$thisfile_id3v2['majorversion'] = ord($header{3});
-			$thisfile_id3v2['minorversion'] = ord($header{4});
+			$thisfile_id3v2['majorversion'] = ord($header[3]);
+			$thisfile_id3v2['minorversion'] = ord($header[4]);
 
 			// shortcut
 			$id3v2_majorversion = &$thisfile_id3v2['majorversion'];
@@ -79,7 +79,7 @@ class getid3_id3v2 extends getid3_handler
 
 		}
 
-		$id3_flags = ord($header{5});
+		$id3_flags = ord($header[5]);
 		switch ($id3v2_majorversion) {
 			case 2:
 				// %ab000000 in v2.2
@@ -260,7 +260,7 @@ class getid3_id3v2 extends getid3_handler
 					$thisfile_id3v2['padding']['length'] = strlen($framedata);
 					$thisfile_id3v2['padding']['valid']  = true;
 					for ($i = 0; $i < $thisfile_id3v2['padding']['length']; $i++) {
-						if ($framedata{$i} != "\x00") {
+						if ($framedata[$i] != "\x00") {
 							$thisfile_id3v2['padding']['valid'] = false;
 							$thisfile_id3v2['padding']['errorpos'] = $thisfile_id3v2['padding']['start'] + $i;
 							$this->warning('Invalid ID3v2 padding found at offset '.$thisfile_id3v2['padding']['errorpos'].' (the remaining '.($thisfile_id3v2['padding']['length'] - $i).' bytes are considered invalid)');
@@ -326,7 +326,7 @@ class getid3_id3v2 extends getid3_handler
 
 					$len = strlen($framedata);
 					for ($i = 0; $i < $len; $i++) {
-						if ($framedata{$i} != "\x00") {
+						if ($framedata[$i] != "\x00") {
 							$thisfile_id3v2['padding']['valid'] = false;
 							$thisfile_id3v2['padding']['errorpos'] = $thisfile_id3v2['padding']['start'] + $i;
 							$this->warning('Invalid ID3v2 padding found at offset '.$thisfile_id3v2['padding']['errorpos'].' (the remaining '.($thisfile_id3v2['padding']['length'] - $i).' bytes are considered invalid)');
@@ -434,11 +434,11 @@ class getid3_id3v2 extends getid3_handler
 			$footer = $this->fread(10);
 			if (substr($footer, 0, 3) == '3DI') {
 				$thisfile_id3v2['footer'] = true;
-				$thisfile_id3v2['majorversion_footer'] = ord($footer{3});
-				$thisfile_id3v2['minorversion_footer'] = ord($footer{4});
+				$thisfile_id3v2['majorversion_footer'] = ord($footer[3]);
+				$thisfile_id3v2['minorversion_footer'] = ord($footer[4]);
 			}
 			if ($thisfile_id3v2['majorversion_footer'] <= 4) {
-				$id3_flags = ord($footer{5});
+				$id3_flags = ord($footer[5]);
 				$thisfile_id3v2_flags['unsynch_footer']  = (bool) ($id3_flags & 0x80);
 				$thisfile_id3v2_flags['extfoot_footer']  = (bool) ($id3_flags & 0x40);
 				$thisfile_id3v2_flags['experim_footer']  = (bool) ($id3_flags & 0x20);
@@ -459,10 +459,10 @@ class getid3_id3v2 extends getid3_handler
 			unset($key, $value, $genres, $genre);
 		}
 
-		if (isset($thisfile_id3v2['comments']['track'])) {
-			foreach ($thisfile_id3v2['comments']['track'] as $key => $value) {
+		if (isset($thisfile_id3v2['comments']['track_number'])) {
+			foreach ($thisfile_id3v2['comments']['track_number'] as $key => $value) {
 				if (strstr($value, '/')) {
-					list($thisfile_id3v2['comments']['tracknum'][$key], $thisfile_id3v2['comments']['totaltracks'][$key]) = explode('/', $thisfile_id3v2['comments']['track'][$key]);
+					list($thisfile_id3v2['comments']['track_number'][$key], $thisfile_id3v2['comments']['totaltracks'][$key]) = explode('/', $thisfile_id3v2['comments']['track_number'][$key]);
 				}
 			}
 		}
@@ -672,16 +672,14 @@ class getid3_id3v2 extends getid3_handler
 			if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($frame_textencoding_terminator), 1)) === 0) {
 				$frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00
 			}
-			$frame_description = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
-			if (in_array($frame_description, array("\x00", "\x00\x00", "\xFF\xFE", "\xFE\xFF"))) {
-				// if description only contains a BOM or terminator then make it blank
-				$frame_description = '';
-			}
+			$parsedFrame['description'] = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
+			$parsedFrame['description'] = $this->MakeUTF16emptyStringEmpty($parsedFrame['description']);
 			$parsedFrame['encodingid']  = $frame_textencoding;
 			$parsedFrame['encoding']    = $this->TextEncodingNameLookup($frame_textencoding);
 
-			$parsedFrame['description'] = trim(getid3_lib::iconv_fallback($parsedFrame['encoding'], $info['id3v2']['encoding'], $frame_description));
+			$parsedFrame['description'] = trim(getid3_lib::iconv_fallback($parsedFrame['encoding'], $info['id3v2']['encoding'], $parsedFrame['description']));
 			$parsedFrame['data'] = substr($parsedFrame['data'], $frame_terminatorpos + strlen($frame_textencoding_terminator));
+			$parsedFrame['data'] = $this->RemoveStringTerminator($parsedFrame['data'], $frame_textencoding_terminator);
 			if (!empty($parsedFrame['framenameshort']) && !empty($parsedFrame['data'])) {
 				$commentkey = ($parsedFrame['description'] ? $parsedFrame['description'] : (isset($info['id3v2']['comments'][$parsedFrame['framenameshort']]) ? count($info['id3v2']['comments'][$parsedFrame['framenameshort']]) : 0));
 				if (!isset($info['id3v2']['comments'][$parsedFrame['framenameshort']]) || !array_key_exists($commentkey, $info['id3v2']['comments'][$parsedFrame['framenameshort']])) {
@@ -693,7 +691,7 @@ class getid3_id3v2 extends getid3_handler
 			//unset($parsedFrame['data']); do not unset, may be needed elsewhere, e.g. for replaygain
 
 
-		} elseif ($parsedFrame['frame_name']{0} == 'T') { // 4.2. T??[?] Text information frame
+		} elseif ($parsedFrame['frame_name'][0] == 'T') { // 4.2. T??[?] Text information frame
 			//   There may only be one text information frame of its kind in an tag.
 			// <Header for 'Text information frame', ID: 'T000' - 'TZZZ',
 			// excluding 'TXXX' described in 4.2.6.>
@@ -707,10 +705,10 @@ class getid3_id3v2 extends getid3_handler
 			}
 
 			$parsedFrame['data'] = (string) substr($parsedFrame['data'], $frame_offset);
+			$parsedFrame['data'] = $this->RemoveStringTerminator($parsedFrame['data'], $this->TextEncodingTerminatorLookup($frame_textencoding));
 
 			$parsedFrame['encodingid'] = $frame_textencoding;
 			$parsedFrame['encoding']   = $this->TextEncodingNameLookup($frame_textencoding);
-
 			if (!empty($parsedFrame['framenameshort']) && !empty($parsedFrame['data'])) {
 				// ID3v2.3 specs say that TPE1 (and others) can contain multiple artist values separated with /
 				// This of course breaks when an artist name contains slash character, e.g. "AC/DC"
@@ -766,38 +764,20 @@ class getid3_id3v2 extends getid3_handler
 			if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($frame_textencoding_terminator), 1)) === 0) {
 				$frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00
 			}
-			$frame_description = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
-			if (in_array($frame_description, array("\x00", "\x00\x00", "\xFF\xFE", "\xFE\xFF"))) {
-				// if description only contains a BOM or terminator then make it blank
-				$frame_description = '';
-			}
-			$parsedFrame['data'] = substr($parsedFrame['data'], $frame_terminatorpos + strlen($frame_textencoding_terminator));
-
-			$frame_terminatorpos = strpos($parsedFrame['data'], $frame_textencoding_terminator);
-			if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($frame_textencoding_terminator), 1)) === 0) {
-				$frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00
-			}
-			if ($frame_terminatorpos) {
-				// there are null bytes after the data - this is not according to spec
-				// only use data up to first null byte
-				$frame_urldata = (string) substr($parsedFrame['data'], 0, $frame_terminatorpos);
-			} else {
-				// no null bytes following data, just use all data
-				$frame_urldata = (string) $parsedFrame['data'];
-			}
-
 			$parsedFrame['encodingid']  = $frame_textencoding;
 			$parsedFrame['encoding']    = $this->TextEncodingNameLookup($frame_textencoding);
+			$parsedFrame['description'] = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);           // according to the frame text encoding
+			$parsedFrame['url']         = substr($parsedFrame['data'], $frame_terminatorpos + strlen($frame_textencoding_terminator)); // always ISO-8859-1
+			$parsedFrame['description'] = $this->RemoveStringTerminator($parsedFrame['description'], $frame_textencoding_terminator);
+			$parsedFrame['description'] = $this->MakeUTF16emptyStringEmpty($parsedFrame['description']);
 
-			$parsedFrame['url']         = $frame_urldata;     // always ISO-8859-1
-			$parsedFrame['description'] = $frame_description; // according to the frame text encoding
 			if (!empty($parsedFrame['framenameshort']) && $parsedFrame['url']) {
 				$info['id3v2']['comments'][$parsedFrame['framenameshort']][] = getid3_lib::iconv_fallback('ISO-8859-1', $info['id3v2']['encoding'], $parsedFrame['url']);
 			}
 			unset($parsedFrame['data']);
 
 
-		} elseif ($parsedFrame['frame_name']{0} == 'W') { // 4.3. W??? URL link frames
+		} elseif ($parsedFrame['frame_name'][0] == 'W') { // 4.3. W??? URL link frames
 			//   There may only be one URL link frame of its kind in a tag,
 			//   except when stated otherwise in the frame description
 			// <Header for 'URL link frame', ID: 'W000' - 'WZZZ', excluding 'WXXX'
@@ -1010,19 +990,16 @@ class getid3_id3v2 extends getid3_handler
 			if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($frame_textencoding_terminator), 1)) === 0) {
 				$frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00
 			}
-			$frame_description = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
-			if (in_array($frame_description, array("\x00", "\x00\x00", "\xFF\xFE", "\xFE\xFF"))) {
-				// if description only contains a BOM or terminator then make it blank
-				$frame_description = '';
-			}
+			$parsedFrame['description'] = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
+			$parsedFrame['description'] = $this->MakeUTF16emptyStringEmpty($parsedFrame['description']);
 			$parsedFrame['data'] = substr($parsedFrame['data'], $frame_terminatorpos + strlen($frame_textencoding_terminator));
+			$parsedFrame['data'] = $this->RemoveStringTerminator($parsedFrame['data'], $frame_textencoding_terminator);
 
 			$parsedFrame['encodingid']   = $frame_textencoding;
 			$parsedFrame['encoding']     = $this->TextEncodingNameLookup($frame_textencoding);
 
 			$parsedFrame['language']     = $frame_language;
 			$parsedFrame['languagename'] = $this->LanguageLookup($frame_language, false);
-			$parsedFrame['description']  = $frame_description;
 			if (!empty($parsedFrame['framenameshort']) && !empty($parsedFrame['data'])) {
 				$info['id3v2']['comments'][$parsedFrame['framenameshort']][] = getid3_lib::iconv_fallback($parsedFrame['encoding'], $info['id3v2']['encoding'], $parsedFrame['data']);
 			}
@@ -1077,7 +1054,7 @@ class getid3_id3v2 extends getid3_handler
 					$parsedFrame['lyrics'][$timestampindex]['data'] = substr($frame_remainingdata, $frame_offset, $frame_terminatorpos - $frame_offset);
 
 					$frame_remainingdata = substr($frame_remainingdata, $frame_terminatorpos + strlen($frame_textencoding_terminator));
-					if (($timestampindex == 0) && (ord($frame_remainingdata{0}) != 0)) {
+					if (($timestampindex == 0) && (ord($frame_remainingdata[0]) != 0)) {
 						// timestamp probably omitted for first data item
 					} else {
 						$parsedFrame['lyrics'][$timestampindex]['timestamp'] = getid3_lib::BigEndian2Int(substr($frame_remainingdata, 0, 4));
@@ -1118,19 +1095,16 @@ class getid3_id3v2 extends getid3_handler
 				if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($frame_textencoding_terminator), 1)) === 0) {
 					$frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00
 				}
-				$frame_description = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
-				if (in_array($frame_description, array("\x00", "\x00\x00", "\xFF\xFE", "\xFE\xFF"))) {
-					// if description only contains a BOM or terminator then make it blank
-					$frame_description = '';
-				}
+				$parsedFrame['description'] = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
+				$parsedFrame['description'] = $this->MakeUTF16emptyStringEmpty($parsedFrame['description']);
 				$frame_text = (string) substr($parsedFrame['data'], $frame_terminatorpos + strlen($frame_textencoding_terminator));
+				$frame_text = $this->RemoveStringTerminator($frame_text, $frame_textencoding_terminator);
 
 				$parsedFrame['encodingid']   = $frame_textencoding;
 				$parsedFrame['encoding']     = $this->TextEncodingNameLookup($frame_textencoding);
 
 				$parsedFrame['language']     = $frame_language;
 				$parsedFrame['languagename'] = $this->LanguageLookup($frame_language, false);
-				$parsedFrame['description']  = $frame_description;
 				$parsedFrame['data']         = $frame_text;
 				if (!empty($parsedFrame['framenameshort']) && !empty($parsedFrame['data'])) {
 					$commentkey = ($parsedFrame['description'] ? $parsedFrame['description'] : (!empty($info['id3v2']['comments'][$parsedFrame['framenameshort']]) ? count($info['id3v2']['comments'][$parsedFrame['framenameshort']]) : 0));
@@ -1423,26 +1397,22 @@ class getid3_id3v2 extends getid3_handler
 				if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($frame_textencoding_terminator), 1)) === 0) {
 					$frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00
 				}
-				$frame_description = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
-				if (in_array($frame_description, array("\x00", "\x00\x00", "\xFF\xFE", "\xFE\xFF"))) {
-					// if description only contains a BOM or terminator then make it blank
-					$frame_description = '';
-				}
-				$parsedFrame['encodingid']       = $frame_textencoding;
-				$parsedFrame['encoding']         = $this->TextEncodingNameLookup($frame_textencoding);
+				$parsedFrame['description']   = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
+				$parsedFrame['description']   = $this->MakeUTF16emptyStringEmpty($parsedFrame['description']);
+				$parsedFrame['encodingid']    = $frame_textencoding;
+				$parsedFrame['encoding']      = $this->TextEncodingNameLookup($frame_textencoding);
 
 				if ($id3v2_majorversion == 2) {
-					$parsedFrame['imagetype']    = isset($frame_imagetype) ? $frame_imagetype : null;
+					$parsedFrame['imagetype'] = isset($frame_imagetype) ? $frame_imagetype : null;
 				} else {
-					$parsedFrame['mime']         = isset($frame_mimetype) ? $frame_mimetype : null;
+					$parsedFrame['mime']      = isset($frame_mimetype) ? $frame_mimetype : null;
 				}
-				$parsedFrame['picturetypeid']    = $frame_picturetype;
-				$parsedFrame['picturetype']      = $this->APICPictureTypeLookup($frame_picturetype);
-				$parsedFrame['description']      = $frame_description;
-				$parsedFrame['data']             = substr($parsedFrame['data'], $frame_terminatorpos + strlen($frame_textencoding_terminator));
-				$parsedFrame['datalength']       = strlen($parsedFrame['data']);
+				$parsedFrame['picturetypeid'] = $frame_picturetype;
+				$parsedFrame['picturetype']   = $this->APICPictureTypeLookup($frame_picturetype);
+				$parsedFrame['data']          = substr($parsedFrame['data'], $frame_terminatorpos + strlen($frame_textencoding_terminator));
+				$parsedFrame['datalength']    = strlen($parsedFrame['data']);
 
-				$parsedFrame['image_mime'] = '';
+				$parsedFrame['image_mime']    = '';
 				$imageinfo = array();
 				if ($imagechunkcheck = getid3_lib::GetDataImageSize($parsedFrame['data'], $imageinfo)) {
 					if (($imagechunkcheck[2] >= 1) && ($imagechunkcheck[2] <= 3)) {
@@ -1550,11 +1520,8 @@ class getid3_id3v2 extends getid3_handler
 			if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($frame_textencoding_terminator), 1)) === 0) {
 				$frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00
 			}
-			$frame_description = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
-			if (in_array($frame_description, array("\x00", "\x00\x00", "\xFF\xFE", "\xFE\xFF"))) {
-				// if description only contains a BOM or terminator then make it blank
-				$frame_description = '';
-			}
+			$parsedFrame['description'] = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
+			$parsedFrame['description'] = $this->MakeUTF16emptyStringEmpty($parsedFrame['description']);
 			$frame_offset = $frame_terminatorpos + strlen($frame_textencoding_terminator);
 
 			$parsedFrame['objectdata']  = (string) substr($parsedFrame['data'], $frame_offset);
@@ -1563,7 +1530,6 @@ class getid3_id3v2 extends getid3_handler
 
 			$parsedFrame['mime']        = $frame_mimetype;
 			$parsedFrame['filename']    = $frame_filename;
-			$parsedFrame['description'] = $frame_description;
 			unset($parsedFrame['data']);
 
 
@@ -1633,16 +1599,12 @@ class getid3_id3v2 extends getid3_handler
 			$frame_offset = $frame_terminatorpos + strlen("\x00");
 
 			$frame_terminatorpos = strpos($parsedFrame['data'], "\x00", $frame_offset);
-			$frame_description = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
-			if (in_array($frame_description, array("\x00", "\x00\x00", "\xFF\xFE", "\xFE\xFF"))) {
-				// if description only contains a BOM or terminator then make it blank
-				$frame_description = '';
-			}
+			$parsedFrame['description'] = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
+			$parsedFrame['description'] = $this->MakeUTF16emptyStringEmpty($parsedFrame['description']);
 			$frame_offset = $frame_terminatorpos + strlen("\x00");
 
 			$parsedFrame['ownerid']     = $frame_ownerid;
 			$parsedFrame['data']        = (string) substr($parsedFrame['data'], $frame_offset);
-			$parsedFrame['description'] = $frame_description;
 			unset($parsedFrame['data']);
 
 
@@ -1738,7 +1700,8 @@ class getid3_id3v2 extends getid3_handler
 			$parsedFrame['encodingid']   = $frame_textencoding;
 			$parsedFrame['encoding']     = $this->TextEncodingNameLookup($frame_textencoding);
 
-			$parsedFrame['data']         = (string) substr($parsedFrame['data'], $frame_offset);
+			$parsedFrame['data'] = (string) substr($parsedFrame['data'], $frame_offset);
+			$parsedFrame['data'] = $this->RemoveStringTerminator($parsedFrame['data'], $this->TextEncodingTerminatorLookup($frame_textencoding));
 			if (!empty($parsedFrame['framenameshort']) && !empty($parsedFrame['data'])) {
 				$info['id3v2']['comments'][$parsedFrame['framenameshort']][] = getid3_lib::iconv_fallback($parsedFrame['encoding'], $info['id3v2']['encoding'], $parsedFrame['data']);
 			}
@@ -1776,6 +1739,7 @@ class getid3_id3v2 extends getid3_handler
 			$frame_offset += 8;
 
 			$parsedFrame['seller'] = (string) substr($parsedFrame['data'], $frame_offset);
+			$parsedFrame['seller'] = $this->RemoveStringTerminator($parsedFrame['seller'], $this->TextEncodingTerminatorLookup($frame_textencoding));
 			unset($parsedFrame['data']);
 
 
@@ -1834,11 +1798,8 @@ class getid3_id3v2 extends getid3_handler
 			if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($frame_textencoding_terminator), 1)) === 0) {
 				$frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00
 			}
-			$frame_description = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
-			if (in_array($frame_description, array("\x00", "\x00\x00", "\xFF\xFE", "\xFE\xFF"))) {
-				// if description only contains a BOM or terminator then make it blank
-				$frame_description = '';
-			}
+			$parsedFrame['description'] = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
+			$parsedFrame['description'] = $this->MakeUTF16emptyStringEmpty($parsedFrame['description']);
 			$frame_offset = $frame_terminatorpos + strlen($frame_textencoding_terminator);
 
 			$frame_terminatorpos = strpos($parsedFrame['data'], "\x00", $frame_offset);
@@ -1855,7 +1816,6 @@ class getid3_id3v2 extends getid3_handler
 			$parsedFrame['receivedasid']      = $frame_receivedasid;
 			$parsedFrame['receivedas']        = $this->COMRReceivedAsLookup($frame_receivedasid);
 			$parsedFrame['sellername']        = $frame_sellername;
-			$parsedFrame['description']       = $frame_description;
 			$parsedFrame['mime']              = $frame_mimetype;
 			$parsedFrame['logo']              = $frame_sellerlogo;
 			unset($parsedFrame['data']);
@@ -2019,9 +1979,9 @@ class getid3_id3v2 extends getid3_handler
 			// Element ID      <text string> $00
 			// Start time      $xx xx xx xx
 			// End time        $xx xx xx xx
-            // Start offset    $xx xx xx xx
-            // End offset      $xx xx xx xx
-            // <Optional embedded sub-frames>
+			// Start offset    $xx xx xx xx
+			// End offset      $xx xx xx xx
+			// <Optional embedded sub-frames>
 
 			$frame_offset = 0;
 			@list($parsedFrame['element_id']) = explode("\x00", $parsedFrame['data'], 2);
@@ -2062,7 +2022,7 @@ class getid3_id3v2 extends getid3_handler
 					$subframe['encodingid'] = ord(substr($subframe_rawdata, 0, 1));
 					$subframe['text']       =     substr($subframe_rawdata, 1);
 					$subframe['encoding']   = $this->TextEncodingNameLookup($subframe['encodingid']);
-					$encoding_converted_text = trim(getid3_lib::iconv_fallback($subframe['encoding'], $info['encoding'], $subframe['text']));;
+					$encoding_converted_text = trim(getid3_lib::iconv_fallback($subframe['encoding'], $info['encoding'], $subframe['text']));
 					switch (substr($encoding_converted_text, 0, 2)) {
 						case "\xFF\xFE":
 						case "\xFE\xFF":
@@ -2082,22 +2042,51 @@ class getid3_id3v2 extends getid3_handler
 							break;
 					}
 
-					if (($subframe['name'] == 'TIT2') || ($subframe['name'] == 'TIT3')) {
-						if ($subframe['name'] == 'TIT2') {
+					switch ($subframe['name']) {
+						case 'TIT2':
 							$parsedFrame['chapter_name']        = $encoding_converted_text;
-						} elseif ($subframe['name'] == 'TIT3') {
+							$parsedFrame['subframes'][] = $subframe;
+							break;
+						case 'TIT3':
 							$parsedFrame['chapter_description'] = $encoding_converted_text;
-						}
-						$parsedFrame['subframes'][] = $subframe;
-					} else {
-						$this->warning('ID3v2.CHAP subframe "'.$subframe['name'].'" not handled (only TIT2 and TIT3)');
+							$parsedFrame['subframes'][] = $subframe;
+							break;
+						case 'WXXX':
+							list($subframe['chapter_url_description'], $subframe['chapter_url']) = explode("\x00", $encoding_converted_text, 2);
+							$parsedFrame['chapter_url'][$subframe['chapter_url_description']] = $subframe['chapter_url'];
+							$parsedFrame['subframes'][] = $subframe;
+							break;
+						case 'APIC':
+							if (preg_match('#^([^\\x00]+)*\\x00(.)([^\\x00]+)*\\x00(.+)$#s', $subframe['text'], $matches)) {
+								list($dummy, $subframe_apic_mime, $subframe_apic_picturetype, $subframe_apic_description, $subframe_apic_picturedata) = $matches;
+								$subframe['image_mime']   = trim(getid3_lib::iconv_fallback($subframe['encoding'], $info['encoding'], $subframe_apic_mime));
+								$subframe['picture_type'] = $this->APICPictureTypeLookup($subframe_apic_picturetype);
+								$subframe['description']  = trim(getid3_lib::iconv_fallback($subframe['encoding'], $info['encoding'], $subframe_apic_description));
+								if (strlen($this->TextEncodingTerminatorLookup($subframe['encoding'])) == 2) {
+									// the null terminator between "description" and "picture data" could be either 1 byte (ISO-8859-1, UTF-8) or two bytes (UTF-16)
+									// the above regex assumes one byte, if it's actually two then strip the second one here
+									$subframe_apic_picturedata = substr($subframe_apic_picturedata, 1);
+								}
+								$subframe['data'] = $subframe_apic_picturedata;
+								unset($dummy, $subframe_apic_mime, $subframe_apic_picturetype, $subframe_apic_description, $subframe_apic_picturedata);
+								unset($subframe['text'], $parsedFrame['text']);
+								$parsedFrame['subframes'][] = $subframe;
+								$parsedFrame['picture_present'] = true;
+							} else {
+								$this->warning('ID3v2.CHAP subframe #'.(count($parsedFrame['subframes']) + 1).' "'.$subframe['name'].'" not in expected format');
+							}
+							break;
+						default:
+							$this->warning('ID3v2.CHAP subframe "'.$subframe['name'].'" not handled (supported: TIT2, TIT3, WXXX, APIC)');
+							break;
 					}
 				}
 				unset($subframe_rawdata, $subframe, $encoding_converted_text);
+				unset($parsedFrame['data']); // debatable whether this this be here, without it the returned structure may contain a large amount of duplicate data if chapters contain APIC
 			}
 
 			$id3v2_chapter_entry = array();
-			foreach (array('id', 'time_begin', 'time_end', 'offset_begin', 'offset_end', 'chapter_name', 'chapter_description') as $id3v2_chapter_key) {
+			foreach (array('id', 'time_begin', 'time_end', 'offset_begin', 'offset_end', 'chapter_name', 'chapter_description', 'chapter_url', 'picture_present') as $id3v2_chapter_key) {
 				if (isset($parsedFrame[$id3v2_chapter_key])) {
 					$id3v2_chapter_entry[$id3v2_chapter_key] = $parsedFrame[$id3v2_chapter_key];
 				}
@@ -2116,7 +2105,7 @@ class getid3_id3v2 extends getid3_handler
 			// CTOC flags        %xx
 			// Entry count       $xx
 			// Child Element ID  <string>$00   /* zero or more child CHAP or CTOC entries */
-            // <Optional embedded sub-frames>
+			// <Optional embedded sub-frames>
 
 			$frame_offset = 0;
 			@list($parsedFrame['element_id']) = explode("\x00", $parsedFrame['data'], 2);
@@ -3695,6 +3684,35 @@ class getid3_id3v2 extends getid3_handler
 		return (isset($TextEncodingNameLookup[$encoding]) ? $TextEncodingNameLookup[$encoding] : 'ISO-8859-1');
 	}
 
+	/**
+	 * @param string $string
+	 * @param string $terminator
+	 *
+	 * @return string
+	 */
+	public static function RemoveStringTerminator($string, $terminator) {
+		// Null terminator at end of comment string is somewhat ambiguous in the specification, may or may not be implemented by various taggers. Remove terminator only if present.
+		// https://github.com/JamesHeinrich/getID3/issues/121
+		// https://community.mp3tag.de/t/x-trailing-nulls-in-id3v2-comments/19227
+		if (substr($string, -strlen($terminator), strlen($terminator)) === $terminator) {
+			$string = substr($string, 0, -strlen($terminator));
+		}
+		return $string;
+	}
+
+	/**
+	 * @param string $string
+	 *
+	 * @return string
+	 */
+	public static function MakeUTF16emptyStringEmpty($string) {
+		if (in_array($string, array("\x00", "\x00\x00", "\xFF\xFE", "\xFE\xFF"))) {
+			// if string only contains a BOM or terminator then make it actually an empty string
+			$string = '';
+		}
+		return $string;
+	}
+
 	/**
 	 * @param string $framename
 	 * @param int    $id3v2majorversion
@@ -3724,10 +3742,10 @@ class getid3_id3v2 extends getid3_handler
 	 */
 	public static function IsANumber($numberstring, $allowdecimal=false, $allownegative=false) {
 		for ($i = 0; $i < strlen($numberstring); $i++) {
-			if ((chr($numberstring{$i}) < chr('0')) || (chr($numberstring{$i}) > chr('9'))) {
-				if (($numberstring{$i} == '.') && $allowdecimal) {
+			if ((chr($numberstring[$i]) < chr('0')) || (chr($numberstring[$i]) > chr('9'))) {
+				if (($numberstring[$i] == '.') && $allowdecimal) {
 					// allowed
-				} elseif (($numberstring{$i} == '-') && $allownegative && ($i == 0)) {
+				} elseif (($numberstring[$i] == '-') && $allownegative && ($i == 0)) {
 					// allowed
 				} else {
 					return false;
diff --git a/lib/getid3/module.tag.xmp.php b/lib/getid3/module.tag.xmp.php
index a976ef8eaa2cf85479c8ffb066630996137bf1f1..00f61eaa3771f9dbf435f4becd5f7d1af27aeedc 100644
--- a/lib/getid3/module.tag.xmp.php
+++ b/lib/getid3/module.tag.xmp.php
@@ -114,7 +114,7 @@ class Image_XMP
 		$data = fread($filehnd, 2);
 
 		// Check that the third character is 0xFF (Start of first segment header)
-		if ($data{0} != "\xFF")
+		if ($data[0] != "\xFF")
 		{
 			// NO FF found - close file and return - JPEG is probably corrupted
 			fclose($filehnd);
@@ -124,16 +124,16 @@ class Image_XMP
 		// Flag that we havent yet hit the compressed image data
 		$hit_compressed_image_data = false;
 
-        $headerdata = array();
+		$headerdata = array();
 		// Cycle through the file until, one of: 1) an EOI (End of image) marker is hit,
 		//                                       2) we have hit the compressed image data (no more headers are allowed after data)
 		//                                       3) or end of file is hit
 
-		while (($data{1} != "\xD9") && (!$hit_compressed_image_data) && (!feof($filehnd)))
+		while (($data[1] != "\xD9") && (!$hit_compressed_image_data) && (!feof($filehnd)))
 		{
 			// Found a segment to look at.
 			// Check that the segment marker is not a Restart marker - restart markers don't have size or data after them
-			if ((ord($data{1}) < 0xD0) || (ord($data{1}) > 0xD7))
+			if ((ord($data[1]) < 0xD0) || (ord($data[1]) > 0xD7))
 			{
 				// Segment isn't a Restart marker
 				// Read the next two bytes (size)
@@ -150,15 +150,15 @@ class Image_XMP
 
 				// Store the segment information in the output array
 				$headerdata[] = array(
-					'SegType'      => ord($data{1}),
-					'SegName'      => $GLOBALS['JPEG_Segment_Names'][ord($data{1})],
+					'SegType'      => ord($data[1]),
+					'SegName'      => $GLOBALS['JPEG_Segment_Names'][ord($data[1])],
 					'SegDataStart' => $segdatastart,
 					'SegData'      => $segdata,
 				);
 			}
 
 			// If this is a SOS (Start Of Scan) segment, then there is no more header data - the compressed image data follows
-			if ($data{1} == "\xDA")
+			if ($data[1] == "\xDA")
 			{
 				// Flag that we have hit the compressed image data - exit loop as no more headers available.
 				$hit_compressed_image_data = true;
@@ -169,7 +169,7 @@ class Image_XMP
 				$data = fread($filehnd, 2);
 
 				// Check that the first byte of the two is 0xFF as it should be for a marker
-				if ($data{0} != "\xFF")
+				if ($data[0] != "\xFF")
 				{
 					// NO FF found - close file and return - JPEG is probably corrupted
 					fclose($filehnd);
diff --git a/lib/getid3/write.id3v1.php b/lib/getid3/write.id3v1.php
index 6f84867d7469f90635557f5bbc8de639c37f533f..48d4e2eaa27de040b8de3bd56e162f19e03c984a 100644
--- a/lib/getid3/write.id3v1.php
+++ b/lib/getid3/write.id3v1.php
@@ -68,16 +68,16 @@ class getid3_write_id3v1
 				} else {
 					fseek($fp_source, 0, SEEK_END);    // append new ID3v1 tag
 				}
-				$this->tag_data['track'] = (isset($this->tag_data['track']) ? $this->tag_data['track'] : (isset($this->tag_data['track_number']) ? $this->tag_data['track_number'] : (isset($this->tag_data['tracknumber']) ? $this->tag_data['tracknumber'] : '')));
+				$this->tag_data['track_number'] = (isset($this->tag_data['track_number']) ? $this->tag_data['track_number'] : '');
 
 				$new_id3v1_tag_data = getid3_id3v1::GenerateID3v1Tag(
-														(isset($this->tag_data['title']  ) ? $this->tag_data['title']   : ''),
-														(isset($this->tag_data['artist'] ) ? $this->tag_data['artist']  : ''),
-														(isset($this->tag_data['album']  ) ? $this->tag_data['album']   : ''),
-														(isset($this->tag_data['year']   ) ? $this->tag_data['year']    : ''),
-														(isset($this->tag_data['genreid']) ? $this->tag_data['genreid'] : ''),
-														(isset($this->tag_data['comment']) ? $this->tag_data['comment'] : ''),
-														(isset($this->tag_data['track']  ) ? $this->tag_data['track']   : ''));
+														(isset($this->tag_data['title']       ) ? $this->tag_data['title']        : ''),
+														(isset($this->tag_data['artist']      ) ? $this->tag_data['artist']       : ''),
+														(isset($this->tag_data['album']       ) ? $this->tag_data['album']        : ''),
+														(isset($this->tag_data['year']        ) ? $this->tag_data['year']         : ''),
+														(isset($this->tag_data['genreid']     ) ? $this->tag_data['genreid']      : ''),
+														(isset($this->tag_data['comment']     ) ? $this->tag_data['comment']      : ''),
+														(isset($this->tag_data['track_number']) ? $this->tag_data['track_number'] : ''));
 				fwrite($fp_source, $new_id3v1_tag_data, 128);
 				fclose($fp_source);
 				return true;
diff --git a/lib/getid3/write.id3v2.php b/lib/getid3/write.id3v2.php
index fae45d29cd21060161f8fef5cb27d2c30bde3cfc..a013014856d2b04a10f8d2275fd51c206fe4425c 100644
--- a/lib/getid3/write.id3v2.php
+++ b/lib/getid3/write.id3v2.php
@@ -345,7 +345,7 @@ class getid3_write_id3v2
 	public function GenerateID3v2FrameFlags($TagAlter=false, $FileAlter=false, $ReadOnly=false, $Compression=false, $Encryption=false, $GroupingIdentity=false, $Unsynchronisation=false, $DataLengthIndicator=false) {
 		$flag1 = null;
 		$flag2 = null;
-	    switch ($this->majorversion) {
+		switch ($this->majorversion) {
 			case 4:
 				// %0abc0000 %0h00kmnp
 				$flag1  = '0';
@@ -1235,7 +1235,7 @@ class getid3_write_id3v2
 				default:
 					if ((($this->majorversion == 2) && (strlen($frame_name) != 3)) || (($this->majorversion > 2) && (strlen($frame_name) != 4))) {
 						$this->errors[] = 'Invalid frame name "'.$frame_name.'" for ID3v2.'.$this->majorversion;
-					} elseif ($frame_name{0} == 'T') {
+					} elseif ($frame_name[0] == 'T') {
 						// 4.2. T???  Text information frames
 						// Text encoding                $xx
 						// Information                  <text string(s) according to encoding>
@@ -1246,7 +1246,7 @@ class getid3_write_id3v2
 							$framedata .= chr($source_data_array['encodingid']);
 							$framedata .= $source_data_array['data'];
 						}
-					} elseif ($frame_name{0} == 'W') {
+					} elseif ($frame_name[0] == 'W') {
 						// 4.3. W???  URL link frames
 						// URL              <text string>
 						if (!$this->IsValidURL($source_data_array['data'], false)) {
@@ -1402,7 +1402,7 @@ class getid3_write_id3v2
 					break;
 
 				default:
-					if (($frame_name{0} != 'T') && ($frame_name{0} != 'W')) {
+					if (($frame_name[0] != 'T') && ($frame_name[0] != 'W')) {
 						$this->errors[] = 'Frame not allowed in ID3v2.'.$this->majorversion.': '.$frame_name;
 					}
 					break;
@@ -1525,7 +1525,7 @@ class getid3_write_id3v2
 					break;
 
 				default:
-					if (($frame_name{0} != 'T') && ($frame_name{0} != 'W')) {
+					if (($frame_name[0] != 'T') && ($frame_name[0] != 'W')) {
 						$this->errors[] = 'Frame not allowed in ID3v2.'.$this->majorversion.': '.$frame_name;
 					}
 					break;
@@ -1617,7 +1617,7 @@ class getid3_write_id3v2
 					break;
 
 				default:
-					if (($frame_name{0} != 'T') && ($frame_name{0} != 'W')) {
+					if (($frame_name[0] != 'T') && ($frame_name[0] != 'W')) {
 						$this->errors[] = 'Frame not allowed in ID3v2.'.$this->majorversion.': '.$frame_name;
 					}
 					break;
@@ -1958,10 +1958,10 @@ class getid3_write_id3v2
 		$unsyncheddata = '';
 		$datalength = strlen($data);
 		for ($i = 0; $i < $datalength; $i++) {
-			$thischar = $data{$i};
+			$thischar = $data[$i];
 			$unsyncheddata .= $thischar;
 			if ($thischar == "\xFF") {
-				$nextchar = ord($data{$i + 1});
+				$nextchar = ord($data[$i + 1]);
 				if (($nextchar & 0xE0) == 0xE0) {
 					// previous byte = 11111111, this byte = 111?????
 					$unsyncheddata .= "\x00";
@@ -2316,6 +2316,7 @@ class getid3_write_id3v2
 			$ID3v2ShortFrameNameLookup[4]['performer_sort_order']              = 'TSOP';
 			$ID3v2ShortFrameNameLookup[4]['title_sort_order']                  = 'TSOT';
 			$ID3v2ShortFrameNameLookup[4]['set_subtitle']                      = 'TSST';
+			$ID3v2ShortFrameNameLookup[4]['year']                              = 'TDRC'; // subset of ISO 8601: valid timestamps are yyyy, yyyy-MM, yyyy-MM-dd, yyyy-MM-ddTHH, yyyy-MM-ddTHH:mm and yyyy-MM-ddTHH:mm:ss. All time stamps are UTC
 		}
 		return (isset($ID3v2ShortFrameNameLookup[$majorversion][strtolower($long_description)]) ? $ID3v2ShortFrameNameLookup[$majorversion][strtolower($long_description)] : '');
 
diff --git a/lib/getid3/write.php b/lib/getid3/write.php
index cc98e5d3289b535d4428fe1c440655b387480968..8ffa3635ec4f4ea7bdd20d5fcf9391cac8ea9f27 100644
--- a/lib/getid3/write.php
+++ b/lib/getid3/write.php
@@ -311,9 +311,9 @@ class getid3_writetags
 			}
 		}
 
-		// 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'];
+		// Convert "TRACK" to "TRACK_NUMBER" (if needed) for compatability with all formats
+		if (isset($this->tag_data['TRACK']) && !isset($this->tag_data['TRACK_NUMBER'])) {
+			$this->tag_data['TRACK_NUMBER'] = $this->tag_data['TRACK'];
 			unset($this->tag_data['TRACK']);
 		}
 
@@ -559,14 +559,14 @@ class getid3_writetags
 				}
 			}
 		}
-		$tag_data_id3v1['title']   =        getid3_lib::iconv_fallback($this->tag_encoding, 'ISO-8859-1', implode(' ', (isset($this->tag_data['TITLE']      ) ? $this->tag_data['TITLE']       : array())));
-		$tag_data_id3v1['artist']  =        getid3_lib::iconv_fallback($this->tag_encoding, 'ISO-8859-1', implode(' ', (isset($this->tag_data['ARTIST']     ) ? $this->tag_data['ARTIST']      : array())));
-		$tag_data_id3v1['album']   =        getid3_lib::iconv_fallback($this->tag_encoding, 'ISO-8859-1', implode(' ', (isset($this->tag_data['ALBUM']      ) ? $this->tag_data['ALBUM']       : array())));
-		$tag_data_id3v1['year']    =        getid3_lib::iconv_fallback($this->tag_encoding, 'ISO-8859-1', implode(' ', (isset($this->tag_data['YEAR']       ) ? $this->tag_data['YEAR']        : array())));
-		$tag_data_id3v1['comment'] =        getid3_lib::iconv_fallback($this->tag_encoding, 'ISO-8859-1', implode(' ', (isset($this->tag_data['COMMENT']    ) ? $this->tag_data['COMMENT']     : array())));
-		$tag_data_id3v1['track']   = intval(getid3_lib::iconv_fallback($this->tag_encoding, 'ISO-8859-1', implode(' ', (isset($this->tag_data['TRACKNUMBER']) ? $this->tag_data['TRACKNUMBER'] : array()))));
-		if ($tag_data_id3v1['track'] <= 0) {
-			$tag_data_id3v1['track'] = '';
+		$tag_data_id3v1['title']        =        getid3_lib::iconv_fallback($this->tag_encoding, 'ISO-8859-1', implode(' ', (isset($this->tag_data['TITLE']       ) ? $this->tag_data['TITLE']        : array())));
+		$tag_data_id3v1['artist']       =        getid3_lib::iconv_fallback($this->tag_encoding, 'ISO-8859-1', implode(' ', (isset($this->tag_data['ARTIST']      ) ? $this->tag_data['ARTIST']       : array())));
+		$tag_data_id3v1['album']        =        getid3_lib::iconv_fallback($this->tag_encoding, 'ISO-8859-1', implode(' ', (isset($this->tag_data['ALBUM']       ) ? $this->tag_data['ALBUM']        : array())));
+		$tag_data_id3v1['year']         =        getid3_lib::iconv_fallback($this->tag_encoding, 'ISO-8859-1', implode(' ', (isset($this->tag_data['YEAR']        ) ? $this->tag_data['YEAR']         : array())));
+		$tag_data_id3v1['comment']      =        getid3_lib::iconv_fallback($this->tag_encoding, 'ISO-8859-1', implode(' ', (isset($this->tag_data['COMMENT']     ) ? $this->tag_data['COMMENT']      : array())));
+		$tag_data_id3v1['track_number'] = intval(getid3_lib::iconv_fallback($this->tag_encoding, 'ISO-8859-1', implode(' ', (isset($this->tag_data['TRACK_NUMBER']) ? $this->tag_data['TRACK_NUMBER'] : array()))));
+		if ($tag_data_id3v1['track_number'] <= 0) {
+			$tag_data_id3v1['track_number'] = '';
 		}
 
 		$this->MergeExistingTagData('id3v1', $tag_data_id3v1);
@@ -675,7 +675,7 @@ class getid3_writetags
 									do {
 										// if UTF-8 string does not include any characters above chr(127) then it is identical to ISO-8859-1
 										for ($i = 0; $i < strlen($value); $i++) {
-											if (ord($value{$i}) > 127) {
+											if (ord($value[$i]) > 127) {
 												break 2;
 											}
 										}
diff --git a/paquet.xml b/paquet.xml
index 9f7960bc4a5b8e4d39db635225eca43dfe9bceba..c083c8deb5618eae9a7ac5cdd2f7ecc993bda686 100644
--- a/paquet.xml
+++ b/paquet.xml
@@ -1,7 +1,7 @@
 <paquet
 	prefix="medias"
 	categorie="multimedia"
-	version="2.25.2"
+	version="2.25.3"
 	etat="stable"
 	compatibilite="[3.3.0-dev;3.3.*]"
 	logo="prive/themes/spip/images/portfolio-32.png"