123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169 |
- using System;
- using System.Collections.Generic;
- using System.IO;
- using System.Linq;
- using System.Reflection;
- using System.Text;
- using Masuit.Tools.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 / _sectorSize);
- nToRead = Math.Min(_sectorChain[0].Size - ((int)_position % _sectorSize), count);
- if (secIndex < _sectorChain.Count)
- {
- Buffer.BlockCopy(_sectorChain[secIndex].GetData(),
- (int)(_position % _sectorSize), buffer, offset, nToRead);
- }
- nRead += nToRead;
- ++secIndex;
- // Central sectors
- while (nRead < (count - _sectorSize))
- {
- nToRead = _sectorSize;
- Buffer.BlockCopy(_sectorChain[secIndex].GetData(), 0, buffer, offset + nRead, nToRead);
- nRead += nToRead;
- ++secIndex;
- }
- // Last sector
- nToRead = count - nRead;
- if (nToRead != 0)
- {
- Buffer.BlockCopy(_sectorChain[secIndex].GetData(), 0, buffer, offset + nRead, nToRead);
- nRead += nToRead;
- }
- _position += nRead;
- return nRead;
- }
- else
- return 0;
- }
- public override long Seek(long offset, SeekOrigin origin)
- {
- switch (origin)
- {
- case SeekOrigin.Begin: _position = offset; break;
- case SeekOrigin.Current: _position += offset; break;
- case SeekOrigin.End: _position = Length - offset; break;
- }
- AdjustLength(_position);
- return _position;
- }
- private void AdjustLength(long value, Queue<Sector> availableSectors = null)
- {
- this._length = value;
- long delta = value - (_sectorChain.Count * (long)_sectorSize);
- if (delta > 0)
- {
- int nSec = (int)Math.Ceiling(((double)delta / _sectorSize));
- while (nSec > 0)
- {
- Sector t = null;
- if (availableSectors == null || availableSectors.Count == 0)
- {
- t = new Sector(_sectorSize, _stream);
- if (_sectorSize == Sector.MinisectorSize)
- t.Type = SectorType.Mini;
- }
- else
- t = availableSectors.Dequeue();
- if (_isFatStream)
- t.InitFATData();
- _sectorChain.Add(t);
- nSec--;
- }
- }
- }
- public override void SetLength(long value) => AdjustLength(value);
- public override void Write(byte[] buffer, int offset, int count) => throw new NotImplementedException();
- }
- [Flags]
- internal enum CFSConfiguration
- {
- Default = 1,
- LeaveOpen = 16,
- }
- internal sealed class CompoundFile : IDisposable
- {
- public CFSConfiguration Configuration { get; private set; } = CFSConfiguration.Default;
- internal int GetSectorSize() => 2 << (header.SectorShift - 1);
- private const int HEADER_DIFAT_ENTRIES_COUNT = 109;
- private readonly int DIFAT_SECTOR_FAT_ENTRIES_COUNT = 127;
- private readonly int FAT_SECTOR_ENTRIES_COUNT = 128;
- private const int SIZE_OF_SID = 4;
- private const int FLUSHING_QUEUE_SIZE = 6000;
- private const int FLUSHING_BUFFER_MAX_SIZE = 1024 * 1024 * 16;
- private List<Sector> sectors = new List<Sector>();
- private Header header;
- internal Stream sourceStream = null;
- public CompoundFile(Stream stream, CFSConfiguration configParameters)
- {
- this.closeStream = !configParameters.HasFlag(CFSConfiguration.LeaveOpen);
- LoadStream(stream);
- DIFAT_SECTOR_FAT_ENTRIES_COUNT = (GetSectorSize() / 4) - 1;
- FAT_SECTOR_ENTRIES_COUNT = (GetSectorSize() / 4);
- }
- private string fileName = string.Empty;
- private void Load(Stream stream)
- {
- try
- {
- this.header = new Header();
- this.directoryEntries = new List<DirectoryEntry>();
- this.sourceStream = stream;
- header.Read(stream);
- int n_sector = Ceiling(((stream.Length - GetSectorSize()) / (double)GetSectorSize()));
- if (stream.Length > 0x7FFFFF0)
- this._transactionLockAllocated = true;
- sectors = new List<Sector>();
- for (int i = 0; i < n_sector; i++)
- sectors.Add(null);
- LoadDirectories();
- this.RootStorage = new CFStorage(this, directoryEntries[0]);
- }
- catch (Exception)
- {
- if (stream != null && closeStream)
- stream.Dispose();
- throw;
- }
- }
- private void LoadFile(String fileName)
- {
- this.fileName = fileName;
- FileStream fs = null;
- try
- {
- fs = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
- Load(fs);
- }
- catch
- {
- if (fs != null)
- fs.Dispose();
- throw;
- }
- }
- private void LoadStream(Stream stream)
- {
- if (stream == null)
- throw new Exception("Stream parameter cannot be null");
- if (!stream.CanSeek)
- throw new Exception("Cannot load a non-seekable Stream");
- stream.Seek(0, SeekOrigin.Begin);
- Load(stream);
- }
- public bool HasSourceStream => sourceStream != null;
- private void PersistMiniStreamToStream(List<Sector> miniSectorChain)
- {
- List<Sector> miniStream = GetSectorChain(RootEntry.StartSetc, SectorType.Normal);
- StreamView miniStreamView = new StreamView(miniStream, GetSectorSize(), this.RootStorage.Size, null, sourceStream);
- for (int i = 0; i < miniSectorChain.Count; i++)
- {
- Sector s = miniSectorChain[i];
- if (s.Id == -1)
- throw new Exception("Invalid minisector index");
- miniStreamView.Seek(Sector.MinisectorSize * s.Id, SeekOrigin.Begin);
- miniStreamView.Write(s.GetData(), 0, Sector.MinisectorSize);
- }
- }
- private void AllocateMiniSectorChain(List<Sector> sectorChain)
- {
- List<Sector> miniFAT = GetSectorChain(header.FirstMiniFATSectorID, SectorType.Normal);
- List<Sector> miniStream = GetSectorChain(RootEntry.StartSetc, SectorType.Normal);
- StreamView miniFATView = new StreamView(miniFAT, GetSectorSize(),
- header.MiniFATSectorsNumber * Sector.MinisectorSize,
- null, this.sourceStream, true);
- StreamView miniStreamView = new StreamView(miniStream, GetSectorSize(),
- this.RootStorage.Size, null, sourceStream);
- for (int i = 0; i < sectorChain.Count; i++)
- {
- Sector s = sectorChain[i];
- if (s.Id == -1)
- {
- miniStreamView.Seek(this.RootStorage.Size + Sector.MinisectorSize, SeekOrigin.Begin);
- s.Id = (int)(miniStreamView.Position - Sector.MinisectorSize) / Sector.MinisectorSize;
- this.RootStorage.DirEntry.Size = miniStreamView.Length;
- }
- }
- for (int i = 0; i < sectorChain.Count - 1; i++)
- {
- Int32 currentId = sectorChain[i].Id;
- Int32 nextId = sectorChain[i + 1].Id;
- miniFATView.Seek(currentId * 4, SeekOrigin.Begin);
- miniFATView.Write(BitConverter.GetBytes(nextId), 0, 4);
- }
- miniFATView.Seek(sectorChain[sectorChain.Count - 1].Id * SIZE_OF_SID, SeekOrigin.Begin);
- miniFATView.Write(BitConverter.GetBytes(Sector.Endofchain), 0, 4);
- AllocateSectorChain(miniStreamView.BaseSectorChain);
- AllocateSectorChain(miniFATView.BaseSectorChain);
- if (miniFAT.Count > 0)
- {
- this.RootStorage.DirEntry.StartSetc = miniStream[0].Id;
- header.MiniFATSectorsNumber = (uint)miniFAT.Count;
- header.FirstMiniFATSectorID = miniFAT[0].Id;
- }
- }
- private void SetSectorChain(List<Sector> sectorChain)
- {
- if (sectorChain == null || sectorChain.Count == 0)
- return;
- SectorType _st = sectorChain[0].Type;
- if (_st == SectorType.Normal)
- AllocateSectorChain(sectorChain);
- else if (_st == SectorType.Mini)
- AllocateMiniSectorChain(sectorChain);
- }
- private void AllocateSectorChain(List<Sector> sectorChain)
- {
- foreach (Sector s in sectorChain)
- {
- if (s.Id == -1)
- {
- sectors.Add(s);
- s.Id = sectors.Count - 1;
- }
- }
- AllocateFATSectorChain(sectorChain);
- }
- internal bool _transactionLockAdded = false;
- internal int _lockSectorId = -1;
- internal bool _transactionLockAllocated = false;
- private void CheckForLockSector()
- {
- if (_transactionLockAdded && !_transactionLockAllocated)
- {
- StreamView fatStream = new StreamView(GetFatSectorChain(), GetSectorSize(), sourceStream);
- fatStream.Seek(_lockSectorId * 4, SeekOrigin.Begin);
- fatStream.Write(BitConverter.GetBytes(Sector.Endofchain), 0, 4);
- _transactionLockAllocated = true;
- }
- }
- private void AllocateFATSectorChain(List<Sector> sectorChain)
- {
- List<Sector> fatSectors = GetSectorChain(-1, SectorType.FAT);
- StreamView fatStream = new StreamView(fatSectors, GetSectorSize(),
- header.FATSectorsNumber * GetSectorSize(), null,
- sourceStream, true);
- for (int i = 0; i < sectorChain.Count - 1; i++)
- {
- Sector sN = sectorChain[i + 1];
- Sector sC = sectorChain[i];
- fatStream.Seek(sC.Id * 4, SeekOrigin.Begin);
- fatStream.Write(BitConverter.GetBytes(sN.Id), 0, 4);
- }
- fatStream.Seek(sectorChain[sectorChain.Count - 1].Id * 4, SeekOrigin.Begin);
- fatStream.Write(BitConverter.GetBytes(Sector.Endofchain), 0, 4);
- AllocateDIFATSectorChain(fatStream.BaseSectorChain);
- }
- private void AllocateDIFATSectorChain(List<Sector> FATsectorChain)
- {
- header.FATSectorsNumber = FATsectorChain.Count;
- foreach (Sector s in FATsectorChain)
- {
- if (s.Id == -1)
- {
- sectors.Add(s);
- s.Id = sectors.Count - 1;
- s.Type = SectorType.FAT;
- }
- }
- int nCurrentSectors = sectors.Count;
- int nDIFATSectors = (int)header.DIFATSectorsNumber;
- if (FATsectorChain.Count > HEADER_DIFAT_ENTRIES_COUNT)
- {
- nDIFATSectors = Ceiling((double)(FATsectorChain.Count - HEADER_DIFAT_ENTRIES_COUNT) / DIFAT_SECTOR_FAT_ENTRIES_COUNT);
- nDIFATSectors = LowSaturation(nDIFATSectors - (int)header.DIFATSectorsNumber); //required DIFAT
- }
- nCurrentSectors += nDIFATSectors;
- while (header.FATSectorsNumber * FAT_SECTOR_ENTRIES_COUNT < nCurrentSectors)
- {
- Sector extraFATSector = new Sector(GetSectorSize(), sourceStream);
- sectors.Add(extraFATSector);
- extraFATSector.Id = sectors.Count - 1;
- extraFATSector.Type = SectorType.FAT;
- FATsectorChain.Add(extraFATSector);
- header.FATSectorsNumber++;
- nCurrentSectors++;
- if (nDIFATSectors * DIFAT_SECTOR_FAT_ENTRIES_COUNT <
- (header.FATSectorsNumber > HEADER_DIFAT_ENTRIES_COUNT ?
- header.FATSectorsNumber - HEADER_DIFAT_ENTRIES_COUNT :
- 0))
- {
- nDIFATSectors++;
- nCurrentSectors++;
- }
- }
- List<Sector> difatSectors = GetSectorChain(-1, SectorType.DIFAT);
- StreamView difatStream = new StreamView(difatSectors, GetSectorSize(), sourceStream);
- for (int i = 0; i < FATsectorChain.Count; i++)
- {
- if (i < HEADER_DIFAT_ENTRIES_COUNT)
- header.DIFAT[i] = FATsectorChain[i].Id;
- else
- {
- if (i != HEADER_DIFAT_ENTRIES_COUNT && (i - HEADER_DIFAT_ENTRIES_COUNT) % DIFAT_SECTOR_FAT_ENTRIES_COUNT == 0)
- {
- difatStream.Write(new byte[sizeof(int)], 0, sizeof(int));
- }
- difatStream.Write(BitConverter.GetBytes(FATsectorChain[i].Id), 0, sizeof(int));
- }
- }
- for (int i = 0; i < difatStream.BaseSectorChain.Count; i++)
- {
- if (difatStream.BaseSectorChain[i].Id == -1)
- {
- sectors.Add(difatStream.BaseSectorChain[i]);
- difatStream.BaseSectorChain[i].Id = sectors.Count - 1;
- difatStream.BaseSectorChain[i].Type = SectorType.DIFAT;
- }
- }
- header.DIFATSectorsNumber = (uint)nDIFATSectors;
- if (difatStream.BaseSectorChain != null && difatStream.BaseSectorChain.Count > 0)
- {
- header.FirstDIFATSectorID = difatStream.BaseSectorChain[0].Id;
- header.DIFATSectorsNumber = (uint)difatStream.BaseSectorChain.Count;
- for (int i = 0; i < difatStream.BaseSectorChain.Count - 1; i++)
- Buffer.BlockCopy(BitConverter.GetBytes(difatStream.BaseSectorChain[i + 1].Id),
- 0, difatStream.BaseSectorChain[i].GetData(),
- GetSectorSize() - sizeof(int), 4);
- Buffer.BlockCopy(BitConverter.GetBytes(Sector.Endofchain), 0,
- difatStream.BaseSectorChain[difatStream.BaseSectorChain.Count - 1].GetData(),
- GetSectorSize() - sizeof(int), sizeof(int));
- }
- else header.FirstDIFATSectorID = Sector.Endofchain;
- StreamView fatSv = new StreamView(FATsectorChain, GetSectorSize(), header.FATSectorsNumber * GetSectorSize(), null, sourceStream);
- for (int i = 0; i < header.DIFATSectorsNumber; i++)
- {
- fatSv.Seek(difatStream.BaseSectorChain[i].Id * 4, SeekOrigin.Begin);
- fatSv.Write(BitConverter.GetBytes(Sector.Difsect), 0, 4);
- }
- for (int i = 0; i < header.FATSectorsNumber; i++)
- {
- fatSv.Seek(fatSv.BaseSectorChain[i].Id * 4, SeekOrigin.Begin);
- fatSv.Write(BitConverter.GetBytes(Sector.Fatsect), 0, 4);
- }
- header.FATSectorsNumber = fatSv.BaseSectorChain.Count;
- }
- private List<Sector> GetDifatSectorChain()
- {
- int validationCount = 0;
- List<Sector> result = new List<Sector>();
- int nextSecID
- = Sector.Endofchain;
- if (header.DIFATSectorsNumber != 0)
- {
- validationCount = (int)header.DIFATSectorsNumber;
- Sector s = sectors[header.FirstDIFATSectorID];
- if (s == null)
- {
- sectors[header.FirstDIFATSectorID] = s = new Sector(GetSectorSize(), sourceStream)
- {
- Type = SectorType.DIFAT,
- Id = header.FirstDIFATSectorID
- };
- }
- result.Add(s);
- while (true && validationCount >= 0)
- {
- nextSecID = BitConverter.ToInt32(s.GetData(), GetSectorSize() - 4);
- if (nextSecID == Sector.Freesect || nextSecID == Sector.Endofchain) break;
- validationCount--;
- if (validationCount < 0)
- {
- Dispose();
- throw new InvalidDataException("DIFAT sectors count mismatched. Corrupted compound file");
- }
- s = sectors[nextSecID];
- if (s == null)
- sectors[nextSecID] = s = new Sector(GetSectorSize(), sourceStream) { Id = nextSecID };
- result.Add(s);
- }
- }
- return result;
- }
- private List<Sector> GetFatSectorChain()
- {
- int N_HEADER_FAT_ENTRY = 109;
- List<Sector> result = new List<Sector>();
- int nextSecID = Sector.Endofchain;
- List<Sector> difatSectors = GetDifatSectorChain();
- int idx = 0;
- while (idx < header.FATSectorsNumber && idx < N_HEADER_FAT_ENTRY)
- {
- nextSecID = header.DIFAT[idx];
- Sector s = sectors[nextSecID];
- if (s == null)
- {
- sectors[nextSecID] = s = new Sector(GetSectorSize(), sourceStream)
- {
- Id = nextSecID,
- Type = SectorType.FAT
- };
- }
- result.Add(s);
- ++idx;
- }
- if (difatSectors.Count > 0)
- {
- var difatStream = new StreamView(difatSectors, GetSectorSize(),
- header.FATSectorsNumber > N_HEADER_FAT_ENTRY ? (header.FATSectorsNumber - N_HEADER_FAT_ENTRY) * 4 : 0,
- null, sourceStream);
- byte[] nextDIFATSectorBuffer = new byte[4];
- difatStream.Read(nextDIFATSectorBuffer, 0, 4);
- nextSecID = BitConverter.ToInt32(nextDIFATSectorBuffer, 0);
- int i = 0;
- int nFat = N_HEADER_FAT_ENTRY;
- while (nFat < header.FATSectorsNumber)
- {
- if (difatStream.Position == ((GetSectorSize() - 4) + i * GetSectorSize()))
- {
- difatStream.Seek(4, SeekOrigin.Current);
- ++i;
- continue;
- }
- Sector s = sectors[nextSecID];
- 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];
- if (s == null)
- {
- sectors[nextSecID] = s = new Sector(GetSectorSize(), sourceStream)
- {
- Id = nextSecID,
- Type = SectorType.Normal
- };
- }
- result.Add(s);
- fatStream.Seek(nextSecID * 4, SeekOrigin.Begin);
- int next = fatStream.ReadInt32();
- if (next != nextSecID)
- nextSecID = next;
- else
- throw new InvalidDataException("Cyclic sector chain found. File is corrupted");
- }
- return result;
- }
- private List<Sector> GetMiniSectorChain(int secID)
- {
- List<Sector> result = new List<Sector>();
- if (secID != Sector.Endofchain)
- {
- int nextSecID = secID;
- List<Sector> miniFAT = GetNormalSectorChain(header.FirstMiniFATSectorID);
- List<Sector> miniStream = GetNormalSectorChain(RootEntry.StartSetc);
- StreamView miniFATView = new StreamView(miniFAT, GetSectorSize(), header.MiniFATSectorsNumber * Sector.MinisectorSize, null, sourceStream);
- StreamView miniStreamView = new StreamView(miniStream, GetSectorSize(), RootStorage.Size, null, sourceStream);
- BinaryReader miniFATReader = new BinaryReader(miniFATView);
- nextSecID = secID;
- while (true)
- {
- if (nextSecID == Sector.Endofchain)
- break;
- Sector ms = new Sector(Sector.MinisectorSize, sourceStream);
- byte[] temp = new byte[Sector.MinisectorSize];
- ms.Id = nextSecID;
- ms.Type = SectorType.Mini;
- miniStreamView.Seek(nextSecID * Sector.MinisectorSize, SeekOrigin.Begin);
- miniStreamView.Read(ms.GetData(), 0, Sector.MinisectorSize);
- result.Add(ms);
- miniFATView.Seek(nextSecID * 4, SeekOrigin.Begin);
- nextSecID = miniFATReader.ReadInt32();
- }
- }
- return result;
- }
- internal List<Sector> GetSectorChain(int secID, SectorType chainType)
- {
- switch (chainType)
- {
- case SectorType.DIFAT:
- return GetDifatSectorChain();
- case SectorType.FAT:
- return GetFatSectorChain();
- case SectorType.Normal:
- return GetNormalSectorChain(secID);
- case SectorType.Mini:
- return GetMiniSectorChain(secID);
- default:
- throw new Exception("Unsupproted chain type");
- }
- }
- public CFStorage RootStorage { get; private set; }
- public int Version => this.header.MajorVersion;
- internal RBTree CreateNewTree()
- {
- RBTree bst = new RBTree();
- return bst;
- }
- internal RBTree GetChildrenTree(int sid)
- {
- RBTree bst = new RBTree();
- DoLoadChildren(bst, directoryEntries[sid]);
- return bst;
- }
- private RBTree DoLoadChildrenTrusted(DirectoryEntry de)
- {
- RBTree bst = null;
- if (de.Child != DirectoryEntry.NOSTREAM)
- bst = new RBTree(directoryEntries[de.Child]);
- return bst;
- }
- private void DoLoadChildren(RBTree bst, DirectoryEntry de)
- {
- if (de.Child != DirectoryEntry.NOSTREAM)
- {
- if (directoryEntries[de.Child].StgType == StgType.StgInvalid) return;
- LoadSiblings(bst, directoryEntries[de.Child]);
- NullifyChildNodes(directoryEntries[de.Child]);
- bst.Insert(directoryEntries[de.Child]);
- }
- }
- private void NullifyChildNodes(DirectoryEntry de)
- {
- de.Parent = null;
- de.Left = null;
- de.Right = null;
- }
- private readonly List<int> _levelSiDs = new List<int>();
- private void LoadSiblings(RBTree bst, DirectoryEntry de)
- {
- _levelSiDs.Clear();
- if (de.LeftSibling != DirectoryEntry.NOSTREAM)
- DoLoadSiblings(bst, directoryEntries[de.LeftSibling]);
- if (de.RightSibling != DirectoryEntry.NOSTREAM)
- {
- _levelSiDs.Add(de.RightSibling);
- DoLoadSiblings(bst, directoryEntries[de.RightSibling]);
- }
- }
- private void DoLoadSiblings(RBTree bst, DirectoryEntry de)
- {
- if (ValidateSibling(de.LeftSibling))
- {
- _levelSiDs.Add(de.LeftSibling);
- DoLoadSiblings(bst, directoryEntries[de.LeftSibling]);
- }
- if (ValidateSibling(de.RightSibling))
- {
- _levelSiDs.Add(de.RightSibling);
- DoLoadSiblings(bst, directoryEntries[de.RightSibling]);
- }
- NullifyChildNodes(de);
- bst.Insert(de);
- }
- private bool ValidateSibling(int sid)
- {
- if (sid != DirectoryEntry.NOSTREAM)
- {
- if (sid >= directoryEntries.Count)
- return false;
- if (directoryEntries[sid].StgType == StgType.StgInvalid)
- return false;
- if (!Enum.IsDefined(typeof(StgType), directoryEntries[sid].StgType))
- return false;
- if (_levelSiDs.Contains(sid))
- throw new InvalidDataException("Cyclic reference of directory item");
- return true;
- }
- return false;
- }
- private void LoadDirectories()
- {
- List<Sector> directoryChain = GetSectorChain(header.FirstDirectorySectorID, SectorType.Normal);
- if (header.FirstDirectorySectorID == Sector.Endofchain)
- header.FirstDirectorySectorID = directoryChain[0].Id;
- StreamView dirReader = new StreamView(directoryChain, GetSectorSize(), directoryChain.Count * GetSectorSize(), null, sourceStream);
- while (dirReader.Position < directoryChain.Count * GetSectorSize())
- {
- DirectoryEntry de = DirectoryEntry.New(String.Empty, StgType.StgInvalid, directoryEntries);
- de.Read(dirReader, this.Version);
- }
- }
- private void CheckFileLength() => throw new NotImplementedException();
- internal int ReadData(CFStream cFStream, long position, byte[] buffer, int count)
- {
- if (count > buffer.Length)
- throw new ArgumentException("count parameter exceeds buffer size");
- DirectoryEntry de = cFStream.DirEntry;
- count = (int)Math.Min(de.Size - position, count);
- StreamView sView = null;
- if (de.Size < header.MinSizeStandardStream)
- sView = new StreamView(GetSectorChain(de.StartSetc, SectorType.Mini), Sector.MinisectorSize, de.Size, null, sourceStream);
- else
- sView = new StreamView(GetSectorChain(de.StartSetc, SectorType.Normal), GetSectorSize(), de.Size, null, sourceStream);
- sView.Seek(position, SeekOrigin.Begin);
- int result = sView.Read(buffer, 0, count);
- return result;
- }
- internal int ReadData(CFStream cFStream, long position, byte[] buffer, int offset, int count)
- {
- DirectoryEntry de = cFStream.DirEntry;
- count = (int)Math.Min(de.Size - offset, count);
- StreamView sView = null;
- if (de.Size < header.MinSizeStandardStream)
- sView = new StreamView(GetSectorChain(de.StartSetc, SectorType.Mini), Sector.MinisectorSize, de.Size, null, sourceStream);
- else
- sView = new StreamView(GetSectorChain(de.StartSetc, SectorType.Normal), GetSectorSize(), de.Size, null, sourceStream);
- sView.Seek(position, SeekOrigin.Begin);
- int result = sView.Read(buffer, offset, count);
- return result;
- }
- internal byte[] GetData(CFStream cFStream)
- {
- AssertDisposed();
- byte[] result = null;
- DirectoryEntry de = cFStream.DirEntry;
- if (de.Size < header.MinSizeStandardStream)
- {
- var miniView = new StreamView(GetSectorChain(de.StartSetc, SectorType.Mini), Sector.MinisectorSize, de.Size, null, sourceStream);
- using (BinaryReader br = new BinaryReader(miniView))
- result = br.ReadBytes((int)de.Size);
- }
- else
- {
- var sView = new StreamView(GetSectorChain(de.StartSetc, SectorType.Normal), GetSectorSize(), de.Size, null, sourceStream);
- result = new byte[(int)de.Size];
- sView.Read(result, 0, result.Length);
- }
- return result;
- }
- public byte[] GetDataBySID(int sid)
- {
- AssertDisposed();
- if (sid < 0)
- return null;
- byte[] result = null;
- try
- {
- DirectoryEntry de = directoryEntries[sid];
- if (de.Size < header.MinSizeStandardStream)
- {
- var miniView = new StreamView(GetSectorChain(de.StartSetc, SectorType.Mini), Sector.MinisectorSize, de.Size, null, sourceStream);
- BinaryReader br = new BinaryReader(miniView);
- result = br.ReadBytes((int)de.Size);
- br.Dispose();
- }
- else
- {
- var sView = new StreamView(GetSectorChain(de.StartSetc, SectorType.Normal), GetSectorSize(), de.Size, null, sourceStream);
- result = new byte[(int)de.Size];
- sView.Read(result, 0, result.Length);
- }
- }
- catch
- {
- throw new Exception("Cannot get data for SID");
- }
- return result;
- }
- public Guid GetGuidBySID(int sid)
- {
- AssertDisposed();
- if (sid < 0)
- throw new Exception("Invalid SID");
- DirectoryEntry de = directoryEntries[sid];
- return de.StorageCLSID;
- }
- public Guid GetGuidForStream(int sid)
- {
- AssertDisposed();
- if (sid < 0)
- throw new Exception("Invalid SID");
- Guid g = new Guid("00000000000000000000000000000000");
- for (int i = sid - 1; i >= 0; i--)
- {
- if (directoryEntries[i].StorageCLSID != g && directoryEntries[i].StgType == StgType.StgStorage)
- return directoryEntries[i].StorageCLSID;
- }
- return g;
- }
- private static int Ceiling(double d) => (int)Math.Ceiling(d);
- private static int LowSaturation(int i) => i > 0 ? i : 0;
- private bool closeStream = true;
- internal bool IsClosed => _disposed;
- #region IDisposable Members
- private bool _disposed;//false
- private object lockObject = new Object();
- public void Dispose()
- {
- try
- {
- if (!_disposed)
- {
- lock (lockObject)
- {
- if (sectors != null)
- {
- sectors.Clear();
- sectors = null;
- }
- this.RootStorage = null;
- this.header = null;
- this.directoryEntries.Clear();
- this.directoryEntries = null;
- this.fileName = null;
- }
- if (this.sourceStream != null && closeStream && !Configuration.HasFlag(CFSConfiguration.LeaveOpen))
- this.sourceStream.Dispose();
- }
- }
- finally
- {
- _disposed = true;
- }
- GC.SuppressFinalize(this);
- }
- #endregion IDisposable Members
- private List<DirectoryEntry> directoryEntries = new List<DirectoryEntry>();
- internal IList<DirectoryEntry> GetDirectories() => directoryEntries;
- internal DirectoryEntry RootEntry => directoryEntries[0];
- private IList<DirectoryEntry> FindDirectoryEntries(String entryName)
- {
- List<DirectoryEntry> result = new List<DirectoryEntry>();
- foreach (DirectoryEntry d in directoryEntries)
- if (d.GetEntryName() == entryName && d.StgType != StgType.StgInvalid)
- result.Add(d);
- return result;
- }
- public IList<CFItem> GetAllNamedEntries(String entryName)
- {
- IList<DirectoryEntry> r = FindDirectoryEntries(entryName);
- List<CFItem> result = new List<CFItem>();
- foreach (DirectoryEntry id in r)
- if (id.GetEntryName() == entryName && id.StgType != StgType.StgInvalid)
- result.Add(id.StgType == StgType.StgStorage ? new CFStorage(this, id) : new CFStream(this, id));
- return result;
- }
- public int GetNumDirectories()
- {
- AssertDisposed();
- return directoryEntries.Count;
- }
- public string GetNameDirEntry(int id)
- {
- AssertDisposed();
- if (id < 0)
- throw new Exception("Invalid Storage ID");
- return directoryEntries[id].Name;
- }
- public StgType GetStorageType(int id)
- {
- AssertDisposed();
- if (id < 0)
- throw new Exception("Invalid Storage ID");
- return directoryEntries[id].StgType;
- }
- private void AssertDisposed()
- {
- if (_disposed)
- throw new ObjectDisposedException("Compound File closed: cannot access data");
- }
- }
- #endregion Modified OpenMCDF
- }
|