懒得勤快 2 年之前
父節點
當前提交
ef5aa5a19f
共有 1 個文件被更改,包括 2117 次插入2117 次删除
  1. 2117 2117
      Masuit.Tools.Abstractions/Files/FileDetector/AbstractCompoundFileDetailDetector.cs

+ 2117 - 2117
Masuit.Tools.Abstractions/Files/FileDetector/AbstractCompoundFileDetailDetector.cs

@@ -1,331 +1,331 @@
-using System;
+using Masuit.Tools.AspNetCore.Mime;
+using System;
 using System.Collections.Generic;
 using System.IO;
 using System.Linq;
 using System.Reflection;
 using System.Text;
-using Masuit.Tools.AspNetCore.Mime;
 
 namespace Masuit.Tools.Files.FileDetector;
 
 public abstract class AbstractCompoundFileDetailDetector : AbstractSignatureDetector
 {
-    private static readonly SignatureInformation[] CfSignatureInfo = {
-        new() { Position = 0, Signature = new byte [] { 0xd0, 0xcf, 0x11, 0xe0, 0xa1, 0xb1, 0x1a, 0xe1 } },
-    };
-
-    /// <inheritdoc />
-    protected override SignatureInformation[] SignatureInformations => CfSignatureInfo;
-
-    public abstract IEnumerable<string> Chunks { get; }
-
-    public override string MimeType => new MimeMapper().GetMimeFromExtension("." + Extension);
-
-    public override List<FormatCategory> FormatCategories => GetType().GetCustomAttributes<FormatCategoryAttribute>().Select(a => a.Category).ToList();
-
-    protected abstract bool IsValidChunk(string chunkName, byte[] chunkData);
-
-    public override bool Detect(Stream stream)
-    {
-        if (!base.Detect(stream))
-        {
-            return false;
-        }
-
-        stream.Position = 0;
-        try
-        {
-            using var cf = new CompoundFile(stream, CFSConfiguration.LeaveOpen | CFSConfiguration.Default);
-            foreach (var chunk in Chunks)
-            {
-                var compoundFileStream = cf.RootStorage.GetStream(chunk);
-                if (compoundFileStream == null || !IsValidChunk(chunk, compoundFileStream.GetData()))
-                {
-                    return false;
-                }
-            }
-
-            return true;
-        }
-        catch
-        {
-            return false;
-        }
-    }
-
-    #region Modified OpenMCDF
-
-    // -------------------------------------------------------------
-    // This is a porting from java code, under MIT license of       |
-    // the beautiful Red-Black Tree implementation you can find at  |
-    // http://en.literateprograms.org/Red-black_tree_(Java)#chunk   |
-    // Many Thanks to original Implementors.                        |
-    // -------------------------------------------------------------
-    internal enum Color
-    { RED = 0, BLACK = 1 }
-
-    internal enum NodeOp
-    {
-        LAssigned, RAssigned, CAssigned, PAssigned, VAssigned
-    }
-
-    internal interface IRBNode : IComparable
-    {
-        IRBNode Left { get; set; }
-
-        IRBNode Right { get; set; }
-
-        Color Color { get; set; }
-
-        IRBNode Parent { get; set; }
-
-        IRBNode Grandparent();
-
-        IRBNode Sibling();
-
-        IRBNode Uncle();
-    }
-
-    internal sealed class RBTree
-    {
-        public IRBNode Root { get; set; }
-
-        private static Color NodeColor(IRBNode n) => n == null ? Color.BLACK : n.Color;
-
-        public RBTree()
-        { }
-
-        public RBTree(IRBNode root)
-        { Root = root; }
-
-        private IRBNode Lookup(IRBNode template)
-        {
-            IRBNode n = Root;
-            while (n != null)
-            {
-                int compResult = template.CompareTo(n);
-                if (compResult == 0) return n;
-                n = compResult < 0 ? n.Left : n.Right;
-            }
-            return n;
-        }
-
-        public bool TryLookup(IRBNode template, out IRBNode val)
-        {
-            val = Lookup(template);
-            return val != null;
-        }
-
-        private void Replace(IRBNode oldn, IRBNode newn)
-        {
-            if (oldn.Parent == null) Root = newn;
-            else
-            {
-                if (oldn == oldn.Parent.Left)
-                {
-                    oldn.Parent.Left = newn;
-                }
-                else
-                {
-                    oldn.Parent.Right = newn;
-                }
-            }
-            if (newn != null) newn.Parent = oldn.Parent;
-        }
-
-        private void RotateL(IRBNode n)
-        {
-            IRBNode r = n.Right;
-            Replace(n, r);
-            n.Right = r.Left;
-            if (r.Left != null) r.Left.Parent = n;
-            r.Left = n;
-            n.Parent = r;
-        }
-
-        private void RotateR(IRBNode n)
-        {
-            IRBNode l = n.Left;
-            Replace(n, l);
-            n.Left = l.Right;
-            if (l.Right != null) l.Right.Parent = n;
-            l.Right = n;
-            n.Parent = l;
-        }
-
-        public void Insert(IRBNode newNode)
-        {
-            newNode.Color = Color.RED;
-            IRBNode insertedNode = newNode;
-
-            if (Root == null) Root = insertedNode;
-            else
-            {
-                IRBNode n = Root;
-                while (true)
-                {
-                    int compResult = newNode.CompareTo(n);
-                    if (compResult == 0) throw new Exception($"RBNode {newNode} already present in tree");
-                    if (compResult < 0)
-                    {
-                        if (n.Left == null)
-                        {
-                            n.Left = insertedNode;
-                            break;
-                        }
-
-                        n = n.Left;
-                    }
-                    else
-                    {
-                        if (n.Right == null)
-                        {
-                            n.Right = insertedNode;
-                            break;
-                        }
-                        n = n.Right;
-                    }
-                }
-                insertedNode.Parent = n;
-            }
-
-            Insert1(insertedNode);
-            NodeInserted?.Invoke(insertedNode);
-        }
-
-        private void Insert1(IRBNode n)
-        {
-            if (n.Parent == null) n.Color = Color.BLACK;
-            else Insert2(n);
-        }
-
-        private void Insert2(IRBNode n)
-        {
-            if (NodeColor(n.Parent) == Color.BLACK) return;
-            Insert3(n);
-        }
-
-        private void Insert3(IRBNode n)
-        {
-            if (NodeColor(n.Uncle()) == Color.RED)
-            {
-                n.Parent.Color = Color.BLACK;
-                n.Uncle().Color = Color.BLACK;
-                n.Grandparent().Color = Color.RED;
-                Insert1(n.Grandparent());
-            }
-            else Insert4(n);
-        }
-
-        private void Insert4(IRBNode n)
-        {
-            if (n == n.Parent.Right && n.Parent == n.Grandparent().Left)
-            {
-                RotateL(n.Parent);
-                n = n.Left;
-            }
-            else if (n == n.Parent.Left && n.Parent == n.Grandparent().Right)
-            {
-                RotateR(n.Parent);
-                n = n.Right;
-            }
-            Insert5(n);
-        }
-
-        private void Insert5(IRBNode n)
-        {
-            n.Parent.Color = Color.BLACK;
-            n.Grandparent().Color = Color.RED;
-            if (n == n.Parent.Left && n.Parent == n.Grandparent().Left)
-                RotateR(n.Grandparent());
-            else RotateL(n.Grandparent());
-        }
-
-        private static IRBNode MaximumNode(IRBNode n)
-        {
-            while (n.Right != null)
-                n = n.Right;
-            return n;
-        }
-
-        public void VisitTree(Action<IRBNode> action)
-        {
-            IRBNode walker = Root;
-            if (walker != null)
-                DoVisitTree(action, walker);
-        }
-
-        private void DoVisitTree(Action<IRBNode> action, IRBNode walker)
-        {
-            if (walker.Left != null)
-            {
-                DoVisitTree(action, walker.Left);
-            }
-
-            action?.Invoke(walker);
-            if (walker.Right != null)
-            {
-                DoVisitTree(action, walker.Right);
-            }
-        }
-
-        internal void VisitTreeNodes(Action<IRBNode> action)
-        {
-            IRBNode walker = Root;
-            if (walker != null)
-            {
-                DoVisitTreeNodes(action, walker);
-            }
-        }
-
-        private void DoVisitTreeNodes(Action<IRBNode> action, IRBNode walker)
-        {
-            if (walker.Left != null)
-            {
-                DoVisitTreeNodes(action, walker.Left);
-            }
-
-            action?.Invoke(walker);
-            if (walker.Right != null)
-            {
-                DoVisitTreeNodes(action, walker.Right);
-            }
-        }
-
-        public class RBTreeEnumerator : IEnumerator<IRBNode>
-        {
-            private int position = -1;
-            private Queue<IRBNode> heap = new();
-
-            internal RBTreeEnumerator(RBTree tree)
-            { tree.VisitTreeNodes(item => heap.Enqueue(item)); }
-
-            public IRBNode Current => heap.ElementAt(position);
-
-            public void Dispose()
-            { }
-
-            object System.Collections.IEnumerator.Current => heap.ElementAt(position);
-
-            public bool MoveNext() => (++position < heap.Count);
-
-            public void Reset()
-            { position = -1; }
-        }
-
-        public RBTreeEnumerator GetEnumerator() => new RBTreeEnumerator(this);
-
-        internal void FireNodeOperation(IRBNode node, NodeOp operation)
-        {
-            NodeOperation?.Invoke(node, operation);
-        }
-
-        internal event Action<IRBNode> NodeInserted;
-
-        internal event Action<IRBNode, NodeOp> NodeOperation;
-    }
-
-    /* This Source Code Form is subject to the terms of the Mozilla Public
+	private static readonly SignatureInformation[] CfSignatureInfo = {
+		new() { Position = 0, Signature = new byte [] { 0xd0, 0xcf, 0x11, 0xe0, 0xa1, 0xb1, 0x1a, 0xe1 } },
+	};
+
+	/// <inheritdoc />
+	protected override SignatureInformation[] SignatureInformations => CfSignatureInfo;
+
+	public abstract IEnumerable<string> Chunks { get; }
+
+	public override string MimeType => new MimeMapper().GetMimeFromExtension("." + Extension);
+
+	public override List<FormatCategory> FormatCategories => GetType().GetCustomAttributes<FormatCategoryAttribute>().Select(a => a.Category).ToList();
+
+	protected abstract bool IsValidChunk(string chunkName, byte[] chunkData);
+
+	public override bool Detect(Stream stream)
+	{
+		if (!base.Detect(stream))
+		{
+			return false;
+		}
+
+		stream.Position = 0;
+		try
+		{
+			using var cf = new CompoundFile(stream, CFSConfiguration.LeaveOpen | CFSConfiguration.Default);
+			foreach (var chunk in Chunks)
+			{
+				var compoundFileStream = cf.RootStorage.GetStream(chunk);
+				if (compoundFileStream == null || !IsValidChunk(chunk, compoundFileStream.GetData()))
+				{
+					return false;
+				}
+			}
+
+			return true;
+		}
+		catch
+		{
+			return false;
+		}
+	}
+
+	#region Modified OpenMCDF
+
+	// -------------------------------------------------------------
+	// This is a porting from java code, under MIT license of       |
+	// the beautiful Red-Black Tree implementation you can find at  |
+	// http://en.literateprograms.org/Red-black_tree_(Java)#chunk   |
+	// Many Thanks to original Implementors.                        |
+	// -------------------------------------------------------------
+	internal enum Color
+	{ RED = 0, BLACK = 1 }
+
+	internal enum NodeOp
+	{
+		LAssigned, RAssigned, CAssigned, PAssigned, VAssigned
+	}
+
+	internal interface IRBNode : IComparable
+	{
+		IRBNode Left { get; set; }
+
+		IRBNode Right { get; set; }
+
+		Color Color { get; set; }
+
+		IRBNode Parent { get; set; }
+
+		IRBNode Grandparent();
+
+		IRBNode Sibling();
+
+		IRBNode Uncle();
+	}
+
+	internal sealed class RBTree
+	{
+		public IRBNode Root { get; set; }
+
+		private static Color NodeColor(IRBNode n) => n == null ? Color.BLACK : n.Color;
+
+		public RBTree()
+		{ }
+
+		public RBTree(IRBNode root)
+		{ Root = root; }
+
+		private IRBNode Lookup(IRBNode template)
+		{
+			IRBNode n = Root;
+			while (n != null)
+			{
+				int compResult = template.CompareTo(n);
+				if (compResult == 0) return n;
+				n = compResult < 0 ? n.Left : n.Right;
+			}
+			return n;
+		}
+
+		public bool TryLookup(IRBNode template, out IRBNode val)
+		{
+			val = Lookup(template);
+			return val != null;
+		}
+
+		private void Replace(IRBNode oldn, IRBNode newn)
+		{
+			if (oldn.Parent == null) Root = newn;
+			else
+			{
+				if (oldn == oldn.Parent.Left)
+				{
+					oldn.Parent.Left = newn;
+				}
+				else
+				{
+					oldn.Parent.Right = newn;
+				}
+			}
+			if (newn != null) newn.Parent = oldn.Parent;
+		}
+
+		private void RotateL(IRBNode n)
+		{
+			IRBNode r = n.Right;
+			Replace(n, r);
+			n.Right = r.Left;
+			if (r.Left != null) r.Left.Parent = n;
+			r.Left = n;
+			n.Parent = r;
+		}
+
+		private void RotateR(IRBNode n)
+		{
+			IRBNode l = n.Left;
+			Replace(n, l);
+			n.Left = l.Right;
+			if (l.Right != null) l.Right.Parent = n;
+			l.Right = n;
+			n.Parent = l;
+		}
+
+		public void Insert(IRBNode newNode)
+		{
+			newNode.Color = Color.RED;
+			IRBNode insertedNode = newNode;
+
+			if (Root == null) Root = insertedNode;
+			else
+			{
+				IRBNode n = Root;
+				while (true)
+				{
+					int compResult = newNode.CompareTo(n);
+					if (compResult == 0) throw new Exception($"RBNode {newNode} already present in tree");
+					if (compResult < 0)
+					{
+						if (n.Left == null)
+						{
+							n.Left = insertedNode;
+							break;
+						}
+
+						n = n.Left;
+					}
+					else
+					{
+						if (n.Right == null)
+						{
+							n.Right = insertedNode;
+							break;
+						}
+						n = n.Right;
+					}
+				}
+				insertedNode.Parent = n;
+			}
+
+			Insert1(insertedNode);
+			NodeInserted?.Invoke(insertedNode);
+		}
+
+		private void Insert1(IRBNode n)
+		{
+			if (n.Parent == null) n.Color = Color.BLACK;
+			else Insert2(n);
+		}
+
+		private void Insert2(IRBNode n)
+		{
+			if (NodeColor(n.Parent) == Color.BLACK) return;
+			Insert3(n);
+		}
+
+		private void Insert3(IRBNode n)
+		{
+			if (NodeColor(n.Uncle()) == Color.RED)
+			{
+				n.Parent.Color = Color.BLACK;
+				n.Uncle().Color = Color.BLACK;
+				n.Grandparent().Color = Color.RED;
+				Insert1(n.Grandparent());
+			}
+			else Insert4(n);
+		}
+
+		private void Insert4(IRBNode n)
+		{
+			if (n == n.Parent.Right && n.Parent == n.Grandparent().Left)
+			{
+				RotateL(n.Parent);
+				n = n.Left;
+			}
+			else if (n == n.Parent.Left && n.Parent == n.Grandparent().Right)
+			{
+				RotateR(n.Parent);
+				n = n.Right;
+			}
+			Insert5(n);
+		}
+
+		private void Insert5(IRBNode n)
+		{
+			n.Parent.Color = Color.BLACK;
+			n.Grandparent().Color = Color.RED;
+			if (n == n.Parent.Left && n.Parent == n.Grandparent().Left)
+				RotateR(n.Grandparent());
+			else RotateL(n.Grandparent());
+		}
+
+		private static IRBNode MaximumNode(IRBNode n)
+		{
+			while (n.Right != null)
+				n = n.Right;
+			return n;
+		}
+
+		public void VisitTree(Action<IRBNode> action)
+		{
+			IRBNode walker = Root;
+			if (walker != null)
+				DoVisitTree(action, walker);
+		}
+
+		private void DoVisitTree(Action<IRBNode> action, IRBNode walker)
+		{
+			if (walker.Left != null)
+			{
+				DoVisitTree(action, walker.Left);
+			}
+
+			action?.Invoke(walker);
+			if (walker.Right != null)
+			{
+				DoVisitTree(action, walker.Right);
+			}
+		}
+
+		internal void VisitTreeNodes(Action<IRBNode> action)
+		{
+			IRBNode walker = Root;
+			if (walker != null)
+			{
+				DoVisitTreeNodes(action, walker);
+			}
+		}
+
+		private void DoVisitTreeNodes(Action<IRBNode> action, IRBNode walker)
+		{
+			if (walker.Left != null)
+			{
+				DoVisitTreeNodes(action, walker.Left);
+			}
+
+			action?.Invoke(walker);
+			if (walker.Right != null)
+			{
+				DoVisitTreeNodes(action, walker.Right);
+			}
+		}
+
+		public class RBTreeEnumerator : IEnumerator<IRBNode>
+		{
+			private int position = -1;
+			private Queue<IRBNode> heap = new();
+
+			internal RBTreeEnumerator(RBTree tree)
+			{ tree.VisitTreeNodes(item => heap.Enqueue(item)); }
+
+			public IRBNode Current => heap.ElementAt(position);
+
+			public void Dispose()
+			{ }
+
+			object System.Collections.IEnumerator.Current => heap.ElementAt(position);
+
+			public bool MoveNext() => (++position < heap.Count);
+
+			public void Reset()
+			{ position = -1; }
+		}
+
+		public RBTreeEnumerator GetEnumerator() => new RBTreeEnumerator(this);
+
+		internal void FireNodeOperation(IRBNode node, NodeOp operation)
+		{
+			NodeOperation?.Invoke(node, operation);
+		}
+
+		internal event Action<IRBNode> NodeInserted;
+
+		internal event Action<IRBNode, NodeOp> NodeOperation;
+	}
+
+	/* This Source Code Form is subject to the terms of the Mozilla Public
      * License, v. 2.0. If a copy of the MPL was not distributed with this
      * file, You can obtain one at http://mozilla.org/MPL/2.0/.
      *
@@ -333,1837 +333,1837 @@ public abstract class AbstractCompoundFileDetailDetector : AbstractSignatureDete
      *
      * The Initial Developer of the Original Code is Federico Blaseotto.*/
 
-    internal enum SectorType
-    {
-        Normal, Mini, FAT, DIFAT, RangeLockSector, Directory
-    }
-
-    internal sealed class Sector : IDisposable
-    {
-        public static int MinisectorSize = 64;
-
-        public const int Freesect = unchecked((int)0xFFFFFFFF);
-        public const int Endofchain = unchecked((int)0xFFFFFFFE);
-        public const int Fatsect = unchecked((int)0xFFFFFFFD);
-        public const int Difsect = unchecked((int)0xFFFFFFFC);
-
-        public bool DirtyFlag { get; set; }
-
-        public bool IsStreamed => _stream != null && Size != MinisectorSize && Id * Size + Size < _stream.Length;
-
-        private readonly Stream _stream;
-
-        public Sector(int size, Stream stream)
-        {
-            this.Size = size;
-            this._stream = stream;
-        }
-
-        public Sector(int size, byte[] data)
-        {
-            this.Size = size;
-            this._data = data;
-            this._stream = null;
-        }
-
-        public Sector(int size)
-        {
-            this.Size = size;
-            this._data = null;
-            this._stream = null;
-        }
-
-        internal SectorType Type { get; set; }
-
-        public int Id { get; set; } = -1;
-
-        public int Size { get; private set; } = 0;
-
-        private byte[] _data;
-
-        public byte[] GetData()
-        {
-            if (_data == null)
-            {
-                _data = new byte[Size];
-
-                if (IsStreamed)
-                {
-                    _stream.Seek(Size + Id * Size, SeekOrigin.Begin);
-                    _stream.Read(_data, 0, Size);
-                }
-            }
-
-            return _data;
-        }
-
-        public void ZeroData()
-        {
-            _data = new byte[Size];
-            DirtyFlag = true;
-        }
-
-        public void InitFATData()
-        {
-            _data = new byte[Size];
-
-            for (int i = 0; i < Size; i++)
-                _data[i] = 0xFF;
-
-            DirtyFlag = true;
-        }
-
-        internal void ReleaseData() => this._data = null;
-
-        private readonly object _lockObject = new Object();
-
-        #region IDisposable Members
-
-        private bool _disposed;
-
-        void IDisposable.Dispose()
-        {
-            try
-            {
-                if (!_disposed)
-                {
-                    lock (_lockObject)
-                    {
-                        this._data = null;
-                        this.DirtyFlag = false;
-                        this.Id = Sector.Endofchain;
-                        this.Size = 0;
-                    }
-                }
-            }
-            finally { _disposed = true; }
-            GC.SuppressFinalize(this);
-        }
+	internal enum SectorType
+	{
+		Normal, Mini, FAT, DIFAT, RangeLockSector, Directory
+	}
+
+	internal sealed class Sector : IDisposable
+	{
+		public static int MinisectorSize = 64;
+
+		public const int Freesect = unchecked((int)0xFFFFFFFF);
+		public const int Endofchain = unchecked((int)0xFFFFFFFE);
+		public const int Fatsect = unchecked((int)0xFFFFFFFD);
+		public const int Difsect = unchecked((int)0xFFFFFFFC);
+
+		public bool DirtyFlag { get; set; }
+
+		public bool IsStreamed => _stream != null && Size != MinisectorSize && Id * Size + Size < _stream.Length;
+
+		private readonly Stream _stream;
+
+		public Sector(int size, Stream stream)
+		{
+			this.Size = size;
+			this._stream = stream;
+		}
+
+		public Sector(int size, byte[] data)
+		{
+			this.Size = size;
+			this._data = data;
+			this._stream = null;
+		}
+
+		public Sector(int size)
+		{
+			this.Size = size;
+			this._data = null;
+			this._stream = null;
+		}
+
+		internal SectorType Type { get; set; }
+
+		public int Id { get; set; } = -1;
+
+		public int Size { get; private set; } = 0;
+
+		private byte[] _data;
+
+		public byte[] GetData()
+		{
+			if (_data == null)
+			{
+				_data = new byte[Size];
+
+				if (IsStreamed)
+				{
+					_stream.Seek(Size + Id * Size, SeekOrigin.Begin);
+					_stream.Read(_data, 0, Size);
+				}
+			}
+
+			return _data;
+		}
+
+		public void ZeroData()
+		{
+			_data = new byte[Size];
+			DirtyFlag = true;
+		}
+
+		public void InitFATData()
+		{
+			_data = new byte[Size];
+
+			for (int i = 0; i < Size; i++)
+				_data[i] = 0xFF;
+
+			DirtyFlag = true;
+		}
+
+		internal void ReleaseData() => this._data = null;
+
+		private readonly object _lockObject = new Object();
+
+		#region IDisposable Members
+
+		private bool _disposed;
+
+		void IDisposable.Dispose()
+		{
+			try
+			{
+				if (!_disposed)
+				{
+					lock (_lockObject)
+					{
+						this._data = null;
+						this.DirtyFlag = false;
+						this.Id = Sector.Endofchain;
+						this.Size = 0;
+					}
+				}
+			}
+			finally { _disposed = true; }
+			GC.SuppressFinalize(this);
+		}
 
-        #endregion IDisposable Members
-    }
+		#endregion IDisposable Members
+	}
 
-    internal enum StgType : int
-    {
-        StgInvalid = 0,
-        StgStorage = 1,
-        StgStream = 2,
-        StgLockbytes = 3,
-        StgProperty = 4,
-        StgRoot = 5
-    }
+	internal enum StgType : int
+	{
+		StgInvalid = 0,
+		StgStorage = 1,
+		StgStream = 2,
+		StgLockbytes = 3,
+		StgProperty = 4,
+		StgRoot = 5
+	}
 
-    internal sealed class DirectoryEntry : IRBNode
-    {
-        internal const int THIS_IS_GREATER = 1;
-        internal const int OTHER_IS_GREATER = -1;
-        private IList<DirectoryEntry> dirRepository;
+	internal sealed class DirectoryEntry : IRBNode
+	{
+		internal const int THIS_IS_GREATER = 1;
+		internal const int OTHER_IS_GREATER = -1;
+		private IList<DirectoryEntry> dirRepository;
 
-        public int SID { get; set; } = -1;
+		public int SID { get; set; } = -1;
 
-        internal const Int32 NOSTREAM = unchecked((int)0xFFFFFFFF);
+		internal const Int32 NOSTREAM = unchecked((int)0xFFFFFFFF);
 
-        private DirectoryEntry(String name, StgType stgType, IList<DirectoryEntry> dirRepository)
-        {
-            this.dirRepository = dirRepository;
+		private DirectoryEntry(String name, StgType stgType, IList<DirectoryEntry> dirRepository)
+		{
+			this.dirRepository = dirRepository;
 
-            this.StgType = stgType;
+			this.StgType = stgType;
 
-            switch (stgType)
-            {
-                case StgType.StgStream:
+			switch (stgType)
+			{
+				case StgType.StgStream:
 
-                    StorageCLSID = new Guid(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
-                    CreationDate = new byte[8];
-                    ModifyDate = new byte[8];
-                    break;
+					StorageCLSID = new Guid(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
+					CreationDate = new byte[8];
+					ModifyDate = new byte[8];
+					break;
 
-                case StgType.StgStorage:
-                    CreationDate = BitConverter.GetBytes((DateTime.Now.ToFileTime()));
-                    break;
-
-                case StgType.StgRoot:
-                    CreationDate = new byte[8];
-                    ModifyDate = new byte[8];
-                    break;
-            }
+				case StgType.StgStorage:
+					CreationDate = BitConverter.GetBytes((DateTime.Now.ToFileTime()));
+					break;
+
+				case StgType.StgRoot:
+					CreationDate = new byte[8];
+					ModifyDate = new byte[8];
+					break;
+			}
 
-            this.SetEntryName(name);
-        }
+			this.SetEntryName(name);
+		}
 
-        public byte[] EntryName { get; private set; } = new byte[64];
+		public byte[] EntryName { get; private set; } = new byte[64];
 
-        public String GetEntryName()
-        {
-            if (EntryName != null && EntryName.Length > 0)
-                return Encoding.Unicode.GetString(EntryName).Remove((NameLength - 1) / 2);
-            else return String.Empty;
-        }
+		public String GetEntryName()
+		{
+			if (EntryName != null && EntryName.Length > 0)
+				return Encoding.Unicode.GetString(EntryName).Remove((NameLength - 1) / 2);
+			else return String.Empty;
+		}
 
-        public void SetEntryName(String entryName)
-        {
-            if (entryName.Contains(@"\") || entryName.Contains(@"/") ||
-                entryName.Contains(@":") || entryName.Contains(@"!"))
-                throw new Exception("Invalid character in entry: the characters '\\', '/', ':','!' cannot be used in entry name");
-
-            if (entryName.Length > 31)
-                throw new Exception("Entry name MUST be smaller than 31 characters");
-
-            byte[] newName = null;
-            byte[] temp = Encoding.Unicode.GetBytes(entryName);
-            newName = new byte[64];
-            Buffer.BlockCopy(temp, 0, newName, 0, temp.Length);
-            newName[temp.Length] = 0x00;
-            newName[temp.Length + 1] = 0x00;
-
-            EntryName = newName;
-            NameLength = (ushort)(temp.Length + 2);
-        }
-
-        public ushort NameLength { get; private set; }
-
-        public StgType StgType { get; set; } = StgType.StgInvalid;
-
-        public Color Color { get; set; } = Color.BLACK;
-
-        public Int32 LeftSibling { get; set; } = NOSTREAM;
-
-        public Int32 RightSibling { get; set; } = NOSTREAM;
-
-        public Int32 Child { get; set; } = NOSTREAM;
-
-        public Guid StorageCLSID { get; set; } = Guid.NewGuid();
-
-        public Int32 StateBits { get; set; }
-
-        public byte[] CreationDate { get; set; } = new byte[8] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
-
-        public byte[] ModifyDate { get; set; } = new byte[8] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
-
-        public Int32 StartSetc { get; set; } = Sector.Endofchain;
-
-        public long Size { get; set; }
-
-        public int CompareTo(object obj)
-        {
-            DirectoryEntry otherDir = obj as DirectoryEntry;
-
-            if (otherDir == null)
-                throw new Exception("Invalid casting: compared object does not implement IDirectorEntry interface");
-
-            if (this.NameLength > otherDir.NameLength)
-                return THIS_IS_GREATER;
-            else if (this.NameLength < otherDir.NameLength)
-                return OTHER_IS_GREATER;
-            else
-            {
-                String thisName = Encoding.Unicode.GetString(this.EntryName, 0, this.NameLength);
-                String otherName = Encoding.Unicode.GetString(otherDir.EntryName, 0, otherDir.NameLength);
-
-                for (int z = 0; z < thisName.Length; z++)
-                {
-                    char thisChar = char.ToUpperInvariant(thisName[z]);
-                    char otherChar = char.ToUpperInvariant(otherName[z]);
-
-                    if (thisChar > otherChar)
-                        return THIS_IS_GREATER;
-                    else if (thisChar < otherChar)
-                        return OTHER_IS_GREATER;
-                }
-
-                return 0;
-            }
-        }
-
-        public override bool Equals(object obj)
-        {
-            return CompareTo(obj) == 0;
-        }
-
-        private static ulong Fnv_hash(byte[] buffer)
-        {
-            ulong h = 2166136261;
-            int i;
-
-            for (i = 0; i < buffer.Length; i++)
-                h = (h * 16777619) ^ buffer[i];
-
-            return h;
-        }
-
-        public override int GetHashCode()
-        {
-            return (int)Fnv_hash(EntryName);
-        }
-
-        public void Read(Stream stream, int ver = 3)
-        {
-            using (BinaryReader rw = new BinaryReader(stream, Encoding.UTF8, true))
-            {
-                EntryName = rw.ReadBytes(64);
-                NameLength = rw.ReadUInt16();
-                StgType = (StgType)rw.ReadByte();
-                Color = (Color)rw.ReadByte();
-                LeftSibling = rw.ReadInt32();
-                RightSibling = rw.ReadInt32();
-                Child = rw.ReadInt32();
-
-                if (StgType == StgType.StgInvalid)
-                {
-                    LeftSibling = NOSTREAM;
-                    RightSibling = NOSTREAM;
-                    Child = NOSTREAM;
-                }
-
-                StorageCLSID = new Guid(rw.ReadBytes(16));
-                StateBits = rw.ReadInt32();
-                CreationDate = rw.ReadBytes(8);
-                ModifyDate = rw.ReadBytes(8);
-                StartSetc = rw.ReadInt32();
-
-                if (ver == 3)
-                {
-                    Size = rw.ReadInt32();
-                    rw.ReadBytes(4);
-                }
-                else
-                    Size = rw.ReadInt64();
-            }
-        }
-
-        public string Name => GetEntryName();
-
-        public IRBNode Left
-        {
-            get
-            {
-                if (LeftSibling == NOSTREAM)
-                    return null;
-                return dirRepository[LeftSibling];
-            }
-
-            set
-            {
-                LeftSibling = value != null ? (value as DirectoryEntry).SID : NOSTREAM;
-                if (LeftSibling != NOSTREAM)
-                    dirRepository[LeftSibling].Parent = this;
-            }
-        }
-
-        public IRBNode Right
-        {
-            get
-            {
-                if (RightSibling == NOSTREAM)
-                    return null;
-                return dirRepository[RightSibling];
-            }
-
-            set
-            {
-                RightSibling = value != null ? ((DirectoryEntry)value).SID : NOSTREAM;
-                if (RightSibling != NOSTREAM)
-                    dirRepository[RightSibling].Parent = this;
-            }
-        }
-
-        public IRBNode Parent { get; set; }
-
-        public IRBNode Grandparent() => Parent?.Parent;
-
-        public IRBNode Sibling() => (this == Parent.Left) ? Parent.Right : Parent.Left;
-
-        public IRBNode Uncle() => Parent?.Sibling();
-
-        internal static DirectoryEntry New(String name, StgType stgType, IList<DirectoryEntry> dirRepository)
-        {
-            DirectoryEntry de = null;
-            if (dirRepository != null)
-            {
-                de = new DirectoryEntry(name, stgType, dirRepository);
-                dirRepository.Add(de);
-                de.SID = dirRepository.Count - 1;
-            }
-            else
-                throw new ArgumentNullException("dirRepository", "Directory repository cannot be null in New() method");
-
-            return de;
-        }
-
-        internal static DirectoryEntry Mock(String name, StgType stgType) => new DirectoryEntry(name, stgType, null);
-
-        internal static DirectoryEntry TryNew(String name, StgType stgType, IList<DirectoryEntry> dirRepository)
-        {
-            DirectoryEntry de = new DirectoryEntry(name, stgType, dirRepository);
-
-            if (de != null)
-            {
-                for (int i = 0; i < dirRepository.Count; i++)
-                {
-                    if (dirRepository[i].StgType == StgType.StgInvalid)
-                    {
-                        dirRepository[i] = de;
-                        de.SID = i;
-                        return de;
-                    }
-                }
-            }
-
-            dirRepository.Add(de);
-            de.SID = dirRepository.Count - 1;
-
-            return de;
-        }
-
-        public override string ToString() => $"{Name} [{SID}]{(StgType == StgType.StgStream ? "Stream" : "Storage")}";
-
-        public void AssignValueTo(IRBNode other)
-        {
-            DirectoryEntry d = other as DirectoryEntry;
-
-            d.SetEntryName(GetEntryName());
-
-            d.CreationDate = new byte[CreationDate.Length];
-            CreationDate.CopyTo(d.CreationDate, 0);
-
-            d.ModifyDate = new byte[ModifyDate.Length];
-            ModifyDate.CopyTo(d.ModifyDate, 0);
-
-            d.Size = Size;
-            d.StartSetc = StartSetc;
-            d.StateBits = StateBits;
-            d.StgType = StgType;
-            d.StorageCLSID = new Guid(StorageCLSID.ToByteArray());
-            d.Child = Child;
-        }
-    }
-
-    internal abstract class CFItem : IComparable<CFItem>
-    {
-        private CompoundFile compoundFile;
-
-        protected CompoundFile CompoundFile => compoundFile;
-
-        protected void CheckDisposed()
-        {
-            if (compoundFile.IsClosed)
-                throw new ObjectDisposedException("Owner Compound file has been closed and owned items have been invalidated");
-        }
-
-        protected CFItem()
-        { }
-
-        protected CFItem(CompoundFile compoundFile)
-        { this.compoundFile = compoundFile; }
-
-        internal DirectoryEntry DirEntry { get; set; }
-
-        internal int CompareTo(CFItem other) => DirEntry.CompareTo(other.DirEntry);
-
-        public int CompareTo(object obj) => DirEntry.CompareTo((obj as CFItem).DirEntry);
-
-        public static bool operator ==(CFItem leftItem, CFItem rightItem)
-        {
-            if (System.Object.ReferenceEquals(leftItem, rightItem))
-                return true;
-            if (((object)leftItem == null) || ((object)rightItem == null))
-                return false;
-            return leftItem.CompareTo(rightItem) == 0;
-        }
-
-        public static bool operator !=(CFItem leftItem, CFItem rightItem) => !(leftItem == rightItem);
-
-        public override bool Equals(object obj) => CompareTo(obj) == 0;
-
-        public override int GetHashCode() => DirEntry.GetEntryName().GetHashCode();
-
-        public string Name
-        {
-            get
-            {
-                var n = DirEntry.GetEntryName();
-                return (n != null && n.Length > 0) ? n.TrimEnd('\0') : string.Empty;
-            }
-        }
-
-        public long Size => DirEntry.Size;
-
-        public bool IsStorage => DirEntry.StgType == StgType.StgStorage;
-
-        public bool IsStream => DirEntry.StgType == StgType.StgStream;
-
-        public bool IsRoot => DirEntry.StgType == StgType.StgRoot;
-
-        public DateTime CreationDate
-        {
-            get => DateTime.FromFileTime(BitConverter.ToInt64(DirEntry.CreationDate, 0));
-
-            set
-            {
-                if (DirEntry.StgType != StgType.StgStream && DirEntry.StgType != StgType.StgRoot)
-                    DirEntry.CreationDate = BitConverter.GetBytes((value.ToFileTime()));
-                else
-                    throw new Exception("Creation Date can only be set on storage entries");
-            }
-        }
-
-        public DateTime ModifyDate
-        {
-            get => DateTime.FromFileTime(BitConverter.ToInt64(DirEntry.ModifyDate, 0));
-
-            set
-            {
-                if (DirEntry.StgType != StgType.StgStream && DirEntry.StgType != StgType.StgRoot)
-                    DirEntry.ModifyDate = BitConverter.GetBytes((value.ToFileTime()));
-                else
-                    throw new Exception("Modify Date can only be set on storage entries");
-            }
-        }
-
-        public Guid CLSID
-        {
-            get => DirEntry.StorageCLSID;
-
-            set
-            {
-                if (DirEntry.StgType != StgType.StgStream)
-                    DirEntry.StorageCLSID = value;
-                else
-                    throw new Exception("Object class GUID can only be set on Root and Storage entries");
-            }
-        }
-
-        int IComparable<CFItem>.CompareTo(CFItem other) => DirEntry.CompareTo(other.DirEntry);
-
-        public override string ToString()
-        {
-            return (DirEntry != null)
-                ? $"[{DirEntry.LeftSibling},{DirEntry.SID},{DirEntry.RightSibling}] {DirEntry.GetEntryName()}"
-                : string.Empty;
-        }
-    }
-
-    internal sealed class CFStream : CFItem
-    {
-        internal CFStream(CompoundFile compoundFile, DirectoryEntry dirEntry)
-            : base(compoundFile)
-        {
-            if (dirEntry == null || dirEntry.SID < 0)
-                throw new Exception("Attempting to add a CFStream using an unitialized directory");
-            this.DirEntry = dirEntry;
-        }
-
-        public Byte[] GetData()
-        {
-            CheckDisposed();
-            return this.CompoundFile.GetData(this);
-        }
-
-        public int Read(byte[] buffer, long position, int count)
-        {
-            CheckDisposed();
-            return this.CompoundFile.ReadData(this, position, buffer, 0, count);
-        }
-
-        internal int Read(byte[] buffer, long position, int offset, int count)
-        {
-            CheckDisposed();
-            return this.CompoundFile.ReadData(this, position, buffer, offset, count);
-        }
-    }
-
-    internal sealed class CFStorage : CFItem
-    {
-        private RBTree children;
-
-        internal RBTree Children
-        {
-            get
-            {
-                if (children == null)
-                    children = LoadChildren(this.DirEntry.SID) ?? this.CompoundFile.CreateNewTree();
-                return children;
-            }
-        }
-
-        internal CFStorage(CompoundFile compFile, DirectoryEntry dirEntry)
-            : base(compFile)
-        {
-            if (dirEntry == null || dirEntry.SID < 0)
-                throw new Exception("Attempting to create a CFStorage using an unitialized directory");
-            this.DirEntry = dirEntry;
-        }
-
-        private RBTree LoadChildren(int SID)
-        {
-            RBTree childrenTree = this.CompoundFile.GetChildrenTree(SID);
-
-            if (childrenTree.Root != null)
-                this.DirEntry.Child = (childrenTree.Root as DirectoryEntry).SID;
-            else
-                this.DirEntry.Child = DirectoryEntry.NOSTREAM;
-
-            return childrenTree;
-        }
-
-        public CFStream GetStream(String streamName)
-        {
-            CheckDisposed();
-
-            DirectoryEntry tmp = DirectoryEntry.Mock(streamName, StgType.StgStream);
-
-            if (Children.TryLookup(tmp, out IRBNode outDe) && (((DirectoryEntry)outDe).StgType == StgType.StgStream))
-                return new CFStream(this.CompoundFile, (DirectoryEntry)outDe);
-            else
-                throw new KeyNotFoundException("Cannot find item [" + streamName + "] within the current storage");
-        }
-
-        public CFStream TryGetStream(String streamName)
-        {
-            CheckDisposed();
-
-            DirectoryEntry tmp = DirectoryEntry.Mock(streamName, StgType.StgStream);
+		public void SetEntryName(String entryName)
+		{
+			if (entryName.Contains(@"\") || entryName.Contains(@"/") ||
+				entryName.Contains(@":") || entryName.Contains(@"!"))
+				throw new Exception("Invalid character in entry: the characters '\\', '/', ':','!' cannot be used in entry name");
+
+			if (entryName.Length > 31)
+				throw new Exception("Entry name MUST be smaller than 31 characters");
+
+			byte[] newName = null;
+			byte[] temp = Encoding.Unicode.GetBytes(entryName);
+			newName = new byte[64];
+			Buffer.BlockCopy(temp, 0, newName, 0, temp.Length);
+			newName[temp.Length] = 0x00;
+			newName[temp.Length + 1] = 0x00;
+
+			EntryName = newName;
+			NameLength = (ushort)(temp.Length + 2);
+		}
+
+		public ushort NameLength { get; private set; }
+
+		public StgType StgType { get; set; } = StgType.StgInvalid;
+
+		public Color Color { get; set; } = Color.BLACK;
+
+		public Int32 LeftSibling { get; set; } = NOSTREAM;
+
+		public Int32 RightSibling { get; set; } = NOSTREAM;
+
+		public Int32 Child { get; set; } = NOSTREAM;
+
+		public Guid StorageCLSID { get; set; } = Guid.NewGuid();
+
+		public Int32 StateBits { get; set; }
+
+		public byte[] CreationDate { get; set; } = new byte[8] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+
+		public byte[] ModifyDate { get; set; } = new byte[8] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+
+		public Int32 StartSetc { get; set; } = Sector.Endofchain;
+
+		public long Size { get; set; }
+
+		public int CompareTo(object obj)
+		{
+			DirectoryEntry otherDir = obj as DirectoryEntry;
+
+			if (otherDir == null)
+				throw new Exception("Invalid casting: compared object does not implement IDirectorEntry interface");
+
+			if (this.NameLength > otherDir.NameLength)
+				return THIS_IS_GREATER;
+			else if (this.NameLength < otherDir.NameLength)
+				return OTHER_IS_GREATER;
+			else
+			{
+				String thisName = Encoding.Unicode.GetString(this.EntryName, 0, this.NameLength);
+				String otherName = Encoding.Unicode.GetString(otherDir.EntryName, 0, otherDir.NameLength);
+
+				for (int z = 0; z < thisName.Length; z++)
+				{
+					char thisChar = char.ToUpperInvariant(thisName[z]);
+					char otherChar = char.ToUpperInvariant(otherName[z]);
+
+					if (thisChar > otherChar)
+						return THIS_IS_GREATER;
+					else if (thisChar < otherChar)
+						return OTHER_IS_GREATER;
+				}
+
+				return 0;
+			}
+		}
+
+		public override bool Equals(object obj)
+		{
+			return CompareTo(obj) == 0;
+		}
+
+		private static ulong Fnv_hash(byte[] buffer)
+		{
+			ulong h = 2166136261;
+			int i;
+
+			for (i = 0; i < buffer.Length; i++)
+				h = (h * 16777619) ^ buffer[i];
+
+			return h;
+		}
+
+		public override int GetHashCode()
+		{
+			return (int)Fnv_hash(EntryName);
+		}
+
+		public void Read(Stream stream, int ver = 3)
+		{
+			using (BinaryReader rw = new BinaryReader(stream, Encoding.UTF8, true))
+			{
+				EntryName = rw.ReadBytes(64);
+				NameLength = rw.ReadUInt16();
+				StgType = (StgType)rw.ReadByte();
+				Color = (Color)rw.ReadByte();
+				LeftSibling = rw.ReadInt32();
+				RightSibling = rw.ReadInt32();
+				Child = rw.ReadInt32();
+
+				if (StgType == StgType.StgInvalid)
+				{
+					LeftSibling = NOSTREAM;
+					RightSibling = NOSTREAM;
+					Child = NOSTREAM;
+				}
+
+				StorageCLSID = new Guid(rw.ReadBytes(16));
+				StateBits = rw.ReadInt32();
+				CreationDate = rw.ReadBytes(8);
+				ModifyDate = rw.ReadBytes(8);
+				StartSetc = rw.ReadInt32();
+
+				if (ver == 3)
+				{
+					Size = rw.ReadInt32();
+					rw.ReadBytes(4);
+				}
+				else
+					Size = rw.ReadInt64();
+			}
+		}
+
+		public string Name => GetEntryName();
+
+		public IRBNode Left
+		{
+			get
+			{
+				if (LeftSibling == NOSTREAM)
+					return null;
+				return dirRepository[LeftSibling];
+			}
+
+			set
+			{
+				LeftSibling = value != null ? (value as DirectoryEntry).SID : NOSTREAM;
+				if (LeftSibling != NOSTREAM)
+					dirRepository[LeftSibling].Parent = this;
+			}
+		}
+
+		public IRBNode Right
+		{
+			get
+			{
+				if (RightSibling == NOSTREAM)
+					return null;
+				return dirRepository[RightSibling];
+			}
+
+			set
+			{
+				RightSibling = value != null ? ((DirectoryEntry)value).SID : NOSTREAM;
+				if (RightSibling != NOSTREAM)
+					dirRepository[RightSibling].Parent = this;
+			}
+		}
+
+		public IRBNode Parent { get; set; }
+
+		public IRBNode Grandparent() => Parent?.Parent;
+
+		public IRBNode Sibling() => (this == Parent.Left) ? Parent.Right : Parent.Left;
+
+		public IRBNode Uncle() => Parent?.Sibling();
+
+		internal static DirectoryEntry New(String name, StgType stgType, IList<DirectoryEntry> dirRepository)
+		{
+			DirectoryEntry de = null;
+			if (dirRepository != null)
+			{
+				de = new DirectoryEntry(name, stgType, dirRepository);
+				dirRepository.Add(de);
+				de.SID = dirRepository.Count - 1;
+			}
+			else
+				throw new ArgumentNullException("dirRepository", "Directory repository cannot be null in New() method");
+
+			return de;
+		}
+
+		internal static DirectoryEntry Mock(String name, StgType stgType) => new DirectoryEntry(name, stgType, null);
+
+		internal static DirectoryEntry TryNew(String name, StgType stgType, IList<DirectoryEntry> dirRepository)
+		{
+			DirectoryEntry de = new DirectoryEntry(name, stgType, dirRepository);
+
+			if (de != null)
+			{
+				for (int i = 0; i < dirRepository.Count; i++)
+				{
+					if (dirRepository[i].StgType == StgType.StgInvalid)
+					{
+						dirRepository[i] = de;
+						de.SID = i;
+						return de;
+					}
+				}
+			}
+
+			dirRepository.Add(de);
+			de.SID = dirRepository.Count - 1;
+
+			return de;
+		}
+
+		public override string ToString() => $"{Name} [{SID}]{(StgType == StgType.StgStream ? "Stream" : "Storage")}";
+
+		public void AssignValueTo(IRBNode other)
+		{
+			DirectoryEntry d = other as DirectoryEntry;
+
+			d.SetEntryName(GetEntryName());
+
+			d.CreationDate = new byte[CreationDate.Length];
+			CreationDate.CopyTo(d.CreationDate, 0);
+
+			d.ModifyDate = new byte[ModifyDate.Length];
+			ModifyDate.CopyTo(d.ModifyDate, 0);
+
+			d.Size = Size;
+			d.StartSetc = StartSetc;
+			d.StateBits = StateBits;
+			d.StgType = StgType;
+			d.StorageCLSID = new Guid(StorageCLSID.ToByteArray());
+			d.Child = Child;
+		}
+	}
+
+	internal abstract class CFItem : IComparable<CFItem>
+	{
+		private CompoundFile compoundFile;
+
+		protected CompoundFile CompoundFile => compoundFile;
+
+		protected void CheckDisposed()
+		{
+			if (compoundFile.IsClosed)
+				throw new ObjectDisposedException("Owner Compound file has been closed and owned items have been invalidated");
+		}
+
+		protected CFItem()
+		{ }
+
+		protected CFItem(CompoundFile compoundFile)
+		{ this.compoundFile = compoundFile; }
+
+		internal DirectoryEntry DirEntry { get; set; }
+
+		internal int CompareTo(CFItem other) => DirEntry.CompareTo(other.DirEntry);
+
+		public int CompareTo(object obj) => DirEntry.CompareTo((obj as CFItem).DirEntry);
+
+		public static bool operator ==(CFItem leftItem, CFItem rightItem)
+		{
+			if (System.Object.ReferenceEquals(leftItem, rightItem))
+				return true;
+			if (((object)leftItem == null) || ((object)rightItem == null))
+				return false;
+			return leftItem.CompareTo(rightItem) == 0;
+		}
+
+		public static bool operator !=(CFItem leftItem, CFItem rightItem) => !(leftItem == rightItem);
+
+		public override bool Equals(object obj) => CompareTo(obj) == 0;
+
+		public override int GetHashCode() => DirEntry.GetEntryName().GetHashCode();
+
+		public string Name
+		{
+			get
+			{
+				var n = DirEntry.GetEntryName();
+				return (n != null && n.Length > 0) ? n.TrimEnd('\0') : string.Empty;
+			}
+		}
+
+		public long Size => DirEntry.Size;
+
+		public bool IsStorage => DirEntry.StgType == StgType.StgStorage;
+
+		public bool IsStream => DirEntry.StgType == StgType.StgStream;
+
+		public bool IsRoot => DirEntry.StgType == StgType.StgRoot;
+
+		public DateTime CreationDate
+		{
+			get => DateTime.FromFileTime(BitConverter.ToInt64(DirEntry.CreationDate, 0));
+
+			set
+			{
+				if (DirEntry.StgType != StgType.StgStream && DirEntry.StgType != StgType.StgRoot)
+					DirEntry.CreationDate = BitConverter.GetBytes((value.ToFileTime()));
+				else
+					throw new Exception("Creation Date can only be set on storage entries");
+			}
+		}
+
+		public DateTime ModifyDate
+		{
+			get => DateTime.FromFileTime(BitConverter.ToInt64(DirEntry.ModifyDate, 0));
+
+			set
+			{
+				if (DirEntry.StgType != StgType.StgStream && DirEntry.StgType != StgType.StgRoot)
+					DirEntry.ModifyDate = BitConverter.GetBytes((value.ToFileTime()));
+				else
+					throw new Exception("Modify Date can only be set on storage entries");
+			}
+		}
+
+		public Guid CLSID
+		{
+			get => DirEntry.StorageCLSID;
+
+			set
+			{
+				if (DirEntry.StgType != StgType.StgStream)
+					DirEntry.StorageCLSID = value;
+				else
+					throw new Exception("Object class GUID can only be set on Root and Storage entries");
+			}
+		}
+
+		int IComparable<CFItem>.CompareTo(CFItem other) => DirEntry.CompareTo(other.DirEntry);
+
+		public override string ToString()
+		{
+			return (DirEntry != null)
+				? $"[{DirEntry.LeftSibling},{DirEntry.SID},{DirEntry.RightSibling}] {DirEntry.GetEntryName()}"
+				: string.Empty;
+		}
+	}
+
+	internal sealed class CFStream : CFItem
+	{
+		internal CFStream(CompoundFile compoundFile, DirectoryEntry dirEntry)
+			: base(compoundFile)
+		{
+			if (dirEntry == null || dirEntry.SID < 0)
+				throw new Exception("Attempting to add a CFStream using an unitialized directory");
+			this.DirEntry = dirEntry;
+		}
+
+		public Byte[] GetData()
+		{
+			CheckDisposed();
+			return this.CompoundFile.GetData(this);
+		}
+
+		public int Read(byte[] buffer, long position, int count)
+		{
+			CheckDisposed();
+			return this.CompoundFile.ReadData(this, position, buffer, 0, count);
+		}
+
+		internal int Read(byte[] buffer, long position, int offset, int count)
+		{
+			CheckDisposed();
+			return this.CompoundFile.ReadData(this, position, buffer, offset, count);
+		}
+	}
+
+	internal sealed class CFStorage : CFItem
+	{
+		private RBTree children;
+
+		internal RBTree Children
+		{
+			get
+			{
+				if (children == null)
+					children = LoadChildren(this.DirEntry.SID) ?? this.CompoundFile.CreateNewTree();
+				return children;
+			}
+		}
+
+		internal CFStorage(CompoundFile compFile, DirectoryEntry dirEntry)
+			: base(compFile)
+		{
+			if (dirEntry == null || dirEntry.SID < 0)
+				throw new Exception("Attempting to create a CFStorage using an unitialized directory");
+			this.DirEntry = dirEntry;
+		}
+
+		private RBTree LoadChildren(int SID)
+		{
+			RBTree childrenTree = this.CompoundFile.GetChildrenTree(SID);
+
+			if (childrenTree.Root != null)
+				this.DirEntry.Child = (childrenTree.Root as DirectoryEntry).SID;
+			else
+				this.DirEntry.Child = DirectoryEntry.NOSTREAM;
+
+			return childrenTree;
+		}
+
+		public CFStream GetStream(String streamName)
+		{
+			CheckDisposed();
+
+			DirectoryEntry tmp = DirectoryEntry.Mock(streamName, StgType.StgStream);
+
+			if (Children.TryLookup(tmp, out IRBNode outDe) && (((DirectoryEntry)outDe).StgType == StgType.StgStream))
+				return new CFStream(this.CompoundFile, (DirectoryEntry)outDe);
+			else
+				throw new KeyNotFoundException("Cannot find item [" + streamName + "] within the current storage");
+		}
+
+		public CFStream TryGetStream(String streamName)
+		{
+			CheckDisposed();
+
+			DirectoryEntry tmp = DirectoryEntry.Mock(streamName, StgType.StgStream);
 
-            if (Children.TryLookup(tmp, out IRBNode outDe) && ((outDe as DirectoryEntry).StgType == StgType.StgStream))
-                return new CFStream(this.CompoundFile, (DirectoryEntry)outDe);
-            else
-                return null;
-        }
+			if (Children.TryLookup(tmp, out IRBNode outDe) && ((outDe as DirectoryEntry).StgType == StgType.StgStream))
+				return new CFStream(this.CompoundFile, (DirectoryEntry)outDe);
+			else
+				return null;
+		}
 
-        public CFStorage GetStorage(String storageName)
-        {
-            CheckDisposed();
-
-            DirectoryEntry template = DirectoryEntry.Mock(storageName, StgType.StgInvalid);
-
-            if (Children.TryLookup(template, out IRBNode outDe) && (outDe as DirectoryEntry).StgType == StgType.StgStorage)
-                return new CFStorage(this.CompoundFile, outDe as DirectoryEntry);
-            else
-                throw new KeyNotFoundException("Cannot find item [" + storageName + "] within the current storage");
-        }
+		public CFStorage GetStorage(String storageName)
+		{
+			CheckDisposed();
+
+			DirectoryEntry template = DirectoryEntry.Mock(storageName, StgType.StgInvalid);
+
+			if (Children.TryLookup(template, out IRBNode outDe) && (outDe as DirectoryEntry).StgType == StgType.StgStorage)
+				return new CFStorage(this.CompoundFile, outDe as DirectoryEntry);
+			else
+				throw new KeyNotFoundException("Cannot find item [" + storageName + "] within the current storage");
+		}
 
-        public CFStorage TryGetStorage(String storageName)
-        {
-            CheckDisposed();
+		public CFStorage TryGetStorage(String storageName)
+		{
+			CheckDisposed();
 
-            DirectoryEntry template = DirectoryEntry.Mock(storageName, StgType.StgInvalid);
+			DirectoryEntry template = DirectoryEntry.Mock(storageName, StgType.StgInvalid);
 
-            if (Children.TryLookup(template, out IRBNode outDe) && ((DirectoryEntry)outDe).StgType == StgType.StgStorage)
-                return new CFStorage(this.CompoundFile, outDe as DirectoryEntry);
-            else
-                return null;
-        }
+			if (Children.TryLookup(template, out IRBNode outDe) && ((DirectoryEntry)outDe).StgType == StgType.StgStorage)
+				return new CFStorage(this.CompoundFile, outDe as DirectoryEntry);
+			else
+				return null;
+		}
 
-        public void VisitEntries(Action<CFItem> action, bool recursive)
-        {
-            CheckDisposed();
+		public void VisitEntries(Action<CFItem> action, bool recursive)
+		{
+			CheckDisposed();
 
-            if (action != null)
-            {
-                List<IRBNode> subStorages = new List<IRBNode>();
-
-                void internalAction(IRBNode targetNode)
-                {
-                    DirectoryEntry d = targetNode as DirectoryEntry;
-                    if (d.StgType == StgType.StgStream)
-                        action(new CFStream(this.CompoundFile, d));
-                    else
-                        action(new CFStorage(this.CompoundFile, d));
-
-                    if (d.Child != DirectoryEntry.NOSTREAM)
-                        subStorages.Add(targetNode);
-
-                    return;
-                }
-
-                this.Children.VisitTreeNodes(internalAction);
-
-                if (recursive && subStorages.Count > 0)
-                    foreach (IRBNode n in subStorages)
-                        (new CFStorage(this.CompoundFile, n as DirectoryEntry)).VisitEntries(action, recursive);
-            }
-        }
-    }
-
-    internal class CFItemComparer : IComparer<CFItem>
-    {
-        public int Compare(CFItem x, CFItem y) => (x.DirEntry.CompareTo(y.DirEntry));
-    }
-
-    internal sealed class Header
-    {
-        public byte[] HeaderSignature { get; private set; } = new byte[] { 0xD0, 0xCF, 0x11, 0xE0, 0xA1, 0xB1, 0x1A, 0xE1 };
-
-        public byte[] CLSID { get; set; } = new byte[16];
-
-        public ushort MinorVersion { get; private set; } = 0x003E;
-
-        public ushort MajorVersion { get; private set; } = 0x0003;
-
-        public ushort ByteOrder { get; private set; } = 0xFFFE;
-
-        public ushort SectorShift { get; private set; } = 9;
-
-        public ushort MiniSectorShift { get; private set; } = 6;
-
-        public int DirectorySectorsNumber { get; set; }
-
-        public int FATSectorsNumber { get; set; }
-
-        public int FirstDirectorySectorID { get; set; } = Sector.Endofchain;
-
-        public uint MinSizeStandardStream { get; set; } = 4096;
-
-        public int FirstMiniFATSectorID { get; set; } = unchecked((int)0xFFFFFFFE);
-
-        public uint MiniFATSectorsNumber { get; set; }
-
-        public int FirstDIFATSectorID { get; set; } = Sector.Endofchain;
-
-        public uint DIFATSectorsNumber { get; set; }
-
-        public int[] DIFAT { get; private set; } = new int[109];
-
-        public Header() : this(3)
-        {
-        }
-
-        public Header(ushort version)
-        {
-            switch (version)
-            {
-                case 3:
-                    MajorVersion = 3;
-                    SectorShift = 0x0009;
-                    break;
+			if (action != null)
+			{
+				List<IRBNode> subStorages = new List<IRBNode>();
+
+				void internalAction(IRBNode targetNode)
+				{
+					DirectoryEntry d = targetNode as DirectoryEntry;
+					if (d.StgType == StgType.StgStream)
+						action(new CFStream(this.CompoundFile, d));
+					else
+						action(new CFStorage(this.CompoundFile, d));
+
+					if (d.Child != DirectoryEntry.NOSTREAM)
+						subStorages.Add(targetNode);
+
+					return;
+				}
+
+				this.Children.VisitTreeNodes(internalAction);
+
+				if (recursive && subStorages.Count > 0)
+					foreach (IRBNode n in subStorages)
+						(new CFStorage(this.CompoundFile, n as DirectoryEntry)).VisitEntries(action, recursive);
+			}
+		}
+	}
+
+	internal class CFItemComparer : IComparer<CFItem>
+	{
+		public int Compare(CFItem x, CFItem y) => (x.DirEntry.CompareTo(y.DirEntry));
+	}
+
+	internal sealed class Header
+	{
+		public byte[] HeaderSignature { get; private set; } = new byte[] { 0xD0, 0xCF, 0x11, 0xE0, 0xA1, 0xB1, 0x1A, 0xE1 };
+
+		public byte[] CLSID { get; set; } = new byte[16];
+
+		public ushort MinorVersion { get; private set; } = 0x003E;
+
+		public ushort MajorVersion { get; private set; } = 0x0003;
+
+		public ushort ByteOrder { get; private set; } = 0xFFFE;
+
+		public ushort SectorShift { get; private set; } = 9;
+
+		public ushort MiniSectorShift { get; private set; } = 6;
+
+		public int DirectorySectorsNumber { get; set; }
+
+		public int FATSectorsNumber { get; set; }
+
+		public int FirstDirectorySectorID { get; set; } = Sector.Endofchain;
+
+		public uint MinSizeStandardStream { get; set; } = 4096;
+
+		public int FirstMiniFATSectorID { get; set; } = unchecked((int)0xFFFFFFFE);
+
+		public uint MiniFATSectorsNumber { get; set; }
+
+		public int FirstDIFATSectorID { get; set; } = Sector.Endofchain;
+
+		public uint DIFATSectorsNumber { get; set; }
+
+		public int[] DIFAT { get; private set; } = new int[109];
+
+		public Header() : this(3)
+		{
+		}
+
+		public Header(ushort version)
+		{
+			switch (version)
+			{
+				case 3:
+					MajorVersion = 3;
+					SectorShift = 0x0009;
+					break;
 
-                case 4:
-                    MajorVersion = 4;
-                    SectorShift = 0x000C;
-                    break;
+				case 4:
+					MajorVersion = 4;
+					SectorShift = 0x000C;
+					break;
 
-                default:
-                    throw new Exception("Invalid Compound File Format version");
-            }
+				default:
+					throw new Exception("Invalid Compound File Format version");
+			}
 
-            for (int i = 0; i < 109; i++)
-                DIFAT[i] = Sector.Freesect;
-        }
-
-        public void Read(Stream stream)
-        {
-            using (BinaryReader rw = new BinaryReader(stream, Encoding.UTF8, true))
-            {
-                HeaderSignature = rw.ReadBytes(8);
-                CheckSignature();
-                CLSID = rw.ReadBytes(16);
-                MinorVersion = rw.ReadUInt16();
-                MajorVersion = rw.ReadUInt16();
-                CheckVersion();
-                ByteOrder = rw.ReadUInt16();
-                SectorShift = rw.ReadUInt16();
-                MiniSectorShift = rw.ReadUInt16();
-                rw.ReadBytes(6);
-                DirectorySectorsNumber = rw.ReadInt32();
-                FATSectorsNumber = rw.ReadInt32();
-                FirstDirectorySectorID = rw.ReadInt32();
-                rw.ReadUInt32();
-                MinSizeStandardStream = rw.ReadUInt32();
-                FirstMiniFATSectorID = rw.ReadInt32();
-                MiniFATSectorsNumber = rw.ReadUInt32();
-                FirstDIFATSectorID = rw.ReadInt32();
-                DIFATSectorsNumber = rw.ReadUInt32();
+			for (int i = 0; i < 109; i++)
+				DIFAT[i] = Sector.Freesect;
+		}
+
+		public void Read(Stream stream)
+		{
+			using (BinaryReader rw = new BinaryReader(stream, Encoding.UTF8, true))
+			{
+				HeaderSignature = rw.ReadBytes(8);
+				CheckSignature();
+				CLSID = rw.ReadBytes(16);
+				MinorVersion = rw.ReadUInt16();
+				MajorVersion = rw.ReadUInt16();
+				CheckVersion();
+				ByteOrder = rw.ReadUInt16();
+				SectorShift = rw.ReadUInt16();
+				MiniSectorShift = rw.ReadUInt16();
+				rw.ReadBytes(6);
+				DirectorySectorsNumber = rw.ReadInt32();
+				FATSectorsNumber = rw.ReadInt32();
+				FirstDirectorySectorID = rw.ReadInt32();
+				rw.ReadUInt32();
+				MinSizeStandardStream = rw.ReadUInt32();
+				FirstMiniFATSectorID = rw.ReadInt32();
+				MiniFATSectorsNumber = rw.ReadUInt32();
+				FirstDIFATSectorID = rw.ReadInt32();
+				DIFATSectorsNumber = rw.ReadUInt32();
 
-                for (int i = 0; i < 109; i++)
-                    DIFAT[i] = rw.ReadInt32();
-            }
-        }
+				for (int i = 0; i < 109; i++)
+					DIFAT[i] = rw.ReadInt32();
+			}
+		}
 
-        private void CheckVersion()
-        {
-            if (MajorVersion != 3 && MajorVersion != 4)
-                throw new InvalidDataException("Unsupported Binary File Format version: OpenMcdf only supports Compound Files with major version equal to 3 or 4 ");
-        }
+		private void CheckVersion()
+		{
+			if (MajorVersion != 3 && MajorVersion != 4)
+				throw new InvalidDataException("Unsupported Binary File Format version: OpenMcdf only supports Compound Files with major version equal to 3 or 4 ");
+		}
 
-        private byte[] OLE_CFS_SIGNATURE = new byte[] { 0xD0, 0xCF, 0x11, 0xE0, 0xA1, 0xB1, 0x1A, 0xE1 };
+		private byte[] OLE_CFS_SIGNATURE = new byte[] { 0xD0, 0xCF, 0x11, 0xE0, 0xA1, 0xB1, 0x1A, 0xE1 };
 
-        private void CheckSignature()
-        {
-            for (int i = 0; i < HeaderSignature.Length; i++)
-                if (HeaderSignature[i] != OLE_CFS_SIGNATURE[i])
-                    throw new InvalidDataException("Invalid OLE structured storage file");
-        }
-    }
+		private void CheckSignature()
+		{
+			for (int i = 0; i < HeaderSignature.Length; i++)
+				if (HeaderSignature[i] != OLE_CFS_SIGNATURE[i])
+					throw new InvalidDataException("Invalid OLE structured storage file");
+		}
+	}
 
-    internal sealed class StreamView : Stream
-    {
-        private readonly int _sectorSize;
-        private long _position;
-
-        private readonly List<Sector> _sectorChain;
-        private readonly Stream _stream;
-        private readonly bool _isFatStream = false;
-        private readonly List<Sector> _freeSectors = new List<Sector>();
-
-        public IEnumerable<Sector> FreeSectors => _freeSectors;
-
-        public StreamView(List<Sector> sectorChain, int sectorSize, Stream stream)
-        {
-            if (sectorSize <= 0)
-                throw new Exception("Sector size must be greater than zero");
-
-            this._sectorChain = sectorChain ?? throw new Exception("Sector Chain cannot be null");
-            this._sectorSize = sectorSize;
-            this._stream = stream;
-        }
-
-        public StreamView(List<Sector> sectorChain, int sectorSize, long length, Queue<Sector> availableSectors, Stream stream, bool isFatStream = false)
-            : this(sectorChain, sectorSize, stream)
-        {
-            this._isFatStream = isFatStream;
-            AdjustLength(length, availableSectors);
-        }
-
-        public List<Sector> BaseSectorChain => _sectorChain;
-
-        public override bool CanRead => true;
-
-        public override bool CanSeek => true;
-
-        public override bool CanWrite => true;
-
-        public override void Flush()
-        { }
-
-        private long _length;
-
-        public override long Length => _length;
-
-        public override long Position
-        {
-            get => _position;
-
-            set
-            {
-                if (_position > _length - 1)
-                    throw new ArgumentOutOfRangeException("value");
-                _position = value;
-            }
-        }
-
-        private byte[] buf = new byte[4];
-
-        public int ReadInt32()
-        {
-            this.Read(buf, 0, 4);
-            return (((buf[0] | (buf[1] << 8)) | (buf[2] << 16)) | (buf[3] << 24));
-        }
-
-        public override int Read(byte[] buffer, int offset, int count)
-        {
-            int nRead = 0;
-            int nToRead = 0;
-
-            if (_sectorChain != null && _sectorChain.Count > 0)
-            {
-                // First sector
-                int secIndex = (int)(_position / (long)_sectorSize);
-
-                nToRead = Math.Min(_sectorChain[0].Size - ((int)_position % _sectorSize), count);
-
-                if (secIndex < _sectorChain.Count)
-                {
-                    Buffer.BlockCopy(_sectorChain[secIndex].GetData(),
-                        (int)(_position % _sectorSize), buffer, offset, nToRead);
-                }
-
-                nRead += nToRead;
-
-                ++secIndex;
-
-                // Central sectors
-                while (nRead < (count - _sectorSize))
-                {
-                    nToRead = _sectorSize;
-                    Buffer.BlockCopy(_sectorChain[secIndex].GetData(), 0, buffer, offset + nRead, nToRead);
-                    nRead += nToRead;
-                    ++secIndex;
-                }
-
-                // Last sector
-                nToRead = count - nRead;
-
-                if (nToRead != 0)
-                {
-                    Buffer.BlockCopy(_sectorChain[secIndex].GetData(), 0, buffer, offset + nRead, nToRead);
-                    nRead += nToRead;
-                }
-
-                _position += nRead;
-
-                return nRead;
-            }
-            else
-                return 0;
-        }
-
-        public override long Seek(long offset, SeekOrigin origin)
-        {
-            switch (origin)
-            {
-                case SeekOrigin.Begin: _position = offset; break;
-                case SeekOrigin.Current: _position += offset; break;
-                case SeekOrigin.End: _position = Length - offset; break;
-            }
-            AdjustLength(_position);
-            return _position;
-        }
-
-        private void AdjustLength(long value, Queue<Sector> availableSectors = null)
-        {
-            this._length = value;
-            long delta = value - ((long)this._sectorChain.Count * (long)_sectorSize);
-
-            if (delta > 0)
-            {
-                int nSec = (int)Math.Ceiling(((double)delta / _sectorSize));
-                while (nSec > 0)
-                {
-                    Sector t = null;
-
-                    if (availableSectors == null || availableSectors.Count == 0)
-                    {
-                        t = new Sector(_sectorSize, _stream);
-                        if (_sectorSize == Sector.MinisectorSize)
-                            t.Type = SectorType.Mini;
-                    }
-                    else
-                        t = availableSectors.Dequeue();
-
-                    if (_isFatStream)
-                        t.InitFATData();
-                    _sectorChain.Add(t);
-                    nSec--;
-                }
-            }
-        }
-
-        public override void SetLength(long value) => AdjustLength(value);
-
-        public override void Write(byte[] buffer, int offset, int count) => throw new NotImplementedException();
-    }
-
-    [Flags]
-    internal enum CFSConfiguration
-    {
-        Default = 1,
-        LeaveOpen = 16,
-    }
-
-    internal sealed class CompoundFile : IDisposable
-    {
-        public CFSConfiguration Configuration { get; private set; } = CFSConfiguration.Default;
-
-        internal int GetSectorSize() => 2 << (header.SectorShift - 1);
-
-        private const int HEADER_DIFAT_ENTRIES_COUNT = 109;
-        private readonly int DIFAT_SECTOR_FAT_ENTRIES_COUNT = 127;
-        private readonly int FAT_SECTOR_ENTRIES_COUNT = 128;
-        private const int SIZE_OF_SID = 4;
-        private const int FLUSHING_QUEUE_SIZE = 6000;
-        private const int FLUSHING_BUFFER_MAX_SIZE = 1024 * 1024 * 16;
-
-        private List<Sector> sectors = new List<Sector>();
-
-        private Header header;
-
-        internal Stream sourceStream = null;
-
-        public CompoundFile(Stream stream, CFSConfiguration configParameters)
-        {
-            this.closeStream = !configParameters.HasFlag(CFSConfiguration.LeaveOpen);
-
-            LoadStream(stream);
-
-            DIFAT_SECTOR_FAT_ENTRIES_COUNT = (GetSectorSize() / 4) - 1;
-            FAT_SECTOR_ENTRIES_COUNT = (GetSectorSize() / 4);
-        }
-
-        private string fileName = string.Empty;
-
-        private void Load(Stream stream)
-        {
-            try
-            {
-                this.header = new Header();
-                this.directoryEntries = new List<DirectoryEntry>();
-
-                this.sourceStream = stream;
-
-                header.Read(stream);
-
-                int n_sector = Ceiling(((double)(stream.Length - GetSectorSize()) / (double)GetSectorSize()));
-
-                if (stream.Length > 0x7FFFFF0)
-                    this._transactionLockAllocated = true;
-
-                sectors = new List<Sector>();
-                for (int i = 0; i < n_sector; i++)
-                    sectors.Add(null);
-
-                LoadDirectories();
-
-                this.RootStorage = new CFStorage(this, directoryEntries[0]);
-            }
-            catch (Exception)
-            {
-                if (stream != null && closeStream)
-                    stream.Dispose();
-                throw;
-            }
-        }
-
-        private void LoadFile(String fileName)
-        {
-            this.fileName = fileName;
-            FileStream fs = null;
-
-            try
-            {
-                fs = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
-                Load(fs);
-            }
-            catch
-            {
-                if (fs != null)
-                    fs.Dispose();
-                throw;
-            }
-        }
-
-        private void LoadStream(Stream stream)
-        {
-            if (stream == null)
-                throw new Exception("Stream parameter cannot be null");
-            if (!stream.CanSeek)
-                throw new Exception("Cannot load a non-seekable Stream");
-            stream.Seek(0, SeekOrigin.Begin);
-            Load(stream);
-        }
-
-        public bool HasSourceStream => sourceStream != null;
-
-        private void PersistMiniStreamToStream(List<Sector> miniSectorChain)
-        {
-            List<Sector> miniStream = GetSectorChain(RootEntry.StartSetc, SectorType.Normal);
-
-            StreamView miniStreamView = new StreamView(miniStream, GetSectorSize(), this.RootStorage.Size, null, sourceStream);
-
-            for (int i = 0; i < miniSectorChain.Count; i++)
-            {
-                Sector s = miniSectorChain[i];
-
-                if (s.Id == -1)
-                    throw new Exception("Invalid minisector index");
-
-                miniStreamView.Seek(Sector.MinisectorSize * s.Id, SeekOrigin.Begin);
-                miniStreamView.Write(s.GetData(), 0, Sector.MinisectorSize);
-            }
-        }
-
-        private void AllocateMiniSectorChain(List<Sector> sectorChain)
-        {
-            List<Sector> miniFAT = GetSectorChain(header.FirstMiniFATSectorID, SectorType.Normal);
-            List<Sector> miniStream = GetSectorChain(RootEntry.StartSetc, SectorType.Normal);
-
-            StreamView miniFATView = new StreamView(miniFAT, GetSectorSize(),
-                header.MiniFATSectorsNumber * Sector.MinisectorSize,
-                null, this.sourceStream, true);
-
-            StreamView miniStreamView = new StreamView(miniStream, GetSectorSize(),
-                this.RootStorage.Size, null, sourceStream);
-
-            for (int i = 0; i < sectorChain.Count; i++)
-            {
-                Sector s = sectorChain[i];
-
-                if (s.Id == -1)
-                {
-                    miniStreamView.Seek(this.RootStorage.Size + Sector.MinisectorSize, SeekOrigin.Begin);
-                    s.Id = (int)(miniStreamView.Position - Sector.MinisectorSize) / Sector.MinisectorSize;
-
-                    this.RootStorage.DirEntry.Size = miniStreamView.Length;
-                }
-            }
-
-            for (int i = 0; i < sectorChain.Count - 1; i++)
-            {
-                Int32 currentId = sectorChain[i].Id;
-                Int32 nextId = sectorChain[i + 1].Id;
-
-                miniFATView.Seek(currentId * 4, SeekOrigin.Begin);
-                miniFATView.Write(BitConverter.GetBytes(nextId), 0, 4);
-            }
-
-            miniFATView.Seek(sectorChain[sectorChain.Count - 1].Id * SIZE_OF_SID, SeekOrigin.Begin);
-            miniFATView.Write(BitConverter.GetBytes(Sector.Endofchain), 0, 4);
-
-            AllocateSectorChain(miniStreamView.BaseSectorChain);
-            AllocateSectorChain(miniFATView.BaseSectorChain);
-
-            if (miniFAT.Count > 0)
-            {
-                this.RootStorage.DirEntry.StartSetc = miniStream[0].Id;
-                header.MiniFATSectorsNumber = (uint)miniFAT.Count;
-                header.FirstMiniFATSectorID = miniFAT[0].Id;
-            }
-        }
-
-        private void SetSectorChain(List<Sector> sectorChain)
-        {
-            if (sectorChain == null || sectorChain.Count == 0)
-                return;
-
-            SectorType _st = sectorChain[0].Type;
-
-            if (_st == SectorType.Normal)
-                AllocateSectorChain(sectorChain);
-            else if (_st == SectorType.Mini)
-                AllocateMiniSectorChain(sectorChain);
-        }
-
-        private void AllocateSectorChain(List<Sector> sectorChain)
-        {
-            foreach (Sector s in sectorChain)
-            {
-                if (s.Id == -1)
-                {
-                    sectors.Add(s);
-                    s.Id = sectors.Count - 1;
-                }
-            }
-
-            AllocateFATSectorChain(sectorChain);
-        }
-
-        internal bool _transactionLockAdded = false;
-        internal int _lockSectorId = -1;
-        internal bool _transactionLockAllocated = false;
-
-        private void CheckForLockSector()
-        {
-            if (_transactionLockAdded && !_transactionLockAllocated)
-            {
-                StreamView fatStream = new StreamView(GetFatSectorChain(), GetSectorSize(), sourceStream);
-
-                fatStream.Seek(_lockSectorId * 4, SeekOrigin.Begin);
-                fatStream.Write(BitConverter.GetBytes(Sector.Endofchain), 0, 4);
-
-                _transactionLockAllocated = true;
-            }
-        }
-
-        private void AllocateFATSectorChain(List<Sector> sectorChain)
-        {
-            List<Sector> fatSectors = GetSectorChain(-1, SectorType.FAT);
-
-            StreamView fatStream = new StreamView(fatSectors, GetSectorSize(),
-                header.FATSectorsNumber * GetSectorSize(), null,
-                sourceStream, true);
-
-            for (int i = 0; i < sectorChain.Count - 1; i++)
-            {
-                Sector sN = sectorChain[i + 1];
-                Sector sC = sectorChain[i];
-
-                fatStream.Seek(sC.Id * 4, SeekOrigin.Begin);
-                fatStream.Write(BitConverter.GetBytes(sN.Id), 0, 4);
-            }
-
-            fatStream.Seek(sectorChain[sectorChain.Count - 1].Id * 4, SeekOrigin.Begin);
-            fatStream.Write(BitConverter.GetBytes(Sector.Endofchain), 0, 4);
-
-            AllocateDIFATSectorChain(fatStream.BaseSectorChain);
-        }
-
-        private void AllocateDIFATSectorChain(List<Sector> FATsectorChain)
-        {
-            header.FATSectorsNumber = FATsectorChain.Count;
-
-            foreach (Sector s in FATsectorChain)
-            {
-                if (s.Id == -1)
-                {
-                    sectors.Add(s);
-                    s.Id = sectors.Count - 1;
-                    s.Type = SectorType.FAT;
-                }
-            }
-
-            int nCurrentSectors = sectors.Count;
-
-            int nDIFATSectors = (int)header.DIFATSectorsNumber;
-
-            if (FATsectorChain.Count > HEADER_DIFAT_ENTRIES_COUNT)
-            {
-                nDIFATSectors = Ceiling((double)(FATsectorChain.Count - HEADER_DIFAT_ENTRIES_COUNT) / DIFAT_SECTOR_FAT_ENTRIES_COUNT);
-                nDIFATSectors = LowSaturation(nDIFATSectors - (int)header.DIFATSectorsNumber); //required DIFAT
-            }
-
-            nCurrentSectors += nDIFATSectors;
-
-            while (header.FATSectorsNumber * FAT_SECTOR_ENTRIES_COUNT < nCurrentSectors)
-            {
-                Sector extraFATSector = new Sector(GetSectorSize(), sourceStream);
-                sectors.Add(extraFATSector);
-
-                extraFATSector.Id = sectors.Count - 1;
-                extraFATSector.Type = SectorType.FAT;
-
-                FATsectorChain.Add(extraFATSector);
-
-                header.FATSectorsNumber++;
-                nCurrentSectors++;
-
-                if (nDIFATSectors * DIFAT_SECTOR_FAT_ENTRIES_COUNT <
-                    (header.FATSectorsNumber > HEADER_DIFAT_ENTRIES_COUNT ?
-                        header.FATSectorsNumber - HEADER_DIFAT_ENTRIES_COUNT :
-                        0))
-                {
-                    nDIFATSectors++;
-                    nCurrentSectors++;
-                }
-            }
-
-            List<Sector> difatSectors = GetSectorChain(-1, SectorType.DIFAT);
-            StreamView difatStream = new StreamView(difatSectors, GetSectorSize(), sourceStream);
-
-            for (int i = 0; i < FATsectorChain.Count; i++)
-            {
-                if (i < HEADER_DIFAT_ENTRIES_COUNT)
-                    header.DIFAT[i] = FATsectorChain[i].Id;
-                else
-                {
-                    if (i != HEADER_DIFAT_ENTRIES_COUNT && (i - HEADER_DIFAT_ENTRIES_COUNT) % DIFAT_SECTOR_FAT_ENTRIES_COUNT == 0)
-                    {
-                        difatStream.Write(new byte[sizeof(int)], 0, sizeof(int));
-                    }
-
-                    difatStream.Write(BitConverter.GetBytes(FATsectorChain[i].Id), 0, sizeof(int));
-                }
-            }
-
-            for (int i = 0; i < difatStream.BaseSectorChain.Count; i++)
-            {
-                if (difatStream.BaseSectorChain[i].Id == -1)
-                {
-                    sectors.Add(difatStream.BaseSectorChain[i]);
-                    difatStream.BaseSectorChain[i].Id = sectors.Count - 1;
-                    difatStream.BaseSectorChain[i].Type = SectorType.DIFAT;
-                }
-            }
-
-            header.DIFATSectorsNumber = (uint)nDIFATSectors;
-
-            if (difatStream.BaseSectorChain != null && difatStream.BaseSectorChain.Count > 0)
-            {
-                header.FirstDIFATSectorID = difatStream.BaseSectorChain[0].Id;
-                header.DIFATSectorsNumber = (uint)difatStream.BaseSectorChain.Count;
-
-                for (int i = 0; i < difatStream.BaseSectorChain.Count - 1; i++)
-                    Buffer.BlockCopy(BitConverter.GetBytes(difatStream.BaseSectorChain[i + 1].Id),
-                        0, difatStream.BaseSectorChain[i].GetData(),
-                        GetSectorSize() - sizeof(int), 4);
-
-                Buffer.BlockCopy(BitConverter.GetBytes(Sector.Endofchain), 0,
-                    difatStream.BaseSectorChain[difatStream.BaseSectorChain.Count - 1].GetData(),
-                    GetSectorSize() - sizeof(int), sizeof(int));
-            }
-            else header.FirstDIFATSectorID = Sector.Endofchain;
-
-            StreamView fatSv = new StreamView(FATsectorChain, GetSectorSize(), header.FATSectorsNumber * GetSectorSize(), null, sourceStream);
-
-            for (int i = 0; i < header.DIFATSectorsNumber; i++)
-            {
-                fatSv.Seek(difatStream.BaseSectorChain[i].Id * 4, SeekOrigin.Begin);
-                fatSv.Write(BitConverter.GetBytes(Sector.Difsect), 0, 4);
-            }
-
-            for (int i = 0; i < header.FATSectorsNumber; i++)
-            {
-                fatSv.Seek(fatSv.BaseSectorChain[i].Id * 4, SeekOrigin.Begin);
-                fatSv.Write(BitConverter.GetBytes(Sector.Fatsect), 0, 4);
-            }
-
-            header.FATSectorsNumber = fatSv.BaseSectorChain.Count;
-        }
-
-        private List<Sector> GetDifatSectorChain()
-        {
-            int validationCount = 0;
-
-            List<Sector> result = new List<Sector>();
-
-            int nextSecID
-                = Sector.Endofchain;
-
-            if (header.DIFATSectorsNumber != 0)
-            {
-                validationCount = (int)header.DIFATSectorsNumber;
-
-                Sector s = sectors[header.FirstDIFATSectorID] as Sector;
-
-                if (s == null)
-                {
-                    sectors[header.FirstDIFATSectorID] = s = new Sector(GetSectorSize(), sourceStream)
-                    {
-                        Type = SectorType.DIFAT,
-                        Id = header.FirstDIFATSectorID
-                    };
-                }
-
-                result.Add(s);
-
-                while (true && validationCount >= 0)
-                {
-                    nextSecID = BitConverter.ToInt32(s.GetData(), GetSectorSize() - 4);
-
-                    if (nextSecID == Sector.Freesect || nextSecID == Sector.Endofchain) break;
-
-                    validationCount--;
-
-                    if (validationCount < 0)
-                    {
-                        Dispose();
-                        throw new InvalidDataException("DIFAT sectors count mismatched. Corrupted compound file");
-                    }
-
-                    s = sectors[nextSecID] as Sector;
-
-                    if (s == null)
-                        sectors[nextSecID] = s = new Sector(GetSectorSize(), sourceStream) { Id = nextSecID };
-
-                    result.Add(s);
-                }
-            }
-
-            return result;
-        }
-
-        private List<Sector> GetFatSectorChain()
-        {
-            int N_HEADER_FAT_ENTRY = 109;
-
-            List<Sector> result = new List<Sector>();
-            int nextSecID = Sector.Endofchain;
-            List<Sector> difatSectors = GetDifatSectorChain();
-
-            int idx = 0;
-            while (idx < header.FATSectorsNumber && idx < N_HEADER_FAT_ENTRY)
-            {
-                nextSecID = header.DIFAT[idx];
-                Sector s = sectors[nextSecID] as Sector;
-
-                if (s == null)
-                {
-                    sectors[nextSecID] = s = new Sector(GetSectorSize(), sourceStream)
-                    {
-                        Id = nextSecID,
-                        Type = SectorType.FAT
-                    };
-                }
-
-                result.Add(s);
-                ++idx;
-            }
-
-            if (difatSectors.Count > 0)
-            {
-                var difatStream = new StreamView(difatSectors, GetSectorSize(),
-                    header.FATSectorsNumber > N_HEADER_FAT_ENTRY ? (header.FATSectorsNumber - N_HEADER_FAT_ENTRY) * 4 : 0,
-                    null, sourceStream);
-
-                byte[] nextDIFATSectorBuffer = new byte[4];
-                difatStream.Read(nextDIFATSectorBuffer, 0, 4);
-                nextSecID = BitConverter.ToInt32(nextDIFATSectorBuffer, 0);
-
-                int i = 0;
-                int nFat = N_HEADER_FAT_ENTRY;
-
-                while (nFat < header.FATSectorsNumber)
-                {
-                    if (difatStream.Position == ((GetSectorSize() - 4) + i * GetSectorSize()))
-                    {
-                        difatStream.Seek(4, SeekOrigin.Current);
-                        ++i;
-                        continue;
-                    }
+	internal sealed class StreamView : Stream
+	{
+		private readonly int _sectorSize;
+		private long _position;
+
+		private readonly List<Sector> _sectorChain;
+		private readonly Stream _stream;
+		private readonly bool _isFatStream = false;
+		private readonly List<Sector> _freeSectors = new List<Sector>();
+
+		public IEnumerable<Sector> FreeSectors => _freeSectors;
+
+		public StreamView(List<Sector> sectorChain, int sectorSize, Stream stream)
+		{
+			if (sectorSize <= 0)
+				throw new Exception("Sector size must be greater than zero");
+
+			this._sectorChain = sectorChain ?? throw new Exception("Sector Chain cannot be null");
+			this._sectorSize = sectorSize;
+			this._stream = stream;
+		}
+
+		public StreamView(List<Sector> sectorChain, int sectorSize, long length, Queue<Sector> availableSectors, Stream stream, bool isFatStream = false)
+			: this(sectorChain, sectorSize, stream)
+		{
+			this._isFatStream = isFatStream;
+			AdjustLength(length, availableSectors);
+		}
+
+		public List<Sector> BaseSectorChain => _sectorChain;
+
+		public override bool CanRead => true;
+
+		public override bool CanSeek => true;
+
+		public override bool CanWrite => true;
+
+		public override void Flush()
+		{ }
+
+		private long _length;
+
+		public override long Length => _length;
+
+		public override long Position
+		{
+			get => _position;
+
+			set
+			{
+				if (_position > _length - 1)
+					throw new ArgumentOutOfRangeException("value");
+				_position = value;
+			}
+		}
+
+		private byte[] buf = new byte[4];
+
+		public int ReadInt32()
+		{
+			this.Read(buf, 0, 4);
+			return (((buf[0] | (buf[1] << 8)) | (buf[2] << 16)) | (buf[3] << 24));
+		}
+
+		public override int Read(byte[] buffer, int offset, int count)
+		{
+			int nRead = 0;
+			int nToRead = 0;
+
+			if (_sectorChain != null && _sectorChain.Count > 0)
+			{
+				// First sector
+				int secIndex = (int)(_position / _sectorSize);
+
+				nToRead = Math.Min(_sectorChain[0].Size - ((int)_position % _sectorSize), count);
+
+				if (secIndex < _sectorChain.Count)
+				{
+					Buffer.BlockCopy(_sectorChain[secIndex].GetData(),
+						(int)(_position % _sectorSize), buffer, offset, nToRead);
+				}
+
+				nRead += nToRead;
+
+				++secIndex;
+
+				// Central sectors
+				while (nRead < (count - _sectorSize))
+				{
+					nToRead = _sectorSize;
+					Buffer.BlockCopy(_sectorChain[secIndex].GetData(), 0, buffer, offset + nRead, nToRead);
+					nRead += nToRead;
+					++secIndex;
+				}
+
+				// Last sector
+				nToRead = count - nRead;
+
+				if (nToRead != 0)
+				{
+					Buffer.BlockCopy(_sectorChain[secIndex].GetData(), 0, buffer, offset + nRead, nToRead);
+					nRead += nToRead;
+				}
+
+				_position += nRead;
+
+				return nRead;
+			}
+			else
+				return 0;
+		}
+
+		public override long Seek(long offset, SeekOrigin origin)
+		{
+			switch (origin)
+			{
+				case SeekOrigin.Begin: _position = offset; break;
+				case SeekOrigin.Current: _position += offset; break;
+				case SeekOrigin.End: _position = Length - offset; break;
+			}
+			AdjustLength(_position);
+			return _position;
+		}
+
+		private void AdjustLength(long value, Queue<Sector> availableSectors = null)
+		{
+			this._length = value;
+			long delta = value - (_sectorChain.Count * (long)_sectorSize);
+
+			if (delta > 0)
+			{
+				int nSec = (int)Math.Ceiling(((double)delta / _sectorSize));
+				while (nSec > 0)
+				{
+					Sector t = null;
+
+					if (availableSectors == null || availableSectors.Count == 0)
+					{
+						t = new Sector(_sectorSize, _stream);
+						if (_sectorSize == Sector.MinisectorSize)
+							t.Type = SectorType.Mini;
+					}
+					else
+						t = availableSectors.Dequeue();
+
+					if (_isFatStream)
+						t.InitFATData();
+					_sectorChain.Add(t);
+					nSec--;
+				}
+			}
+		}
+
+		public override void SetLength(long value) => AdjustLength(value);
+
+		public override void Write(byte[] buffer, int offset, int count) => throw new NotImplementedException();
+	}
+
+	[Flags]
+	internal enum CFSConfiguration
+	{
+		Default = 1,
+		LeaveOpen = 16,
+	}
+
+	internal sealed class CompoundFile : IDisposable
+	{
+		public CFSConfiguration Configuration { get; private set; } = CFSConfiguration.Default;
+
+		internal int GetSectorSize() => 2 << (header.SectorShift - 1);
+
+		private const int HEADER_DIFAT_ENTRIES_COUNT = 109;
+		private readonly int DIFAT_SECTOR_FAT_ENTRIES_COUNT = 127;
+		private readonly int FAT_SECTOR_ENTRIES_COUNT = 128;
+		private const int SIZE_OF_SID = 4;
+		private const int FLUSHING_QUEUE_SIZE = 6000;
+		private const int FLUSHING_BUFFER_MAX_SIZE = 1024 * 1024 * 16;
+
+		private List<Sector> sectors = new List<Sector>();
+
+		private Header header;
+
+		internal Stream sourceStream = null;
+
+		public CompoundFile(Stream stream, CFSConfiguration configParameters)
+		{
+			this.closeStream = !configParameters.HasFlag(CFSConfiguration.LeaveOpen);
+
+			LoadStream(stream);
+
+			DIFAT_SECTOR_FAT_ENTRIES_COUNT = (GetSectorSize() / 4) - 1;
+			FAT_SECTOR_ENTRIES_COUNT = (GetSectorSize() / 4);
+		}
+
+		private string fileName = string.Empty;
+
+		private void Load(Stream stream)
+		{
+			try
+			{
+				this.header = new Header();
+				this.directoryEntries = new List<DirectoryEntry>();
+
+				this.sourceStream = stream;
+
+				header.Read(stream);
+
+				int n_sector = Ceiling(((stream.Length - GetSectorSize()) / (double)GetSectorSize()));
+
+				if (stream.Length > 0x7FFFFF0)
+					this._transactionLockAllocated = true;
+
+				sectors = new List<Sector>();
+				for (int i = 0; i < n_sector; i++)
+					sectors.Add(null);
+
+				LoadDirectories();
+
+				this.RootStorage = new CFStorage(this, directoryEntries[0]);
+			}
+			catch (Exception)
+			{
+				if (stream != null && closeStream)
+					stream.Dispose();
+				throw;
+			}
+		}
+
+		private void LoadFile(String fileName)
+		{
+			this.fileName = fileName;
+			FileStream fs = null;
+
+			try
+			{
+				fs = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
+				Load(fs);
+			}
+			catch
+			{
+				if (fs != null)
+					fs.Dispose();
+				throw;
+			}
+		}
+
+		private void LoadStream(Stream stream)
+		{
+			if (stream == null)
+				throw new Exception("Stream parameter cannot be null");
+			if (!stream.CanSeek)
+				throw new Exception("Cannot load a non-seekable Stream");
+			stream.Seek(0, SeekOrigin.Begin);
+			Load(stream);
+		}
+
+		public bool HasSourceStream => sourceStream != null;
+
+		private void PersistMiniStreamToStream(List<Sector> miniSectorChain)
+		{
+			List<Sector> miniStream = GetSectorChain(RootEntry.StartSetc, SectorType.Normal);
+
+			StreamView miniStreamView = new StreamView(miniStream, GetSectorSize(), this.RootStorage.Size, null, sourceStream);
+
+			for (int i = 0; i < miniSectorChain.Count; i++)
+			{
+				Sector s = miniSectorChain[i];
+
+				if (s.Id == -1)
+					throw new Exception("Invalid minisector index");
+
+				miniStreamView.Seek(Sector.MinisectorSize * s.Id, SeekOrigin.Begin);
+				miniStreamView.Write(s.GetData(), 0, Sector.MinisectorSize);
+			}
+		}
+
+		private void AllocateMiniSectorChain(List<Sector> sectorChain)
+		{
+			List<Sector> miniFAT = GetSectorChain(header.FirstMiniFATSectorID, SectorType.Normal);
+			List<Sector> miniStream = GetSectorChain(RootEntry.StartSetc, SectorType.Normal);
+
+			StreamView miniFATView = new StreamView(miniFAT, GetSectorSize(),
+				header.MiniFATSectorsNumber * Sector.MinisectorSize,
+				null, this.sourceStream, true);
+
+			StreamView miniStreamView = new StreamView(miniStream, GetSectorSize(),
+				this.RootStorage.Size, null, sourceStream);
+
+			for (int i = 0; i < sectorChain.Count; i++)
+			{
+				Sector s = sectorChain[i];
+
+				if (s.Id == -1)
+				{
+					miniStreamView.Seek(this.RootStorage.Size + Sector.MinisectorSize, SeekOrigin.Begin);
+					s.Id = (int)(miniStreamView.Position - Sector.MinisectorSize) / Sector.MinisectorSize;
+
+					this.RootStorage.DirEntry.Size = miniStreamView.Length;
+				}
+			}
+
+			for (int i = 0; i < sectorChain.Count - 1; i++)
+			{
+				Int32 currentId = sectorChain[i].Id;
+				Int32 nextId = sectorChain[i + 1].Id;
+
+				miniFATView.Seek(currentId * 4, SeekOrigin.Begin);
+				miniFATView.Write(BitConverter.GetBytes(nextId), 0, 4);
+			}
+
+			miniFATView.Seek(sectorChain[sectorChain.Count - 1].Id * SIZE_OF_SID, SeekOrigin.Begin);
+			miniFATView.Write(BitConverter.GetBytes(Sector.Endofchain), 0, 4);
+
+			AllocateSectorChain(miniStreamView.BaseSectorChain);
+			AllocateSectorChain(miniFATView.BaseSectorChain);
+
+			if (miniFAT.Count > 0)
+			{
+				this.RootStorage.DirEntry.StartSetc = miniStream[0].Id;
+				header.MiniFATSectorsNumber = (uint)miniFAT.Count;
+				header.FirstMiniFATSectorID = miniFAT[0].Id;
+			}
+		}
+
+		private void SetSectorChain(List<Sector> sectorChain)
+		{
+			if (sectorChain == null || sectorChain.Count == 0)
+				return;
+
+			SectorType _st = sectorChain[0].Type;
+
+			if (_st == SectorType.Normal)
+				AllocateSectorChain(sectorChain);
+			else if (_st == SectorType.Mini)
+				AllocateMiniSectorChain(sectorChain);
+		}
+
+		private void AllocateSectorChain(List<Sector> sectorChain)
+		{
+			foreach (Sector s in sectorChain)
+			{
+				if (s.Id == -1)
+				{
+					sectors.Add(s);
+					s.Id = sectors.Count - 1;
+				}
+			}
+
+			AllocateFATSectorChain(sectorChain);
+		}
+
+		internal bool _transactionLockAdded = false;
+		internal int _lockSectorId = -1;
+		internal bool _transactionLockAllocated = false;
+
+		private void CheckForLockSector()
+		{
+			if (_transactionLockAdded && !_transactionLockAllocated)
+			{
+				StreamView fatStream = new StreamView(GetFatSectorChain(), GetSectorSize(), sourceStream);
+
+				fatStream.Seek(_lockSectorId * 4, SeekOrigin.Begin);
+				fatStream.Write(BitConverter.GetBytes(Sector.Endofchain), 0, 4);
+
+				_transactionLockAllocated = true;
+			}
+		}
+
+		private void AllocateFATSectorChain(List<Sector> sectorChain)
+		{
+			List<Sector> fatSectors = GetSectorChain(-1, SectorType.FAT);
+
+			StreamView fatStream = new StreamView(fatSectors, GetSectorSize(),
+				header.FATSectorsNumber * GetSectorSize(), null,
+				sourceStream, true);
+
+			for (int i = 0; i < sectorChain.Count - 1; i++)
+			{
+				Sector sN = sectorChain[i + 1];
+				Sector sC = sectorChain[i];
+
+				fatStream.Seek(sC.Id * 4, SeekOrigin.Begin);
+				fatStream.Write(BitConverter.GetBytes(sN.Id), 0, 4);
+			}
+
+			fatStream.Seek(sectorChain[sectorChain.Count - 1].Id * 4, SeekOrigin.Begin);
+			fatStream.Write(BitConverter.GetBytes(Sector.Endofchain), 0, 4);
+
+			AllocateDIFATSectorChain(fatStream.BaseSectorChain);
+		}
+
+		private void AllocateDIFATSectorChain(List<Sector> FATsectorChain)
+		{
+			header.FATSectorsNumber = FATsectorChain.Count;
+
+			foreach (Sector s in FATsectorChain)
+			{
+				if (s.Id == -1)
+				{
+					sectors.Add(s);
+					s.Id = sectors.Count - 1;
+					s.Type = SectorType.FAT;
+				}
+			}
+
+			int nCurrentSectors = sectors.Count;
+
+			int nDIFATSectors = (int)header.DIFATSectorsNumber;
+
+			if (FATsectorChain.Count > HEADER_DIFAT_ENTRIES_COUNT)
+			{
+				nDIFATSectors = Ceiling((double)(FATsectorChain.Count - HEADER_DIFAT_ENTRIES_COUNT) / DIFAT_SECTOR_FAT_ENTRIES_COUNT);
+				nDIFATSectors = LowSaturation(nDIFATSectors - (int)header.DIFATSectorsNumber); //required DIFAT
+			}
+
+			nCurrentSectors += nDIFATSectors;
+
+			while (header.FATSectorsNumber * FAT_SECTOR_ENTRIES_COUNT < nCurrentSectors)
+			{
+				Sector extraFATSector = new Sector(GetSectorSize(), sourceStream);
+				sectors.Add(extraFATSector);
+
+				extraFATSector.Id = sectors.Count - 1;
+				extraFATSector.Type = SectorType.FAT;
+
+				FATsectorChain.Add(extraFATSector);
+
+				header.FATSectorsNumber++;
+				nCurrentSectors++;
+
+				if (nDIFATSectors * DIFAT_SECTOR_FAT_ENTRIES_COUNT <
+					(header.FATSectorsNumber > HEADER_DIFAT_ENTRIES_COUNT ?
+						header.FATSectorsNumber - HEADER_DIFAT_ENTRIES_COUNT :
+						0))
+				{
+					nDIFATSectors++;
+					nCurrentSectors++;
+				}
+			}
+
+			List<Sector> difatSectors = GetSectorChain(-1, SectorType.DIFAT);
+			StreamView difatStream = new StreamView(difatSectors, GetSectorSize(), sourceStream);
+
+			for (int i = 0; i < FATsectorChain.Count; i++)
+			{
+				if (i < HEADER_DIFAT_ENTRIES_COUNT)
+					header.DIFAT[i] = FATsectorChain[i].Id;
+				else
+				{
+					if (i != HEADER_DIFAT_ENTRIES_COUNT && (i - HEADER_DIFAT_ENTRIES_COUNT) % DIFAT_SECTOR_FAT_ENTRIES_COUNT == 0)
+					{
+						difatStream.Write(new byte[sizeof(int)], 0, sizeof(int));
+					}
+
+					difatStream.Write(BitConverter.GetBytes(FATsectorChain[i].Id), 0, sizeof(int));
+				}
+			}
+
+			for (int i = 0; i < difatStream.BaseSectorChain.Count; i++)
+			{
+				if (difatStream.BaseSectorChain[i].Id == -1)
+				{
+					sectors.Add(difatStream.BaseSectorChain[i]);
+					difatStream.BaseSectorChain[i].Id = sectors.Count - 1;
+					difatStream.BaseSectorChain[i].Type = SectorType.DIFAT;
+				}
+			}
+
+			header.DIFATSectorsNumber = (uint)nDIFATSectors;
+
+			if (difatStream.BaseSectorChain != null && difatStream.BaseSectorChain.Count > 0)
+			{
+				header.FirstDIFATSectorID = difatStream.BaseSectorChain[0].Id;
+				header.DIFATSectorsNumber = (uint)difatStream.BaseSectorChain.Count;
+
+				for (int i = 0; i < difatStream.BaseSectorChain.Count - 1; i++)
+					Buffer.BlockCopy(BitConverter.GetBytes(difatStream.BaseSectorChain[i + 1].Id),
+						0, difatStream.BaseSectorChain[i].GetData(),
+						GetSectorSize() - sizeof(int), 4);
+
+				Buffer.BlockCopy(BitConverter.GetBytes(Sector.Endofchain), 0,
+					difatStream.BaseSectorChain[difatStream.BaseSectorChain.Count - 1].GetData(),
+					GetSectorSize() - sizeof(int), sizeof(int));
+			}
+			else header.FirstDIFATSectorID = Sector.Endofchain;
+
+			StreamView fatSv = new StreamView(FATsectorChain, GetSectorSize(), header.FATSectorsNumber * GetSectorSize(), null, sourceStream);
+
+			for (int i = 0; i < header.DIFATSectorsNumber; i++)
+			{
+				fatSv.Seek(difatStream.BaseSectorChain[i].Id * 4, SeekOrigin.Begin);
+				fatSv.Write(BitConverter.GetBytes(Sector.Difsect), 0, 4);
+			}
+
+			for (int i = 0; i < header.FATSectorsNumber; i++)
+			{
+				fatSv.Seek(fatSv.BaseSectorChain[i].Id * 4, SeekOrigin.Begin);
+				fatSv.Write(BitConverter.GetBytes(Sector.Fatsect), 0, 4);
+			}
+
+			header.FATSectorsNumber = fatSv.BaseSectorChain.Count;
+		}
+
+		private List<Sector> GetDifatSectorChain()
+		{
+			int validationCount = 0;
+
+			List<Sector> result = new List<Sector>();
+
+			int nextSecID
+				= Sector.Endofchain;
+
+			if (header.DIFATSectorsNumber != 0)
+			{
+				validationCount = (int)header.DIFATSectorsNumber;
+
+				Sector s = sectors[header.FirstDIFATSectorID];
+
+				if (s == null)
+				{
+					sectors[header.FirstDIFATSectorID] = s = new Sector(GetSectorSize(), sourceStream)
+					{
+						Type = SectorType.DIFAT,
+						Id = header.FirstDIFATSectorID
+					};
+				}
+
+				result.Add(s);
+
+				while (true && validationCount >= 0)
+				{
+					nextSecID = BitConverter.ToInt32(s.GetData(), GetSectorSize() - 4);
+
+					if (nextSecID == Sector.Freesect || nextSecID == Sector.Endofchain) break;
+
+					validationCount--;
+
+					if (validationCount < 0)
+					{
+						Dispose();
+						throw new InvalidDataException("DIFAT sectors count mismatched. Corrupted compound file");
+					}
+
+					s = sectors[nextSecID];
+
+					if (s == null)
+						sectors[nextSecID] = s = new Sector(GetSectorSize(), sourceStream) { Id = nextSecID };
+
+					result.Add(s);
+				}
+			}
+
+			return result;
+		}
+
+		private List<Sector> GetFatSectorChain()
+		{
+			int N_HEADER_FAT_ENTRY = 109;
+
+			List<Sector> result = new List<Sector>();
+			int nextSecID = Sector.Endofchain;
+			List<Sector> difatSectors = GetDifatSectorChain();
+
+			int idx = 0;
+			while (idx < header.FATSectorsNumber && idx < N_HEADER_FAT_ENTRY)
+			{
+				nextSecID = header.DIFAT[idx];
+				Sector s = sectors[nextSecID];
+
+				if (s == null)
+				{
+					sectors[nextSecID] = s = new Sector(GetSectorSize(), sourceStream)
+					{
+						Id = nextSecID,
+						Type = SectorType.FAT
+					};
+				}
+
+				result.Add(s);
+				++idx;
+			}
+
+			if (difatSectors.Count > 0)
+			{
+				var difatStream = new StreamView(difatSectors, GetSectorSize(),
+					header.FATSectorsNumber > N_HEADER_FAT_ENTRY ? (header.FATSectorsNumber - N_HEADER_FAT_ENTRY) * 4 : 0,
+					null, sourceStream);
+
+				byte[] nextDIFATSectorBuffer = new byte[4];
+				difatStream.Read(nextDIFATSectorBuffer, 0, 4);
+				nextSecID = BitConverter.ToInt32(nextDIFATSectorBuffer, 0);
+
+				int i = 0;
+				int nFat = N_HEADER_FAT_ENTRY;
+
+				while (nFat < header.FATSectorsNumber)
+				{
+					if (difatStream.Position == ((GetSectorSize() - 4) + i * GetSectorSize()))
+					{
+						difatStream.Seek(4, SeekOrigin.Current);
+						++i;
+						continue;
+					}
 
-                    Sector s = sectors[nextSecID] as Sector;
+					Sector s = sectors[nextSecID];
 
-                    if (s == null)
-                    {
-                        sectors[nextSecID] = s = new Sector(GetSectorSize(), sourceStream)
-                        {
-                            Type = SectorType.FAT,
-                            Id = nextSecID
-                        };
-                    }
-
-                    result.Add(s);
-
-                    difatStream.Read(nextDIFATSectorBuffer, 0, 4);
-                    nextSecID = BitConverter.ToInt32(nextDIFATSectorBuffer, 0);
-                    nFat++;
-                }
-            }
-            return result;
-        }
+					if (s == null)
+					{
+						sectors[nextSecID] = s = new Sector(GetSectorSize(), sourceStream)
+						{
+							Type = SectorType.FAT,
+							Id = nextSecID
+						};
+					}
+
+					result.Add(s);
+
+					difatStream.Read(nextDIFATSectorBuffer, 0, 4);
+					nextSecID = BitConverter.ToInt32(nextDIFATSectorBuffer, 0);
+					nFat++;
+				}
+			}
+			return result;
+		}
 
-        private List<Sector> GetNormalSectorChain(int secID)
-        {
-            List<Sector> result = new List<Sector>();
-
-            int nextSecID = secID;
-
-            List<Sector> fatSectors = GetFatSectorChain();
+		private List<Sector> GetNormalSectorChain(int secID)
+		{
+			List<Sector> result = new List<Sector>();
+
+			int nextSecID = secID;
+
+			List<Sector> fatSectors = GetFatSectorChain();
 
-            var fatStream = new StreamView(fatSectors, GetSectorSize(), fatSectors.Count * GetSectorSize(), null, sourceStream);
+			var fatStream = new StreamView(fatSectors, GetSectorSize(), fatSectors.Count * GetSectorSize(), null, sourceStream);
 
-            while (true)
-            {
-                if (nextSecID == Sector.Endofchain) break;
-
-                if (nextSecID < 0)
-                    throw new InvalidDataException(String.Format("Next Sector ID reference is below zero. NextID : {0}", nextSecID));
-
-                if (nextSecID >= sectors.Count)
-                    throw new InvalidDataException(String.Format("Next Sector ID reference an out of range sector. NextID : {0} while sector count {1}", nextSecID, sectors.Count));
-
-                Sector s = sectors[nextSecID] as Sector;
-                if (s == null)
-                {
-                    sectors[nextSecID] = s = new Sector(GetSectorSize(), sourceStream)
-                    {
-                        Id = nextSecID,
-                        Type = SectorType.Normal
-                    };
-                }
-
-                result.Add(s);
-
-                fatStream.Seek(nextSecID * 4, SeekOrigin.Begin);
-                int next = fatStream.ReadInt32();
-
-                if (next != nextSecID)
-                    nextSecID = next;
-                else
-                    throw new InvalidDataException("Cyclic sector chain found. File is corrupted");
-            }
-
-            return result;
-        }
-
-        private List<Sector> GetMiniSectorChain(int secID)
-        {
-            List<Sector> result = new List<Sector>();
-
-            if (secID != Sector.Endofchain)
-            {
-                int nextSecID = secID;
-
-                List<Sector> miniFAT = GetNormalSectorChain(header.FirstMiniFATSectorID);
-                List<Sector> miniStream = GetNormalSectorChain(RootEntry.StartSetc);
-
-                StreamView miniFATView = new StreamView(miniFAT, GetSectorSize(), header.MiniFATSectorsNumber * Sector.MinisectorSize, null, sourceStream);
-                StreamView miniStreamView = new StreamView(miniStream, GetSectorSize(), RootStorage.Size, null, sourceStream);
-                BinaryReader miniFATReader = new BinaryReader(miniFATView);
-
-                nextSecID = secID;
-
-                while (true)
-                {
-                    if (nextSecID == Sector.Endofchain)
-                        break;
-
-                    Sector ms = new Sector(Sector.MinisectorSize, sourceStream);
-                    byte[] temp = new byte[Sector.MinisectorSize];
-
-                    ms.Id = nextSecID;
-                    ms.Type = SectorType.Mini;
-
-                    miniStreamView.Seek(nextSecID * Sector.MinisectorSize, SeekOrigin.Begin);
-                    miniStreamView.Read(ms.GetData(), 0, Sector.MinisectorSize);
-
-                    result.Add(ms);
-
-                    miniFATView.Seek(nextSecID * 4, SeekOrigin.Begin);
-                    nextSecID = miniFATReader.ReadInt32();
-                }
-            }
-            return result;
-        }
-
-        internal List<Sector> GetSectorChain(int secID, SectorType chainType)
-        {
-            switch (chainType)
-            {
-                case SectorType.DIFAT:
-                    return GetDifatSectorChain();
-
-                case SectorType.FAT:
-                    return GetFatSectorChain();
-
-                case SectorType.Normal:
-                    return GetNormalSectorChain(secID);
-
-                case SectorType.Mini:
-                    return GetMiniSectorChain(secID);
-
-                default:
-                    throw new Exception("Unsupproted chain type");
-            }
-        }
-
-        public CFStorage RootStorage { get; private set; }
-
-        public int Version => this.header.MajorVersion;
-
-        internal RBTree CreateNewTree()
-        {
-            RBTree bst = new RBTree();
-            return bst;
-        }
-
-        internal RBTree GetChildrenTree(int sid)
-        {
-            RBTree bst = new RBTree();
-            DoLoadChildren(bst, directoryEntries[sid]);
-            return bst;
-        }
-
-        private RBTree DoLoadChildrenTrusted(DirectoryEntry de)
-        {
-            RBTree bst = null;
-            if (de.Child != DirectoryEntry.NOSTREAM)
-                bst = new RBTree(directoryEntries[de.Child]);
-            return bst;
-        }
-
-        private void DoLoadChildren(RBTree bst, DirectoryEntry de)
-        {
-            if (de.Child != DirectoryEntry.NOSTREAM)
-            {
-                if (directoryEntries[de.Child].StgType == StgType.StgInvalid) return;
-
-                LoadSiblings(bst, directoryEntries[de.Child]);
-                NullifyChildNodes(directoryEntries[de.Child]);
-                bst.Insert(directoryEntries[de.Child]);
-            }
-        }
-
-        private void NullifyChildNodes(DirectoryEntry de)
-        {
-            de.Parent = null;
-            de.Left = null;
-            de.Right = null;
-        }
-
-        private readonly List<int> _levelSiDs = new List<int>();
-
-        private void LoadSiblings(RBTree bst, DirectoryEntry de)
-        {
-            _levelSiDs.Clear();
-
-            if (de.LeftSibling != DirectoryEntry.NOSTREAM)
-                DoLoadSiblings(bst, directoryEntries[de.LeftSibling]);
-
-            if (de.RightSibling != DirectoryEntry.NOSTREAM)
-            {
-                _levelSiDs.Add(de.RightSibling);
-                DoLoadSiblings(bst, directoryEntries[de.RightSibling]);
-            }
-        }
-
-        private void DoLoadSiblings(RBTree bst, DirectoryEntry de)
-        {
-            if (ValidateSibling(de.LeftSibling))
-            {
-                _levelSiDs.Add(de.LeftSibling);
-                DoLoadSiblings(bst, directoryEntries[de.LeftSibling]);
-            }
-
-            if (ValidateSibling(de.RightSibling))
-            {
-                _levelSiDs.Add(de.RightSibling);
-                DoLoadSiblings(bst, directoryEntries[de.RightSibling]);
-            }
-
-            NullifyChildNodes(de);
-            bst.Insert(de);
-        }
-
-        private bool ValidateSibling(int sid)
-        {
-            if (sid != DirectoryEntry.NOSTREAM)
-            {
-                if (sid >= directoryEntries.Count)
-                    return false;
-
-                if (directoryEntries[sid].StgType == StgType.StgInvalid)
-                    return false;
-
-                if (!Enum.IsDefined(typeof(StgType), directoryEntries[sid].StgType))
-                    return false;
-
-                if (_levelSiDs.Contains(sid))
-                    throw new InvalidDataException("Cyclic reference of directory item");
-
-                return true;
-            }
-
-            return false;
-        }
-
-        private void LoadDirectories()
-        {
-            List<Sector> directoryChain = GetSectorChain(header.FirstDirectorySectorID, SectorType.Normal);
-
-            if (header.FirstDirectorySectorID == Sector.Endofchain)
-                header.FirstDirectorySectorID = directoryChain[0].Id;
-
-            StreamView dirReader = new StreamView(directoryChain, GetSectorSize(), directoryChain.Count * GetSectorSize(), null, sourceStream);
-
-            while (dirReader.Position < directoryChain.Count * GetSectorSize())
-            {
-                DirectoryEntry de = DirectoryEntry.New(String.Empty, StgType.StgInvalid, directoryEntries);
-                de.Read(dirReader, this.Version);
-            }
-        }
-
-        private void CheckFileLength() => throw new NotImplementedException();
-
-        internal int ReadData(CFStream cFStream, long position, byte[] buffer, int count)
-        {
-            if (count > buffer.Length)
-                throw new ArgumentException("count parameter exceeds buffer size");
-
-            DirectoryEntry de = cFStream.DirEntry;
-
-            count = (int)Math.Min((long)(de.Size - position), (long)count);
-
-            StreamView sView = null;
-            if (de.Size < header.MinSizeStandardStream)
-                sView = new StreamView(GetSectorChain(de.StartSetc, SectorType.Mini), Sector.MinisectorSize, de.Size, null, sourceStream);
-            else
-                sView = new StreamView(GetSectorChain(de.StartSetc, SectorType.Normal), GetSectorSize(), de.Size, null, sourceStream);
-
-            sView.Seek(position, SeekOrigin.Begin);
-            int result = sView.Read(buffer, 0, count);
-
-            return result;
-        }
-
-        internal int ReadData(CFStream cFStream, long position, byte[] buffer, int offset, int count)
-        {
-            DirectoryEntry de = cFStream.DirEntry;
-
-            count = (int)Math.Min((long)(de.Size - offset), (long)count);
-
-            StreamView sView = null;
-            if (de.Size < header.MinSizeStandardStream)
-                sView = new StreamView(GetSectorChain(de.StartSetc, SectorType.Mini), Sector.MinisectorSize, de.Size, null, sourceStream);
-            else
-                sView = new StreamView(GetSectorChain(de.StartSetc, SectorType.Normal), GetSectorSize(), de.Size, null, sourceStream);
-
-            sView.Seek(position, SeekOrigin.Begin);
-            int result = sView.Read(buffer, offset, count);
-
-            return result;
-        }
-
-        internal byte[] GetData(CFStream cFStream)
-        {
-            AssertDisposed();
-
-            byte[] result = null;
-
-            DirectoryEntry de = cFStream.DirEntry;
-
-            if (de.Size < header.MinSizeStandardStream)
-            {
-                var miniView = new StreamView(GetSectorChain(de.StartSetc, SectorType.Mini), Sector.MinisectorSize, de.Size, null, sourceStream);
-
-                using (BinaryReader br = new BinaryReader(miniView))
-                    result = br.ReadBytes((int)de.Size);
-            }
-            else
-            {
-                var sView = new StreamView(GetSectorChain(de.StartSetc, SectorType.Normal), GetSectorSize(), de.Size, null, sourceStream);
-                result = new byte[(int)de.Size];
-                sView.Read(result, 0, result.Length);
-            }
-
-            return result;
-        }
-
-        public byte[] GetDataBySID(int sid)
-        {
-            AssertDisposed();
-            if (sid < 0)
-                return null;
-            byte[] result = null;
-            try
-            {
-                DirectoryEntry de = directoryEntries[sid];
-                if (de.Size < header.MinSizeStandardStream)
-                {
-                    var miniView = new StreamView(GetSectorChain(de.StartSetc, SectorType.Mini), Sector.MinisectorSize, de.Size, null, sourceStream);
-                    BinaryReader br = new BinaryReader(miniView);
-                    result = br.ReadBytes((int)de.Size);
-                    br.Dispose();
-                }
-                else
-                {
-                    var sView = new StreamView(GetSectorChain(de.StartSetc, SectorType.Normal), GetSectorSize(), de.Size, null, sourceStream);
-                    result = new byte[(int)de.Size];
-                    sView.Read(result, 0, result.Length);
-                }
-            }
-            catch
-            {
-                throw new Exception("Cannot get data for SID");
-            }
-            return result;
-        }
-
-        public Guid GetGuidBySID(int sid)
-        {
-            AssertDisposed();
-            if (sid < 0)
-                throw new Exception("Invalid SID");
-            DirectoryEntry de = directoryEntries[sid];
-            return de.StorageCLSID;
-        }
-
-        public Guid GetGuidForStream(int sid)
-        {
-            AssertDisposed();
-            if (sid < 0)
-                throw new Exception("Invalid SID");
-            Guid g = new Guid("00000000000000000000000000000000");
-            for (int i = sid - 1; i >= 0; i--)
-            {
-                if (directoryEntries[i].StorageCLSID != g && directoryEntries[i].StgType == StgType.StgStorage)
-                    return directoryEntries[i].StorageCLSID;
-            }
-            return g;
-        }
-
-        private static int Ceiling(double d) => (int)Math.Ceiling(d);
-
-        private static int LowSaturation(int i) => i > 0 ? i : 0;
-
-        private bool closeStream = true;
-
-        internal bool IsClosed => _disposed;
-
-        #region IDisposable Members
-
-        private bool _disposed;//false
-        private object lockObject = new Object();
-
-        public void Dispose()
-        {
-            try
-            {
-                if (!_disposed)
-                {
-                    lock (lockObject)
-                    {
-                        if (sectors != null)
-                        {
-                            sectors.Clear();
-                            sectors = null;
-                        }
-
-                        this.RootStorage = null;
-                        this.header = null;
-                        this.directoryEntries.Clear();
-                        this.directoryEntries = null;
-                        this.fileName = null;
-                    }
-
-                    if (this.sourceStream != null && closeStream && !Configuration.HasFlag(CFSConfiguration.LeaveOpen))
-                        this.sourceStream.Dispose();
-                }
-            }
-            finally
-            {
-                _disposed = true;
-            }
-            GC.SuppressFinalize(this);
-        }
-
-        #endregion IDisposable Members
-
-        private List<DirectoryEntry> directoryEntries = new List<DirectoryEntry>();
-
-        internal IList<DirectoryEntry> GetDirectories() => directoryEntries;
-
-        internal DirectoryEntry RootEntry => directoryEntries[0];
-
-        private IList<DirectoryEntry> FindDirectoryEntries(String entryName)
-        {
-            List<DirectoryEntry> result = new List<DirectoryEntry>();
-
-            foreach (DirectoryEntry d in directoryEntries)
-                if (d.GetEntryName() == entryName && d.StgType != StgType.StgInvalid)
-                    result.Add(d);
-
-            return result;
-        }
-
-        public IList<CFItem> GetAllNamedEntries(String entryName)
-        {
-            IList<DirectoryEntry> r = FindDirectoryEntries(entryName);
-            List<CFItem> result = new List<CFItem>();
-
-            foreach (DirectoryEntry id in r)
-                if (id.GetEntryName() == entryName && id.StgType != StgType.StgInvalid)
-                    result.Add(id.StgType == StgType.StgStorage ? (CFItem)new CFStorage(this, id) : (CFItem)new CFStream(this, id));
-
-            return result;
-        }
-
-        public int GetNumDirectories()
-        {
-            AssertDisposed();
-            return directoryEntries.Count;
-        }
-
-        public string GetNameDirEntry(int id)
-        {
-            AssertDisposed();
-            if (id < 0)
-                throw new Exception("Invalid Storage ID");
-            return directoryEntries[id].Name;
-        }
-
-        public StgType GetStorageType(int id)
-        {
-            AssertDisposed();
-            if (id < 0)
-                throw new Exception("Invalid Storage ID");
-            return directoryEntries[id].StgType;
-        }
-
-        private void AssertDisposed()
-        {
-            if (_disposed)
-                throw new ObjectDisposedException("Compound File closed: cannot access data");
-        }
-    }
-
-    #endregion Modified OpenMCDF
+			while (true)
+			{
+				if (nextSecID == Sector.Endofchain) break;
+
+				if (nextSecID < 0)
+					throw new InvalidDataException(String.Format("Next Sector ID reference is below zero. NextID : {0}", nextSecID));
+
+				if (nextSecID >= sectors.Count)
+					throw new InvalidDataException(String.Format("Next Sector ID reference an out of range sector. NextID : {0} while sector count {1}", nextSecID, sectors.Count));
+
+				Sector s = sectors[nextSecID];
+				if (s == null)
+				{
+					sectors[nextSecID] = s = new Sector(GetSectorSize(), sourceStream)
+					{
+						Id = nextSecID,
+						Type = SectorType.Normal
+					};
+				}
+
+				result.Add(s);
+
+				fatStream.Seek(nextSecID * 4, SeekOrigin.Begin);
+				int next = fatStream.ReadInt32();
+
+				if (next != nextSecID)
+					nextSecID = next;
+				else
+					throw new InvalidDataException("Cyclic sector chain found. File is corrupted");
+			}
+
+			return result;
+		}
+
+		private List<Sector> GetMiniSectorChain(int secID)
+		{
+			List<Sector> result = new List<Sector>();
+
+			if (secID != Sector.Endofchain)
+			{
+				int nextSecID = secID;
+
+				List<Sector> miniFAT = GetNormalSectorChain(header.FirstMiniFATSectorID);
+				List<Sector> miniStream = GetNormalSectorChain(RootEntry.StartSetc);
+
+				StreamView miniFATView = new StreamView(miniFAT, GetSectorSize(), header.MiniFATSectorsNumber * Sector.MinisectorSize, null, sourceStream);
+				StreamView miniStreamView = new StreamView(miniStream, GetSectorSize(), RootStorage.Size, null, sourceStream);
+				BinaryReader miniFATReader = new BinaryReader(miniFATView);
+
+				nextSecID = secID;
+
+				while (true)
+				{
+					if (nextSecID == Sector.Endofchain)
+						break;
+
+					Sector ms = new Sector(Sector.MinisectorSize, sourceStream);
+					byte[] temp = new byte[Sector.MinisectorSize];
+
+					ms.Id = nextSecID;
+					ms.Type = SectorType.Mini;
+
+					miniStreamView.Seek(nextSecID * Sector.MinisectorSize, SeekOrigin.Begin);
+					miniStreamView.Read(ms.GetData(), 0, Sector.MinisectorSize);
+
+					result.Add(ms);
+
+					miniFATView.Seek(nextSecID * 4, SeekOrigin.Begin);
+					nextSecID = miniFATReader.ReadInt32();
+				}
+			}
+			return result;
+		}
+
+		internal List<Sector> GetSectorChain(int secID, SectorType chainType)
+		{
+			switch (chainType)
+			{
+				case SectorType.DIFAT:
+					return GetDifatSectorChain();
+
+				case SectorType.FAT:
+					return GetFatSectorChain();
+
+				case SectorType.Normal:
+					return GetNormalSectorChain(secID);
+
+				case SectorType.Mini:
+					return GetMiniSectorChain(secID);
+
+				default:
+					throw new Exception("Unsupproted chain type");
+			}
+		}
+
+		public CFStorage RootStorage { get; private set; }
+
+		public int Version => this.header.MajorVersion;
+
+		internal RBTree CreateNewTree()
+		{
+			RBTree bst = new RBTree();
+			return bst;
+		}
+
+		internal RBTree GetChildrenTree(int sid)
+		{
+			RBTree bst = new RBTree();
+			DoLoadChildren(bst, directoryEntries[sid]);
+			return bst;
+		}
+
+		private RBTree DoLoadChildrenTrusted(DirectoryEntry de)
+		{
+			RBTree bst = null;
+			if (de.Child != DirectoryEntry.NOSTREAM)
+				bst = new RBTree(directoryEntries[de.Child]);
+			return bst;
+		}
+
+		private void DoLoadChildren(RBTree bst, DirectoryEntry de)
+		{
+			if (de.Child != DirectoryEntry.NOSTREAM)
+			{
+				if (directoryEntries[de.Child].StgType == StgType.StgInvalid) return;
+
+				LoadSiblings(bst, directoryEntries[de.Child]);
+				NullifyChildNodes(directoryEntries[de.Child]);
+				bst.Insert(directoryEntries[de.Child]);
+			}
+		}
+
+		private void NullifyChildNodes(DirectoryEntry de)
+		{
+			de.Parent = null;
+			de.Left = null;
+			de.Right = null;
+		}
+
+		private readonly List<int> _levelSiDs = new List<int>();
+
+		private void LoadSiblings(RBTree bst, DirectoryEntry de)
+		{
+			_levelSiDs.Clear();
+
+			if (de.LeftSibling != DirectoryEntry.NOSTREAM)
+				DoLoadSiblings(bst, directoryEntries[de.LeftSibling]);
+
+			if (de.RightSibling != DirectoryEntry.NOSTREAM)
+			{
+				_levelSiDs.Add(de.RightSibling);
+				DoLoadSiblings(bst, directoryEntries[de.RightSibling]);
+			}
+		}
+
+		private void DoLoadSiblings(RBTree bst, DirectoryEntry de)
+		{
+			if (ValidateSibling(de.LeftSibling))
+			{
+				_levelSiDs.Add(de.LeftSibling);
+				DoLoadSiblings(bst, directoryEntries[de.LeftSibling]);
+			}
+
+			if (ValidateSibling(de.RightSibling))
+			{
+				_levelSiDs.Add(de.RightSibling);
+				DoLoadSiblings(bst, directoryEntries[de.RightSibling]);
+			}
+
+			NullifyChildNodes(de);
+			bst.Insert(de);
+		}
+
+		private bool ValidateSibling(int sid)
+		{
+			if (sid != DirectoryEntry.NOSTREAM)
+			{
+				if (sid >= directoryEntries.Count)
+					return false;
+
+				if (directoryEntries[sid].StgType == StgType.StgInvalid)
+					return false;
+
+				if (!Enum.IsDefined(typeof(StgType), directoryEntries[sid].StgType))
+					return false;
+
+				if (_levelSiDs.Contains(sid))
+					throw new InvalidDataException("Cyclic reference of directory item");
+
+				return true;
+			}
+
+			return false;
+		}
+
+		private void LoadDirectories()
+		{
+			List<Sector> directoryChain = GetSectorChain(header.FirstDirectorySectorID, SectorType.Normal);
+
+			if (header.FirstDirectorySectorID == Sector.Endofchain)
+				header.FirstDirectorySectorID = directoryChain[0].Id;
+
+			StreamView dirReader = new StreamView(directoryChain, GetSectorSize(), directoryChain.Count * GetSectorSize(), null, sourceStream);
+
+			while (dirReader.Position < directoryChain.Count * GetSectorSize())
+			{
+				DirectoryEntry de = DirectoryEntry.New(String.Empty, StgType.StgInvalid, directoryEntries);
+				de.Read(dirReader, this.Version);
+			}
+		}
+
+		private void CheckFileLength() => throw new NotImplementedException();
+
+		internal int ReadData(CFStream cFStream, long position, byte[] buffer, int count)
+		{
+			if (count > buffer.Length)
+				throw new ArgumentException("count parameter exceeds buffer size");
+
+			DirectoryEntry de = cFStream.DirEntry;
+
+			count = (int)Math.Min(de.Size - position, count);
+
+			StreamView sView = null;
+			if (de.Size < header.MinSizeStandardStream)
+				sView = new StreamView(GetSectorChain(de.StartSetc, SectorType.Mini), Sector.MinisectorSize, de.Size, null, sourceStream);
+			else
+				sView = new StreamView(GetSectorChain(de.StartSetc, SectorType.Normal), GetSectorSize(), de.Size, null, sourceStream);
+
+			sView.Seek(position, SeekOrigin.Begin);
+			int result = sView.Read(buffer, 0, count);
+
+			return result;
+		}
+
+		internal int ReadData(CFStream cFStream, long position, byte[] buffer, int offset, int count)
+		{
+			DirectoryEntry de = cFStream.DirEntry;
+
+			count = (int)Math.Min(de.Size - offset, count);
+
+			StreamView sView = null;
+			if (de.Size < header.MinSizeStandardStream)
+				sView = new StreamView(GetSectorChain(de.StartSetc, SectorType.Mini), Sector.MinisectorSize, de.Size, null, sourceStream);
+			else
+				sView = new StreamView(GetSectorChain(de.StartSetc, SectorType.Normal), GetSectorSize(), de.Size, null, sourceStream);
+
+			sView.Seek(position, SeekOrigin.Begin);
+			int result = sView.Read(buffer, offset, count);
+
+			return result;
+		}
+
+		internal byte[] GetData(CFStream cFStream)
+		{
+			AssertDisposed();
+
+			byte[] result = null;
+
+			DirectoryEntry de = cFStream.DirEntry;
+
+			if (de.Size < header.MinSizeStandardStream)
+			{
+				var miniView = new StreamView(GetSectorChain(de.StartSetc, SectorType.Mini), Sector.MinisectorSize, de.Size, null, sourceStream);
+
+				using (BinaryReader br = new BinaryReader(miniView))
+					result = br.ReadBytes((int)de.Size);
+			}
+			else
+			{
+				var sView = new StreamView(GetSectorChain(de.StartSetc, SectorType.Normal), GetSectorSize(), de.Size, null, sourceStream);
+				result = new byte[(int)de.Size];
+				sView.Read(result, 0, result.Length);
+			}
+
+			return result;
+		}
+
+		public byte[] GetDataBySID(int sid)
+		{
+			AssertDisposed();
+			if (sid < 0)
+				return null;
+			byte[] result = null;
+			try
+			{
+				DirectoryEntry de = directoryEntries[sid];
+				if (de.Size < header.MinSizeStandardStream)
+				{
+					var miniView = new StreamView(GetSectorChain(de.StartSetc, SectorType.Mini), Sector.MinisectorSize, de.Size, null, sourceStream);
+					BinaryReader br = new BinaryReader(miniView);
+					result = br.ReadBytes((int)de.Size);
+					br.Dispose();
+				}
+				else
+				{
+					var sView = new StreamView(GetSectorChain(de.StartSetc, SectorType.Normal), GetSectorSize(), de.Size, null, sourceStream);
+					result = new byte[(int)de.Size];
+					sView.Read(result, 0, result.Length);
+				}
+			}
+			catch
+			{
+				throw new Exception("Cannot get data for SID");
+			}
+			return result;
+		}
+
+		public Guid GetGuidBySID(int sid)
+		{
+			AssertDisposed();
+			if (sid < 0)
+				throw new Exception("Invalid SID");
+			DirectoryEntry de = directoryEntries[sid];
+			return de.StorageCLSID;
+		}
+
+		public Guid GetGuidForStream(int sid)
+		{
+			AssertDisposed();
+			if (sid < 0)
+				throw new Exception("Invalid SID");
+			Guid g = new Guid("00000000000000000000000000000000");
+			for (int i = sid - 1; i >= 0; i--)
+			{
+				if (directoryEntries[i].StorageCLSID != g && directoryEntries[i].StgType == StgType.StgStorage)
+					return directoryEntries[i].StorageCLSID;
+			}
+			return g;
+		}
+
+		private static int Ceiling(double d) => (int)Math.Ceiling(d);
+
+		private static int LowSaturation(int i) => i > 0 ? i : 0;
+
+		private bool closeStream = true;
+
+		internal bool IsClosed => _disposed;
+
+		#region IDisposable Members
+
+		private bool _disposed;//false
+		private object lockObject = new Object();
+
+		public void Dispose()
+		{
+			try
+			{
+				if (!_disposed)
+				{
+					lock (lockObject)
+					{
+						if (sectors != null)
+						{
+							sectors.Clear();
+							sectors = null;
+						}
+
+						this.RootStorage = null;
+						this.header = null;
+						this.directoryEntries.Clear();
+						this.directoryEntries = null;
+						this.fileName = null;
+					}
+
+					if (this.sourceStream != null && closeStream && !Configuration.HasFlag(CFSConfiguration.LeaveOpen))
+						this.sourceStream.Dispose();
+				}
+			}
+			finally
+			{
+				_disposed = true;
+			}
+			GC.SuppressFinalize(this);
+		}
+
+		#endregion IDisposable Members
+
+		private List<DirectoryEntry> directoryEntries = new List<DirectoryEntry>();
+
+		internal IList<DirectoryEntry> GetDirectories() => directoryEntries;
+
+		internal DirectoryEntry RootEntry => directoryEntries[0];
+
+		private IList<DirectoryEntry> FindDirectoryEntries(String entryName)
+		{
+			List<DirectoryEntry> result = new List<DirectoryEntry>();
+
+			foreach (DirectoryEntry d in directoryEntries)
+				if (d.GetEntryName() == entryName && d.StgType != StgType.StgInvalid)
+					result.Add(d);
+
+			return result;
+		}
+
+		public IList<CFItem> GetAllNamedEntries(String entryName)
+		{
+			IList<DirectoryEntry> r = FindDirectoryEntries(entryName);
+			List<CFItem> result = new List<CFItem>();
+
+			foreach (DirectoryEntry id in r)
+				if (id.GetEntryName() == entryName && id.StgType != StgType.StgInvalid)
+					result.Add(id.StgType == StgType.StgStorage ? new CFStorage(this, id) : new CFStream(this, id));
+
+			return result;
+		}
+
+		public int GetNumDirectories()
+		{
+			AssertDisposed();
+			return directoryEntries.Count;
+		}
+
+		public string GetNameDirEntry(int id)
+		{
+			AssertDisposed();
+			if (id < 0)
+				throw new Exception("Invalid Storage ID");
+			return directoryEntries[id].Name;
+		}
+
+		public StgType GetStorageType(int id)
+		{
+			AssertDisposed();
+			if (id < 0)
+				throw new Exception("Invalid Storage ID");
+			return directoryEntries[id].StgType;
+		}
+
+		private void AssertDisposed()
+		{
+			if (_disposed)
+				throw new ObjectDisposedException("Compound File closed: cannot access data");
+		}
+	}
+
+	#endregion Modified OpenMCDF
 }