context: //
  // File.cs:
  //
  // Author:
  //   Brian Nickel (brian.nickel@gmail.com)
  //
  // Copyright (C) 2007 Brian Nickel
  //
  // This library is free software; you can redistribute it and/or modify
  // it  under the terms of the GNU Lesser General Public License version
  // 2.1 as published by the Free Software Foundation.
  //
  // This library is distributed in the hope that it will be useful, but
  // WITHOUT ANY WARRANTY; without even the implied warranty of
  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  // Lesser General Public License for more details.
  //
  // You should have received a copy of the GNU Lesser General Public
  // License along with this library; if not, write to the Free Software
  // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307
  // USA
  //
  
  using System;
  
  namespace TagLib.Riff
  {
  	/// <summary>
  	///    This class extends <see cref="TagLib.File" /> to provide
  	///    support for reading and writing tags and properties for files
  	///    using the RIFF file format such as AVI and Wave files.
  	/// </summary>
  	[SupportedMimeType("taglib/avi", "avi")]
  	[SupportedMimeType("taglib/wav", "wav")]
  	[SupportedMimeType("taglib/divx", "divx")]
  	[SupportedMimeType("video/avi")]
  	[SupportedMimeType("video/msvideo")]
  	[SupportedMimeType("video/x-msvideo")]
  	[SupportedMimeType("image/avi")]
  	[SupportedMimeType("application/x-troff-msvideo")]
  	[SupportedMimeType("audio/avi")]
  	[SupportedMimeType("audio/wav")]
  	[SupportedMimeType("audio/wave")]
  	[SupportedMimeType("audio/x-wav")]
  	public class File : TagLib.File
  	{
  #region Private Fields
  		
  		/// <summary>
  		///  Contains all the tags of the file.
  		/// </summary>
  		private CombinedTag tag = new CombinedTag ();
  		
  		/// <summary>
  		///  Contains the INFO tag.
  		/// </summary>
  		private InfoTag info_tag = null;
  		
  		/// <summary>
  		///  Contains the MovieID tag.
  		/// </summary>
  		private MovieIdTag mid_tag = null;
  		
  		/// <summary>
  		///  Contains the DivX tag.
  		/// </summary>
  		private DivXTag divx_tag = null;
  		
  		/// <summary>
  		///  Contains the Id3v2 tag.
  		/// </summary>
  		private Id3v2.Tag id32_tag = null;
  		
  		/// <summary>
  		///  Contains the media properties.
  		/// </summary>
  		private Properties properties = null;
  		
  #endregion
  		
  		
  		
  #region Public Static Fields
  		
  		/// <summary>
  		///    The identifier used to recognize a RIFF files.
  		/// </summary>
  		/// <value>
  		///    "RIFF"
  		/// </value>
  		public static readonly ReadOnlyByteVector FileIdentifier = "RIFF";
  		
  #endregion
  		
  		
  		
  #region Public Constructors
  		
  		/// <summary>
  		///    Constructs and initializes a new instance of <see
  		///    cref="File" /> for a specified path in the local file
  		///    system and specified read style.
  		/// </summary>
  		/// <param name="path">
  		///    A <see cref="string" /> object containing the path of the
  		///    file to use in the new instance.
  		/// </param>
  		/// <param name="propertiesStyle">
  		///    A <see cref="ReadStyle" /> value specifying at what level
  		///    of accuracy to read the media properties, or <see
  		///    cref="ReadStyle.None" /> to ignore the properties.
  		/// </param>
  		/// <exception cref="ArgumentNullException">
  		///    <paramref name="path" /> is <see langword="null" />.
  		/// </exception>
  		public File (string path, ReadStyle propertiesStyle)
  			: this (new File.LocalFileAbstraction (path),
  			        propertiesStyle)
  		{
  		}
  
  		/// <summary>
  		///    Constructs and initializes a new instance of <see
  		///    cref="File" /> for a specified path in the local file
  		///    system with an average read style.
  		/// </summary>
  		/// <param name="path">
  		///    A <see cref="string" /> object containing the path of the
  		///    file to use in the new instance.
  		/// </param>
  		/// <exception cref="ArgumentNullException">
  		///    <paramref name="path" /> is <see langword="null" />.
  		/// </exception>
  		public File (string path) : this (path, ReadStyle.Average)
  		{
  		}
        
  		/// <summary>
  		///    Constructs and initializes a new instance of <see
  		///    cref="File" /> for a specified file abstraction and
  		///    specified read style.
  		/// </summary>
  		/// <param name="abstraction">
  		///    A <see cref="IFileAbstraction" /> object to use when
  		///    reading from and writing to the file.
  		/// </param>
  		/// <param name="propertiesStyle">
  		///    A <see cref="ReadStyle" /> value specifying at what level
  		///    of accuracy to read the media properties, or <see
  		///    cref="ReadStyle.None" /> to ignore the properties.
  		/// </param>
  		/// <exception cref="ArgumentNullException">
  		///    <paramref name="abstraction" /> is <see langword="null"
  		///    />.
  		/// </exception>
  		public File (File.IFileAbstraction abstraction,
  		             ReadStyle propertiesStyle) : base (abstraction)
  		{
  			uint riff_size;
  			long tag_start, tag_end;
  
  			Mode = AccessMode.Read;
  			try {
  				Read (true, propertiesStyle, out riff_size,
  					out tag_start, out tag_end);
  			} finally {
  				Mode = AccessMode.Closed;
  			}
  
  			TagTypesOnDisk = TagTypes;
  
  			GetTag (TagTypes.Id3v2, true);
  			GetTag (TagTypes.RiffInfo, true);
  			GetTag (TagTypes.MovieId, true);
  			GetTag (TagTypes.DivX, true);
  		}
  
  		/// <summary>
  		///    Constructs and initializes a new instance of <see
  		///    cref="File" /> for a specified file abstraction with an
  		///    average read style.
  		/// </summary>
  		/// <param name="abstraction">
  		///    A <see cref="IFileAbstraction" /> object to use when
  		///    reading from and writing to the file.
  		/// </param>
  		/// <exception cref="ArgumentNullException">
  		///    <paramref name="abstraction" /> is <see langword="null"
  		///    />.
  		/// </exception>
  		public File (File.IFileAbstraction abstraction)
  			: this (abstraction, ReadStyle.Average)
  		{}
  		
  #endregion
  		
  		
  		
  #region Public Properties
  		
  		/// <summary>
  		///    Gets a abstract representation of all tags stored in the
  		///    current instance.
  		/// </summary>
  		/// <value>
  		///    A <see cref="TagLib.Tag" /> object representing all tags
  		///    stored in the current instance.
  		/// </value>
  		public override Tag Tag {
  			get {return tag;}
  		}
  		
  		/// <summary>
  		///    Gets the media properties of the file represented by the
  		///    current instance.
  		/// </summary>
  		/// <value>
  		///    A <see cref="TagLib.Properties" /> object containing the
  		///    media properties of the file represented by the current
  		///    instance.
  		/// </value>
  		public override TagLib.Properties Properties {
  			get {return properties;}
  		}
  		
  #endregion
  		
  		
  		
  #region Public Methods
  		
  		/// <summary>
  		///    Saves the changes made in the current instance to the
  		///    file it represents.
  		/// </summary>
  		public override void Save ()
  		{
  			Mode = AccessMode.Write;
  			try {
  				ByteVector data = new ByteVector ();
  				
  				// Enclose the Id3v2 tag in an "ID32" item and
  				// embed it as the first tag.
  				if (id32_tag != null) {
  					ByteVector tag_data = id32_tag.Render ();
  					if (tag_data.Count > 10) {
  						if (tag_data.Count % 2 == 1)
  							tag_data.Add (0);
  						data.Add ("ID32");
  						data.Add (ByteVector.FromUInt (
  							(uint) tag_data.Count,
  							false));
  						data.Add (tag_data);
  					}
  				}
  				
  				// Embed "INFO" as the second tag.
  				if (info_tag != null)
  					data.Add (info_tag.RenderEnclosed ());
  
  				// Embed "MID " as the third tag.
  				if (mid_tag != null)
  					data.Add (mid_tag.RenderEnclosed ());
  
  				// Embed the DivX tag in "IDVX and embed it as
  				// the fourth tag.
  				if (divx_tag != null && !divx_tag.IsEmpty) {
  					ByteVector tag_data = divx_tag.Render ();
  					data.Add ("IDVX");
  					data.Add (ByteVector.FromUInt (
  						(uint) tag_data.Count, false));
  					data.Add (tag_data);
  				}
  				
  				// Read the file to determine the current RIFF
  				// size and the area tagging does in.
  				uint riff_size;
  				long tag_start, tag_end;
  				Read (false, ReadStyle.None, out riff_size,
  					out tag_start, out tag_end);
  				
  				// If tagging info cannot be found, place it at
  				// the end of the file.
  				if (tag_start < 12 || tag_end < tag_start)
  					tag_start = tag_end = Length;
  				
  				int length = (int)(tag_end - tag_start);
  				
  				// If the tag isn't at the end of the file,
  				// try appending using padding to improve
  				// write time now or for subsequent writes.
  				if (tag_end != Length) {
  					int padding_size = length - data.Count - 8;
  					if (padding_size < 0)
  						padding_size = 1024;
  					
  					
  					data.Add ("JUNK");
  					data.Add (ByteVector.FromUInt (
  						(uint)padding_size, false));
  					data.Add (new ByteVector (padding_size));
  				}
  				
  				// Insert the tagging data.
  				Insert (data, tag_start, length);
  				
  				// If the data size changed, and the tagging
  				// data is within the RIFF portion of the file,
  				// update the riff size.
  				if (data.Count - length != 0 &&
  					tag_start <= riff_size)
  					Insert (ByteVector.FromUInt ((uint)
  						(riff_size + data.Count - length),
  						false), 4, 4);
  				
  				// Update the tag types.
  				TagTypesOnDisk = TagTypes;
  			} finally {
  				Mode = AccessMode.Closed;
  			}
  		}
  		
  		/// <summary>
  		///    Removes a set of tag types from the current instance.
  		/// </summary>
  		/// <param name="types">
  		///    A bitwise combined <see cref="TagLib.TagTypes" /> value
  		///    containing tag types to be removed from the file.
  		/// </param>
  		/// <remarks>
  		///    In order to remove all tags from a file, pass <see
  		///    cref="TagTypes.AllTags" /> as <paramref name="types" />.
  		/// </remarks>
  		public override void RemoveTags (TagTypes types)
  		{
  			if ((types & TagLib.TagTypes.Id3v2) != TagLib.TagTypes.None)
  				id32_tag = null;
  
gt: 			if ((types & TagLib.TagTypes.RiffInfo) != TagLib.TagTypes.None)