懒得勤快 3 سال پیش
والد
کامیت
eba1dfe8ea
100فایلهای تغییر یافته به همراه5375 افزوده شده و 127 حذف شده
  1. 20 0
      Masuit.Tools.Abstractions/Extensions/BaseType/IEnumerableExtensions.cs
  2. 2169 0
      Masuit.Tools.Abstractions/Files/FileDetector/AbstractCompoundFileDetailDetector.cs
  3. 56 0
      Masuit.Tools.Abstractions/Files/FileDetector/AbstractFullRegexDetector.cs
  4. 43 0
      Masuit.Tools.Abstractions/Files/FileDetector/AbstractISOBaseMediaFileDetailDetector.cs
  5. 49 0
      Masuit.Tools.Abstractions/Files/FileDetector/AbstractRegexSignatureDetector.cs
  6. 99 0
      Masuit.Tools.Abstractions/Files/FileDetector/AbstractSignatureDetector.cs
  7. 48 0
      Masuit.Tools.Abstractions/Files/FileDetector/AbstractZipDetailDetector.cs
  8. 31 0
      Masuit.Tools.Abstractions/Files/FileDetector/Detectors/ApkDetector.cs
  9. 27 0
      Masuit.Tools.Abstractions/Files/FileDetector/Detectors/AudioVideoInterleaveDetector.cs
  10. 23 0
      Masuit.Tools.Abstractions/Files/FileDetector/Detectors/BashShellScriptDetector.cs
  11. 25 0
      Masuit.Tools.Abstractions/Files/FileDetector/Detectors/BinaryPropertyListDetector.cs
  12. 25 0
      Masuit.Tools.Abstractions/Files/FileDetector/Detectors/BitmapDetector.cs
  13. 24 0
      Masuit.Tools.Abstractions/Files/FileDetector/Detectors/Bzip2Detector.cs
  14. 24 0
      Masuit.Tools.Abstractions/Files/FileDetector/Detectors/CabDetector.cs
  15. 27 0
      Masuit.Tools.Abstractions/Files/FileDetector/Detectors/CertDetector.cs
  16. 32 0
      Masuit.Tools.Abstractions/Files/FileDetector/Detectors/CompoundHWPDetector.cs
  17. 47 0
      Masuit.Tools.Abstractions/Files/FileDetector/Detectors/ConfigurationDetector.cs
  18. 24 0
      Masuit.Tools.Abstractions/Files/FileDetector/Detectors/CursorDetector.cs
  19. 32 0
      Masuit.Tools.Abstractions/Files/FileDetector/Detectors/DLLDetector.cs
  20. 25 0
      Masuit.Tools.Abstractions/Files/FileDetector/Detectors/DMGDetector.cs
  21. 31 0
      Masuit.Tools.Abstractions/Files/FileDetector/Detectors/DOCXDetector.cs
  22. 49 0
      Masuit.Tools.Abstractions/Files/FileDetector/Detectors/DirectDrawSurfaceFormatDetector.cs
  23. 38 0
      Masuit.Tools.Abstractions/Files/FileDetector/Detectors/EXEDetector.cs
  24. 51 0
      Masuit.Tools.Abstractions/Files/FileDetector/Detectors/EpubDetector.cs
  25. 25 0
      Masuit.Tools.Abstractions/Files/FileDetector/Detectors/FLVDetector.cs
  26. 25 0
      Masuit.Tools.Abstractions/Files/FileDetector/Detectors/FlacDetector.cs
  27. 27 0
      Masuit.Tools.Abstractions/Files/FileDetector/Detectors/GifDetector.cs
  28. 25 0
      Masuit.Tools.Abstractions/Files/FileDetector/Detectors/GzDetector.cs
  29. 22 0
      Masuit.Tools.Abstractions/Files/FileDetector/Detectors/HWPDetector.cs
  30. 25 0
      Masuit.Tools.Abstractions/Files/FileDetector/Detectors/ISODetector.cs
  31. 25 0
      Masuit.Tools.Abstractions/Files/FileDetector/Detectors/IconDetector.cs
  32. 24 0
      Masuit.Tools.Abstractions/Files/FileDetector/Detectors/InitializationDetector.cs
  33. 25 0
      Masuit.Tools.Abstractions/Files/FileDetector/Detectors/JavaClassDetector.cs
  34. 25 0
      Masuit.Tools.Abstractions/Files/FileDetector/Detectors/Jpeg2000Detector.cs
  35. 31 0
      Masuit.Tools.Abstractions/Files/FileDetector/Detectors/JpegDetector.cs
  36. 25 0
      Masuit.Tools.Abstractions/Files/FileDetector/Detectors/JpegXRDetector.cs
  37. 25 0
      Masuit.Tools.Abstractions/Files/FileDetector/Detectors/KTXDetector.cs
  38. 25 0
      Masuit.Tools.Abstractions/Files/FileDetector/Detectors/LzhDetector.cs
  39. 27 0
      Masuit.Tools.Abstractions/Files/FileDetector/Detectors/M4ADetector.cs
  40. 27 0
      Masuit.Tools.Abstractions/Files/FileDetector/Detectors/M4VDetector.cs
  41. 26 0
      Masuit.Tools.Abstractions/Files/FileDetector/Detectors/MP3Detector.cs
  42. 29 0
      Masuit.Tools.Abstractions/Files/FileDetector/Detectors/MP4Detector.cs
  43. 32 0
      Masuit.Tools.Abstractions/Files/FileDetector/Detectors/MicrosoftExcelXLSDetector.cs
  44. 33 0
      Masuit.Tools.Abstractions/Files/FileDetector/Detectors/MicrosoftInstallerDetector.cs
  45. 32 0
      Masuit.Tools.Abstractions/Files/FileDetector/Detectors/MicrosoftPowerPointPPTDetector.cs
  46. 32 0
      Masuit.Tools.Abstractions/Files/FileDetector/Detectors/MicrosoftWordDocDetector.cs
  47. 25 0
      Masuit.Tools.Abstractions/Files/FileDetector/Detectors/MidiDetector.cs
  48. 25 0
      Masuit.Tools.Abstractions/Files/FileDetector/Detectors/MikeOBrienPackDetector.cs
  49. 26 0
      Masuit.Tools.Abstractions/Files/FileDetector/Detectors/OggDetector.cs
  50. 51 0
      Masuit.Tools.Abstractions/Files/FileDetector/Detectors/OpenDocumentFormulaDetector.cs
  51. 51 0
      Masuit.Tools.Abstractions/Files/FileDetector/Detectors/OpenDocumentGraphicsDetector.cs
  52. 51 0
      Masuit.Tools.Abstractions/Files/FileDetector/Detectors/OpenDocumentPresentationDetector.cs
  53. 51 0
      Masuit.Tools.Abstractions/Files/FileDetector/Detectors/OpenDocumentSpreadSheetDetector.cs
  54. 51 0
      Masuit.Tools.Abstractions/Files/FileDetector/Detectors/OpenDocumentTextDetector.cs
  55. 25 0
      Masuit.Tools.Abstractions/Files/FileDetector/Detectors/PAKArchiveDetector.cs
  56. 22 0
      Masuit.Tools.Abstractions/Files/FileDetector/Detectors/PDBDetector.cs
  57. 25 0
      Masuit.Tools.Abstractions/Files/FileDetector/Detectors/PFXDetector.cs
  58. 26 0
      Masuit.Tools.Abstractions/Files/FileDetector/Detectors/PKMDetector.cs
  59. 44 0
      Masuit.Tools.Abstractions/Files/FileDetector/Detectors/PPSXDetector.cs
  60. 44 0
      Masuit.Tools.Abstractions/Files/FileDetector/Detectors/PPTXDetector.cs
  61. 22 0
      Masuit.Tools.Abstractions/Files/FileDetector/Detectors/PdfDetector.cs
  62. 25 0
      Masuit.Tools.Abstractions/Files/FileDetector/Detectors/PngDetector.cs
  63. 31 0
      Masuit.Tools.Abstractions/Files/FileDetector/Detectors/PrePOSIXtarDetector.cs
  64. 25 0
      Masuit.Tools.Abstractions/Files/FileDetector/Detectors/PsdDetector.cs
  65. 25 0
      Masuit.Tools.Abstractions/Files/FileDetector/Detectors/QuakeArchiveDetector.cs
  66. 41 0
      Masuit.Tools.Abstractions/Files/FileDetector/Detectors/QuickTimeDetector.cs
  67. 25 0
      Masuit.Tools.Abstractions/Files/FileDetector/Detectors/REGDetector.cs
  68. 27 0
      Masuit.Tools.Abstractions/Files/FileDetector/Detectors/RarDetector.cs
  69. 25 0
      Masuit.Tools.Abstractions/Files/FileDetector/Detectors/RedhatPackageManagerPackageDetector.cs
  70. 25 0
      Masuit.Tools.Abstractions/Files/FileDetector/Detectors/RichTextFormatDetector.cs
  71. 25 0
      Masuit.Tools.Abstractions/Files/FileDetector/Detectors/SQLiteDetector.cs
  72. 24 0
      Masuit.Tools.Abstractions/Files/FileDetector/Detectors/SRTDetector.cs
  73. 27 0
      Masuit.Tools.Abstractions/Files/FileDetector/Detectors/ShockwaveFlashDetector.cs
  74. 117 0
      Masuit.Tools.Abstractions/Files/FileDetector/Detectors/TextDetector.cs
  75. 42 0
      Masuit.Tools.Abstractions/Files/FileDetector/Detectors/TgaDetector.cs
  76. 25 0
      Masuit.Tools.Abstractions/Files/FileDetector/Detectors/ThumbsDBDetector.cs
  77. 28 0
      Masuit.Tools.Abstractions/Files/FileDetector/Detectors/TiffDetector.cs
  78. 26 0
      Masuit.Tools.Abstractions/Files/FileDetector/Detectors/UStarFormatTarDetector.cs
  79. 24 0
      Masuit.Tools.Abstractions/Files/FileDetector/Detectors/VisualStudioSolutionDetector.cs
  80. 26 0
      Masuit.Tools.Abstractions/Files/FileDetector/Detectors/WaveDetector.cs
  81. 39 0
      Masuit.Tools.Abstractions/Files/FileDetector/Detectors/WebMDetector.cs
  82. 27 0
      Masuit.Tools.Abstractions/Files/FileDetector/Detectors/WebPDetector.cs
  83. 26 0
      Masuit.Tools.Abstractions/Files/FileDetector/Detectors/WindowsMemoryDumpDetector.cs
  84. 25 0
      Masuit.Tools.Abstractions/Files/FileDetector/Detectors/WindowsShortcutDetector.cs
  85. 31 0
      Masuit.Tools.Abstractions/Files/FileDetector/Detectors/XLSXDetector.cs
  86. 24 0
      Masuit.Tools.Abstractions/Files/FileDetector/Detectors/XMLDetector.cs
  87. 25 0
      Masuit.Tools.Abstractions/Files/FileDetector/Detectors/XarDetector.cs
  88. 26 0
      Masuit.Tools.Abstractions/Files/FileDetector/Detectors/ZDetector.cs
  89. 28 0
      Masuit.Tools.Abstractions/Files/FileDetector/Detectors/ZipDetector.cs
  90. 27 0
      Masuit.Tools.Abstractions/Files/FileDetector/Detectors/_3GPDetector.cs
  91. 25 0
      Masuit.Tools.Abstractions/Files/FileDetector/Detectors/_7zDetector.cs
  92. 79 0
      Masuit.Tools.Abstractions/Files/FileDetector/FileSignatureDetector.cs
  93. 46 0
      Masuit.Tools.Abstractions/Files/FileDetector/FormatCategoryAttribute.cs
  94. 39 0
      Masuit.Tools.Abstractions/Files/FileDetector/IDetector.cs
  95. 50 0
      Masuit.Tools.Abstractions/Files/FileDetector/InstantDetector.cs
  96. 83 80
      Masuit.Tools.Abstractions/Masuit.Tools.Abstractions.csproj
  97. 1 1
      Masuit.Tools.AspNetCore/Masuit.Tools.AspNetCore.csproj
  98. 8 1
      Masuit.Tools.Core/Masuit.Tools.Core.csproj
  99. 2 44
      Masuit.Tools.Net45/Masuit.Tools.Net45.csproj
  100. 1 1
      Masuit.Tools.Net45/Media/ImageUtilities.cs

+ 20 - 0
Masuit.Tools.Abstractions/Extensions/BaseType/IEnumerableExtensions.cs

@@ -902,5 +902,25 @@ namespace Masuit.Tools
         {
             return list ?? new List<T>();
         }
+
+        public static IEnumerable<T> WhereIf<T>(this IEnumerable<T> source, bool condition, Func<T, bool> where)
+        {
+            return condition ? source.Where(where) : source;
+        }
+
+        public static IEnumerable<T> WhereIf<T>(this IEnumerable<T> source, Func<bool> condition, Func<T, bool> where)
+        {
+            return condition() ? source.Where(where) : source;
+        }
+
+        public static IQueryable<T> WhereIf<T>(this IQueryable<T> source, bool condition, Expression<Func<T, bool>> where)
+        {
+            return condition ? source.Where(where) : source;
+        }
+
+        public static IQueryable<T> WhereIf<T>(this IQueryable<T> source, Func<bool> condition, Expression<Func<T, bool>> where)
+        {
+            return condition() ? source.Where(where) : source;
+        }
     }
 }

+ 2169 - 0
Masuit.Tools.Abstractions/Files/FileDetector/AbstractCompoundFileDetailDetector.cs

@@ -0,0 +1,2169 @@
+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
+     * 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/.
+     *
+     * The Original Code is OpenMCDF - Compound Document Format library.
+     *
+     * 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);
+        }
+
+        #endregion IDisposable Members
+    }
+
+    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;
+
+        public int SID { get; set; } = -1;
+
+        internal const Int32 NOSTREAM = unchecked((int)0xFFFFFFFF);
+
+        private DirectoryEntry(String name, StgType stgType, IList<DirectoryEntry> dirRepository)
+        {
+            this.dirRepository = dirRepository;
+
+            this.StgType = stgType;
+
+            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;
+
+                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);
+        }
+
+        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 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;
+        }
+
+        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();
+
+            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;
+        }
+
+        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;
+
+                case 4:
+                    MajorVersion = 4;
+                    SectorShift = 0x000C;
+                    break;
+
+                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] = 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 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");
+        }
+    }
+
+    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;
+                    }
+
+                    Sector s = sectors[nextSecID] as Sector;
+
+                    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();
+
+            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
+}

+ 56 - 0
Masuit.Tools.Abstractions/Files/FileDetector/AbstractFullRegexDetector.cs

@@ -0,0 +1,56 @@
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Reflection;
+using System.Text;
+using System.Text.RegularExpressions;
+using Masuit.Tools.AspNetCore.Mime;
+
+namespace Masuit.Tools.Files.FileDetector;
+
+public abstract class AbstractFullRegexDetector : IDetector
+{
+    public abstract string Extension { get; }
+
+    public virtual string Precondition => "txt";
+
+    protected abstract Regex Pattern { get; }
+
+    public virtual string MimeType => new MimeMapper().GetMimeFromExtension("." + Extension);
+
+    public virtual List<FormatCategory> FormatCategories => GetType().GetCustomAttributes<FormatCategoryAttribute>().Select(a => a.Category).ToList();
+
+    public virtual bool Detect(Stream stream)
+    {
+        var encodings = new[]
+        {
+            Encoding.UTF8,
+            Encoding.Unicode,
+            Encoding.GetEncoding ( "utf-7" ),
+            Encoding.GetEncoding ( "utf-32" ),
+            Encoding.BigEndianUnicode,
+            Encoding.GetEncoding ( "ascii" ),
+            Encoding.GetEncoding ( "ks_c_5601-1987" ),
+            Encoding.GetEncoding ( "iso-2022-kr" ),
+            Encoding.GetEncoding ( "shift_jis" ),
+            Encoding.GetEncoding ( "csISO2022JP" ),
+            Encoding.GetEncoding ( "windows-1250" ),
+            Encoding.GetEncoding ( "windows-1251" ),
+            Encoding.GetEncoding ( "windows-1252" ),
+            Encoding.GetEncoding ( "windows-1253" ),
+            Encoding.GetEncoding ( "windows-1254" ),
+        };
+        foreach (var encoding in encodings)
+        {
+            stream.Position = 0;
+            using var reader = new StreamReader(stream, encoding, true, 4096, true);
+            string buffer = reader.ReadToEnd();
+            if (Pattern.Replace(buffer, "") == string.Empty)
+            {
+                return true;
+            }
+        }
+
+        return false;
+    }
+}

+ 43 - 0
Masuit.Tools.Abstractions/Files/FileDetector/AbstractISOBaseMediaFileDetailDetector.cs

@@ -0,0 +1,43 @@
+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 AbstractISOBaseMediaFileDetailDetector : IDetector
+{
+    protected abstract IEnumerable<string> NextSignature { get; }
+
+    public abstract string Extension { get; }
+
+    public virtual string Precondition => null;
+
+    public virtual string MimeType => new MimeMapper().GetMimeFromExtension("." + Extension);
+
+    public virtual List<FormatCategory> FormatCategories => GetType().GetCustomAttributes<FormatCategoryAttribute>().Select(a => a.Category).ToList();
+
+    public virtual bool Detect(Stream stream)
+    {
+        using var reader = new BinaryReader(stream, Encoding.UTF8, true);
+        int offset = reader.ReadInt32();
+
+        if (reader.ReadByte() == 0x66 && reader.ReadByte() == 0x74 && reader.ReadByte() == 0x79 && reader.ReadByte() == 0x70)
+        {
+            foreach (var ns in NextSignature)
+            {
+                stream.Position = 8;
+                var readed = Encoding.GetEncoding("ascii").GetString(reader.ReadBytes(ns.Length), 0, ns.Length);
+                stream.Position = offset;
+                if (ns == readed)
+                {
+                    return true;
+                }
+            }
+        }
+
+        return false;
+    }
+}

+ 49 - 0
Masuit.Tools.Abstractions/Files/FileDetector/AbstractRegexSignatureDetector.cs

@@ -0,0 +1,49 @@
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Reflection;
+using System.Text;
+using System.Text.RegularExpressions;
+using Masuit.Tools.AspNetCore.Mime;
+
+namespace Masuit.Tools.Files.FileDetector;
+
+public abstract class AbstractRegexSignatureDetector : IDetector
+{
+    public abstract string Extension { get; }
+
+    public virtual string Precondition => null;
+
+    public virtual string MimeType => new MimeMapper().GetMimeFromExtension("." + Extension);
+
+    public virtual List<FormatCategory> FormatCategories => GetType().GetCustomAttributes<FormatCategoryAttribute>().Select(a => a.Category).ToList();
+
+    protected abstract Regex Signature { get; }
+
+    public virtual bool Detect(Stream stream)
+    {
+        int readBufferSize = Signature.ToString().Length * 8;
+        char[] buffer = new char[readBufferSize];
+
+        var encodings = new[]
+        {
+            Encoding.ASCII,
+            Encoding.UTF8,
+            Encoding.UTF32,
+            Encoding.Unicode,
+            Encoding.BigEndianUnicode
+        };
+        foreach (var encoding in encodings)
+        {
+            stream.Position = 0;
+            using StreamReader reader = new StreamReader(stream, encoding, true, readBufferSize, true);
+            reader.ReadBlock(buffer, 0, readBufferSize);
+            if (Signature.IsMatch(new string(buffer)))
+            {
+                return true;
+            }
+        }
+
+        return false;
+    }
+}

+ 99 - 0
Masuit.Tools.Abstractions/Files/FileDetector/AbstractSignatureDetector.cs

@@ -0,0 +1,99 @@
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Reflection;
+using System.Runtime.InteropServices;
+using Masuit.Tools.AspNetCore.Mime;
+
+namespace Masuit.Tools.Files.FileDetector;
+
+[StructLayout(LayoutKind.Sequential)]
+public struct SignatureInformation
+{
+    /// <summary>
+    ///
+    /// </summary>
+    public int Position;
+
+    /// <summary>
+    ///
+    /// </summary>
+    public byte[] Signature;
+
+    /// <summary>
+    ///
+    /// </summary>
+    public byte[] Presignature;
+}
+
+public abstract class AbstractSignatureDetector : IDetector
+{
+    public abstract string Extension { get; }
+
+    public virtual string Precondition => null;
+
+    protected abstract SignatureInformation[] SignatureInformations { get; }
+
+    public virtual string MimeType => new MimeMapper().GetMimeFromExtension("." + Extension);
+
+    public virtual List<FormatCategory> FormatCategories => GetType().GetCustomAttributes<FormatCategoryAttribute>().Select(a => a.Category).ToList();
+
+    private int _cachedLongestLength = -1;
+
+    public virtual bool Detect(Stream stream)
+    {
+        if (_cachedLongestLength == -1)
+        {
+            foreach (var sig in SignatureInformations)
+            {
+                _cachedLongestLength = _cachedLongestLength < sig.Signature.Length ? sig.Signature.Length : _cachedLongestLength;
+            }
+        }
+
+        byte[] buffer = new byte[_cachedLongestLength];
+
+        byte[] preSignature = null;
+        bool correct = false;
+        while (true)
+        {
+            bool found = false;
+            foreach (var siginfo in SignatureInformations.Where(si => CompareArray(si.Presignature, preSignature)).OrderBy(si => si.Position))
+            {
+                correct = false;
+                stream.Position = siginfo.Position;
+                stream.Read(buffer, 0, _cachedLongestLength);
+                if (CompareArray(siginfo.Signature, buffer))
+                {
+                    preSignature = siginfo.Signature;
+                    correct = true;
+                    found = true;
+                    break;
+                }
+            }
+            if (!found)
+            {
+                break;
+            }
+        }
+
+        return correct;
+    }
+
+    private bool CompareArray(byte[] a1, byte[] a2)
+    {
+        if (a1 == null && a2 == null) return true;
+        if (a1 == null || a2 == null) return false;
+
+        bool failed = false;
+        int min = a1.Length > a2.Length ? a2.Length : a1.Length;
+        for (int i = 0; i < min; ++i)
+        {
+            if (a1[i] != a2[i])
+            {
+                failed = true;
+                break;
+            }
+        }
+        return !failed;
+    }
+}

+ 48 - 0
Masuit.Tools.Abstractions/Files/FileDetector/AbstractZipDetailDetector.cs

@@ -0,0 +1,48 @@
+using System.Collections.Generic;
+using System.IO;
+using System.IO.Compression;
+using System.Linq;
+using System.Reflection;
+using Masuit.Tools.AspNetCore.Mime;
+
+namespace Masuit.Tools.Files.FileDetector;
+
+public abstract class AbstractZipDetailDetector : IDetector
+{
+    public abstract IEnumerable<string> Files { get; }
+
+    public abstract string Extension { get; }
+
+    public virtual string Precondition => null;
+
+    public virtual string MimeType => new MimeMapper().GetMimeFromExtension("." + Extension);
+
+    public virtual List<FormatCategory> FormatCategories => GetType().GetCustomAttributes<FormatCategoryAttribute>().Select(a => a.Category).ToList();
+
+    protected virtual bool IsValid(string filename, ZipArchiveEntry entry)
+    {
+        return true;
+    }
+
+    public bool Detect(Stream stream)
+    {
+        try
+        {
+            using var archive = new ZipArchive(stream, ZipArchiveMode.Read, true);
+            foreach (string filename in Files)
+            {
+                bool succeed = archive.Entries.Any(entry => entry.FullName == filename && IsValid(filename, entry));
+                if (!succeed)
+                {
+                    return false;
+                }
+            }
+
+            return true;
+        }
+        catch
+        {
+            return false;
+        }
+    }
+}

+ 31 - 0
Masuit.Tools.Abstractions/Files/FileDetector/Detectors/ApkDetector.cs

@@ -0,0 +1,31 @@
+using System.Collections.Generic;
+using System.Linq;
+using System.Reflection;
+using Masuit.Tools.AspNetCore.Mime;
+
+namespace Masuit.Tools.Files.FileDetector.Detectors;
+
+[FormatCategory(FormatCategory.Archive)]
+[FormatCategory(FormatCategory.Compression)]
+[FormatCategory(FormatCategory.Executable)]
+internal class ApkDetector : AbstractZipDetailDetector
+{
+    public override IEnumerable<string> Files
+    {
+        get
+        {
+            yield return "classes.dex";
+            yield return "AndroidManifest.xml";
+        }
+    }
+
+    public override string Precondition => "zip";
+
+    public override string Extension => "apk";
+
+    public override string MimeType => new MimeMapper().GetMimeFromExtension("." + Extension);
+
+    public override List<FormatCategory> FormatCategories => GetType().GetCustomAttributes<FormatCategoryAttribute>().Select(a => a.Category).ToList();
+
+    public override string ToString() => "Android Package(APK) Detector";
+}

+ 27 - 0
Masuit.Tools.Abstractions/Files/FileDetector/Detectors/AudioVideoInterleaveDetector.cs

@@ -0,0 +1,27 @@
+using System.Collections.Generic;
+using System.Linq;
+using System.Reflection;
+using Masuit.Tools.AspNetCore.Mime;
+
+namespace Masuit.Tools.Files.FileDetector.Detectors;
+
+[FormatCategory(FormatCategory.Video)]
+[FormatCategory(FormatCategory.Audio)]
+internal class AudioVideoInterleaveDetector : AbstractSignatureDetector
+{
+    private static SignatureInformation[] AVI_SignatureInfo = new[]
+    {
+        new SignatureInformation () { Position = 0, Signature = new byte [] { 0x52, 0x49, 0x46, 0x46 } },
+        new SignatureInformation () { Position = 8, Signature = new byte [] { 0x41, 0x56, 0x49, 0x20 }, Presignature = new byte [] { 0x52, 0x49, 0x46, 0x46 } },
+    };
+
+    public override string Extension => "avi";
+
+    protected override SignatureInformation[] SignatureInformations => AVI_SignatureInfo;
+
+    public override string MimeType => new MimeMapper().GetMimeFromExtension("." + Extension);
+
+    public override List<FormatCategory> FormatCategories => GetType().GetCustomAttributes<FormatCategoryAttribute>().Select(a => a.Category).ToList();
+
+    public override string ToString() => "Audio Video Interleave(AVI) Detector";
+}

+ 23 - 0
Masuit.Tools.Abstractions/Files/FileDetector/Detectors/BashShellScriptDetector.cs

@@ -0,0 +1,23 @@
+using System.Collections.Generic;
+using System.Linq;
+using System.Reflection;
+using System.Text.RegularExpressions;
+using Masuit.Tools.AspNetCore.Mime;
+
+namespace Masuit.Tools.Files.FileDetector.Detectors;
+
+[FormatCategory(FormatCategory.Executable)]
+internal class BashShellScriptDetector : AbstractRegexSignatureDetector
+{
+    public override string Precondition => "txt";
+
+    public override string Extension => "sh";
+
+    protected override Regex Signature => new("^#!\\/(.+)\n");
+
+    public override string MimeType => new MimeMapper().GetMimeFromExtension("." + Extension);
+
+    public override List<FormatCategory> FormatCategories => GetType().GetCustomAttributes<FormatCategoryAttribute>().Select(a => a.Category).ToList();
+
+    public override string ToString() => "Bash Shell Script Detector";
+}

+ 25 - 0
Masuit.Tools.Abstractions/Files/FileDetector/Detectors/BinaryPropertyListDetector.cs

@@ -0,0 +1,25 @@
+using System.Collections.Generic;
+using System.Linq;
+using System.Reflection;
+using Masuit.Tools.AspNetCore.Mime;
+
+namespace Masuit.Tools.Files.FileDetector.Detectors;
+
+[FormatCategory(FormatCategory.Document)]
+internal class BinaryPropertyListDetector : AbstractSignatureDetector
+{
+    private static SignatureInformation[] BPLIST_SignatureInfo = new[]
+    {
+        new SignatureInformation () { Position = 0, Signature = new byte [] { 0x62, 0x70, 0x6C, 0x69, 0x73, 0x74 } },
+    };
+
+    public override string Extension => "bplist";
+
+    protected override SignatureInformation[] SignatureInformations => BPLIST_SignatureInfo;
+
+    public override string MimeType => new MimeMapper().GetMimeFromExtension("." + Extension);
+
+    public override List<FormatCategory> FormatCategories => GetType().GetCustomAttributes<FormatCategoryAttribute>().Select(a => a.Category).ToList();
+
+    public override string ToString() => "Apple Binary Property List Detector";
+}

+ 25 - 0
Masuit.Tools.Abstractions/Files/FileDetector/Detectors/BitmapDetector.cs

@@ -0,0 +1,25 @@
+using System.Collections.Generic;
+using System.Linq;
+using System.Reflection;
+using Masuit.Tools.AspNetCore.Mime;
+
+namespace Masuit.Tools.Files.FileDetector.Detectors;
+
+[FormatCategory(FormatCategory.Image)]
+internal class BitmapDetector : AbstractSignatureDetector
+{
+    private static SignatureInformation[] BMP_SignatureInfo = {
+        new() { Position = 0, Signature = new byte [] { 0x42, 0x4D } },
+        new() { Position = 6, Signature = new byte [] { 0x00, 0x00, 0x00, 0x00 }, Presignature = new byte [] { 0x42, 0x4D } },
+    };
+
+    public override string Extension => "bmp";
+
+    protected override SignatureInformation[] SignatureInformations => BMP_SignatureInfo;
+
+    public override string MimeType => new MimeMapper().GetMimeFromExtension("." + Extension);
+
+    public override List<FormatCategory> FormatCategories => GetType().GetCustomAttributes<FormatCategoryAttribute>().Select(a => a.Category).ToList();
+
+    public override string ToString() => "Bitmap(BMP) Detector";
+}

+ 24 - 0
Masuit.Tools.Abstractions/Files/FileDetector/Detectors/Bzip2Detector.cs

@@ -0,0 +1,24 @@
+using System.Collections.Generic;
+using System.Linq;
+using System.Reflection;
+using Masuit.Tools.AspNetCore.Mime;
+
+namespace Masuit.Tools.Files.FileDetector.Detectors;
+
+[FormatCategory(FormatCategory.Compression)]
+internal class Bzip2Detector : AbstractSignatureDetector
+{
+    private static SignatureInformation[] BZ2_SignatureInfo = {
+        new() { Position = 0, Signature = new byte [] { 0x42, 0x5A, 0x68 } },
+    };
+
+    public override string Extension => "bz2";
+
+    protected override SignatureInformation[] SignatureInformations => BZ2_SignatureInfo;
+
+    public override string MimeType => new MimeMapper().GetMimeFromExtension("." + Extension);
+
+    public override List<FormatCategory> FormatCategories => GetType().GetCustomAttributes<FormatCategoryAttribute>().Select(a => a.Category).ToList();
+
+    public override string ToString() => "Bunzip2 Detector";
+}

+ 24 - 0
Masuit.Tools.Abstractions/Files/FileDetector/Detectors/CabDetector.cs

@@ -0,0 +1,24 @@
+using System.Collections.Generic;
+using System.Linq;
+using System.Reflection;
+using Masuit.Tools.AspNetCore.Mime;
+
+namespace Masuit.Tools.Files.FileDetector.Detectors;
+
+[FormatCategory(FormatCategory.Archive)]
+internal class CabDetector : AbstractSignatureDetector
+{
+    private static readonly SignatureInformation[] CabSignatureInfo = {
+        new() { Position = 0, Signature = new byte [] { 0x4D, 0x53, 0x43, 0x46 } },
+    };
+
+    public override string Extension => "cab";
+
+    protected override SignatureInformation[] SignatureInformations => CabSignatureInfo;
+
+    public override string MimeType => new MimeMapper().GetMimeFromExtension("." + Extension);
+
+    public override List<FormatCategory> FormatCategories => GetType().GetCustomAttributes<FormatCategoryAttribute>().Select(a => a.Category).ToList();
+
+    public override string ToString() => "Windows Cabinet Detector";
+}

+ 27 - 0
Masuit.Tools.Abstractions/Files/FileDetector/Detectors/CertDetector.cs

@@ -0,0 +1,27 @@
+using System.Collections.Generic;
+using System.Linq;
+using System.Reflection;
+using System.Text;
+using Masuit.Tools.AspNetCore.Mime;
+
+namespace Masuit.Tools.Files.FileDetector.Detectors;
+
+[FormatCategory(FormatCategory.Document)]
+internal class CertDetector : AbstractSignatureDetector
+{
+    private static SignatureInformation[] CRT_SignatureInfo = {
+        new() { Position = 0, Signature = Encoding.GetEncoding ( "ascii" ).GetBytes ( "-----BEGIN CERTIFICATE-----" ) },
+    };
+
+    public override string Precondition => "txt";
+
+    public override string Extension => "crt";
+
+    protected override SignatureInformation[] SignatureInformations => CRT_SignatureInfo;
+
+    public override string MimeType => new MimeMapper().GetMimeFromExtension("." + Extension);
+
+    public override List<FormatCategory> FormatCategories => GetType().GetCustomAttributes<FormatCategoryAttribute>().Select(a => a.Category).ToList();
+
+    public override string ToString() => "Certificate Detector";
+}

+ 32 - 0
Masuit.Tools.Abstractions/Files/FileDetector/Detectors/CompoundHWPDetector.cs

@@ -0,0 +1,32 @@
+using System.Collections.Generic;
+using System.Linq;
+using System.Reflection;
+using System.Text;
+using Masuit.Tools.AspNetCore.Mime;
+
+namespace Masuit.Tools.Files.FileDetector.Detectors;
+
+[FormatCategory(FormatCategory.Document)]
+internal class CompoundHWPDetector : AbstractCompoundFileDetailDetector
+{
+    public override IEnumerable<string> Chunks
+    {
+        get
+        {
+            yield return "FileHeader";
+        }
+    }
+
+    public override string Extension => "hwp";
+
+    protected override bool IsValidChunk(string chunkName, byte[] chunkData)
+    {
+        return chunkName == "FileHeader" && Encoding.UTF8.GetString(chunkData, 0, 18) == "HWP Document File\0";
+    }
+
+    public override string MimeType => new MimeMapper().GetMimeFromExtension("." + Extension);
+
+    public override List<FormatCategory> FormatCategories => GetType().GetCustomAttributes<FormatCategoryAttribute>().Select(a => a.Category).ToList();
+
+    public override string ToString() => "Hancom Compound File Binary Format Type HWP Document Detector";
+}

+ 47 - 0
Masuit.Tools.Abstractions/Files/FileDetector/Detectors/ConfigurationDetector.cs

@@ -0,0 +1,47 @@
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Reflection;
+using System.Xml;
+using Masuit.Tools.AspNetCore.Mime;
+
+namespace Masuit.Tools.Files.FileDetector.Detectors;
+
+[FormatCategory(FormatCategory.Document)]
+internal class ConfigurationDetector : IDetector
+{
+    public string Precondition => "txt";
+
+    public string Extension => "config";
+
+    public string MimeType => new MimeMapper().GetMimeFromExtension("." + Extension);
+
+    public List<FormatCategory> FormatCategories => GetType().GetCustomAttributes<FormatCategoryAttribute>().Select(a => a.Category).ToList();
+
+    public bool Detect(Stream stream)
+    {
+        try
+        {
+            var reader = XmlReader.Create(stream, new XmlReaderSettings()
+            {
+            });
+            if (reader.Read())
+            {
+                if (reader.IsStartElement())
+                {
+                    if (reader.Name == "configuration")
+                    {
+                        return true;
+                    }
+                }
+            }
+        }
+        catch
+        {
+        }
+
+        return false;
+    }
+
+    public override string ToString() => "Microsoft .NET Configuration File Detector";
+}

+ 24 - 0
Masuit.Tools.Abstractions/Files/FileDetector/Detectors/CursorDetector.cs

@@ -0,0 +1,24 @@
+using System.Collections.Generic;
+using System.Linq;
+using System.Reflection;
+using Masuit.Tools.AspNetCore.Mime;
+
+namespace Masuit.Tools.Files.FileDetector.Detectors;
+
+[FormatCategory(FormatCategory.Image)]
+internal class CursorDetector : AbstractSignatureDetector
+{
+    private static readonly SignatureInformation[] CurSignatureInfo = {
+        new() { Position = 0, Signature = new byte [] { 0x00, 0x00, 0x02, 0x00 } },
+    };
+
+    public override string Extension => "cur";
+
+    protected override SignatureInformation[] SignatureInformations => CurSignatureInfo;
+
+    public override string MimeType => new MimeMapper().GetMimeFromExtension("." + Extension);
+
+    public override List<FormatCategory> FormatCategories => GetType().GetCustomAttributes<FormatCategoryAttribute>().Select(a => a.Category).ToList();
+
+    public override string ToString() => "Cursor Detector";
+}

+ 32 - 0
Masuit.Tools.Abstractions/Files/FileDetector/Detectors/DLLDetector.cs

@@ -0,0 +1,32 @@
+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.Detectors;
+
+[FormatCategory(FormatCategory.System)]
+[FormatCategory(FormatCategory.Executable)]
+internal class DLLDetector : IDetector
+{
+    public string Precondition => "exe";
+
+    public string Extension => "dll";
+
+    public string MimeType => new MimeMapper().GetMimeFromExtension("." + Extension);
+
+    public List<FormatCategory> FormatCategories => GetType().GetCustomAttributes<FormatCategoryAttribute>().Select(a => a.Category).ToList();
+
+    public bool Detect(Stream stream)
+    {
+        stream.Position = 60;
+        using var reader = new BinaryReader(stream, Encoding.UTF8, true);
+        stream.Position = reader.ReadInt32() + 4 + 18;
+        short characteristics = reader.ReadInt16();
+        return (characteristics & 0x2000) != 0;
+    }
+
+    public override string ToString() => "Windows Dynamic Linkage Library Detector";
+}

+ 25 - 0
Masuit.Tools.Abstractions/Files/FileDetector/Detectors/DMGDetector.cs

@@ -0,0 +1,25 @@
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Reflection;
+using Masuit.Tools.AspNetCore.Mime;
+
+namespace Masuit.Tools.Files.FileDetector.Detectors;
+
+[FormatCategory(FormatCategory.Archive)]
+internal class DMGDetector : AbstractSignatureDetector
+{
+    private static SignatureInformation[] DMG_SignatureInfo = {
+        new() { Position = 0, Signature = new byte [] { 0x78, 0x01, 0x73, 0x0D, 0x62, 0x62, 0x60 } },
+    };
+
+    public override string Extension => "dmg";
+
+    public override string MimeType => new MimeMapper().GetMimeFromExtension("." + Extension);
+
+    public override List<FormatCategory> FormatCategories => GetType().GetCustomAttributes<FormatCategoryAttribute>().Select(a => a.Category).ToList();
+
+    protected override SignatureInformation[] SignatureInformations => DMG_SignatureInfo;
+
+    public override string ToString() => "Apple Disk Mount Image(DMG) Detector";
+}

+ 31 - 0
Masuit.Tools.Abstractions/Files/FileDetector/Detectors/DOCXDetector.cs

@@ -0,0 +1,31 @@
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Reflection;
+using Masuit.Tools.AspNetCore.Mime;
+
+namespace Masuit.Tools.Files.FileDetector.Detectors;
+
+[FormatCategory(FormatCategory.Document)]
+internal class DOCXDetector : AbstractZipDetailDetector
+{
+    public override IEnumerable<string> Files
+    {
+        get
+        {
+            yield return "[Content_Types].xml";
+            yield return "_rels/.rels";
+            yield return "word/_rels/document.xml.rels";
+        }
+    }
+
+    public override string Precondition => "zip";
+
+    public override string Extension => "docx";
+
+    public override string MimeType => new MimeMapper().GetMimeFromExtension("." + Extension);
+
+    public override List<FormatCategory> FormatCategories => GetType().GetCustomAttributes<FormatCategoryAttribute>().Select(a => a.Category).ToList();
+
+    public override string ToString() => "Microsoft Word Open XML Document(DOCX) Detector";
+}

+ 49 - 0
Masuit.Tools.Abstractions/Files/FileDetector/Detectors/DirectDrawSurfaceFormatDetector.cs

@@ -0,0 +1,49 @@
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Reflection;
+using Masuit.Tools.AspNetCore.Mime;
+
+namespace Masuit.Tools.Files.FileDetector.Detectors;
+
+[FormatCategory(FormatCategory.Image)]
+internal class DirectDrawSurfaceFormatDetector : AbstractSignatureDetector
+{
+    private static readonly SignatureInformation[] DdsSignatureInfo = {
+        new() { Position = 0, Signature = new byte [] { 0x44, 0x44, 0x53, 0x20 } },
+    };
+
+    public override string Extension => "dds";
+
+    protected override SignatureInformation[] SignatureInformations => DdsSignatureInfo;
+
+    public override string MimeType => new MimeMapper().GetMimeFromExtension("." + Extension);
+
+    public override List<FormatCategory> FormatCategories => GetType().GetCustomAttributes<FormatCategoryAttribute>().Select(a => a.Category).ToList();
+
+    public override bool Detect(Stream stream)
+    {
+        if (base.Detect(stream))
+        {
+            byte[] buffer = new byte[4];
+
+            stream.Read(buffer, 0, 4);
+            int dwSize = (buffer[3] << 24) + (buffer[2] << 16) + (buffer[1] << 8) + buffer[0];
+            if (dwSize != 0x7C)
+            {
+                return false;
+            }
+
+            stream.Read(buffer, 0, 4);
+            int dwFlags = (buffer[3] << 24) + (buffer[2] << 16) + (buffer[1] << 8) + buffer[0];
+
+            if ((dwFlags & 0x1) != 0 && (dwFlags & 0x2) != 0 && (dwFlags & 0x4) != 0 && (dwFlags & 0x1000) != 0)
+            {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    public override string ToString() => "DirectDraw Surface(DDS) Detector";
+}

+ 38 - 0
Masuit.Tools.Abstractions/Files/FileDetector/Detectors/EXEDetector.cs

@@ -0,0 +1,38 @@
+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.Detectors;
+
+[FormatCategory(FormatCategory.Executable)]
+internal class EXEDetector : AbstractSignatureDetector
+{
+    private static readonly SignatureInformation[] ExeSignatureInfo = {
+        new() { Position = 0, Signature = new byte [] { 0x4D, 0x5A } },
+    };
+
+    public override string Extension => "exe";
+
+    protected override SignatureInformation[] SignatureInformations => ExeSignatureInfo;
+
+    public override string MimeType => new MimeMapper().GetMimeFromExtension("." + Extension);
+
+    public override List<FormatCategory> FormatCategories => GetType().GetCustomAttributes<FormatCategoryAttribute>().Select(a => a.Category).ToList();
+
+    public override bool Detect(Stream stream)
+    {
+        if (base.Detect(stream))
+        {
+            stream.Position = 60;
+            using BinaryReader reader = new BinaryReader(stream, Encoding.UTF8, true);
+            stream.Position = reader.ReadInt32();
+            return reader.ReadByte() == 0x50 && reader.ReadByte() == 0x45 && reader.ReadByte() == 0 && reader.ReadByte() == 0;
+        }
+        return false;
+    }
+
+    public override string ToString() => "Portable Execution File Format Detector";
+}

+ 51 - 0
Masuit.Tools.Abstractions/Files/FileDetector/Detectors/EpubDetector.cs

@@ -0,0 +1,51 @@
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.IO.Compression;
+using System.Reflection;
+using System.Text;
+using Masuit.Tools.AspNetCore.Mime;
+
+namespace Masuit.Tools.Files.FileDetector.Detectors;
+
+[FormatCategory(FormatCategory.Document)]
+internal class EpubDetector : AbstractZipDetailDetector
+{
+    public override IEnumerable<string> Files
+    {
+        get
+        {
+            yield return "mimetype";
+        }
+    }
+
+    public override string Precondition => "zip";
+
+    public override string Extension => "epub";
+
+    public override string MimeType => new MimeMapper().GetMimeFromExtension("." + Extension);
+
+    public override List<FormatCategory> FormatCategories => GetType().GetCustomAttributes<FormatCategoryAttribute>().Select(a => a.Category).ToList();
+
+    protected override bool IsValid(string filename, ZipArchiveEntry entry)
+    {
+        if (filename == "mimetype")
+        {
+            using Stream mimetypeStream = entry.Open();
+            byte[] buffer = new byte["application/epub+zip".Length];
+            if (mimetypeStream.Read(buffer, 0, buffer.Length) != buffer.Length)
+            {
+                return false;
+            }
+
+            if (Encoding.GetEncoding("ascii").GetString(buffer, 0, buffer.Length) != "application/epub+zip")
+            {
+                return false;
+            }
+        }
+
+        return base.IsValid(filename, entry);
+    }
+
+    public override string ToString() => "e-Pub Document File Detector";
+}

+ 25 - 0
Masuit.Tools.Abstractions/Files/FileDetector/Detectors/FLVDetector.cs

@@ -0,0 +1,25 @@
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Reflection;
+using Masuit.Tools.AspNetCore.Mime;
+
+namespace Masuit.Tools.Files.FileDetector.Detectors;
+
+[FormatCategory(FormatCategory.Video)]
+internal class FLVDetector : AbstractSignatureDetector
+{
+    private static SignatureInformation[] FLV_SignatureInfo = {
+        new() { Position = 0, Signature = new byte [] { 0x46, 0x4C, 0x56 } },
+    };
+
+    public override string Extension => "flv";
+
+    protected override SignatureInformation[] SignatureInformations => FLV_SignatureInfo;
+
+    public override string MimeType => new MimeMapper().GetMimeFromExtension("." + Extension);
+
+    public override List<FormatCategory> FormatCategories => GetType().GetCustomAttributes<FormatCategoryAttribute>().Select(a => a.Category).ToList();
+
+    public override string ToString() => "Flash Video Detector";
+}

+ 25 - 0
Masuit.Tools.Abstractions/Files/FileDetector/Detectors/FlacDetector.cs

@@ -0,0 +1,25 @@
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Reflection;
+using Masuit.Tools.AspNetCore.Mime;
+
+namespace Masuit.Tools.Files.FileDetector.Detectors;
+
+[FormatCategory(FormatCategory.Audio)]
+internal class FlacDetector : AbstractSignatureDetector
+{
+    private static readonly SignatureInformation[] FlacSignatureInfo = {
+        new() { Position = 0, Signature = new byte [] { 0x66, 0x4C, 0x61, 0x43 } },
+    };
+
+    public override string Extension => "flac";
+
+    protected override SignatureInformation[] SignatureInformations => FlacSignatureInfo;
+
+    public override string MimeType => new MimeMapper().GetMimeFromExtension("." + Extension);
+
+    public override List<FormatCategory> FormatCategories => GetType().GetCustomAttributes<FormatCategoryAttribute>().Select(a => a.Category).ToList();
+
+    public override string ToString() => "FLAC Detector";
+}

+ 27 - 0
Masuit.Tools.Abstractions/Files/FileDetector/Detectors/GifDetector.cs

@@ -0,0 +1,27 @@
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Reflection;
+using Masuit.Tools.AspNetCore.Mime;
+
+namespace Masuit.Tools.Files.FileDetector.Detectors;
+
+[FormatCategory(FormatCategory.Video)]
+[FormatCategory(FormatCategory.Image)]
+internal class GifDetector : AbstractSignatureDetector
+{
+    private static readonly SignatureInformation[] GifSignatureInfo = {
+        new() { Position = 0, Signature = new byte [] { 0x47, 0x49, 0x46, 0x38, 0x37, 0x61 } },
+        new() { Position = 0, Signature = new byte [] { 0x47, 0x49, 0x46, 0x38, 0x39, 0x61 } },
+    };
+
+    public override string Extension => "gif";
+
+    protected override SignatureInformation[] SignatureInformations => GifSignatureInfo;
+
+    public override string MimeType => new MimeMapper().GetMimeFromExtension("." + Extension);
+
+    public override List<FormatCategory> FormatCategories => GetType().GetCustomAttributes<FormatCategoryAttribute>().Select(a => a.Category).ToList();
+
+    public override string ToString() => "Graphics Interchange Format Detector";
+}

+ 25 - 0
Masuit.Tools.Abstractions/Files/FileDetector/Detectors/GzDetector.cs

@@ -0,0 +1,25 @@
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Reflection;
+using Masuit.Tools.AspNetCore.Mime;
+
+namespace Masuit.Tools.Files.FileDetector.Detectors;
+
+[FormatCategory(FormatCategory.Compression)]
+internal class GzDetector : AbstractSignatureDetector
+{
+    private static readonly SignatureInformation[] GzSignatureInfo = {
+        new() { Position = 0, Signature = new byte [] { 0x1F, 0x8B } },
+    };
+
+    public override string Extension => "gz";
+
+    protected override SignatureInformation[] SignatureInformations => GzSignatureInfo;
+
+    public override string MimeType => new MimeMapper().GetMimeFromExtension("." + Extension);
+
+    public override List<FormatCategory> FormatCategories => GetType().GetCustomAttributes<FormatCategoryAttribute>().Select(a => a.Category).ToList();
+
+    public override string ToString() => "GZ Detector";
+}

+ 22 - 0
Masuit.Tools.Abstractions/Files/FileDetector/Detectors/HWPDetector.cs

@@ -0,0 +1,22 @@
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Reflection;
+using System.Text.RegularExpressions;
+using Masuit.Tools.AspNetCore.Mime;
+
+namespace Masuit.Tools.Files.FileDetector.Detectors;
+
+[FormatCategory(FormatCategory.Document)]
+internal class HWPDetector : AbstractRegexSignatureDetector
+{
+    public override string Extension => "hwp";
+
+    protected override Regex Signature => new("^HWP Document File V[0-9]+.[0-9][0-9]");
+
+    public override string MimeType => new MimeMapper().GetMimeFromExtension("." + Extension);
+
+    public override List<FormatCategory> FormatCategories => GetType().GetCustomAttributes<FormatCategoryAttribute>().Select(a => a.Category).ToList();
+
+    public override string ToString() => "Hancom HWP Document Detector";
+}

+ 25 - 0
Masuit.Tools.Abstractions/Files/FileDetector/Detectors/ISODetector.cs

@@ -0,0 +1,25 @@
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Reflection;
+using Masuit.Tools.AspNetCore.Mime;
+
+namespace Masuit.Tools.Files.FileDetector.Detectors;
+
+[FormatCategory(FormatCategory.Archive)]
+internal class ISODetector : AbstractSignatureDetector
+{
+    private static readonly SignatureInformation[] IsoSignatureInfo = {
+        new() { Position = 0, Signature = new byte [] { 0x43, 0x44, 0x30, 0x30, 0x31 } },
+    };
+
+    public override string Extension => "iso";
+
+    protected override SignatureInformation[] SignatureInformations => IsoSignatureInfo;
+
+    public override string MimeType => new MimeMapper().GetMimeFromExtension("." + Extension);
+
+    public override List<FormatCategory> FormatCategories => GetType().GetCustomAttributes<FormatCategoryAttribute>().Select(a => a.Category).ToList();
+
+    public override string ToString() => "ISO-9660 Disc Image Detector";
+}

+ 25 - 0
Masuit.Tools.Abstractions/Files/FileDetector/Detectors/IconDetector.cs

@@ -0,0 +1,25 @@
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Reflection;
+using Masuit.Tools.AspNetCore.Mime;
+
+namespace Masuit.Tools.Files.FileDetector.Detectors;
+
+[FormatCategory(FormatCategory.Image)]
+internal class IconDetector : AbstractSignatureDetector
+{
+    private static readonly SignatureInformation[] IcoSignatureInfo = {
+        new() { Position = 0, Signature = new byte [] { 0x00, 0x00, 0x01, 0x00 } },
+    };
+
+    public override string Extension => "ico";
+
+    protected override SignatureInformation[] SignatureInformations => IcoSignatureInfo;
+
+    public override string MimeType => new MimeMapper().GetMimeFromExtension("." + Extension);
+
+    public override List<FormatCategory> FormatCategories => GetType().GetCustomAttributes<FormatCategoryAttribute>().Select(a => a.Category).ToList();
+
+    public override string ToString() => "Icon Detector";
+}

+ 24 - 0
Masuit.Tools.Abstractions/Files/FileDetector/Detectors/InitializationDetector.cs

@@ -0,0 +1,24 @@
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Reflection;
+using System.Text.RegularExpressions;
+using Masuit.Tools.AspNetCore.Mime;
+
+namespace Masuit.Tools.Files.FileDetector.Detectors;
+
+[FormatCategory(FormatCategory.Document)]
+internal class InitializationDetector : AbstractRegexSignatureDetector
+{
+    public override string Precondition => "txt";
+
+    public override string Extension => "ini";
+
+    protected override Regex Signature => new("^(\\[(.*)\\]\r?\n((((.*)=(.*))*|(;(.*)))\r?\n)*)+");
+
+    public override string MimeType => new MimeMapper().GetMimeFromExtension("." + Extension);
+
+    public override List<FormatCategory> FormatCategories => GetType().GetCustomAttributes<FormatCategoryAttribute>().Select(a => a.Category).ToList();
+
+    public override string ToString() => "Initialization File Detector";
+}

+ 25 - 0
Masuit.Tools.Abstractions/Files/FileDetector/Detectors/JavaClassDetector.cs

@@ -0,0 +1,25 @@
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Reflection;
+using Masuit.Tools.AspNetCore.Mime;
+
+namespace Masuit.Tools.Files.FileDetector.Detectors;
+
+[FormatCategory(FormatCategory.Executable)]
+internal class JavaClassDetector : AbstractSignatureDetector
+{
+    private static readonly SignatureInformation[] ClassSignatureInfo = {
+        new() { Position = 0, Signature = new byte [] { 0xCA, 0xFE, 0xBA, 0xBE } },
+    };
+
+    public override string Extension => "class";
+
+    protected override SignatureInformation[] SignatureInformations => ClassSignatureInfo;
+
+    public override string MimeType => new MimeMapper().GetMimeFromExtension("." + Extension);
+
+    public override List<FormatCategory> FormatCategories => GetType().GetCustomAttributes<FormatCategoryAttribute>().Select(a => a.Category).ToList();
+
+    public override string ToString() => "Java Bytecode Detector";
+}

+ 25 - 0
Masuit.Tools.Abstractions/Files/FileDetector/Detectors/Jpeg2000Detector.cs

@@ -0,0 +1,25 @@
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Reflection;
+using Masuit.Tools.AspNetCore.Mime;
+
+namespace Masuit.Tools.Files.FileDetector.Detectors;
+
+[FormatCategory(FormatCategory.Image)]
+internal class Jpeg2000Detector : AbstractSignatureDetector
+{
+    private static readonly SignatureInformation[] JpegSignatureInfo = {
+        new() { Position = 0, Signature = new byte [] { 0x00, 0x00, 0x00, 0x0C, 0x6A, 0x50, 0x20, 0x20 } },
+    };
+
+    public override string Extension => "jp2";
+
+    protected override SignatureInformation[] SignatureInformations => JpegSignatureInfo;
+
+    public override string MimeType => new MimeMapper().GetMimeFromExtension("." + Extension);
+
+    public override List<FormatCategory> FormatCategories => GetType().GetCustomAttributes<FormatCategoryAttribute>().Select(a => a.Category).ToList();
+
+    public override string ToString() => "JPEG2000 Detector";
+}

+ 31 - 0
Masuit.Tools.Abstractions/Files/FileDetector/Detectors/JpegDetector.cs

@@ -0,0 +1,31 @@
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Reflection;
+using Masuit.Tools.AspNetCore.Mime;
+
+namespace Masuit.Tools.Files.FileDetector.Detectors;
+
+[FormatCategory(FormatCategory.Image)]
+internal class JpegDetector : AbstractSignatureDetector
+{
+    private static readonly SignatureInformation[] JpegSignatureInfo = {
+        new() { Position = 0, Signature = new byte [] { 0xFF, 0xD8, 0xFF, 0xFE, 0x00 } },
+        new () { Position = 0, Signature = new byte [] { 0xFF, 0xD8, 0xFF, 0xDB } },
+        new () { Position = 0, Signature = new byte [] { 0xFF, 0xD8, 0xFF, 0xE0 } },
+        new () { Position = 0, Signature = new byte [] { 0xFF, 0xD8, 0xFF, 0xE1 } },
+        new () { Position = 0, Signature = new byte [] { 0xFF, 0xD8, 0xFF, 0xE2 } },
+        new () { Position = 0, Signature = new byte [] { 0xFF, 0xD8, 0xFF, 0xE3 } },
+        new () { Position = 0, Signature = new byte [] { 0xFF, 0xD8, 0xFF, 0xE8 } },
+    };
+
+    public override string Extension => "jpg";
+
+    protected override SignatureInformation[] SignatureInformations => JpegSignatureInfo;
+
+    public override string MimeType => new MimeMapper().GetMimeFromExtension("." + Extension);
+
+    public override List<FormatCategory> FormatCategories => GetType().GetCustomAttributes<FormatCategoryAttribute>().Select(a => a.Category).ToList();
+
+    public override string ToString() => "JPEG Detector";
+}

+ 25 - 0
Masuit.Tools.Abstractions/Files/FileDetector/Detectors/JpegXRDetector.cs

@@ -0,0 +1,25 @@
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Reflection;
+using Masuit.Tools.AspNetCore.Mime;
+
+namespace Masuit.Tools.Files.FileDetector.Detectors;
+
+[FormatCategory(FormatCategory.Image)]
+internal class JpegXRDetector : AbstractSignatureDetector
+{
+    private static readonly SignatureInformation[] JpegSignatureInfo = {
+        new() { Position = 0, Signature = new byte [] { 0x49, 0x49, 0xBC, 0x01 } },
+    };
+
+    public override string Extension => "hdp";
+
+    protected override SignatureInformation[] SignatureInformations => JpegSignatureInfo;
+
+    public override string MimeType => new MimeMapper().GetMimeFromExtension("." + Extension);
+
+    public override List<FormatCategory> FormatCategories => GetType().GetCustomAttributes<FormatCategoryAttribute>().Select(a => a.Category).ToList();
+
+    public override string ToString() => "HD Photo(JPEG XR) Detector";
+}

+ 25 - 0
Masuit.Tools.Abstractions/Files/FileDetector/Detectors/KTXDetector.cs

@@ -0,0 +1,25 @@
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Reflection;
+using Masuit.Tools.AspNetCore.Mime;
+
+namespace Masuit.Tools.Files.FileDetector.Detectors;
+
+[FormatCategory(FormatCategory.Image)]
+internal class KTXDetector : AbstractSignatureDetector
+{
+    private static readonly SignatureInformation[] KtxSignatureInfo = {
+        new() { Position = 0, Signature = new byte [] { 0xAB, 0x4B, 0x54, 0x58, 0x20, 0x31, 0x31, 0xBB, 0x0D, 0x0A, 0x1A, 0x0A } },
+    };
+
+    public override string Extension => "ktx";
+
+    protected override SignatureInformation[] SignatureInformations => KtxSignatureInfo;
+
+    public override string MimeType => new MimeMapper().GetMimeFromExtension("." + Extension);
+
+    public override List<FormatCategory> FormatCategories => GetType().GetCustomAttributes<FormatCategoryAttribute>().Select(a => a.Category).ToList();
+
+    public override string ToString() => "Khronos Texture Detector";
+}

+ 25 - 0
Masuit.Tools.Abstractions/Files/FileDetector/Detectors/LzhDetector.cs

@@ -0,0 +1,25 @@
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Reflection;
+using Masuit.Tools.AspNetCore.Mime;
+
+namespace Masuit.Tools.Files.FileDetector.Detectors;
+
+[FormatCategory(FormatCategory.Compression)]
+internal class LzhDetector : AbstractSignatureDetector
+{
+    private static readonly SignatureInformation[] LzhSignatureInfo = {
+        new() { Position = 2, Signature = new byte [] { 0x2D, 0x6C, 0x68 } },
+    };
+
+    public override string Extension => "lzh";
+
+    protected override SignatureInformation[] SignatureInformations => LzhSignatureInfo;
+
+    public override string MimeType => new MimeMapper().GetMimeFromExtension("." + Extension);
+
+    public override List<FormatCategory> FormatCategories => GetType().GetCustomAttributes<FormatCategoryAttribute>().Select(a => a.Category).ToList();
+
+    public override string ToString() => "LZH Detector";
+}

+ 27 - 0
Masuit.Tools.Abstractions/Files/FileDetector/Detectors/M4ADetector.cs

@@ -0,0 +1,27 @@
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Reflection;
+using Masuit.Tools.AspNetCore.Mime;
+
+namespace Masuit.Tools.Files.FileDetector.Detectors;
+
+[FormatCategory(FormatCategory.Audio)]
+internal class M4ADetector : AbstractISOBaseMediaFileDetailDetector
+{
+    public override string Extension => "m4a";
+
+    protected override IEnumerable<string> NextSignature
+    {
+        get
+        {
+            yield return "M4A ";
+        }
+    }
+
+    public override string MimeType => new MimeMapper().GetMimeFromExtension("." + Extension);
+
+    public override List<FormatCategory> FormatCategories => GetType().GetCustomAttributes<FormatCategoryAttribute>().Select(a => a.Category).ToList();
+
+    public override string ToString() => "MP4 Contained Advanced Audio Coding(AAC) Decoder";
+}

+ 27 - 0
Masuit.Tools.Abstractions/Files/FileDetector/Detectors/M4VDetector.cs

@@ -0,0 +1,27 @@
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Reflection;
+using Masuit.Tools.AspNetCore.Mime;
+
+namespace Masuit.Tools.Files.FileDetector.Detectors;
+
+[FormatCategory(FormatCategory.Video)]
+internal class M4VDetector : AbstractISOBaseMediaFileDetailDetector
+{
+    public override string Extension => "m4v";
+
+    protected override IEnumerable<string> NextSignature
+    {
+        get
+        {
+            yield return "mp42";
+        }
+    }
+
+    public override string MimeType => new MimeMapper().GetMimeFromExtension("." + Extension);
+
+    public override List<FormatCategory> FormatCategories => GetType().GetCustomAttributes<FormatCategoryAttribute>().Select(a => a.Category).ToList();
+
+    public override string ToString() => "MP4 Contained H.264(AVC) Decoder";
+}

+ 26 - 0
Masuit.Tools.Abstractions/Files/FileDetector/Detectors/MP3Detector.cs

@@ -0,0 +1,26 @@
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Reflection;
+using Masuit.Tools.AspNetCore.Mime;
+
+namespace Masuit.Tools.Files.FileDetector.Detectors;
+
+[FormatCategory(FormatCategory.Audio)]
+internal class MP3Detector : AbstractSignatureDetector
+{
+    private static readonly SignatureInformation[] Mp3SignatureInfo = {
+        new () { Position = 0, Signature = new byte [] { 0xFF, 0xFB } },
+        new () { Position = 0, Signature = new byte [] { 0x49, 0x44, 0x33 } },
+    };
+
+    public override string Extension => "mp3";
+
+    public override string MimeType => new MimeMapper().GetMimeFromExtension("." + Extension);
+
+    public override List<FormatCategory> FormatCategories => GetType().GetCustomAttributes<FormatCategoryAttribute>().Select(a => a.Category).ToList();
+
+    protected override SignatureInformation[] SignatureInformations => Mp3SignatureInfo;
+
+    public override string ToString() => "MP3 Detector";
+}

+ 29 - 0
Masuit.Tools.Abstractions/Files/FileDetector/Detectors/MP4Detector.cs

@@ -0,0 +1,29 @@
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Reflection;
+using Masuit.Tools.AspNetCore.Mime;
+
+namespace Masuit.Tools.Files.FileDetector.Detectors;
+
+[FormatCategory(FormatCategory.Video)]
+[FormatCategory(FormatCategory.Audio)]
+[FormatCategory(FormatCategory.Image)]
+internal class MP4Detector : AbstractISOBaseMediaFileDetailDetector
+{
+    public override string Extension => "mp4";
+
+    protected override IEnumerable<string> NextSignature
+    {
+        get
+        {
+            yield return "isom";
+        }
+    }
+
+    public override string MimeType => new MimeMapper().GetMimeFromExtension("." + Extension);
+
+    public override List<FormatCategory> FormatCategories => GetType().GetCustomAttributes<FormatCategoryAttribute>().Select(a => a.Category).ToList();
+
+    public override string ToString() => "MP4 Detector";
+}

+ 32 - 0
Masuit.Tools.Abstractions/Files/FileDetector/Detectors/MicrosoftExcelXLSDetector.cs

@@ -0,0 +1,32 @@
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Reflection;
+using Masuit.Tools.AspNetCore.Mime;
+
+namespace Masuit.Tools.Files.FileDetector.Detectors;
+
+[FormatCategory(FormatCategory.Document)]
+internal class MicrosoftExcelXLSDetector : AbstractCompoundFileDetailDetector
+{
+    public override IEnumerable<string> Chunks
+    {
+        get
+        {
+            yield return "Workbook";
+        }
+    }
+
+    public override string Extension => "xls";
+
+    public override string MimeType => new MimeMapper().GetMimeFromExtension("." + Extension);
+
+    public override List<FormatCategory> FormatCategories => GetType().GetCustomAttributes<FormatCategoryAttribute>().Select(a => a.Category).ToList();
+
+    protected override bool IsValidChunk(string chunkName, byte[] chunkData)
+    {
+        return chunkName == "Workbook";
+    }
+
+    public override string ToString() => "Microsoft Office Excel Document(XLS) Detector";
+}

+ 33 - 0
Masuit.Tools.Abstractions/Files/FileDetector/Detectors/MicrosoftInstallerDetector.cs

@@ -0,0 +1,33 @@
+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.Detectors;
+
+[FormatCategory(FormatCategory.Archive)]
+internal class MicrosoftInstallerDetector : AbstractCompoundFileDetailDetector
+{
+    public override IEnumerable<string> Chunks
+    {
+        get
+        {
+            yield return "SummaryInformation";
+        }
+    }
+
+    public override string Extension => "msi";
+
+    public override string MimeType => new MimeMapper().GetMimeFromExtension("." + Extension);
+
+    public override List<FormatCategory> FormatCategories => GetType().GetCustomAttributes<FormatCategoryAttribute>().Select(a => a.Category).ToList();
+
+    protected override bool IsValidChunk(string chunkName, byte[] chunkData)
+    {
+        return chunkName == "SummaryInformation" && Encoding.GetEncoding("ascii").GetString(chunkData, 0, chunkData.Length).IndexOf("Installation Database") != -1;
+    }
+
+    public override string ToString() => "Microsoft Installer Setup File Detector";
+}

+ 32 - 0
Masuit.Tools.Abstractions/Files/FileDetector/Detectors/MicrosoftPowerPointPPTDetector.cs

@@ -0,0 +1,32 @@
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Reflection;
+using Masuit.Tools.AspNetCore.Mime;
+
+namespace Masuit.Tools.Files.FileDetector.Detectors;
+
+[FormatCategory(FormatCategory.Document)]
+internal class MicrosoftPowerPointPPTDetector : AbstractCompoundFileDetailDetector
+{
+    public override IEnumerable<string> Chunks
+    {
+        get
+        {
+            yield return "PowerPoint Document";
+        }
+    }
+
+    public override string Extension => "ppt";
+
+    public override string MimeType => new MimeMapper().GetMimeFromExtension("." + Extension);
+
+    public override List<FormatCategory> FormatCategories => GetType().GetCustomAttributes<FormatCategoryAttribute>().Select(a => a.Category).ToList();
+
+    protected override bool IsValidChunk(string chunkName, byte[] chunkData)
+    {
+        return chunkName == "PowerPoint Document";
+    }
+
+    public override string ToString() => "Microsoft Office PowerPoint Document(PPT) Detector";
+}

+ 32 - 0
Masuit.Tools.Abstractions/Files/FileDetector/Detectors/MicrosoftWordDocDetector.cs

@@ -0,0 +1,32 @@
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Reflection;
+using Masuit.Tools.AspNetCore.Mime;
+
+namespace Masuit.Tools.Files.FileDetector.Detectors;
+
+[FormatCategory(FormatCategory.Document)]
+internal class MicrosoftWordDocDetector : AbstractCompoundFileDetailDetector
+{
+    public override IEnumerable<string> Chunks
+    {
+        get
+        {
+            yield return "WordDocument";
+        }
+    }
+
+    public override string Extension => "doc";
+
+    public override string MimeType => new MimeMapper().GetMimeFromExtension("." + Extension);
+
+    public override List<FormatCategory> FormatCategories => GetType().GetCustomAttributes<FormatCategoryAttribute>().Select(a => a.Category).ToList();
+
+    protected override bool IsValidChunk(string chunkName, byte[] chunkData)
+    {
+        return chunkName == "WordDocument";
+    }
+
+    public override string ToString() => "Microsoft Office Word Document(DOC) Detector";
+}

+ 25 - 0
Masuit.Tools.Abstractions/Files/FileDetector/Detectors/MidiDetector.cs

@@ -0,0 +1,25 @@
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Reflection;
+using Masuit.Tools.AspNetCore.Mime;
+
+namespace Masuit.Tools.Files.FileDetector.Detectors;
+
+[FormatCategory(FormatCategory.Audio)]
+internal class MidiDetector : AbstractSignatureDetector
+{
+    private static readonly SignatureInformation[] MidiSignatureInfo = {
+        new() { Position = 0, Signature = new byte [] { 0x4D, 0x54, 0x68, 0x64 } },
+    };
+
+    public override string Extension => "mid";
+
+    public override string MimeType => new MimeMapper().GetMimeFromExtension("." + Extension);
+
+    public override List<FormatCategory> FormatCategories => GetType().GetCustomAttributes<FormatCategoryAttribute>().Select(a => a.Category).ToList();
+
+    protected override SignatureInformation[] SignatureInformations => MidiSignatureInfo;
+
+    public override string ToString() => "MIDI Detector";
+}

+ 25 - 0
Masuit.Tools.Abstractions/Files/FileDetector/Detectors/MikeOBrienPackDetector.cs

@@ -0,0 +1,25 @@
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Reflection;
+using Masuit.Tools.AspNetCore.Mime;
+
+namespace Masuit.Tools.Files.FileDetector.Detectors;
+
+[FormatCategory(FormatCategory.Archive)]
+internal class MikeOBrienPackDetector : AbstractSignatureDetector
+{
+    private static readonly SignatureInformation[] MpqSignatureInfo = {
+        new() { Position = 0, Signature = new byte [] { 0x4D, 0x50, 0x51, 0x1A } },
+    };
+
+    public override string Extension => "mpq";
+
+    protected override SignatureInformation[] SignatureInformations => MpqSignatureInfo;
+
+    public override string MimeType => new MimeMapper().GetMimeFromExtension("." + Extension);
+
+    public override List<FormatCategory> FormatCategories => GetType().GetCustomAttributes<FormatCategoryAttribute>().Select(a => a.Category).ToList();
+
+    public override string ToString() => "Mo'PaQ Detector";
+}

+ 26 - 0
Masuit.Tools.Abstractions/Files/FileDetector/Detectors/OggDetector.cs

@@ -0,0 +1,26 @@
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Reflection;
+using Masuit.Tools.AspNetCore.Mime;
+
+namespace Masuit.Tools.Files.FileDetector.Detectors;
+
+[FormatCategory(FormatCategory.Audio)]
+[FormatCategory(FormatCategory.Video)]
+internal class OggDetector : AbstractSignatureDetector
+{
+    private static readonly SignatureInformation[] OggSignatureInfo = {
+        new () { Position = 0, Signature = new byte [] { 0x4F, 0x67, 0x67, 0x53 } },
+    };
+
+    public override string Extension => "ogg";
+
+    public override string MimeType => new MimeMapper().GetMimeFromExtension("." + Extension);
+
+    public override List<FormatCategory> FormatCategories => GetType().GetCustomAttributes<FormatCategoryAttribute>().Select(a => a.Category).ToList();
+
+    protected override SignatureInformation[] SignatureInformations => OggSignatureInfo;
+
+    public override string ToString() => "OGG Detector";
+}

+ 51 - 0
Masuit.Tools.Abstractions/Files/FileDetector/Detectors/OpenDocumentFormulaDetector.cs

@@ -0,0 +1,51 @@
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.IO.Compression;
+using System.Reflection;
+using System.Text;
+using Masuit.Tools.AspNetCore.Mime;
+
+namespace Masuit.Tools.Files.FileDetector.Detectors;
+
+[FormatCategory(FormatCategory.Document)]
+internal class OpenDocumentFormulaDetector : AbstractZipDetailDetector
+{
+    public override IEnumerable<string> Files
+    {
+        get
+        {
+            yield return "mimetype";
+        }
+    }
+
+    public override string Precondition => "zip";
+
+    public override string Extension => "odf";
+
+    public override string MimeType => new MimeMapper().GetMimeFromExtension("." + Extension);
+
+    public override List<FormatCategory> FormatCategories => GetType().GetCustomAttributes<FormatCategoryAttribute>().Select(a => a.Category).ToList();
+
+    protected override bool IsValid(string filename, ZipArchiveEntry entry)
+    {
+        if (filename == "mimetype")
+        {
+            using var mimetypeStream = entry.Open();
+            byte[] buffer = new byte["application/vnd.oasis.opendocument.formula".Length];
+            if (mimetypeStream.Read(buffer, 0, buffer.Length) != buffer.Length)
+            {
+                return false;
+            }
+
+            if (Encoding.GetEncoding("ascii").GetString(buffer, 0, buffer.Length) != "application/vnd.oasis.opendocument.formula")
+            {
+                return false;
+            }
+        }
+
+        return base.IsValid(filename, entry);
+    }
+
+    public override string ToString() => "OpenDocument Formula File Detector";
+}

+ 51 - 0
Masuit.Tools.Abstractions/Files/FileDetector/Detectors/OpenDocumentGraphicsDetector.cs

@@ -0,0 +1,51 @@
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.IO.Compression;
+using System.Reflection;
+using System.Text;
+using Masuit.Tools.AspNetCore.Mime;
+
+namespace Masuit.Tools.Files.FileDetector.Detectors;
+
+[FormatCategory(FormatCategory.Document)]
+internal class OpenDocumentGraphicsDetector : AbstractZipDetailDetector
+{
+    public override IEnumerable<string> Files
+    {
+        get
+        {
+            yield return "mimetype";
+        }
+    }
+
+    public override string Precondition => "zip";
+
+    public override string Extension => "odg";
+
+    public override string MimeType => new MimeMapper().GetMimeFromExtension("." + Extension);
+
+    public override List<FormatCategory> FormatCategories => GetType().GetCustomAttributes<FormatCategoryAttribute>().Select(a => a.Category).ToList();
+
+    protected override bool IsValid(string filename, ZipArchiveEntry entry)
+    {
+        if (filename == "mimetype")
+        {
+            using Stream mimetypeStream = entry.Open();
+            byte[] buffer = new byte["application/vnd.oasis.opendocument.graphics".Length];
+            if (mimetypeStream.Read(buffer, 0, buffer.Length) != buffer.Length)
+            {
+                return false;
+            }
+
+            if (Encoding.GetEncoding("ascii").GetString(buffer, 0, buffer.Length) != "application/vnd.oasis.opendocument.graphics")
+            {
+                return false;
+            }
+        }
+
+        return base.IsValid(filename, entry);
+    }
+
+    public override string ToString() => "OpenDocument Graphics File Detector";
+}

+ 51 - 0
Masuit.Tools.Abstractions/Files/FileDetector/Detectors/OpenDocumentPresentationDetector.cs

@@ -0,0 +1,51 @@
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.IO.Compression;
+using System.Reflection;
+using System.Text;
+using Masuit.Tools.AspNetCore.Mime;
+
+namespace Masuit.Tools.Files.FileDetector.Detectors;
+
+[FormatCategory(FormatCategory.Document)]
+internal class OpenDocumentPresentationDetector : AbstractZipDetailDetector
+{
+    public override IEnumerable<string> Files
+    {
+        get
+        {
+            yield return "mimetype";
+        }
+    }
+
+    public override string Precondition => "zip";
+
+    public override string Extension => "odp";
+
+    public override string MimeType => new MimeMapper().GetMimeFromExtension("." + Extension);
+
+    public override List<FormatCategory> FormatCategories => GetType().GetCustomAttributes<FormatCategoryAttribute>().Select(a => a.Category).ToList();
+
+    protected override bool IsValid(string filename, ZipArchiveEntry entry)
+    {
+        if (filename == "mimetype")
+        {
+            using Stream mimetypeStream = entry.Open();
+            byte[] buffer = new byte["application/vnd.oasis.opendocument.presentation".Length];
+            if (mimetypeStream.Read(buffer, 0, buffer.Length) != buffer.Length)
+            {
+                return false;
+            }
+
+            if (Encoding.GetEncoding("ascii").GetString(buffer, 0, buffer.Length) != "application/vnd.oasis.opendocument.presentation")
+            {
+                return false;
+            }
+        }
+
+        return base.IsValid(filename, entry);
+    }
+
+    public override string ToString() => "OpenDocument Presentation File Detector";
+}

+ 51 - 0
Masuit.Tools.Abstractions/Files/FileDetector/Detectors/OpenDocumentSpreadSheetDetector.cs

@@ -0,0 +1,51 @@
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.IO.Compression;
+using System.Reflection;
+using System.Text;
+using Masuit.Tools.AspNetCore.Mime;
+
+namespace Masuit.Tools.Files.FileDetector.Detectors;
+
+[FormatCategory(FormatCategory.Document)]
+internal class OpenDocumentSpreadSheetDetector : AbstractZipDetailDetector
+{
+    public override IEnumerable<string> Files
+    {
+        get
+        {
+            yield return "mimetype";
+        }
+    }
+
+    public override string Precondition => "zip";
+
+    public override string Extension => "ods";
+
+    public override string MimeType => new MimeMapper().GetMimeFromExtension("." + Extension);
+
+    public override List<FormatCategory> FormatCategories => GetType().GetCustomAttributes<FormatCategoryAttribute>().Select(a => a.Category).ToList();
+
+    protected override bool IsValid(string filename, ZipArchiveEntry entry)
+    {
+        if (filename == "mimetype")
+        {
+            using Stream mimetypeStream = entry.Open();
+            byte[] buffer = new byte["application/vnd.oasis.opendocument.spreadsheet".Length];
+            if (mimetypeStream.Read(buffer, 0, buffer.Length) != buffer.Length)
+            {
+                return false;
+            }
+
+            if (Encoding.GetEncoding("ascii").GetString(buffer, 0, buffer.Length) != "application/vnd.oasis.opendocument.spreadsheet")
+            {
+                return false;
+            }
+        }
+
+        return base.IsValid(filename, entry);
+    }
+
+    public override string ToString() => "OpenDocument ShreadSheet File Detector";
+}

+ 51 - 0
Masuit.Tools.Abstractions/Files/FileDetector/Detectors/OpenDocumentTextDetector.cs

@@ -0,0 +1,51 @@
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.IO.Compression;
+using System.Reflection;
+using System.Text;
+using Masuit.Tools.AspNetCore.Mime;
+
+namespace Masuit.Tools.Files.FileDetector.Detectors;
+
+[FormatCategory(FormatCategory.Document)]
+internal class OpenDocumentTextDetector : AbstractZipDetailDetector
+{
+    public override IEnumerable<string> Files
+    {
+        get
+        {
+            yield return "mimetype";
+        }
+    }
+
+    public override string Precondition => "zip";
+
+    public override string Extension => "odt";
+
+    public override string MimeType => new MimeMapper().GetMimeFromExtension("." + Extension);
+
+    public override List<FormatCategory> FormatCategories => GetType().GetCustomAttributes<FormatCategoryAttribute>().Select(a => a.Category).ToList();
+
+    protected override bool IsValid(string filename, ZipArchiveEntry entry)
+    {
+        if (filename == "mimetype")
+        {
+            using Stream mimetypeStream = entry.Open();
+            byte[] buffer = new byte["application/vnd.oasis.opendocument.text".Length];
+            if (mimetypeStream.Read(buffer, 0, buffer.Length) != buffer.Length)
+            {
+                return false;
+            }
+
+            if (Encoding.GetEncoding("ascii").GetString(buffer, 0, buffer.Length) != "application/vnd.oasis.opendocument.text")
+            {
+                return false;
+            }
+        }
+
+        return base.IsValid(filename, entry);
+    }
+
+    public override string ToString() => "OpenDocument Text File Detector";
+}

+ 25 - 0
Masuit.Tools.Abstractions/Files/FileDetector/Detectors/PAKArchiveDetector.cs

@@ -0,0 +1,25 @@
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Reflection;
+using Masuit.Tools.AspNetCore.Mime;
+
+namespace Masuit.Tools.Files.FileDetector.Detectors;
+
+[FormatCategory(FormatCategory.Archive)]
+internal class PAKArchiveDetector : AbstractSignatureDetector
+{
+    private static readonly SignatureInformation[] PakSignatureInfo = {
+        new() { Position = 0, Signature = new byte [] { 0x1A, 0x0B } },
+    };
+
+    public override string Extension => "pak";
+
+    protected override SignatureInformation[] SignatureInformations => PakSignatureInfo;
+
+    public override string MimeType => new MimeMapper().GetMimeFromExtension("." + Extension);
+
+    public override List<FormatCategory> FormatCategories => GetType().GetCustomAttributes<FormatCategoryAttribute>().Select(a => a.Category).ToList();
+
+    public override string ToString() => "PAK Archive Detector";
+}

+ 22 - 0
Masuit.Tools.Abstractions/Files/FileDetector/Detectors/PDBDetector.cs

@@ -0,0 +1,22 @@
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Reflection;
+using System.Text.RegularExpressions;
+using Masuit.Tools.AspNetCore.Mime;
+
+namespace Masuit.Tools.Files.FileDetector.Detectors;
+
+[FormatCategory(FormatCategory.Document)]
+internal class PDBDetector : AbstractRegexSignatureDetector
+{
+    public override string Extension => "pdb";
+
+    protected override Regex Signature => new("^Microsoft C\\/C\\+\\+ MSF [0-9].[0-9][0-9]\r\n");
+
+    public override string MimeType => new MimeMapper().GetMimeFromExtension("." + Extension);
+
+    public override List<FormatCategory> FormatCategories => GetType().GetCustomAttributes<FormatCategoryAttribute>().Select(a => a.Category).ToList();
+
+    public override string ToString() => "Microsoft Program Database Detector";
+}

+ 25 - 0
Masuit.Tools.Abstractions/Files/FileDetector/Detectors/PFXDetector.cs

@@ -0,0 +1,25 @@
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Reflection;
+using Masuit.Tools.AspNetCore.Mime;
+
+namespace Masuit.Tools.Files.FileDetector.Detectors;
+
+[FormatCategory(FormatCategory.Document)]
+internal class PFXDetector : AbstractSignatureDetector
+{
+    private static readonly SignatureInformation[] PfxSignatureInfo = {
+        new() { Position = 0, Signature = new byte [] { 0x30, 0x82, 0x06 } },
+    };
+
+    public override string Extension => "pfx";
+
+    protected override SignatureInformation[] SignatureInformations => PfxSignatureInfo;
+
+    public override string MimeType => new MimeMapper().GetMimeFromExtension("." + Extension);
+
+    public override List<FormatCategory> FormatCategories => GetType().GetCustomAttributes<FormatCategoryAttribute>().Select(a => a.Category).ToList();
+
+    public override string ToString() => "Microsoft Personal inFormation eXchange Certificate Detector";
+}

+ 26 - 0
Masuit.Tools.Abstractions/Files/FileDetector/Detectors/PKMDetector.cs

@@ -0,0 +1,26 @@
+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.Detectors;
+
+[FormatCategory(FormatCategory.Image)]
+internal class PKMDetector : AbstractSignatureDetector
+{
+    private static readonly SignatureInformation[] PkmSignatureInfo = {
+        new() { Position = 0, Signature = Encoding.ASCII.GetBytes ( "PKM 10" ) },
+    };
+
+    public override string Extension => "pkm";
+
+    public override string MimeType => new MimeMapper().GetMimeFromExtension("." + Extension);
+
+    public override List<FormatCategory> FormatCategories => GetType().GetCustomAttributes<FormatCategoryAttribute>().Select(a => a.Category).ToList();
+
+    protected override SignatureInformation[] SignatureInformations => PkmSignatureInfo;
+
+    public override string ToString() => "PKM Detector";
+}

+ 44 - 0
Masuit.Tools.Abstractions/Files/FileDetector/Detectors/PPSXDetector.cs

@@ -0,0 +1,44 @@
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.IO.Compression;
+using System.Reflection;
+using Masuit.Tools.AspNetCore.Mime;
+
+namespace Masuit.Tools.Files.FileDetector.Detectors;
+
+[FormatCategory(FormatCategory.Document)]
+internal class PPSXDetector : AbstractZipDetailDetector
+{
+    public override IEnumerable<string> Files
+    {
+        get
+        {
+            yield return "[Content_Types].xml";
+            yield return "_rels/.rels";
+            yield return "ppt/_rels/presentation.xml.rels";
+        }
+    }
+
+    public override string Precondition => "zip";
+
+    public override string Extension => "ppsx";
+
+    public override string MimeType => new MimeMapper().GetMimeFromExtension("." + Extension);
+
+    public override List<FormatCategory> FormatCategories => GetType().GetCustomAttributes<FormatCategoryAttribute>().Select(a => a.Category).ToList();
+
+    protected override bool IsValid(string filename, ZipArchiveEntry entry)
+    {
+        if (filename == "[Content_Types].xml")
+        {
+            using StreamReader reader = new StreamReader(entry.Open());
+            var text = reader.ReadToEnd();
+            return text.IndexOf("<Override PartName=\"/ppt/presentation.xml\" ContentType=\"application/vnd.openxmlformats-officedocument.presentationml.slideshow.main+xml\"/>") >= 0;
+        }
+
+        return base.IsValid(filename, entry);
+    }
+
+    public override string ToString() => "Microsoft PowerPoint Open XML Document for Slideshow(PPSX) Detector";
+}

+ 44 - 0
Masuit.Tools.Abstractions/Files/FileDetector/Detectors/PPTXDetector.cs

@@ -0,0 +1,44 @@
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.IO.Compression;
+using System.Reflection;
+using Masuit.Tools.AspNetCore.Mime;
+
+namespace Masuit.Tools.Files.FileDetector.Detectors;
+
+[FormatCategory(FormatCategory.Document)]
+internal class PPTXDetector : AbstractZipDetailDetector
+{
+    public override IEnumerable<string> Files
+    {
+        get
+        {
+            yield return "[Content_Types].xml";
+            yield return "_rels/.rels";
+            yield return "ppt/_rels/presentation.xml.rels";
+        }
+    }
+
+    public override string Precondition => "zip";
+
+    public override string Extension => "pptx";
+
+    public override string MimeType => new MimeMapper().GetMimeFromExtension("." + Extension);
+
+    public override List<FormatCategory> FormatCategories => GetType().GetCustomAttributes<FormatCategoryAttribute>().Select(a => a.Category).ToList();
+
+    protected override bool IsValid(string filename, ZipArchiveEntry entry)
+    {
+        if (filename == "[Content_Types].xml")
+        {
+            using StreamReader reader = new StreamReader(entry.Open());
+            var text = reader.ReadToEnd();
+            return text.IndexOf("<Override PartName=\"/ppt/presentation.xml\" ContentType=\"application/vnd.openxmlformats-officedocument.presentationml.presentation.main+xml\"/>") >= 0;
+        }
+
+        return base.IsValid(filename, entry);
+    }
+
+    public override string ToString() => "Microsoft PowerPoint Open XML Document(PPTX) Detector";
+}

+ 22 - 0
Masuit.Tools.Abstractions/Files/FileDetector/Detectors/PdfDetector.cs

@@ -0,0 +1,22 @@
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Reflection;
+using System.Text.RegularExpressions;
+using Masuit.Tools.AspNetCore.Mime;
+
+namespace Masuit.Tools.Files.FileDetector.Detectors;
+
+[FormatCategory(FormatCategory.Document)]
+internal class PdfDetector : AbstractRegexSignatureDetector
+{
+    public override string Extension => "pdf";
+
+    protected override Regex Signature => new("^%PDF-[0-9].[0-9]");
+
+    public override string MimeType => new MimeMapper().GetMimeFromExtension("." + Extension);
+
+    public override List<FormatCategory> FormatCategories => GetType().GetCustomAttributes<FormatCategoryAttribute>().Select(a => a.Category).ToList();
+
+    public override string ToString() => "Portable Document Format Detector";
+}

+ 25 - 0
Masuit.Tools.Abstractions/Files/FileDetector/Detectors/PngDetector.cs

@@ -0,0 +1,25 @@
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Reflection;
+using Masuit.Tools.AspNetCore.Mime;
+
+namespace Masuit.Tools.Files.FileDetector.Detectors;
+
+[FormatCategory(FormatCategory.Image)]
+internal class PngDetector : AbstractSignatureDetector
+{
+    private static readonly SignatureInformation[] PngSignatureInfo = {
+        new () { Position = 0, Signature = new byte [] { 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A } },
+    };
+
+    public override string Extension => "png";
+
+    protected override SignatureInformation[] SignatureInformations => PngSignatureInfo;
+
+    public override string MimeType => new MimeMapper().GetMimeFromExtension("." + Extension);
+
+    public override List<FormatCategory> FormatCategories => GetType().GetCustomAttributes<FormatCategoryAttribute>().Select(a => a.Category).ToList();
+
+    public override string ToString() => "Portable Network Graphics Detector";
+}

+ 31 - 0
Masuit.Tools.Abstractions/Files/FileDetector/Detectors/PrePOSIXtarDetector.cs

@@ -0,0 +1,31 @@
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Reflection;
+using System.Text;
+using System.Text.RegularExpressions;
+using Masuit.Tools.AspNetCore.Mime;
+
+namespace Masuit.Tools.Files.FileDetector.Detectors;
+
+[FormatCategory(FormatCategory.Archive)]
+internal class POSIXtarDetector : IDetector
+{
+    public string Precondition => null;
+
+    public string Extension => "tar";
+
+    public bool Detect(Stream stream)
+    {
+        stream.Position = 100;
+        byte[] mode = new byte[8];
+        stream.Read(mode, 0, 8);
+        return Regex.IsMatch(Encoding.GetEncoding("ascii").GetString(mode, 0, 8), "[0-7][0-7][0-7][0-7][0-7][0-7][0-7][\0]");
+    }
+
+    public string MimeType => new MimeMapper().GetMimeFromExtension("." + Extension);
+
+    public List<FormatCategory> FormatCategories => GetType().GetCustomAttributes<FormatCategoryAttribute>().Select(a => a.Category).ToList();
+
+    public override string ToString() => "pre-POSIX Tar(TAR) Detector";
+}

+ 25 - 0
Masuit.Tools.Abstractions/Files/FileDetector/Detectors/PsdDetector.cs

@@ -0,0 +1,25 @@
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Reflection;
+using Masuit.Tools.AspNetCore.Mime;
+
+namespace Masuit.Tools.Files.FileDetector.Detectors;
+
+[FormatCategory(FormatCategory.Image)]
+internal class PsdDetector : AbstractSignatureDetector
+{
+    private static readonly SignatureInformation[] PsdSignatureInfo = {
+        new() { Position = 0, Signature = new byte [] { 0x38, 0x42, 0x50, 0x53 } },
+    };
+
+    public override string Extension => "psd";
+
+    protected override SignatureInformation[] SignatureInformations => PsdSignatureInfo;
+
+    public override string MimeType => new MimeMapper().GetMimeFromExtension("." + Extension);
+
+    public override List<FormatCategory> FormatCategories => GetType().GetCustomAttributes<FormatCategoryAttribute>().Select(a => a.Category).ToList();
+
+    public override string ToString() => "Photoshop File(PSD) Detector";
+}

+ 25 - 0
Masuit.Tools.Abstractions/Files/FileDetector/Detectors/QuakeArchiveDetector.cs

@@ -0,0 +1,25 @@
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Reflection;
+using Masuit.Tools.AspNetCore.Mime;
+
+namespace Masuit.Tools.Files.FileDetector.Detectors;
+
+[FormatCategory(FormatCategory.Archive)]
+internal class QuakeArchiveDetector : AbstractSignatureDetector
+{
+    private static readonly SignatureInformation[] PakSignatureInfo = {
+        new() { Position = 0, Signature = new byte [] { 0x50, 0x41, 0x43, 0x4B } },
+    };
+
+    public override string Extension => "pak";
+
+    protected override SignatureInformation[] SignatureInformations => PakSignatureInfo;
+
+    public override string MimeType => new MimeMapper().GetMimeFromExtension("." + Extension);
+
+    public override List<FormatCategory> FormatCategories => GetType().GetCustomAttributes<FormatCategoryAttribute>().Select(a => a.Category).ToList();
+
+    public override string ToString() => "Quake™ Package Detector";
+}

+ 41 - 0
Masuit.Tools.Abstractions/Files/FileDetector/Detectors/QuickTimeDetector.cs

@@ -0,0 +1,41 @@
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Reflection;
+using Masuit.Tools.AspNetCore.Mime;
+
+namespace Masuit.Tools.Files.FileDetector.Detectors;
+
+[FormatCategory(FormatCategory.Video)]
+[FormatCategory(FormatCategory.Audio)]
+internal class QuickTimeDetector : AbstractISOBaseMediaFileDetailDetector
+{
+    public override string Extension => "mov";
+
+    protected override IEnumerable<string> NextSignature
+    {
+        get
+        {
+            yield return "qt";
+        }
+    }
+
+    public override string MimeType => new MimeMapper().GetMimeFromExtension("." + Extension);
+
+    public override List<FormatCategory> FormatCategories => GetType().GetCustomAttributes<FormatCategoryAttribute>().Select(a => a.Category).ToList();
+
+    public override string ToString() => "QuickTime(MOV) Detector";
+
+    public override bool Detect(Stream stream)
+    {
+        if (!base.Detect(stream))
+        {
+            stream.Position = 4;
+            byte[] buffer = new byte[4];
+            stream.Read(buffer, 0, 4);
+            return buffer[0] == 0x6D && buffer[1] == 0x6F && buffer[2] == 0x6F && buffer[3] == 0x76;
+        }
+
+        return true;
+    }
+}

+ 25 - 0
Masuit.Tools.Abstractions/Files/FileDetector/Detectors/REGDetector.cs

@@ -0,0 +1,25 @@
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Reflection;
+using System.Text.RegularExpressions;
+using Masuit.Tools.AspNetCore.Mime;
+
+namespace Masuit.Tools.Files.FileDetector.Detectors;
+
+[FormatCategory(FormatCategory.Document)]
+[FormatCategory(FormatCategory.System)]
+internal class REGDetector : AbstractRegexSignatureDetector
+{
+    public override string Precondition => "txt";
+
+    public override string Extension => "reg";
+
+    protected override Regex Signature => new("^Windows Registry Editor Version [0-9]+.[0-9][0-9]\r?\n");
+
+    public override string MimeType => new MimeMapper().GetMimeFromExtension("." + Extension);
+
+    public override List<FormatCategory> FormatCategories => GetType().GetCustomAttributes<FormatCategoryAttribute>().Select(a => a.Category).ToList();
+
+    public override string ToString() => "Windows Registry Detector";
+}

+ 27 - 0
Masuit.Tools.Abstractions/Files/FileDetector/Detectors/RarDetector.cs

@@ -0,0 +1,27 @@
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Reflection;
+using Masuit.Tools.AspNetCore.Mime;
+
+namespace Masuit.Tools.Files.FileDetector.Detectors;
+
+[FormatCategory(FormatCategory.Archive)]
+[FormatCategory(FormatCategory.Compression)]
+internal class RarDetector : AbstractSignatureDetector
+{
+    private static readonly SignatureInformation[] RarSignatureInfo = {
+        new() { Position = 0, Signature = new byte [] { 0x52, 0x61, 0x72, 0x21, 0x1A, 0x07, 0x00 } },
+        new() { Position = 0, Signature = new byte [] { 0x52, 0x61, 0x72, 0x21, 0x1A, 0x07, 0x01, 0x00 } },
+    };
+
+    public override string Extension => "rar";
+
+    protected override SignatureInformation[] SignatureInformations => RarSignatureInfo;
+
+    public override string MimeType => new MimeMapper().GetMimeFromExtension("." + Extension);
+
+    public override List<FormatCategory> FormatCategories => GetType().GetCustomAttributes<FormatCategoryAttribute>().Select(a => a.Category).ToList();
+
+    public override string ToString() => "RAR Detector";
+}

+ 25 - 0
Masuit.Tools.Abstractions/Files/FileDetector/Detectors/RedhatPackageManagerPackageDetector.cs

@@ -0,0 +1,25 @@
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Reflection;
+using Masuit.Tools.AspNetCore.Mime;
+
+namespace Masuit.Tools.Files.FileDetector.Detectors;
+
+[FormatCategory(FormatCategory.Archive)]
+internal class RedhatPackageManagerPackageDetector : AbstractSignatureDetector
+{
+    private static readonly SignatureInformation[] RpmSignatureInfo = {
+        new() { Position = 0, Signature = new byte [] { 0xED, 0xAB, 0xEE, 0xDB } },
+    };
+
+    public override string Extension => "rpm";
+
+    protected override SignatureInformation[] SignatureInformations => RpmSignatureInfo;
+
+    public override string MimeType => new MimeMapper().GetMimeFromExtension("." + Extension);
+
+    public override List<FormatCategory> FormatCategories => GetType().GetCustomAttributes<FormatCategoryAttribute>().Select(a => a.Category).ToList();
+
+    public override string ToString() => "RedHat Package Manager Package File Detector";
+}

+ 25 - 0
Masuit.Tools.Abstractions/Files/FileDetector/Detectors/RichTextFormatDetector.cs

@@ -0,0 +1,25 @@
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Reflection;
+using Masuit.Tools.AspNetCore.Mime;
+
+namespace Masuit.Tools.Files.FileDetector.Detectors;
+
+[FormatCategory(FormatCategory.Document)]
+internal class RichTextFormatDetector : AbstractSignatureDetector
+{
+    private static readonly SignatureInformation[] RtfSignatureInfo = {
+        new () { Position = 0, Signature = new byte [] { 0x7B, 0x5C, 0x72, 0x74, 0x66, 0x31 } },
+    };
+
+    public override string Extension => "rtf";
+
+    protected override SignatureInformation[] SignatureInformations => RtfSignatureInfo;
+
+    public override string MimeType => new MimeMapper().GetMimeFromExtension("." + Extension);
+
+    public override List<FormatCategory> FormatCategories => GetType().GetCustomAttributes<FormatCategoryAttribute>().Select(a => a.Category).ToList();
+
+    public override string ToString() => "Rich Text Format Detector";
+}

+ 25 - 0
Masuit.Tools.Abstractions/Files/FileDetector/Detectors/SQLiteDetector.cs

@@ -0,0 +1,25 @@
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Reflection;
+using Masuit.Tools.AspNetCore.Mime;
+
+namespace Masuit.Tools.Files.FileDetector.Detectors;
+
+[FormatCategory(FormatCategory.Document)]
+internal class SQLiteDetector : AbstractSignatureDetector
+{
+    private static readonly SignatureInformation[] SqliteSignatureInfo = {
+        new () { Position = 0, Signature = new byte [] { 0x53, 0x51, 0x4C, 0x69, 0x74, 0x65, 0x20, 0x66, 0x6F, 0x72, 0x6D, 0x61, 0x74, 0x20, 0x33, 0x00 } },
+    };
+
+    public override string Extension => "sqlite";
+
+    public override string MimeType => new MimeMapper().GetMimeFromExtension("." + Extension);
+
+    public override List<FormatCategory> FormatCategories => GetType().GetCustomAttributes<FormatCategoryAttribute>().Select(a => a.Category).ToList();
+
+    protected override SignatureInformation[] SignatureInformations => SqliteSignatureInfo;
+
+    public override string ToString() => "SQLite Detector";
+}

+ 24 - 0
Masuit.Tools.Abstractions/Files/FileDetector/Detectors/SRTDetector.cs

@@ -0,0 +1,24 @@
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Reflection;
+using System.Text.RegularExpressions;
+using Masuit.Tools.AspNetCore.Mime;
+
+namespace Masuit.Tools.Files.FileDetector.Detectors;
+
+[FormatCategory(FormatCategory.Document)]
+internal class SRTDetector : AbstractRegexSignatureDetector
+{
+    public override string Precondition => "txt";
+
+    public override string Extension => "srt";
+
+    protected override Regex Signature => new("\\d+\r?\n(\\d\\d:\\d\\d:\\d\\d,\\d\\d\\d) --> (\\d\\d:\\d\\d:\\d\\d,\\d\\d\\d)\r?\n(.*)\r?\n\r?\n");
+
+    public override string MimeType => new MimeMapper().GetMimeFromExtension("." + Extension);
+
+    public override List<FormatCategory> FormatCategories => GetType().GetCustomAttributes<FormatCategoryAttribute>().Select(a => a.Category).ToList();
+
+    public override string ToString() => "SubRip Subtitle Detector";
+}

+ 27 - 0
Masuit.Tools.Abstractions/Files/FileDetector/Detectors/ShockwaveFlashDetector.cs

@@ -0,0 +1,27 @@
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Reflection;
+using Masuit.Tools.AspNetCore.Mime;
+
+namespace Masuit.Tools.Files.FileDetector.Detectors;
+
+[FormatCategory(FormatCategory.Video)]
+[FormatCategory(FormatCategory.Executable)]
+internal class ShockwaveFlashDetector : AbstractSignatureDetector
+{
+    private static readonly SignatureInformation[] SwfSignatureInfo = {
+        new() { Position = 0, Signature = new byte [] { 0x43, 0x57, 0x53 } },
+        new() { Position = 0, Signature = new byte [] { 0x46, 0x57, 0x53 } },
+    };
+
+    public override string Extension => "swf";
+
+    public override string MimeType => new MimeMapper().GetMimeFromExtension("." + Extension);
+
+    public override List<FormatCategory> FormatCategories => GetType().GetCustomAttributes<FormatCategoryAttribute>().Select(a => a.Category).ToList();
+
+    protected override SignatureInformation[] SignatureInformations => SwfSignatureInfo;
+
+    public override string ToString() => "Shockwave Flash(SWF) Detector";
+}

+ 117 - 0
Masuit.Tools.Abstractions/Files/FileDetector/Detectors/TextDetector.cs

@@ -0,0 +1,117 @@
+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.Detectors;
+
+[FormatCategory(FormatCategory.Document)]
+internal class TextDetector : IDetector
+{
+    private static readonly byte[] SignatureBuffer = new byte[4];
+    private static readonly char[] TextBuffer = new char[4096];
+    private static readonly byte[] ReadBuffer = new byte[4096];
+    private static readonly byte[] EncodingBuffer = new byte[4096];
+    private static readonly Encoding[] Utf8Encodings = { Encoding.UTF8 };
+    private static readonly Encoding[] Utf16Encodings = { Encoding.Unicode };
+    private static readonly Encoding[] Utf16BeEncodings = { Encoding.BigEndianUnicode };
+    private static readonly Encoding[] Utf32Encodings = { Encoding.GetEncoding("utf-32") };
+
+    private static readonly Encoding[] OtherwiseEncodings = {
+        Encoding.GetEncoding ( "ascii" ),
+        Encoding.UTF8,
+        Encoding.GetEncoding ( "utf-32" ),
+        Encoding.Unicode,
+        Encoding.BigEndianUnicode
+    };
+
+    public string Extension => "txt";
+
+    public string Precondition => null;
+
+    public string MimeType => new MimeMapper().GetMimeFromExtension("." + Extension);
+
+    public List<FormatCategory> FormatCategories => GetType().GetCustomAttributes<FormatCategoryAttribute>().Select(a => a.Category).ToList();
+
+    public bool Detect(Stream stream)
+    {
+        _ = stream.Read(SignatureBuffer, 0, SignatureBuffer.Length);
+        stream.Seek(0, SeekOrigin.Begin);
+
+        Encoding[] encodings;
+
+        if (SignatureBuffer[0] == 0xef && SignatureBuffer[1] == 0xbb && SignatureBuffer[2] == 0xbf)
+        {
+            encodings = Utf8Encodings;
+            stream.Position = 3;
+        }
+        else if (SignatureBuffer[0] == 0xfe && SignatureBuffer[1] == 0xff)
+        {
+            encodings = Utf16Encodings;
+            stream.Position = 2;
+        }
+        else if (SignatureBuffer[0] == 0xff && SignatureBuffer[1] == 0xfe)
+        {
+            encodings = Utf16BeEncodings;
+            stream.Position = 2;
+        }
+        else if (SignatureBuffer[0] == 0 && SignatureBuffer[1] == 0 && SignatureBuffer[2] == 0xfe && SignatureBuffer[3] == 0xff)
+        {
+            encodings = Utf32Encodings;
+            stream.Position = 4;
+        }
+        else
+        {
+            encodings = OtherwiseEncodings;
+            stream.Position = 0;
+        }
+
+        int readed = stream.Read(ReadBuffer, 0, /*2048*/1024);
+
+        foreach (var encoding in encodings)
+        {
+            for (int count = readed; count >= (readed - 16); --count)
+            {
+                bool succeed = true;
+                int texted = encoding.GetChars(ReadBuffer, 0, count, TextBuffer, 0);
+                for (int i = 0; i < texted; ++i)
+                {
+                    char ch = TextBuffer[i];
+                    if ((char.IsControl(ch) && ch != '\r' && ch != '\n' && ch != '\t') || ch == '\0')
+                    {
+                        succeed = false;
+                        break;
+                    }
+                }
+
+                _ = encoding.GetBytes(TextBuffer, 0, texted, EncodingBuffer, 0);
+                if (succeed/* && readed == byted*/ )
+                {
+                    for (int i = 0; i < count; ++i)
+                    {
+                        if (ReadBuffer[i] != EncodingBuffer[i])
+                        {
+                            succeed = false;
+                            break;
+                        }
+                    }
+                }
+                else
+                {
+                    continue;
+                }
+
+                if (succeed)
+                {
+                    return true;
+                }
+            }
+        }
+
+        return false;
+    }
+
+    public override string ToString() => "Text File Detector";
+}

+ 42 - 0
Masuit.Tools.Abstractions/Files/FileDetector/Detectors/TgaDetector.cs

@@ -0,0 +1,42 @@
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Reflection;
+using Masuit.Tools.AspNetCore.Mime;
+
+namespace Masuit.Tools.Files.FileDetector.Detectors;
+
+[FormatCategory(FormatCategory.Image)]
+internal class TgaDetector : IDetector
+{
+    public string Extension => "tga";
+
+    public string Precondition => null;
+
+    public string MimeType => new MimeMapper().GetMimeFromExtension("." + Extension);
+
+    public List<FormatCategory> FormatCategories => GetType().GetCustomAttributes<FormatCategoryAttribute>().Select(a => a.Category).ToList();
+
+    public bool Detect(Stream stream)
+    {
+        stream.Position = 3;
+        int compressionType = stream.ReadByte();
+        if (compressionType is not (0 or 1 or 2 or 3 or 9 or 10 or 11 or 32 or 33))
+        {
+            return false;
+        }
+
+        stream.Position = 17;
+        int depth = stream.ReadByte();
+        if (!(depth == 8 || depth == 24 || depth == 15 || depth == 16 || depth == 32))
+        {
+            return false;
+        }
+
+        stream.Position = 1;
+        int mapType = stream.ReadByte();
+        return mapType == 0 || (mapType == 1 && depth == 8);
+    }
+
+    public override string ToString() => "Targa Detector (Experimental)";
+}

+ 25 - 0
Masuit.Tools.Abstractions/Files/FileDetector/Detectors/ThumbsDBDetector.cs

@@ -0,0 +1,25 @@
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Reflection;
+using Masuit.Tools.AspNetCore.Mime;
+
+namespace Masuit.Tools.Files.FileDetector.Detectors;
+
+[FormatCategory(FormatCategory.System)]
+internal class ThumbsDBDetector : AbstractSignatureDetector
+{
+    private static readonly SignatureInformation[] ThumbdbSignatureInfo = {
+        new () { Position = 0, Signature = new byte [] { 0xFD, 0xFF, 0xFF, 0xFF } },
+    };
+
+    public override string Extension => "db";
+
+    public override string MimeType => new MimeMapper().GetMimeFromExtension("." + Extension);
+
+    public override List<FormatCategory> FormatCategories => GetType().GetCustomAttributes<FormatCategoryAttribute>().Select(a => a.Category).ToList();
+
+    protected override SignatureInformation[] SignatureInformations => ThumbdbSignatureInfo;
+
+    public override string ToString() => "Thumbs.db Detector";
+}

+ 28 - 0
Masuit.Tools.Abstractions/Files/FileDetector/Detectors/TiffDetector.cs

@@ -0,0 +1,28 @@
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Reflection;
+using Masuit.Tools.AspNetCore.Mime;
+
+namespace Masuit.Tools.Files.FileDetector.Detectors;
+
+[FormatCategory(FormatCategory.Image)]
+internal class TiffDetector : AbstractSignatureDetector
+{
+    private static readonly SignatureInformation[] TiffSignatureInfo = {
+        new () { Position = 0, Signature = new byte [] { 0x49, 0x49, 0x49 } },
+        new () { Position = 0, Signature = new byte [] { 0x49, 0x49, 0x2A, 0x00 } },
+        new () { Position = 0, Signature = new byte [] { 0x4D, 0x4D, 0x00, 0x2A } },
+        new () { Position = 0, Signature = new byte [] { 0x4D, 0x4D, 0x00, 0x2B } },
+    };
+
+    public override string Extension => "tif";
+
+    public override string MimeType => new MimeMapper().GetMimeFromExtension("." + Extension);
+
+    public override List<FormatCategory> FormatCategories => GetType().GetCustomAttributes<FormatCategoryAttribute>().Select(a => a.Category).ToList();
+
+    protected override SignatureInformation[] SignatureInformations => TiffSignatureInfo;
+
+    public override string ToString() => "Tagged Image File Format Detector";
+}

+ 26 - 0
Masuit.Tools.Abstractions/Files/FileDetector/Detectors/UStarFormatTarDetector.cs

@@ -0,0 +1,26 @@
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Reflection;
+using Masuit.Tools.AspNetCore.Mime;
+
+namespace Masuit.Tools.Files.FileDetector.Detectors;
+
+[FormatCategory(FormatCategory.Archive)]
+internal class UStarFormatTarDetector : AbstractSignatureDetector
+{
+    private static readonly SignatureInformation[] TarSignatureInfo = {
+        new () { Position = 0x101, Signature = new byte [] { 0x75, 0x73, 0x74, 0x61, 0x72, 0x00, 0x30, 0x30 } },
+        new () { Position = 0x101, Signature = new byte [] { 0x75, 0x73, 0x74, 0x61, 0x72, 0x20, 0x20, 0x00 } },
+    };
+
+    public override string Extension => "tar";
+
+    public override string MimeType => new MimeMapper().GetMimeFromExtension("." + Extension);
+
+    public override List<FormatCategory> FormatCategories => GetType().GetCustomAttributes<FormatCategoryAttribute>().Select(a => a.Category).ToList();
+
+    protected override SignatureInformation[] SignatureInformations => TarSignatureInfo;
+
+    public override string ToString() => "UStar(TAR) Detector";
+}

+ 24 - 0
Masuit.Tools.Abstractions/Files/FileDetector/Detectors/VisualStudioSolutionDetector.cs

@@ -0,0 +1,24 @@
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Reflection;
+using System.Text.RegularExpressions;
+using Masuit.Tools.AspNetCore.Mime;
+
+namespace Masuit.Tools.Files.FileDetector.Detectors;
+
+[FormatCategory(FormatCategory.Document)]
+internal class VisualStudioSolutionDetector : AbstractRegexSignatureDetector
+{
+    public override string Precondition => "txt";
+
+    public override string Extension => "sln";
+
+    public override string MimeType => new MimeMapper().GetMimeFromExtension("." + Extension);
+
+    public override List<FormatCategory> FormatCategories => GetType().GetCustomAttributes<FormatCategoryAttribute>().Select(a => a.Category).ToList();
+
+    protected override Regex Signature => new("\r\nMicrosoft Visual Studio Solution File, Format Version [0-9]+.[0-9]+\r\n");
+
+    public override string ToString() => "Microsoft Visual Studio Solution Detector";
+}

+ 26 - 0
Masuit.Tools.Abstractions/Files/FileDetector/Detectors/WaveDetector.cs

@@ -0,0 +1,26 @@
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Reflection;
+using Masuit.Tools.AspNetCore.Mime;
+
+namespace Masuit.Tools.Files.FileDetector.Detectors;
+
+[FormatCategory(FormatCategory.Audio)]
+internal class WaveDetector : AbstractSignatureDetector
+{
+    private static readonly SignatureInformation[] WavSignatureInfo = {
+        new() { Position = 0, Signature = new byte [] { 0x52, 0x49, 0x46, 0x46 } },
+        new() { Position = 8, Signature = new byte [] { 0x57, 0x41, 0x56, 0x45 }, Presignature = new byte [] { 0x52, 0x49, 0x46, 0x46 } },
+    };
+
+    public override string Extension => "wav";
+
+    public override string MimeType => new MimeMapper().GetMimeFromExtension("." + Extension);
+
+    public override List<FormatCategory> FormatCategories => GetType().GetCustomAttributes<FormatCategoryAttribute>().Select(a => a.Category).ToList();
+
+    protected override SignatureInformation[] SignatureInformations => WavSignatureInfo;
+
+    public override string ToString() => "Wave(WAV) Detector";
+}

+ 39 - 0
Masuit.Tools.Abstractions/Files/FileDetector/Detectors/WebMDetector.cs

@@ -0,0 +1,39 @@
+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.Detectors;
+
+[FormatCategory(FormatCategory.Video)]
+[FormatCategory(FormatCategory.Audio)]
+internal class WebMDetector : AbstractSignatureDetector
+{
+    private static readonly SignatureInformation[] WebmSignatureInfo = {
+        new() { Position = 0, Signature = new byte [] { 0x1A, 0x45, 0xDF, 0xA3 } },
+    };
+
+    public override string Extension => "webm";
+
+    public override string MimeType => new MimeMapper().GetMimeFromExtension("." + Extension);
+
+    public override List<FormatCategory> FormatCategories => GetType().GetCustomAttributes<FormatCategoryAttribute>().Select(a => a.Category).ToList();
+
+    protected override SignatureInformation[] SignatureInformations => WebmSignatureInfo;
+
+    public override string ToString() => "WebM Video Detector";
+
+    public override bool Detect(Stream stream)
+    {
+        if (base.Detect(stream))
+        {
+            stream.Position = 0x1F;
+            byte[] buffer = new byte[4];
+            stream.Read(buffer, 0, 4);
+            return Encoding.GetEncoding("ascii").GetString(buffer, 0, 4) == "webm";
+        }
+        return false;
+    }
+}

+ 27 - 0
Masuit.Tools.Abstractions/Files/FileDetector/Detectors/WebPDetector.cs

@@ -0,0 +1,27 @@
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Reflection;
+using Masuit.Tools.AspNetCore.Mime;
+
+namespace Masuit.Tools.Files.FileDetector.Detectors;
+
+[FormatCategory(FormatCategory.Image)]
+[FormatCategory(FormatCategory.Video)]
+internal class WebPDetector : AbstractSignatureDetector
+{
+    private static readonly SignatureInformation[] WebpSignatureInfo = {
+        new() { Position = 0, Signature = new byte [] { 0x52, 0x49, 0x46, 0x46 } },
+        new() { Position = 8, Signature = new byte [] { 0x57, 0x45, 0x42, 0x50 }, Presignature = new byte [] { 0x52, 0x49, 0x46, 0x46 } },
+    };
+
+    public override string Extension => "webp";
+
+    public override string MimeType => new MimeMapper().GetMimeFromExtension("." + Extension);
+
+    public override List<FormatCategory> FormatCategories => GetType().GetCustomAttributes<FormatCategoryAttribute>().Select(a => a.Category).ToList();
+
+    protected override SignatureInformation[] SignatureInformations => WebpSignatureInfo;
+
+    public override string ToString() => "WebP Detector";
+}

+ 26 - 0
Masuit.Tools.Abstractions/Files/FileDetector/Detectors/WindowsMemoryDumpDetector.cs

@@ -0,0 +1,26 @@
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Reflection;
+using Masuit.Tools.AspNetCore.Mime;
+
+namespace Masuit.Tools.Files.FileDetector.Detectors;
+
+[FormatCategory(FormatCategory.System)]
+internal class WindowsMemoryDumpDetector : AbstractSignatureDetector
+{
+    private static readonly SignatureInformation[] DmpSignatureInfo = {
+        new() { Position = 0, Signature = new byte [] { 0x4D, 0x44, 0x4D, 0x50, 0x93, 0xA7 } },
+        new() { Position = 0, Signature = new byte [] { 0x50, 0x41, 0x47, 0x45, 0x44, 0x55 } },
+    };
+
+    public override string Extension => "dmp";
+
+    public override string MimeType => new MimeMapper().GetMimeFromExtension("." + Extension);
+
+    public override List<FormatCategory> FormatCategories => GetType().GetCustomAttributes<FormatCategoryAttribute>().Select(a => a.Category).ToList();
+
+    protected override SignatureInformation[] SignatureInformations => DmpSignatureInfo;
+
+    public override string ToString() => "Windows Memory Dump File Detector";
+}

+ 25 - 0
Masuit.Tools.Abstractions/Files/FileDetector/Detectors/WindowsShortcutDetector.cs

@@ -0,0 +1,25 @@
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Reflection;
+using Masuit.Tools.AspNetCore.Mime;
+
+namespace Masuit.Tools.Files.FileDetector.Detectors;
+
+[FormatCategory(FormatCategory.System)]
+internal class WindowsShortcutDetector : AbstractSignatureDetector
+{
+    private static readonly SignatureInformation[] LnkSignatureInfo = {
+        new() { Position = 0, Signature = new byte [] { 0x4C, 0x00, 0x00, 0x00, 0x01, 0x14, 0x02, 0x00 } },
+    };
+
+    public override string Extension => "lnk";
+
+    public override string MimeType => new MimeMapper().GetMimeFromExtension("." + Extension);
+
+    public override List<FormatCategory> FormatCategories => GetType().GetCustomAttributes<FormatCategoryAttribute>().Select(a => a.Category).ToList();
+
+    protected override SignatureInformation[] SignatureInformations => LnkSignatureInfo;
+
+    public override string ToString() => "Windows Shortcut File Detector";
+}

+ 31 - 0
Masuit.Tools.Abstractions/Files/FileDetector/Detectors/XLSXDetector.cs

@@ -0,0 +1,31 @@
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Reflection;
+using Masuit.Tools.AspNetCore.Mime;
+
+namespace Masuit.Tools.Files.FileDetector.Detectors;
+
+[FormatCategory(FormatCategory.Document)]
+internal class XLSXDetector : AbstractZipDetailDetector
+{
+    public override IEnumerable<string> Files
+    {
+        get
+        {
+            yield return "[Content_Types].xml";
+            yield return "_rels/.rels";
+            yield return "xl/_rels/workbook.xml.rels";
+        }
+    }
+
+    public override string Precondition => "zip";
+
+    public override string Extension => "xlsx";
+
+    public override string MimeType => new MimeMapper().GetMimeFromExtension("." + Extension);
+
+    public override List<FormatCategory> FormatCategories => GetType().GetCustomAttributes<FormatCategoryAttribute>().Select(a => a.Category).ToList();
+
+    public override string ToString() => "Microsoft SpreadSheet Open XML Document(XLSX) Detector";
+}

+ 24 - 0
Masuit.Tools.Abstractions/Files/FileDetector/Detectors/XMLDetector.cs

@@ -0,0 +1,24 @@
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Reflection;
+using System.Text.RegularExpressions;
+using Masuit.Tools.AspNetCore.Mime;
+
+namespace Masuit.Tools.Files.FileDetector.Detectors;
+
+[FormatCategory(FormatCategory.Document)]
+internal class XMLDetector : AbstractRegexSignatureDetector
+{
+    public override string Precondition => "txt";
+
+    public override string Extension => "xml";
+
+    public override string MimeType => new MimeMapper().GetMimeFromExtension("." + Extension);
+
+    public override List<FormatCategory> FormatCategories => GetType().GetCustomAttributes<FormatCategoryAttribute>().Select(a => a.Category).ToList();
+
+    protected override Regex Signature => new("^<\\?xml[ \t\n\r]+version=\"[0-9]+\\.[0-9]+\"[ \t\n\r]+([a-zA-Z0-9]+=\"[a-zA-Z0-9\\-_]+\"[ \t\n\r]*)*[ \t\n\r]+\\?>");
+
+    public override string ToString() => "eXtensible Markup Language Document Detector";
+}

+ 25 - 0
Masuit.Tools.Abstractions/Files/FileDetector/Detectors/XarDetector.cs

@@ -0,0 +1,25 @@
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Reflection;
+using Masuit.Tools.AspNetCore.Mime;
+
+namespace Masuit.Tools.Files.FileDetector.Detectors;
+
+[FormatCategory(FormatCategory.Compression)]
+internal class XarDetector : AbstractSignatureDetector
+{
+    private static readonly SignatureInformation[] XarSignatureInfo = {
+        new() { Position = 0, Signature = new byte [] { 0x78, 0x61, 0x72, 0x21 } },
+    };
+
+    public override string Extension => "xar";
+
+    public override string MimeType => new MimeMapper().GetMimeFromExtension("." + Extension);
+
+    public override List<FormatCategory> FormatCategories => GetType().GetCustomAttributes<FormatCategoryAttribute>().Select(a => a.Category).ToList();
+
+    protected override SignatureInformation[] SignatureInformations => XarSignatureInfo;
+
+    public override string ToString() => "XAR Detector";
+}

+ 26 - 0
Masuit.Tools.Abstractions/Files/FileDetector/Detectors/ZDetector.cs

@@ -0,0 +1,26 @@
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Reflection;
+using Masuit.Tools.AspNetCore.Mime;
+
+namespace Masuit.Tools.Files.FileDetector.Detectors;
+
+[FormatCategory(FormatCategory.Compression)]
+internal class ZDetector : AbstractSignatureDetector
+{
+    private static readonly SignatureInformation[] ZSignatureInfo = {
+        new() { Position = 0, Signature = new byte [] { 0x1F, 0x9D } },
+        new() { Position = 0, Signature = new byte [] { 0x1F, 0xA0 } },
+    };
+
+    public override string Extension => "z";
+
+    public override string MimeType => new MimeMapper().GetMimeFromExtension("." + Extension);
+
+    public override List<FormatCategory> FormatCategories => GetType().GetCustomAttributes<FormatCategoryAttribute>().Select(a => a.Category).ToList();
+
+    protected override SignatureInformation[] SignatureInformations => ZSignatureInfo;
+
+    public override string ToString() => "Z Detector";
+}

+ 28 - 0
Masuit.Tools.Abstractions/Files/FileDetector/Detectors/ZipDetector.cs

@@ -0,0 +1,28 @@
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Reflection;
+using Masuit.Tools.AspNetCore.Mime;
+
+namespace Masuit.Tools.Files.FileDetector.Detectors;
+
+[FormatCategory(FormatCategory.Compression)]
+[FormatCategory(FormatCategory.Archive)]
+internal class ZipDetector : AbstractSignatureDetector
+{
+    private static readonly SignatureInformation[] ZipSignatureInfo = {
+        new () { Position = 0, Signature = new byte [] { 0x50, 0x4b, 0x03, 0x04 } },
+        new () { Position = 0, Signature = new byte [] { 0x50, 0x4b, 0x05, 0x06 } },
+        new () { Position = 0, Signature = new byte [] { 0x50, 0x4b, 0x07, 0x08 } },
+    };
+
+    public override string Extension => "zip";
+
+    public override string MimeType => new MimeMapper().GetMimeFromExtension("." + Extension);
+
+    public override List<FormatCategory> FormatCategories => GetType().GetCustomAttributes<FormatCategoryAttribute>().Select(a => a.Category).ToList();
+
+    protected override SignatureInformation[] SignatureInformations => ZipSignatureInfo;
+
+    public override string ToString() => "ZIP Detector";
+}

+ 27 - 0
Masuit.Tools.Abstractions/Files/FileDetector/Detectors/_3GPDetector.cs

@@ -0,0 +1,27 @@
+using System.Collections.Generic;
+using System.Linq;
+using System.Reflection;
+using Masuit.Tools.AspNetCore.Mime;
+
+namespace Masuit.Tools.Files.FileDetector.Detectors;
+
+[FormatCategory(FormatCategory.Video)]
+[FormatCategory(FormatCategory.Audio)]
+internal class _3GPDetector : AbstractISOBaseMediaFileDetailDetector
+{
+    public override string Extension => "3gp";
+
+    protected override IEnumerable<string> NextSignature
+    {
+        get
+        {
+            yield return "3gp";
+        }
+    }
+
+    public override string ToString() => "3GPP Detector";
+
+    public override string MimeType => new MimeMapper().GetMimeFromExtension("." + Extension);
+
+    public override List<FormatCategory> FormatCategories => GetType().GetCustomAttributes<FormatCategoryAttribute>().Select(a => a.Category).ToList();
+}

+ 25 - 0
Masuit.Tools.Abstractions/Files/FileDetector/Detectors/_7zDetector.cs

@@ -0,0 +1,25 @@
+using System.Collections.Generic;
+using System.Linq;
+using System.Reflection;
+using Masuit.Tools.AspNetCore.Mime;
+
+namespace Masuit.Tools.Files.FileDetector.Detectors;
+
+[FormatCategory(FormatCategory.Archive)]
+[FormatCategory(FormatCategory.Compression)]
+internal class _7zDetector : AbstractSignatureDetector
+{
+    private static readonly SignatureInformation[] _7ZSignatureInfo = {
+        new() { Position = 0, Signature = new byte [] { 0x37, 0x7A, 0xBC, 0xAF, 0x27, 0x1C } },
+    };
+
+    public override string Extension => "7z";
+
+    protected override SignatureInformation[] SignatureInformations => _7ZSignatureInfo;
+
+    public override string MimeType => new MimeMapper().GetMimeFromExtension("." + Extension);
+
+    public override List<FormatCategory> FormatCategories => GetType().GetCustomAttributes<FormatCategoryAttribute>().Select(a => a.Category).ToList();
+
+    public override string ToString() => "7Z Detector";
+}

+ 79 - 0
Masuit.Tools.Abstractions/Files/FileDetector/FileSignatureDetector.cs

@@ -0,0 +1,79 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Reflection;
+
+namespace Masuit.Tools.Files.FileDetector;
+
+public static class FileSignatureDetector
+{
+    private static List<IDetector> Detectors { get; set; } = new();
+
+    public static IReadOnlyList<IDetector> Registered => Detectors;
+
+    public static void AddDetector<T>() where T : IDetector
+    {
+        var instance = Activator.CreateInstance<T>();
+        AddDetector(instance);
+    }
+
+    public static void AddDetector(IDetector instance)
+    {
+        if (!Detectors.Contains(instance))
+        {
+            Detectors.Add(instance);
+        }
+    }
+
+    static FileSignatureDetector()
+    {
+        var detectorTypeInfo = typeof(IDetector).GetTypeInfo();
+        foreach (var type in AppDomain.CurrentDomain.GetAssemblies().SelectMany(a => a.DefinedTypes))
+        {
+            if (detectorTypeInfo.IsAssignableFrom(type) && !type.IsAbstract && type.DeclaredConstructors.First().GetParameters().Length == 0)
+            {
+                AddDetector(Activator.CreateInstance(type.AsType()) as IDetector);
+            }
+        }
+    }
+
+    public static IDetector DetectFiletype(string filepath)
+    {
+        using var stream = File.OpenRead(filepath);
+        return DetectFiletype(stream);
+    }
+
+    public static IDetector DetectFiletype(this FileInfo file)
+    {
+        using var stream = file.OpenRead();
+        return DetectFiletype(stream);
+    }
+
+    public static IDetector DetectFiletype(this Stream stream)
+    {
+        string pre = null;
+        IDetector foundDetector = new NoneDetector();
+        while (true)
+        {
+            bool found = false;
+            foreach (var detector in Detectors.Where(d => d.Precondition == pre))
+            {
+                stream.Position = 0;
+                if (detector.Detect(stream))
+                {
+                    found = true;
+                    foundDetector = detector;
+                    pre = detector.Extension;
+                    break;
+                }
+            }
+            if (!found)
+            {
+                break;
+            }
+        }
+
+        return foundDetector;
+    }
+}

+ 46 - 0
Masuit.Tools.Abstractions/Files/FileDetector/FormatCategoryAttribute.cs

@@ -0,0 +1,46 @@
+using System;
+using System.ComponentModel;
+
+namespace Masuit.Tools.Files.FileDetector;
+
+[Flags]
+public enum FormatCategory : uint
+{
+    [Description("图片")]
+    Image = 1,
+
+    [Description("视频")]
+    Video = 2,
+
+    [Description("音频")]
+    Audio = 4,
+
+    [Description("档案包")]
+    Archive = 8,
+
+    [Description("压缩包")]
+    Compression = 16,
+
+    [Description("文档")]
+    Document = 32,
+
+    [Description("系统")]
+    System = 64,
+
+    [Description("可执行")]
+    Executable = 128,
+
+    [Description("其他二进制")]
+    All = 0xffffffff
+}
+
+[AttributeUsage(AttributeTargets.Class, AllowMultiple = true)]
+public class FormatCategoryAttribute : Attribute
+{
+    public FormatCategory Category { get; }
+
+    public FormatCategoryAttribute(FormatCategory category)
+    {
+        Category = category;
+    }
+}

+ 39 - 0
Masuit.Tools.Abstractions/Files/FileDetector/IDetector.cs

@@ -0,0 +1,39 @@
+using System.Collections.Generic;
+using System.IO;
+
+namespace Masuit.Tools.Files.FileDetector;
+
+public interface IDetector
+{
+    string Precondition { get; }
+
+    string Extension { get; }
+
+    bool Detect(Stream stream);
+
+    string MimeType { get; }
+
+    List<FormatCategory> FormatCategories { get; }
+}
+
+public class NoneDetector:IDetector
+{
+    public string Precondition { get; }
+    public string Extension { get; }
+
+    public bool Detect(Stream stream)
+    {
+        return false;
+    }
+
+    public string MimeType { get; }
+
+    public List<FormatCategory> FormatCategories { get; }=new List<FormatCategory>();
+
+    /// <summary>Returns a string that represents the current object.</summary>
+    /// <returns>A string that represents the current object.</returns>
+    public override string ToString()
+    {
+        return "不支持的文件格式,请自己实现针对该文件的IDetector";
+    }
+}

+ 50 - 0
Masuit.Tools.Abstractions/Files/FileDetector/InstantDetector.cs

@@ -0,0 +1,50 @@
+using Masuit.Tools.AspNetCore.Mime;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Reflection;
+
+namespace Masuit.Tools.Files.FileDetector;
+
+public class InstantDetector : IDetector
+{
+    public string Description { get; }
+
+    public string Extension { get; }
+
+    public string Precondition { get; }
+
+    public string MimeType => new MimeMapper().GetMimeFromExtension("." + Extension);
+
+    public List<FormatCategory> FormatCategories => GetType().GetCustomAttributes<FormatCategoryAttribute>().Select(a => a.Category).ToList();
+
+    public event Func<Stream, bool> DetectionLogic;
+
+    private int cachedHashcode;
+
+    public InstantDetector(string extension, Func<Stream, bool> logic, string description, string precondition = null)
+    {
+        Extension = extension;
+        Precondition = precondition;
+        Description = description;
+        DetectionLogic += logic;
+
+        cachedHashcode = $"{Description}{Extension}{Precondition}".GetHashCode();
+    }
+
+    public bool Detect(Stream stream)
+    {
+        return DetectionLogic(stream);
+    }
+
+    public override string ToString()
+    {
+        return Description;
+    }
+
+    public override int GetHashCode()
+    {
+        return cachedHashcode;
+    }
+}

+ 83 - 80
Masuit.Tools.Abstractions/Masuit.Tools.Abstractions.csproj

@@ -1,93 +1,96 @@
 <Project Sdk="Microsoft.NET.Sdk">
-  <PropertyGroup>
-    <TargetFrameworks>netstandard2.0;netstandard2.1;net461;net5;net6</TargetFrameworks>
-    <LangVersion>latest</LangVersion>
-    <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
-    <CodeAnalysisRuleSet />
-    <Version>2.5.2</Version>
-    <Authors>懒得勤快</Authors>
-    <Description>Masuit.Tools基础公共库,包含一些常用的操作类,大都是静态类,加密解密,反射操作,Excel简单导出,权重随机筛选算法,分布式短id,表达式树,linq扩展,文件压缩,多线程下载和FTP客户端,硬件信息,字符串扩展方法,日期时间扩展操作,中国农历,大文件拷贝,图像裁剪,验证码,断点续传,集合扩展等常用封装。</Description>
-    <Copyright>懒得勤快,长空X</Copyright>
-    <RepositoryUrl>https://github.com/ldqk/Masuit.Tools</RepositoryUrl>
-    <PackageProjectUrl>https://github.com/ldqk/Masuit.Tools</PackageProjectUrl>
-    <PackageTags>Masuit.Tools,工具库,Utility,Crypt,Extensions</PackageTags>
-    <PackageReleaseNotes>Masuit.Tools基础公共库,如有问题请联系作者QQ:3444764617,或者到项目的github反馈问题,详细的API文档在github上:https://github.com/ldqk/Masuit.Tools</PackageReleaseNotes>
-    <Product>Masuit.Tools.Abstractions</Product>
-    <PackageId>Masuit.Tools.Abstractions</PackageId>
-    <RepositoryType>Github</RepositoryType>
-    <GeneratePackageOnBuild>True</GeneratePackageOnBuild>
-    <PackageRequireLicenseAcceptance>False</PackageRequireLicenseAcceptance>
-    <FileVersion>2.4.5.6</FileVersion>
-    <Company>masuit.com</Company>
-    <AssemblyVersion>2.4.5.6</AssemblyVersion>
-    <PackageLicenseUrl>https://github.com/ldqk/Masuit.Tools/blob/master/LICENSE</PackageLicenseUrl>
-    <EmbedUntrackedSources>true</EmbedUntrackedSources>
-    <IncludeSymbols>true</IncludeSymbols>
-    <SymbolPackageFormat>snupkg</SymbolPackageFormat>
+    <PropertyGroup>
+        <TargetFrameworks>netstandard2.0;netstandard2.1;net461;net5;net6</TargetFrameworks>
+        <LangVersion>latest</LangVersion>
+        <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
+        <Version>2.5.3-beta</Version>
+        <Authors>懒得勤快</Authors>
+        <Description>Masuit.Tools基础公共库,包含一些常用的操作类,大都是静态类,加密解密,反射操作,Excel简单导出,权重随机筛选算法,分布式短id,表达式树,linq扩展,文件压缩,多线程下载和FTP客户端,硬件信息,字符串扩展方法,日期时间扩展操作,中国农历,大文件拷贝,图像裁剪,验证码,断点续传,集合扩展等常用封装。</Description>
+        <Copyright>懒得勤快,长空X</Copyright>
+        <RepositoryUrl>https://github.com/ldqk/Masuit.Tools</RepositoryUrl>
+        <PackageProjectUrl>https://github.com/ldqk/Masuit.Tools</PackageProjectUrl>
+        <PackageTags>Masuit.Tools,工具库,Utility,Crypt,Extensions</PackageTags>
+        <PackageReleaseNotes>Masuit.Tools基础公共库,如有问题请联系作者QQ:3444764617,或者到项目的github反馈问题,详细的API文档在github上:https://github.com/ldqk/Masuit.Tools</PackageReleaseNotes>
+        <Product>Masuit.Tools.Abstractions</Product>
+        <PackageId>Masuit.Tools.Abstractions</PackageId>
+        <RepositoryType>Github</RepositoryType>
+        <GeneratePackageOnBuild>True</GeneratePackageOnBuild>
+        <PackageRequireLicenseAcceptance>False</PackageRequireLicenseAcceptance>
+        <FileVersion>2.4.5.6</FileVersion>
+        <Company>masuit.org</Company>
+        <AssemblyVersion>2.4.5.6</AssemblyVersion>
+        <PackageLicenseUrl>https://github.com/ldqk/Masuit.Tools/blob/master/LICENSE</PackageLicenseUrl>
+        <EmbedUntrackedSources>true</EmbedUntrackedSources>
+        <IncludeSymbols>true</IncludeSymbols>
+        <SymbolPackageFormat>snupkg</SymbolPackageFormat>
         <GenerateDocumentationFile>True</GenerateDocumentationFile>
-  </PropertyGroup>
+    </PropertyGroup>
 
-  <PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='Release|netstandard2.0|AnyCPU'">
-    <DocumentationFile></DocumentationFile>
-  </PropertyGroup>
+    <PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='Release|netstandard2.0|AnyCPU'">
+        <DocumentationFile></DocumentationFile>
+    </PropertyGroup>
 
-  <ItemGroup>
-    <Compile Remove="Mapping\**" />
-    <EmbeddedResource Remove="Mapping\**" />
-    <None Remove="Mapping\**" />
-  </ItemGroup>
+    <ItemGroup>
+        <Compile Remove="Mapping\**" />
+        <EmbeddedResource Remove="Mapping\**" />
+        <None Remove="Mapping\**" />
+    </ItemGroup>
 
-  <ItemGroup>
-    <Compile Remove="Reflection\ClassHelper.cs" />
-  </ItemGroup>
+    <ItemGroup>
+        <Compile Remove="Reflection\ClassHelper.cs" />
+    </ItemGroup>
 
-  <ItemGroup>
-    <PackageReference Include="DnsClient" Version="1.6.1" />
-    <PackageReference Include="HtmlSanitizer" Version="7.1.512" />
-    <PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
-    <PackageReference Include="SixLabors.ImageSharp" Version="2.1.3" />
-    <PackageReference Include="SixLabors.ImageSharp.Drawing" Version="1.0.0-beta14" />
-    <PackageReference Include="System.ComponentModel.Annotations" Version="4.7.0" />
-    <PackageReference Include="System.Diagnostics.PerformanceCounter" Version="4.7.0" />
-    <PackageReference Include="System.Management" Version="4.7.0" />
-    <PackageReference Include="System.Reflection.Emit.Lightweight" Version="4.7.0" />
+    <ItemGroup>
+        <PackageReference Include="DnsClient" Version="1.6.1" />
+        <PackageReference Include="HtmlSanitizer" Version="7.1.512" />
+        <PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
+        <PackageReference Include="SixLabors.ImageSharp" Version="2.1.3" />
+        <PackageReference Include="SixLabors.ImageSharp.Drawing" Version="1.0.0-beta14" />
+        <PackageReference Include="System.ComponentModel.Annotations" Version="4.7.0" />
+        <PackageReference Include="System.Diagnostics.PerformanceCounter" Version="4.7.0" />
+        <PackageReference Include="System.Management" Version="4.7.0" />
+        <PackageReference Include="System.Reflection.Emit.Lightweight" Version="4.7.0" />
         <PackageReference Include="SharpCompress" Version="0.32.1" />
-  </ItemGroup>
+    </ItemGroup>
 
-  <ItemGroup Condition=" '$(TargetFramework)' == 'netstandard2.0'">
-    <PackageReference Include="Microsoft.AspNetCore.Http.Extensions" Version="2.2.0" />
-    <PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="2.2.0" />
-  </ItemGroup>
+    <ItemGroup Condition=" '$(TargetFramework)' == 'netstandard2.0'">
+        <PackageReference Include="Microsoft.AspNetCore.Http.Extensions" Version="2.2.0" />
+        <PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="2.2.0" />
+    </ItemGroup>
 
-  <ItemGroup Condition=" '$(TargetFramework)' == 'netstandard2.1'">
-    <PackageReference Include="Microsoft.AspNetCore.Http.Extensions" Version="2.2.0" />
-    <PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="3.1.9" />
-  </ItemGroup>
+    <ItemGroup Condition=" '$(TargetFramework)' == 'netstandard2.1'">
+        <PackageReference Include="Microsoft.AspNetCore.Http.Extensions" Version="2.2.0" />
+        <PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="3.1.9" />
+    </ItemGroup>
 
-  <ItemGroup Condition=" '$(TargetFramework)' == 'net5'">
-    <PackageReference Include="Microsoft.AspNetCore.Http.Extensions" Version="2.2.0" />
-    <PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="5.0.0" />
-    <PackageReference Include="System.ComponentModel.Annotations" Version="5.0.0" />
-    <PackageReference Include="System.Configuration.ConfigurationManager" Version="5.0.0" />
-    <PackageReference Include="System.Diagnostics.PerformanceCounter" Version="5.0.1" />
-    <PackageReference Include="System.Management" Version="5.0.0" />
-  </ItemGroup>
+    <ItemGroup Condition=" '$(TargetFramework)' == 'net5'">
+        <PackageReference Include="Microsoft.AspNetCore.Http.Extensions" Version="2.2.0" />
+        <PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="5.0.0" />
+        <PackageReference Include="System.ComponentModel.Annotations" Version="5.0.0" />
+        <PackageReference Include="System.Configuration.ConfigurationManager" Version="5.0.0" />
+        <PackageReference Include="System.Diagnostics.PerformanceCounter" Version="5.0.1" />
+        <PackageReference Include="System.Management" Version="5.0.0" />
+    </ItemGroup>
 
-  <ItemGroup Condition=" '$(TargetFramework)' == 'net6'">
-    <PackageReference Include="Microsoft.AspNetCore.Http.Extensions" Version="2.2.0" />
-    <PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="5.0.0" />
-    <PackageReference Include="System.ComponentModel.Annotations" Version="5.0.0" />
-    <PackageReference Include="System.Configuration.ConfigurationManager" Version="6.0.0" />
-    <PackageReference Include="System.Diagnostics.PerformanceCounter" Version="6.0.0" />
-    <PackageReference Include="System.Management" Version="6.0.0" />
-  </ItemGroup>
+    <ItemGroup Condition=" '$(TargetFramework)' == 'net6'">
+        <PackageReference Include="Microsoft.AspNetCore.Http.Extensions" Version="2.2.0" />
+        <PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="5.0.0" />
+        <PackageReference Include="System.ComponentModel.Annotations" Version="5.0.0" />
+        <PackageReference Include="System.Configuration.ConfigurationManager" Version="6.0.0" />
+        <PackageReference Include="System.Diagnostics.PerformanceCounter" Version="6.0.0" />
+        <PackageReference Include="System.Management" Version="6.0.0" />
+    </ItemGroup>
 
-  <ItemGroup Condition=" '$(TargetFramework)' == 'net461' ">
-    <Reference Include="System.Web" />
-    <PackageReference Include="System.Buffers" version="4.5.1" targetFramework="net461" />
-    <PackageReference Include="System.Net.Http" Version="4.3.4" />
-    <PackageReference Include="System.Runtime.Numerics" version="4.3.0" targetFramework="net461" />
-    <PackageReference Include="System.ValueTuple" version="4.5.0" targetFramework="net461" />
-    <PackageReference Include="System.Configuration.ConfigurationManager" Version="4.7.0" />
-  </ItemGroup>
+    <ItemGroup Condition=" '$(TargetFramework)' == 'net461' ">
+        <Reference Include="System.Web" />
+        <PackageReference Include="System.Buffers" version="4.5.1" targetFramework="net461" />
+        <PackageReference Include="System.Net.Http" Version="4.3.4" />
+        <PackageReference Include="System.Runtime.Numerics" version="4.3.0" targetFramework="net461" />
+        <PackageReference Include="System.ValueTuple" version="4.5.0" targetFramework="net461" />
+        <PackageReference Include="System.Configuration.ConfigurationManager" Version="4.7.0" />
+    </ItemGroup>
+
+    <ItemGroup>
+        <Folder Include="Files\FileDetector\" />
+    </ItemGroup>
 </Project>

+ 1 - 1
Masuit.Tools.AspNetCore/Masuit.Tools.AspNetCore.csproj

@@ -18,7 +18,7 @@
         <LangVersion>latest</LangVersion>
         <RepositoryType>Github</RepositoryType>
         <GeneratePackageOnBuild>True</GeneratePackageOnBuild>
-        <Version>1.1.2</Version>
+        <Version>1.1.3-beta</Version>
         <FileVersion>1.0</FileVersion>
         <Company>masuit.com</Company>
         <AssemblyVersion>1.0</AssemblyVersion>

+ 8 - 1
Masuit.Tools.Core/Masuit.Tools.Core.csproj

@@ -14,11 +14,12 @@ github:https://github.com/ldqk/Masuit.Tools
         <PackageLicenseUrl>https://github.com/ldqk/Masuit.Tools/blob/master/LICENSE</PackageLicenseUrl>
         <Product>Masuit.Tools.Core</Product>
         <PackageId>Masuit.Tools.Core</PackageId>
+        <ImplicitUsings>enable</ImplicitUsings>
         <LangVersion>latest</LangVersion>
         <UserSecretsId>830c282f-f7c1-42be-8651-4cd06ac8e73f</UserSecretsId>
         <RepositoryType>Github</RepositoryType>
         <GeneratePackageOnBuild>True</GeneratePackageOnBuild>
-        <Version>2.5.2</Version>
+        <Version>2.5.3-beta</Version>
         <FileVersion>2.4.5.6</FileVersion>
         <Company>masuit.com</Company>
         <AssemblyVersion>2.4.5.6</AssemblyVersion>
@@ -79,6 +80,12 @@ github:https://github.com/ldqk/Masuit.Tools
         <Compile Include="..\Masuit.Tools.Abstractions\Extensions\*\*.cs">
             <Link>Extensions\%(RecursiveDir)%(FileName)%(Extension)</Link>
         </Compile>
+        <Compile Include="..\Masuit.Tools.Abstractions\Files\FileDetector\*.*">
+          <Link>Files\FileDetector\%(RecursiveDir)%(FileName)%(Extension)</Link>
+        </Compile>
+        <Compile Include="..\Masuit.Tools.Abstractions\Files\FileDetector\*\*.*">
+          <Link>Files\FileDetector\%(RecursiveDir)%(FileName)%(Extension)</Link>
+        </Compile>
     </ItemGroup>
     <ItemGroup>
       <Compile Remove="..\Masuit.Tools.Abstractions\Mapping\**" />

+ 2 - 44
Masuit.Tools.Net45/Masuit.Tools.Net45.csproj

@@ -67,47 +67,8 @@
     <Compile Include="..\Masuit.Tools.Abstractions\DateTimeExt\WeekHolidayStruct.cs">
       <Link>DateTimeExt\WeekHolidayStruct.cs</Link>
     </Compile>
-    <Compile Include="..\Masuit.Tools.Abstractions\Extensions\BaseType\BigIntegerExtensions.cs">
-      <Link>Extensions\BaseType\BigIntegerExtensions.cs</Link>
-    </Compile>
-    <Compile Include="..\Masuit.Tools.Abstractions\Extensions\BaseType\DoubleExtensions.cs">
-      <Link>Extensions\BaseType\DoubleExtensions.cs</Link>
-    </Compile>
-    <Compile Include="..\Masuit.Tools.Abstractions\Extensions\BaseType\IConvertibleExtensions.cs">
-      <Link>Extensions\BaseType\IConvertibleExtensions.cs</Link>
-    </Compile>
-    <Compile Include="..\Masuit.Tools.Abstractions\Extensions\BaseType\IDictionaryExtensions.cs">
-      <Link>Extensions\BaseType\IDictionaryExtensions.cs</Link>
-    </Compile>
-    <Compile Include="..\Masuit.Tools.Abstractions\Extensions\BaseType\IEnumerableExtensions.cs">
-      <Link>Extensions\BaseType\IEnumerableExtensions.cs</Link>
-    </Compile>
-    <Compile Include="..\Masuit.Tools.Abstractions\Extensions\BaseType\IntExtensions.cs">
-      <Link>Extensions\BaseType\IntExtensions.cs</Link>
-    </Compile>
-    <Compile Include="..\Masuit.Tools.Abstractions\Extensions\BaseType\IPAddressExtensions.cs">
-      <Link>Extensions\BaseType\IPAddressExtensions.cs</Link>
-    </Compile>
-    <Compile Include="..\Masuit.Tools.Abstractions\Extensions\BaseType\LongExtensions.cs">
-      <Link>Extensions\BaseType\LongExtensions.cs</Link>
-    </Compile>
-    <Compile Include="..\Masuit.Tools.Abstractions\Extensions\BaseType\ObjectExtensions.cs">
-      <Link>Extensions\BaseType\ObjectExtensions.cs</Link>
-    </Compile>
-    <Compile Include="..\Masuit.Tools.Abstractions\Extensions\BaseType\RandomExtensions.cs">
-      <Link>Extensions\BaseType\RandomExtensions.cs</Link>
-    </Compile>
-    <Compile Include="..\Masuit.Tools.Abstractions\Extensions\BaseType\ShortExtensions.cs">
-      <Link>Extensions\BaseType\ShortExtensions.cs</Link>
-    </Compile>
-    <Compile Include="..\Masuit.Tools.Abstractions\Extensions\BaseType\StreamExtensions.cs">
-      <Link>Extensions\BaseType\StreamExtensions.cs</Link>
-    </Compile>
-    <Compile Include="..\Masuit.Tools.Abstractions\Extensions\BaseType\StringExtensions.cs">
-      <Link>Extensions\BaseType\StringExtensions.cs</Link>
-    </Compile>
-    <Compile Include="..\Masuit.Tools.Abstractions\Extensions\BaseType\ValueTypeConvertExtensions.cs">
-      <Link>Extensions\BaseType\ValueTypeConvertExtensions.cs</Link>
+    <Compile Include="..\Masuit.Tools.Abstractions\Extensions\*\*.*">
+      <Link>Extensions\%(RecursiveDir)%(FileName)%(Extension)</Link>
     </Compile>
     <Compile Include="..\Masuit.Tools.Abstractions\Files\FileExt.cs">
       <Link>Files\FileExt.cs</Link>
@@ -139,9 +100,6 @@
     <Compile Include="..\Masuit.Tools.Abstractions\Maths\Vector2D.cs">
       <Link>Maths\Vector2D.cs</Link>
     </Compile>
-    <Compile Include="..\Masuit.Tools.Abstractions\Media\ImageDetector.cs">
-      <Link>Media\ImageDetector.cs</Link>
-    </Compile>
     <Compile Include="..\Masuit.Tools.Abstractions\Media\ImageFormat.cs">
       <Link>Media\ImageFormat.cs</Link>
     </Compile>

+ 1 - 1
Masuit.Tools.Net45/Media/ImageUtilities.cs

@@ -1072,7 +1072,7 @@ namespace Masuit.Tools.Media
             for (int i = 0; i < count; i++) //以Jpeg格式保存各帧
             {
                 gif.SelectActiveFrame(fd, i);
-                gif.Save(pSavedPath + "\\frame_" + i + ".jpg", ImageFormat.Jpeg);
+                gif.Save(pSavedPath + "\\frame_" + i + ".jpg", System.Drawing.Imaging.ImageFormat.Jpeg);
             }
         }
 

برخی فایل ها در این مقایسه diff نمایش داده نمی شوند زیرا تعداد فایل ها بسیار زیاد است