AbstractCompoundFileDetailDetector.cs 69 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169
  1. using System;
  2. using System.Collections.Generic;
  3. using System.IO;
  4. using System.Linq;
  5. using System.Reflection;
  6. using System.Text;
  7. using Masuit.Tools.Mime;
  8. namespace Masuit.Tools.Files.FileDetector;
  9. public abstract class AbstractCompoundFileDetailDetector : AbstractSignatureDetector
  10. {
  11. private static readonly SignatureInformation[] CfSignatureInfo = {
  12. new() { Position = 0, Signature = new byte [] { 0xd0, 0xcf, 0x11, 0xe0, 0xa1, 0xb1, 0x1a, 0xe1 } },
  13. };
  14. /// <inheritdoc />
  15. protected override SignatureInformation[] SignatureInformations => CfSignatureInfo;
  16. public abstract IEnumerable<string> Chunks { get; }
  17. public override string MimeType => new MimeMapper().GetMimeFromExtension("." + Extension);
  18. public override List<FormatCategory> FormatCategories => GetType().GetCustomAttributes<FormatCategoryAttribute>().Select(a => a.Category).ToList();
  19. protected abstract bool IsValidChunk(string chunkName, byte[] chunkData);
  20. public override bool Detect(Stream stream)
  21. {
  22. if (!base.Detect(stream))
  23. {
  24. return false;
  25. }
  26. stream.Position = 0;
  27. try
  28. {
  29. using var cf = new CompoundFile(stream, CFSConfiguration.LeaveOpen | CFSConfiguration.Default);
  30. foreach (var chunk in Chunks)
  31. {
  32. var compoundFileStream = cf.RootStorage.GetStream(chunk);
  33. if (compoundFileStream == null || !IsValidChunk(chunk, compoundFileStream.GetData()))
  34. {
  35. return false;
  36. }
  37. }
  38. return true;
  39. }
  40. catch
  41. {
  42. return false;
  43. }
  44. }
  45. #region Modified OpenMCDF
  46. // -------------------------------------------------------------
  47. // This is a porting from java code, under MIT license of |
  48. // the beautiful Red-Black Tree implementation you can find at |
  49. // http://en.literateprograms.org/Red-black_tree_(Java)#chunk |
  50. // Many Thanks to original Implementors. |
  51. // -------------------------------------------------------------
  52. internal enum Color
  53. { RED = 0, BLACK = 1 }
  54. internal enum NodeOp
  55. {
  56. LAssigned, RAssigned, CAssigned, PAssigned, VAssigned
  57. }
  58. internal interface IRBNode : IComparable
  59. {
  60. IRBNode Left { get; set; }
  61. IRBNode Right { get; set; }
  62. Color Color { get; set; }
  63. IRBNode Parent { get; set; }
  64. IRBNode Grandparent();
  65. IRBNode Sibling();
  66. IRBNode Uncle();
  67. }
  68. internal sealed class RBTree
  69. {
  70. public IRBNode Root { get; set; }
  71. private static Color NodeColor(IRBNode n) => n == null ? Color.BLACK : n.Color;
  72. public RBTree()
  73. { }
  74. public RBTree(IRBNode root)
  75. { Root = root; }
  76. private IRBNode Lookup(IRBNode template)
  77. {
  78. IRBNode n = Root;
  79. while (n != null)
  80. {
  81. int compResult = template.CompareTo(n);
  82. if (compResult == 0) return n;
  83. n = compResult < 0 ? n.Left : n.Right;
  84. }
  85. return n;
  86. }
  87. public bool TryLookup(IRBNode template, out IRBNode val)
  88. {
  89. val = Lookup(template);
  90. return val != null;
  91. }
  92. private void Replace(IRBNode oldn, IRBNode newn)
  93. {
  94. if (oldn.Parent == null) Root = newn;
  95. else
  96. {
  97. if (oldn == oldn.Parent.Left)
  98. {
  99. oldn.Parent.Left = newn;
  100. }
  101. else
  102. {
  103. oldn.Parent.Right = newn;
  104. }
  105. }
  106. if (newn != null) newn.Parent = oldn.Parent;
  107. }
  108. private void RotateL(IRBNode n)
  109. {
  110. IRBNode r = n.Right;
  111. Replace(n, r);
  112. n.Right = r.Left;
  113. if (r.Left != null) r.Left.Parent = n;
  114. r.Left = n;
  115. n.Parent = r;
  116. }
  117. private void RotateR(IRBNode n)
  118. {
  119. IRBNode l = n.Left;
  120. Replace(n, l);
  121. n.Left = l.Right;
  122. if (l.Right != null) l.Right.Parent = n;
  123. l.Right = n;
  124. n.Parent = l;
  125. }
  126. public void Insert(IRBNode newNode)
  127. {
  128. newNode.Color = Color.RED;
  129. IRBNode insertedNode = newNode;
  130. if (Root == null) Root = insertedNode;
  131. else
  132. {
  133. IRBNode n = Root;
  134. while (true)
  135. {
  136. int compResult = newNode.CompareTo(n);
  137. if (compResult == 0) throw new Exception($"RBNode {newNode} already present in tree");
  138. if (compResult < 0)
  139. {
  140. if (n.Left == null)
  141. {
  142. n.Left = insertedNode;
  143. break;
  144. }
  145. n = n.Left;
  146. }
  147. else
  148. {
  149. if (n.Right == null)
  150. {
  151. n.Right = insertedNode;
  152. break;
  153. }
  154. n = n.Right;
  155. }
  156. }
  157. insertedNode.Parent = n;
  158. }
  159. Insert1(insertedNode);
  160. NodeInserted?.Invoke(insertedNode);
  161. }
  162. private void Insert1(IRBNode n)
  163. {
  164. if (n.Parent == null) n.Color = Color.BLACK;
  165. else Insert2(n);
  166. }
  167. private void Insert2(IRBNode n)
  168. {
  169. if (NodeColor(n.Parent) == Color.BLACK) return;
  170. Insert3(n);
  171. }
  172. private void Insert3(IRBNode n)
  173. {
  174. if (NodeColor(n.Uncle()) == Color.RED)
  175. {
  176. n.Parent.Color = Color.BLACK;
  177. n.Uncle().Color = Color.BLACK;
  178. n.Grandparent().Color = Color.RED;
  179. Insert1(n.Grandparent());
  180. }
  181. else Insert4(n);
  182. }
  183. private void Insert4(IRBNode n)
  184. {
  185. if (n == n.Parent.Right && n.Parent == n.Grandparent().Left)
  186. {
  187. RotateL(n.Parent);
  188. n = n.Left;
  189. }
  190. else if (n == n.Parent.Left && n.Parent == n.Grandparent().Right)
  191. {
  192. RotateR(n.Parent);
  193. n = n.Right;
  194. }
  195. Insert5(n);
  196. }
  197. private void Insert5(IRBNode n)
  198. {
  199. n.Parent.Color = Color.BLACK;
  200. n.Grandparent().Color = Color.RED;
  201. if (n == n.Parent.Left && n.Parent == n.Grandparent().Left)
  202. RotateR(n.Grandparent());
  203. else RotateL(n.Grandparent());
  204. }
  205. private static IRBNode MaximumNode(IRBNode n)
  206. {
  207. while (n.Right != null)
  208. n = n.Right;
  209. return n;
  210. }
  211. public void VisitTree(Action<IRBNode> action)
  212. {
  213. IRBNode walker = Root;
  214. if (walker != null)
  215. DoVisitTree(action, walker);
  216. }
  217. private void DoVisitTree(Action<IRBNode> action, IRBNode walker)
  218. {
  219. if (walker.Left != null)
  220. {
  221. DoVisitTree(action, walker.Left);
  222. }
  223. action?.Invoke(walker);
  224. if (walker.Right != null)
  225. {
  226. DoVisitTree(action, walker.Right);
  227. }
  228. }
  229. internal void VisitTreeNodes(Action<IRBNode> action)
  230. {
  231. IRBNode walker = Root;
  232. if (walker != null)
  233. {
  234. DoVisitTreeNodes(action, walker);
  235. }
  236. }
  237. private void DoVisitTreeNodes(Action<IRBNode> action, IRBNode walker)
  238. {
  239. if (walker.Left != null)
  240. {
  241. DoVisitTreeNodes(action, walker.Left);
  242. }
  243. action?.Invoke(walker);
  244. if (walker.Right != null)
  245. {
  246. DoVisitTreeNodes(action, walker.Right);
  247. }
  248. }
  249. public class RBTreeEnumerator : IEnumerator<IRBNode>
  250. {
  251. private int position = -1;
  252. private Queue<IRBNode> heap = new();
  253. internal RBTreeEnumerator(RBTree tree)
  254. { tree.VisitTreeNodes(item => heap.Enqueue(item)); }
  255. public IRBNode Current => heap.ElementAt(position);
  256. public void Dispose()
  257. { }
  258. object System.Collections.IEnumerator.Current => heap.ElementAt(position);
  259. public bool MoveNext() => (++position < heap.Count);
  260. public void Reset()
  261. { position = -1; }
  262. }
  263. public RBTreeEnumerator GetEnumerator() => new RBTreeEnumerator(this);
  264. internal void FireNodeOperation(IRBNode node, NodeOp operation)
  265. {
  266. NodeOperation?.Invoke(node, operation);
  267. }
  268. internal event Action<IRBNode> NodeInserted;
  269. internal event Action<IRBNode, NodeOp> NodeOperation;
  270. }
  271. /* This Source Code Form is subject to the terms of the Mozilla Public
  272. * License, v. 2.0. If a copy of the MPL was not distributed with this
  273. * file, You can obtain one at http://mozilla.org/MPL/2.0/.
  274. *
  275. * The Original Code is OpenMCDF - Compound Document Format library.
  276. *
  277. * The Initial Developer of the Original Code is Federico Blaseotto.*/
  278. internal enum SectorType
  279. {
  280. Normal, Mini, FAT, DIFAT, RangeLockSector, Directory
  281. }
  282. internal sealed class Sector : IDisposable
  283. {
  284. public static int MinisectorSize = 64;
  285. public const int Freesect = unchecked((int)0xFFFFFFFF);
  286. public const int Endofchain = unchecked((int)0xFFFFFFFE);
  287. public const int Fatsect = unchecked((int)0xFFFFFFFD);
  288. public const int Difsect = unchecked((int)0xFFFFFFFC);
  289. public bool DirtyFlag { get; set; }
  290. public bool IsStreamed => _stream != null && Size != MinisectorSize && Id * Size + Size < _stream.Length;
  291. private readonly Stream _stream;
  292. public Sector(int size, Stream stream)
  293. {
  294. this.Size = size;
  295. this._stream = stream;
  296. }
  297. public Sector(int size, byte[] data)
  298. {
  299. this.Size = size;
  300. this._data = data;
  301. this._stream = null;
  302. }
  303. public Sector(int size)
  304. {
  305. this.Size = size;
  306. this._data = null;
  307. this._stream = null;
  308. }
  309. internal SectorType Type { get; set; }
  310. public int Id { get; set; } = -1;
  311. public int Size { get; private set; } = 0;
  312. private byte[] _data;
  313. public byte[] GetData()
  314. {
  315. if (_data == null)
  316. {
  317. _data = new byte[Size];
  318. if (IsStreamed)
  319. {
  320. _stream.Seek(Size + Id * Size, SeekOrigin.Begin);
  321. _stream.Read(_data, 0, Size);
  322. }
  323. }
  324. return _data;
  325. }
  326. public void ZeroData()
  327. {
  328. _data = new byte[Size];
  329. DirtyFlag = true;
  330. }
  331. public void InitFATData()
  332. {
  333. _data = new byte[Size];
  334. for (int i = 0; i < Size; i++)
  335. _data[i] = 0xFF;
  336. DirtyFlag = true;
  337. }
  338. internal void ReleaseData() => this._data = null;
  339. private readonly object _lockObject = new Object();
  340. #region IDisposable Members
  341. private bool _disposed;
  342. void IDisposable.Dispose()
  343. {
  344. try
  345. {
  346. if (!_disposed)
  347. {
  348. lock (_lockObject)
  349. {
  350. this._data = null;
  351. this.DirtyFlag = false;
  352. this.Id = Sector.Endofchain;
  353. this.Size = 0;
  354. }
  355. }
  356. }
  357. finally { _disposed = true; }
  358. GC.SuppressFinalize(this);
  359. }
  360. #endregion IDisposable Members
  361. }
  362. internal enum StgType : int
  363. {
  364. StgInvalid = 0,
  365. StgStorage = 1,
  366. StgStream = 2,
  367. StgLockbytes = 3,
  368. StgProperty = 4,
  369. StgRoot = 5
  370. }
  371. internal sealed class DirectoryEntry : IRBNode
  372. {
  373. internal const int THIS_IS_GREATER = 1;
  374. internal const int OTHER_IS_GREATER = -1;
  375. private IList<DirectoryEntry> dirRepository;
  376. public int SID { get; set; } = -1;
  377. internal const Int32 NOSTREAM = unchecked((int)0xFFFFFFFF);
  378. private DirectoryEntry(String name, StgType stgType, IList<DirectoryEntry> dirRepository)
  379. {
  380. this.dirRepository = dirRepository;
  381. this.StgType = stgType;
  382. switch (stgType)
  383. {
  384. case StgType.StgStream:
  385. StorageCLSID = new Guid(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
  386. CreationDate = new byte[8];
  387. ModifyDate = new byte[8];
  388. break;
  389. case StgType.StgStorage:
  390. CreationDate = BitConverter.GetBytes((DateTime.Now.ToFileTime()));
  391. break;
  392. case StgType.StgRoot:
  393. CreationDate = new byte[8];
  394. ModifyDate = new byte[8];
  395. break;
  396. }
  397. this.SetEntryName(name);
  398. }
  399. public byte[] EntryName { get; private set; } = new byte[64];
  400. public String GetEntryName()
  401. {
  402. if (EntryName != null && EntryName.Length > 0)
  403. return Encoding.Unicode.GetString(EntryName).Remove((NameLength - 1) / 2);
  404. else return String.Empty;
  405. }
  406. public void SetEntryName(String entryName)
  407. {
  408. if (entryName.Contains(@"\") || entryName.Contains(@"/") ||
  409. entryName.Contains(@":") || entryName.Contains(@"!"))
  410. throw new Exception("Invalid character in entry: the characters '\\', '/', ':','!' cannot be used in entry name");
  411. if (entryName.Length > 31)
  412. throw new Exception("Entry name MUST be smaller than 31 characters");
  413. byte[] newName = null;
  414. byte[] temp = Encoding.Unicode.GetBytes(entryName);
  415. newName = new byte[64];
  416. Buffer.BlockCopy(temp, 0, newName, 0, temp.Length);
  417. newName[temp.Length] = 0x00;
  418. newName[temp.Length + 1] = 0x00;
  419. EntryName = newName;
  420. NameLength = (ushort)(temp.Length + 2);
  421. }
  422. public ushort NameLength { get; private set; }
  423. public StgType StgType { get; set; } = StgType.StgInvalid;
  424. public Color Color { get; set; } = Color.BLACK;
  425. public Int32 LeftSibling { get; set; } = NOSTREAM;
  426. public Int32 RightSibling { get; set; } = NOSTREAM;
  427. public Int32 Child { get; set; } = NOSTREAM;
  428. public Guid StorageCLSID { get; set; } = Guid.NewGuid();
  429. public Int32 StateBits { get; set; }
  430. public byte[] CreationDate { get; set; } = new byte[8] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
  431. public byte[] ModifyDate { get; set; } = new byte[8] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
  432. public Int32 StartSetc { get; set; } = Sector.Endofchain;
  433. public long Size { get; set; }
  434. public int CompareTo(object obj)
  435. {
  436. DirectoryEntry otherDir = obj as DirectoryEntry;
  437. if (otherDir == null)
  438. throw new Exception("Invalid casting: compared object does not implement IDirectorEntry interface");
  439. if (this.NameLength > otherDir.NameLength)
  440. return THIS_IS_GREATER;
  441. else if (this.NameLength < otherDir.NameLength)
  442. return OTHER_IS_GREATER;
  443. else
  444. {
  445. String thisName = Encoding.Unicode.GetString(this.EntryName, 0, this.NameLength);
  446. String otherName = Encoding.Unicode.GetString(otherDir.EntryName, 0, otherDir.NameLength);
  447. for (int z = 0; z < thisName.Length; z++)
  448. {
  449. char thisChar = char.ToUpperInvariant(thisName[z]);
  450. char otherChar = char.ToUpperInvariant(otherName[z]);
  451. if (thisChar > otherChar)
  452. return THIS_IS_GREATER;
  453. else if (thisChar < otherChar)
  454. return OTHER_IS_GREATER;
  455. }
  456. return 0;
  457. }
  458. }
  459. public override bool Equals(object obj)
  460. {
  461. return CompareTo(obj) == 0;
  462. }
  463. private static ulong Fnv_hash(byte[] buffer)
  464. {
  465. ulong h = 2166136261;
  466. int i;
  467. for (i = 0; i < buffer.Length; i++)
  468. h = (h * 16777619) ^ buffer[i];
  469. return h;
  470. }
  471. public override int GetHashCode()
  472. {
  473. return (int)Fnv_hash(EntryName);
  474. }
  475. public void Read(Stream stream, int ver = 3)
  476. {
  477. using (BinaryReader rw = new BinaryReader(stream, Encoding.UTF8, true))
  478. {
  479. EntryName = rw.ReadBytes(64);
  480. NameLength = rw.ReadUInt16();
  481. StgType = (StgType)rw.ReadByte();
  482. Color = (Color)rw.ReadByte();
  483. LeftSibling = rw.ReadInt32();
  484. RightSibling = rw.ReadInt32();
  485. Child = rw.ReadInt32();
  486. if (StgType == StgType.StgInvalid)
  487. {
  488. LeftSibling = NOSTREAM;
  489. RightSibling = NOSTREAM;
  490. Child = NOSTREAM;
  491. }
  492. StorageCLSID = new Guid(rw.ReadBytes(16));
  493. StateBits = rw.ReadInt32();
  494. CreationDate = rw.ReadBytes(8);
  495. ModifyDate = rw.ReadBytes(8);
  496. StartSetc = rw.ReadInt32();
  497. if (ver == 3)
  498. {
  499. Size = rw.ReadInt32();
  500. rw.ReadBytes(4);
  501. }
  502. else
  503. Size = rw.ReadInt64();
  504. }
  505. }
  506. public string Name => GetEntryName();
  507. public IRBNode Left
  508. {
  509. get
  510. {
  511. if (LeftSibling == NOSTREAM)
  512. return null;
  513. return dirRepository[LeftSibling];
  514. }
  515. set
  516. {
  517. LeftSibling = value != null ? (value as DirectoryEntry).SID : NOSTREAM;
  518. if (LeftSibling != NOSTREAM)
  519. dirRepository[LeftSibling].Parent = this;
  520. }
  521. }
  522. public IRBNode Right
  523. {
  524. get
  525. {
  526. if (RightSibling == NOSTREAM)
  527. return null;
  528. return dirRepository[RightSibling];
  529. }
  530. set
  531. {
  532. RightSibling = value != null ? ((DirectoryEntry)value).SID : NOSTREAM;
  533. if (RightSibling != NOSTREAM)
  534. dirRepository[RightSibling].Parent = this;
  535. }
  536. }
  537. public IRBNode Parent { get; set; }
  538. public IRBNode Grandparent() => Parent?.Parent;
  539. public IRBNode Sibling() => (this == Parent.Left) ? Parent.Right : Parent.Left;
  540. public IRBNode Uncle() => Parent?.Sibling();
  541. internal static DirectoryEntry New(String name, StgType stgType, IList<DirectoryEntry> dirRepository)
  542. {
  543. DirectoryEntry de = null;
  544. if (dirRepository != null)
  545. {
  546. de = new DirectoryEntry(name, stgType, dirRepository);
  547. dirRepository.Add(de);
  548. de.SID = dirRepository.Count - 1;
  549. }
  550. else
  551. throw new ArgumentNullException("dirRepository", "Directory repository cannot be null in New() method");
  552. return de;
  553. }
  554. internal static DirectoryEntry Mock(String name, StgType stgType) => new DirectoryEntry(name, stgType, null);
  555. internal static DirectoryEntry TryNew(String name, StgType stgType, IList<DirectoryEntry> dirRepository)
  556. {
  557. DirectoryEntry de = new DirectoryEntry(name, stgType, dirRepository);
  558. if (de != null)
  559. {
  560. for (int i = 0; i < dirRepository.Count; i++)
  561. {
  562. if (dirRepository[i].StgType == StgType.StgInvalid)
  563. {
  564. dirRepository[i] = de;
  565. de.SID = i;
  566. return de;
  567. }
  568. }
  569. }
  570. dirRepository.Add(de);
  571. de.SID = dirRepository.Count - 1;
  572. return de;
  573. }
  574. public override string ToString() => $"{Name} [{SID}]{(StgType == StgType.StgStream ? "Stream" : "Storage")}";
  575. public void AssignValueTo(IRBNode other)
  576. {
  577. DirectoryEntry d = other as DirectoryEntry;
  578. d.SetEntryName(GetEntryName());
  579. d.CreationDate = new byte[CreationDate.Length];
  580. CreationDate.CopyTo(d.CreationDate, 0);
  581. d.ModifyDate = new byte[ModifyDate.Length];
  582. ModifyDate.CopyTo(d.ModifyDate, 0);
  583. d.Size = Size;
  584. d.StartSetc = StartSetc;
  585. d.StateBits = StateBits;
  586. d.StgType = StgType;
  587. d.StorageCLSID = new Guid(StorageCLSID.ToByteArray());
  588. d.Child = Child;
  589. }
  590. }
  591. internal abstract class CFItem : IComparable<CFItem>
  592. {
  593. private CompoundFile compoundFile;
  594. protected CompoundFile CompoundFile => compoundFile;
  595. protected void CheckDisposed()
  596. {
  597. if (compoundFile.IsClosed)
  598. throw new ObjectDisposedException("Owner Compound file has been closed and owned items have been invalidated");
  599. }
  600. protected CFItem()
  601. { }
  602. protected CFItem(CompoundFile compoundFile)
  603. { this.compoundFile = compoundFile; }
  604. internal DirectoryEntry DirEntry { get; set; }
  605. internal int CompareTo(CFItem other) => DirEntry.CompareTo(other.DirEntry);
  606. public int CompareTo(object obj) => DirEntry.CompareTo((obj as CFItem).DirEntry);
  607. public static bool operator ==(CFItem leftItem, CFItem rightItem)
  608. {
  609. if (System.Object.ReferenceEquals(leftItem, rightItem))
  610. return true;
  611. if (((object)leftItem == null) || ((object)rightItem == null))
  612. return false;
  613. return leftItem.CompareTo(rightItem) == 0;
  614. }
  615. public static bool operator !=(CFItem leftItem, CFItem rightItem) => !(leftItem == rightItem);
  616. public override bool Equals(object obj) => CompareTo(obj) == 0;
  617. public override int GetHashCode() => DirEntry.GetEntryName().GetHashCode();
  618. public string Name
  619. {
  620. get
  621. {
  622. var n = DirEntry.GetEntryName();
  623. return (n != null && n.Length > 0) ? n.TrimEnd('\0') : string.Empty;
  624. }
  625. }
  626. public long Size => DirEntry.Size;
  627. public bool IsStorage => DirEntry.StgType == StgType.StgStorage;
  628. public bool IsStream => DirEntry.StgType == StgType.StgStream;
  629. public bool IsRoot => DirEntry.StgType == StgType.StgRoot;
  630. public DateTime CreationDate
  631. {
  632. get => DateTime.FromFileTime(BitConverter.ToInt64(DirEntry.CreationDate, 0));
  633. set
  634. {
  635. if (DirEntry.StgType != StgType.StgStream && DirEntry.StgType != StgType.StgRoot)
  636. DirEntry.CreationDate = BitConverter.GetBytes((value.ToFileTime()));
  637. else
  638. throw new Exception("Creation Date can only be set on storage entries");
  639. }
  640. }
  641. public DateTime ModifyDate
  642. {
  643. get => DateTime.FromFileTime(BitConverter.ToInt64(DirEntry.ModifyDate, 0));
  644. set
  645. {
  646. if (DirEntry.StgType != StgType.StgStream && DirEntry.StgType != StgType.StgRoot)
  647. DirEntry.ModifyDate = BitConverter.GetBytes((value.ToFileTime()));
  648. else
  649. throw new Exception("Modify Date can only be set on storage entries");
  650. }
  651. }
  652. public Guid CLSID
  653. {
  654. get => DirEntry.StorageCLSID;
  655. set
  656. {
  657. if (DirEntry.StgType != StgType.StgStream)
  658. DirEntry.StorageCLSID = value;
  659. else
  660. throw new Exception("Object class GUID can only be set on Root and Storage entries");
  661. }
  662. }
  663. int IComparable<CFItem>.CompareTo(CFItem other) => DirEntry.CompareTo(other.DirEntry);
  664. public override string ToString()
  665. {
  666. return (DirEntry != null)
  667. ? $"[{DirEntry.LeftSibling},{DirEntry.SID},{DirEntry.RightSibling}] {DirEntry.GetEntryName()}"
  668. : string.Empty;
  669. }
  670. }
  671. internal sealed class CFStream : CFItem
  672. {
  673. internal CFStream(CompoundFile compoundFile, DirectoryEntry dirEntry)
  674. : base(compoundFile)
  675. {
  676. if (dirEntry == null || dirEntry.SID < 0)
  677. throw new Exception("Attempting to add a CFStream using an unitialized directory");
  678. this.DirEntry = dirEntry;
  679. }
  680. public Byte[] GetData()
  681. {
  682. CheckDisposed();
  683. return this.CompoundFile.GetData(this);
  684. }
  685. public int Read(byte[] buffer, long position, int count)
  686. {
  687. CheckDisposed();
  688. return this.CompoundFile.ReadData(this, position, buffer, 0, count);
  689. }
  690. internal int Read(byte[] buffer, long position, int offset, int count)
  691. {
  692. CheckDisposed();
  693. return this.CompoundFile.ReadData(this, position, buffer, offset, count);
  694. }
  695. }
  696. internal sealed class CFStorage : CFItem
  697. {
  698. private RBTree children;
  699. internal RBTree Children
  700. {
  701. get
  702. {
  703. if (children == null)
  704. children = LoadChildren(this.DirEntry.SID) ?? this.CompoundFile.CreateNewTree();
  705. return children;
  706. }
  707. }
  708. internal CFStorage(CompoundFile compFile, DirectoryEntry dirEntry)
  709. : base(compFile)
  710. {
  711. if (dirEntry == null || dirEntry.SID < 0)
  712. throw new Exception("Attempting to create a CFStorage using an unitialized directory");
  713. this.DirEntry = dirEntry;
  714. }
  715. private RBTree LoadChildren(int SID)
  716. {
  717. RBTree childrenTree = this.CompoundFile.GetChildrenTree(SID);
  718. if (childrenTree.Root != null)
  719. this.DirEntry.Child = (childrenTree.Root as DirectoryEntry).SID;
  720. else
  721. this.DirEntry.Child = DirectoryEntry.NOSTREAM;
  722. return childrenTree;
  723. }
  724. public CFStream GetStream(String streamName)
  725. {
  726. CheckDisposed();
  727. DirectoryEntry tmp = DirectoryEntry.Mock(streamName, StgType.StgStream);
  728. if (Children.TryLookup(tmp, out IRBNode outDe) && (((DirectoryEntry)outDe).StgType == StgType.StgStream))
  729. return new CFStream(this.CompoundFile, (DirectoryEntry)outDe);
  730. else
  731. throw new KeyNotFoundException("Cannot find item [" + streamName + "] within the current storage");
  732. }
  733. public CFStream TryGetStream(String streamName)
  734. {
  735. CheckDisposed();
  736. DirectoryEntry tmp = DirectoryEntry.Mock(streamName, StgType.StgStream);
  737. if (Children.TryLookup(tmp, out IRBNode outDe) && ((outDe as DirectoryEntry).StgType == StgType.StgStream))
  738. return new CFStream(this.CompoundFile, (DirectoryEntry)outDe);
  739. else
  740. return null;
  741. }
  742. public CFStorage GetStorage(String storageName)
  743. {
  744. CheckDisposed();
  745. DirectoryEntry template = DirectoryEntry.Mock(storageName, StgType.StgInvalid);
  746. if (Children.TryLookup(template, out IRBNode outDe) && (outDe as DirectoryEntry).StgType == StgType.StgStorage)
  747. return new CFStorage(this.CompoundFile, outDe as DirectoryEntry);
  748. else
  749. throw new KeyNotFoundException("Cannot find item [" + storageName + "] within the current storage");
  750. }
  751. public CFStorage TryGetStorage(String storageName)
  752. {
  753. CheckDisposed();
  754. DirectoryEntry template = DirectoryEntry.Mock(storageName, StgType.StgInvalid);
  755. if (Children.TryLookup(template, out IRBNode outDe) && ((DirectoryEntry)outDe).StgType == StgType.StgStorage)
  756. return new CFStorage(this.CompoundFile, outDe as DirectoryEntry);
  757. else
  758. return null;
  759. }
  760. public void VisitEntries(Action<CFItem> action, bool recursive)
  761. {
  762. CheckDisposed();
  763. if (action != null)
  764. {
  765. List<IRBNode> subStorages = new List<IRBNode>();
  766. void internalAction(IRBNode targetNode)
  767. {
  768. DirectoryEntry d = targetNode as DirectoryEntry;
  769. if (d.StgType == StgType.StgStream)
  770. action(new CFStream(this.CompoundFile, d));
  771. else
  772. action(new CFStorage(this.CompoundFile, d));
  773. if (d.Child != DirectoryEntry.NOSTREAM)
  774. subStorages.Add(targetNode);
  775. return;
  776. }
  777. this.Children.VisitTreeNodes(internalAction);
  778. if (recursive && subStorages.Count > 0)
  779. foreach (IRBNode n in subStorages)
  780. (new CFStorage(this.CompoundFile, n as DirectoryEntry)).VisitEntries(action, recursive);
  781. }
  782. }
  783. }
  784. internal class CFItemComparer : IComparer<CFItem>
  785. {
  786. public int Compare(CFItem x, CFItem y) => (x.DirEntry.CompareTo(y.DirEntry));
  787. }
  788. internal sealed class Header
  789. {
  790. public byte[] HeaderSignature { get; private set; } = new byte[] { 0xD0, 0xCF, 0x11, 0xE0, 0xA1, 0xB1, 0x1A, 0xE1 };
  791. public byte[] CLSID { get; set; } = new byte[16];
  792. public ushort MinorVersion { get; private set; } = 0x003E;
  793. public ushort MajorVersion { get; private set; } = 0x0003;
  794. public ushort ByteOrder { get; private set; } = 0xFFFE;
  795. public ushort SectorShift { get; private set; } = 9;
  796. public ushort MiniSectorShift { get; private set; } = 6;
  797. public int DirectorySectorsNumber { get; set; }
  798. public int FATSectorsNumber { get; set; }
  799. public int FirstDirectorySectorID { get; set; } = Sector.Endofchain;
  800. public uint MinSizeStandardStream { get; set; } = 4096;
  801. public int FirstMiniFATSectorID { get; set; } = unchecked((int)0xFFFFFFFE);
  802. public uint MiniFATSectorsNumber { get; set; }
  803. public int FirstDIFATSectorID { get; set; } = Sector.Endofchain;
  804. public uint DIFATSectorsNumber { get; set; }
  805. public int[] DIFAT { get; private set; } = new int[109];
  806. public Header() : this(3)
  807. {
  808. }
  809. public Header(ushort version)
  810. {
  811. switch (version)
  812. {
  813. case 3:
  814. MajorVersion = 3;
  815. SectorShift = 0x0009;
  816. break;
  817. case 4:
  818. MajorVersion = 4;
  819. SectorShift = 0x000C;
  820. break;
  821. default:
  822. throw new Exception("Invalid Compound File Format version");
  823. }
  824. for (int i = 0; i < 109; i++)
  825. DIFAT[i] = Sector.Freesect;
  826. }
  827. public void Read(Stream stream)
  828. {
  829. using (BinaryReader rw = new BinaryReader(stream, Encoding.UTF8, true))
  830. {
  831. HeaderSignature = rw.ReadBytes(8);
  832. CheckSignature();
  833. CLSID = rw.ReadBytes(16);
  834. MinorVersion = rw.ReadUInt16();
  835. MajorVersion = rw.ReadUInt16();
  836. CheckVersion();
  837. ByteOrder = rw.ReadUInt16();
  838. SectorShift = rw.ReadUInt16();
  839. MiniSectorShift = rw.ReadUInt16();
  840. rw.ReadBytes(6);
  841. DirectorySectorsNumber = rw.ReadInt32();
  842. FATSectorsNumber = rw.ReadInt32();
  843. FirstDirectorySectorID = rw.ReadInt32();
  844. rw.ReadUInt32();
  845. MinSizeStandardStream = rw.ReadUInt32();
  846. FirstMiniFATSectorID = rw.ReadInt32();
  847. MiniFATSectorsNumber = rw.ReadUInt32();
  848. FirstDIFATSectorID = rw.ReadInt32();
  849. DIFATSectorsNumber = rw.ReadUInt32();
  850. for (int i = 0; i < 109; i++)
  851. DIFAT[i] = rw.ReadInt32();
  852. }
  853. }
  854. private void CheckVersion()
  855. {
  856. if (MajorVersion != 3 && MajorVersion != 4)
  857. throw new InvalidDataException("Unsupported Binary File Format version: OpenMcdf only supports Compound Files with major version equal to 3 or 4 ");
  858. }
  859. private byte[] OLE_CFS_SIGNATURE = new byte[] { 0xD0, 0xCF, 0x11, 0xE0, 0xA1, 0xB1, 0x1A, 0xE1 };
  860. private void CheckSignature()
  861. {
  862. for (int i = 0; i < HeaderSignature.Length; i++)
  863. if (HeaderSignature[i] != OLE_CFS_SIGNATURE[i])
  864. throw new InvalidDataException("Invalid OLE structured storage file");
  865. }
  866. }
  867. internal sealed class StreamView : Stream
  868. {
  869. private readonly int _sectorSize;
  870. private long _position;
  871. private readonly List<Sector> _sectorChain;
  872. private readonly Stream _stream;
  873. private readonly bool _isFatStream = false;
  874. private readonly List<Sector> _freeSectors = new List<Sector>();
  875. public IEnumerable<Sector> FreeSectors => _freeSectors;
  876. public StreamView(List<Sector> sectorChain, int sectorSize, Stream stream)
  877. {
  878. if (sectorSize <= 0)
  879. throw new Exception("Sector size must be greater than zero");
  880. this._sectorChain = sectorChain ?? throw new Exception("Sector Chain cannot be null");
  881. this._sectorSize = sectorSize;
  882. this._stream = stream;
  883. }
  884. public StreamView(List<Sector> sectorChain, int sectorSize, long length, Queue<Sector> availableSectors, Stream stream, bool isFatStream = false)
  885. : this(sectorChain, sectorSize, stream)
  886. {
  887. this._isFatStream = isFatStream;
  888. AdjustLength(length, availableSectors);
  889. }
  890. public List<Sector> BaseSectorChain => _sectorChain;
  891. public override bool CanRead => true;
  892. public override bool CanSeek => true;
  893. public override bool CanWrite => true;
  894. public override void Flush()
  895. { }
  896. private long _length;
  897. public override long Length => _length;
  898. public override long Position
  899. {
  900. get => _position;
  901. set
  902. {
  903. if (_position > _length - 1)
  904. throw new ArgumentOutOfRangeException("value");
  905. _position = value;
  906. }
  907. }
  908. private byte[] buf = new byte[4];
  909. public int ReadInt32()
  910. {
  911. this.Read(buf, 0, 4);
  912. return (((buf[0] | (buf[1] << 8)) | (buf[2] << 16)) | (buf[3] << 24));
  913. }
  914. public override int Read(byte[] buffer, int offset, int count)
  915. {
  916. int nRead = 0;
  917. int nToRead = 0;
  918. if (_sectorChain != null && _sectorChain.Count > 0)
  919. {
  920. // First sector
  921. int secIndex = (int)(_position / _sectorSize);
  922. nToRead = Math.Min(_sectorChain[0].Size - ((int)_position % _sectorSize), count);
  923. if (secIndex < _sectorChain.Count)
  924. {
  925. Buffer.BlockCopy(_sectorChain[secIndex].GetData(),
  926. (int)(_position % _sectorSize), buffer, offset, nToRead);
  927. }
  928. nRead += nToRead;
  929. ++secIndex;
  930. // Central sectors
  931. while (nRead < (count - _sectorSize))
  932. {
  933. nToRead = _sectorSize;
  934. Buffer.BlockCopy(_sectorChain[secIndex].GetData(), 0, buffer, offset + nRead, nToRead);
  935. nRead += nToRead;
  936. ++secIndex;
  937. }
  938. // Last sector
  939. nToRead = count - nRead;
  940. if (nToRead != 0)
  941. {
  942. Buffer.BlockCopy(_sectorChain[secIndex].GetData(), 0, buffer, offset + nRead, nToRead);
  943. nRead += nToRead;
  944. }
  945. _position += nRead;
  946. return nRead;
  947. }
  948. else
  949. return 0;
  950. }
  951. public override long Seek(long offset, SeekOrigin origin)
  952. {
  953. switch (origin)
  954. {
  955. case SeekOrigin.Begin: _position = offset; break;
  956. case SeekOrigin.Current: _position += offset; break;
  957. case SeekOrigin.End: _position = Length - offset; break;
  958. }
  959. AdjustLength(_position);
  960. return _position;
  961. }
  962. private void AdjustLength(long value, Queue<Sector> availableSectors = null)
  963. {
  964. this._length = value;
  965. long delta = value - (_sectorChain.Count * (long)_sectorSize);
  966. if (delta > 0)
  967. {
  968. int nSec = (int)Math.Ceiling(((double)delta / _sectorSize));
  969. while (nSec > 0)
  970. {
  971. Sector t = null;
  972. if (availableSectors == null || availableSectors.Count == 0)
  973. {
  974. t = new Sector(_sectorSize, _stream);
  975. if (_sectorSize == Sector.MinisectorSize)
  976. t.Type = SectorType.Mini;
  977. }
  978. else
  979. t = availableSectors.Dequeue();
  980. if (_isFatStream)
  981. t.InitFATData();
  982. _sectorChain.Add(t);
  983. nSec--;
  984. }
  985. }
  986. }
  987. public override void SetLength(long value) => AdjustLength(value);
  988. public override void Write(byte[] buffer, int offset, int count) => throw new NotImplementedException();
  989. }
  990. [Flags]
  991. internal enum CFSConfiguration
  992. {
  993. Default = 1,
  994. LeaveOpen = 16,
  995. }
  996. internal sealed class CompoundFile : IDisposable
  997. {
  998. public CFSConfiguration Configuration { get; private set; } = CFSConfiguration.Default;
  999. internal int GetSectorSize() => 2 << (header.SectorShift - 1);
  1000. private const int HEADER_DIFAT_ENTRIES_COUNT = 109;
  1001. private readonly int DIFAT_SECTOR_FAT_ENTRIES_COUNT = 127;
  1002. private readonly int FAT_SECTOR_ENTRIES_COUNT = 128;
  1003. private const int SIZE_OF_SID = 4;
  1004. private const int FLUSHING_QUEUE_SIZE = 6000;
  1005. private const int FLUSHING_BUFFER_MAX_SIZE = 1024 * 1024 * 16;
  1006. private List<Sector> sectors = new List<Sector>();
  1007. private Header header;
  1008. internal Stream sourceStream = null;
  1009. public CompoundFile(Stream stream, CFSConfiguration configParameters)
  1010. {
  1011. this.closeStream = !configParameters.HasFlag(CFSConfiguration.LeaveOpen);
  1012. LoadStream(stream);
  1013. DIFAT_SECTOR_FAT_ENTRIES_COUNT = (GetSectorSize() / 4) - 1;
  1014. FAT_SECTOR_ENTRIES_COUNT = (GetSectorSize() / 4);
  1015. }
  1016. private string fileName = string.Empty;
  1017. private void Load(Stream stream)
  1018. {
  1019. try
  1020. {
  1021. this.header = new Header();
  1022. this.directoryEntries = new List<DirectoryEntry>();
  1023. this.sourceStream = stream;
  1024. header.Read(stream);
  1025. int n_sector = Ceiling(((stream.Length - GetSectorSize()) / (double)GetSectorSize()));
  1026. if (stream.Length > 0x7FFFFF0)
  1027. this._transactionLockAllocated = true;
  1028. sectors = new List<Sector>();
  1029. for (int i = 0; i < n_sector; i++)
  1030. sectors.Add(null);
  1031. LoadDirectories();
  1032. this.RootStorage = new CFStorage(this, directoryEntries[0]);
  1033. }
  1034. catch (Exception)
  1035. {
  1036. if (stream != null && closeStream)
  1037. stream.Dispose();
  1038. throw;
  1039. }
  1040. }
  1041. private void LoadFile(String fileName)
  1042. {
  1043. this.fileName = fileName;
  1044. FileStream fs = null;
  1045. try
  1046. {
  1047. fs = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
  1048. Load(fs);
  1049. }
  1050. catch
  1051. {
  1052. if (fs != null)
  1053. fs.Dispose();
  1054. throw;
  1055. }
  1056. }
  1057. private void LoadStream(Stream stream)
  1058. {
  1059. if (stream == null)
  1060. throw new Exception("Stream parameter cannot be null");
  1061. if (!stream.CanSeek)
  1062. throw new Exception("Cannot load a non-seekable Stream");
  1063. stream.Seek(0, SeekOrigin.Begin);
  1064. Load(stream);
  1065. }
  1066. public bool HasSourceStream => sourceStream != null;
  1067. private void PersistMiniStreamToStream(List<Sector> miniSectorChain)
  1068. {
  1069. List<Sector> miniStream = GetSectorChain(RootEntry.StartSetc, SectorType.Normal);
  1070. StreamView miniStreamView = new StreamView(miniStream, GetSectorSize(), this.RootStorage.Size, null, sourceStream);
  1071. for (int i = 0; i < miniSectorChain.Count; i++)
  1072. {
  1073. Sector s = miniSectorChain[i];
  1074. if (s.Id == -1)
  1075. throw new Exception("Invalid minisector index");
  1076. miniStreamView.Seek(Sector.MinisectorSize * s.Id, SeekOrigin.Begin);
  1077. miniStreamView.Write(s.GetData(), 0, Sector.MinisectorSize);
  1078. }
  1079. }
  1080. private void AllocateMiniSectorChain(List<Sector> sectorChain)
  1081. {
  1082. List<Sector> miniFAT = GetSectorChain(header.FirstMiniFATSectorID, SectorType.Normal);
  1083. List<Sector> miniStream = GetSectorChain(RootEntry.StartSetc, SectorType.Normal);
  1084. StreamView miniFATView = new StreamView(miniFAT, GetSectorSize(),
  1085. header.MiniFATSectorsNumber * Sector.MinisectorSize,
  1086. null, this.sourceStream, true);
  1087. StreamView miniStreamView = new StreamView(miniStream, GetSectorSize(),
  1088. this.RootStorage.Size, null, sourceStream);
  1089. for (int i = 0; i < sectorChain.Count; i++)
  1090. {
  1091. Sector s = sectorChain[i];
  1092. if (s.Id == -1)
  1093. {
  1094. miniStreamView.Seek(this.RootStorage.Size + Sector.MinisectorSize, SeekOrigin.Begin);
  1095. s.Id = (int)(miniStreamView.Position - Sector.MinisectorSize) / Sector.MinisectorSize;
  1096. this.RootStorage.DirEntry.Size = miniStreamView.Length;
  1097. }
  1098. }
  1099. for (int i = 0; i < sectorChain.Count - 1; i++)
  1100. {
  1101. Int32 currentId = sectorChain[i].Id;
  1102. Int32 nextId = sectorChain[i + 1].Id;
  1103. miniFATView.Seek(currentId * 4, SeekOrigin.Begin);
  1104. miniFATView.Write(BitConverter.GetBytes(nextId), 0, 4);
  1105. }
  1106. miniFATView.Seek(sectorChain[sectorChain.Count - 1].Id * SIZE_OF_SID, SeekOrigin.Begin);
  1107. miniFATView.Write(BitConverter.GetBytes(Sector.Endofchain), 0, 4);
  1108. AllocateSectorChain(miniStreamView.BaseSectorChain);
  1109. AllocateSectorChain(miniFATView.BaseSectorChain);
  1110. if (miniFAT.Count > 0)
  1111. {
  1112. this.RootStorage.DirEntry.StartSetc = miniStream[0].Id;
  1113. header.MiniFATSectorsNumber = (uint)miniFAT.Count;
  1114. header.FirstMiniFATSectorID = miniFAT[0].Id;
  1115. }
  1116. }
  1117. private void SetSectorChain(List<Sector> sectorChain)
  1118. {
  1119. if (sectorChain == null || sectorChain.Count == 0)
  1120. return;
  1121. SectorType _st = sectorChain[0].Type;
  1122. if (_st == SectorType.Normal)
  1123. AllocateSectorChain(sectorChain);
  1124. else if (_st == SectorType.Mini)
  1125. AllocateMiniSectorChain(sectorChain);
  1126. }
  1127. private void AllocateSectorChain(List<Sector> sectorChain)
  1128. {
  1129. foreach (Sector s in sectorChain)
  1130. {
  1131. if (s.Id == -1)
  1132. {
  1133. sectors.Add(s);
  1134. s.Id = sectors.Count - 1;
  1135. }
  1136. }
  1137. AllocateFATSectorChain(sectorChain);
  1138. }
  1139. internal bool _transactionLockAdded = false;
  1140. internal int _lockSectorId = -1;
  1141. internal bool _transactionLockAllocated = false;
  1142. private void CheckForLockSector()
  1143. {
  1144. if (_transactionLockAdded && !_transactionLockAllocated)
  1145. {
  1146. StreamView fatStream = new StreamView(GetFatSectorChain(), GetSectorSize(), sourceStream);
  1147. fatStream.Seek(_lockSectorId * 4, SeekOrigin.Begin);
  1148. fatStream.Write(BitConverter.GetBytes(Sector.Endofchain), 0, 4);
  1149. _transactionLockAllocated = true;
  1150. }
  1151. }
  1152. private void AllocateFATSectorChain(List<Sector> sectorChain)
  1153. {
  1154. List<Sector> fatSectors = GetSectorChain(-1, SectorType.FAT);
  1155. StreamView fatStream = new StreamView(fatSectors, GetSectorSize(),
  1156. header.FATSectorsNumber * GetSectorSize(), null,
  1157. sourceStream, true);
  1158. for (int i = 0; i < sectorChain.Count - 1; i++)
  1159. {
  1160. Sector sN = sectorChain[i + 1];
  1161. Sector sC = sectorChain[i];
  1162. fatStream.Seek(sC.Id * 4, SeekOrigin.Begin);
  1163. fatStream.Write(BitConverter.GetBytes(sN.Id), 0, 4);
  1164. }
  1165. fatStream.Seek(sectorChain[sectorChain.Count - 1].Id * 4, SeekOrigin.Begin);
  1166. fatStream.Write(BitConverter.GetBytes(Sector.Endofchain), 0, 4);
  1167. AllocateDIFATSectorChain(fatStream.BaseSectorChain);
  1168. }
  1169. private void AllocateDIFATSectorChain(List<Sector> FATsectorChain)
  1170. {
  1171. header.FATSectorsNumber = FATsectorChain.Count;
  1172. foreach (Sector s in FATsectorChain)
  1173. {
  1174. if (s.Id == -1)
  1175. {
  1176. sectors.Add(s);
  1177. s.Id = sectors.Count - 1;
  1178. s.Type = SectorType.FAT;
  1179. }
  1180. }
  1181. int nCurrentSectors = sectors.Count;
  1182. int nDIFATSectors = (int)header.DIFATSectorsNumber;
  1183. if (FATsectorChain.Count > HEADER_DIFAT_ENTRIES_COUNT)
  1184. {
  1185. nDIFATSectors = Ceiling((double)(FATsectorChain.Count - HEADER_DIFAT_ENTRIES_COUNT) / DIFAT_SECTOR_FAT_ENTRIES_COUNT);
  1186. nDIFATSectors = LowSaturation(nDIFATSectors - (int)header.DIFATSectorsNumber); //required DIFAT
  1187. }
  1188. nCurrentSectors += nDIFATSectors;
  1189. while (header.FATSectorsNumber * FAT_SECTOR_ENTRIES_COUNT < nCurrentSectors)
  1190. {
  1191. Sector extraFATSector = new Sector(GetSectorSize(), sourceStream);
  1192. sectors.Add(extraFATSector);
  1193. extraFATSector.Id = sectors.Count - 1;
  1194. extraFATSector.Type = SectorType.FAT;
  1195. FATsectorChain.Add(extraFATSector);
  1196. header.FATSectorsNumber++;
  1197. nCurrentSectors++;
  1198. if (nDIFATSectors * DIFAT_SECTOR_FAT_ENTRIES_COUNT <
  1199. (header.FATSectorsNumber > HEADER_DIFAT_ENTRIES_COUNT ?
  1200. header.FATSectorsNumber - HEADER_DIFAT_ENTRIES_COUNT :
  1201. 0))
  1202. {
  1203. nDIFATSectors++;
  1204. nCurrentSectors++;
  1205. }
  1206. }
  1207. List<Sector> difatSectors = GetSectorChain(-1, SectorType.DIFAT);
  1208. StreamView difatStream = new StreamView(difatSectors, GetSectorSize(), sourceStream);
  1209. for (int i = 0; i < FATsectorChain.Count; i++)
  1210. {
  1211. if (i < HEADER_DIFAT_ENTRIES_COUNT)
  1212. header.DIFAT[i] = FATsectorChain[i].Id;
  1213. else
  1214. {
  1215. if (i != HEADER_DIFAT_ENTRIES_COUNT && (i - HEADER_DIFAT_ENTRIES_COUNT) % DIFAT_SECTOR_FAT_ENTRIES_COUNT == 0)
  1216. {
  1217. difatStream.Write(new byte[sizeof(int)], 0, sizeof(int));
  1218. }
  1219. difatStream.Write(BitConverter.GetBytes(FATsectorChain[i].Id), 0, sizeof(int));
  1220. }
  1221. }
  1222. for (int i = 0; i < difatStream.BaseSectorChain.Count; i++)
  1223. {
  1224. if (difatStream.BaseSectorChain[i].Id == -1)
  1225. {
  1226. sectors.Add(difatStream.BaseSectorChain[i]);
  1227. difatStream.BaseSectorChain[i].Id = sectors.Count - 1;
  1228. difatStream.BaseSectorChain[i].Type = SectorType.DIFAT;
  1229. }
  1230. }
  1231. header.DIFATSectorsNumber = (uint)nDIFATSectors;
  1232. if (difatStream.BaseSectorChain != null && difatStream.BaseSectorChain.Count > 0)
  1233. {
  1234. header.FirstDIFATSectorID = difatStream.BaseSectorChain[0].Id;
  1235. header.DIFATSectorsNumber = (uint)difatStream.BaseSectorChain.Count;
  1236. for (int i = 0; i < difatStream.BaseSectorChain.Count - 1; i++)
  1237. Buffer.BlockCopy(BitConverter.GetBytes(difatStream.BaseSectorChain[i + 1].Id),
  1238. 0, difatStream.BaseSectorChain[i].GetData(),
  1239. GetSectorSize() - sizeof(int), 4);
  1240. Buffer.BlockCopy(BitConverter.GetBytes(Sector.Endofchain), 0,
  1241. difatStream.BaseSectorChain[difatStream.BaseSectorChain.Count - 1].GetData(),
  1242. GetSectorSize() - sizeof(int), sizeof(int));
  1243. }
  1244. else header.FirstDIFATSectorID = Sector.Endofchain;
  1245. StreamView fatSv = new StreamView(FATsectorChain, GetSectorSize(), header.FATSectorsNumber * GetSectorSize(), null, sourceStream);
  1246. for (int i = 0; i < header.DIFATSectorsNumber; i++)
  1247. {
  1248. fatSv.Seek(difatStream.BaseSectorChain[i].Id * 4, SeekOrigin.Begin);
  1249. fatSv.Write(BitConverter.GetBytes(Sector.Difsect), 0, 4);
  1250. }
  1251. for (int i = 0; i < header.FATSectorsNumber; i++)
  1252. {
  1253. fatSv.Seek(fatSv.BaseSectorChain[i].Id * 4, SeekOrigin.Begin);
  1254. fatSv.Write(BitConverter.GetBytes(Sector.Fatsect), 0, 4);
  1255. }
  1256. header.FATSectorsNumber = fatSv.BaseSectorChain.Count;
  1257. }
  1258. private List<Sector> GetDifatSectorChain()
  1259. {
  1260. int validationCount = 0;
  1261. List<Sector> result = new List<Sector>();
  1262. int nextSecID
  1263. = Sector.Endofchain;
  1264. if (header.DIFATSectorsNumber != 0)
  1265. {
  1266. validationCount = (int)header.DIFATSectorsNumber;
  1267. Sector s = sectors[header.FirstDIFATSectorID];
  1268. if (s == null)
  1269. {
  1270. sectors[header.FirstDIFATSectorID] = s = new Sector(GetSectorSize(), sourceStream)
  1271. {
  1272. Type = SectorType.DIFAT,
  1273. Id = header.FirstDIFATSectorID
  1274. };
  1275. }
  1276. result.Add(s);
  1277. while (true && validationCount >= 0)
  1278. {
  1279. nextSecID = BitConverter.ToInt32(s.GetData(), GetSectorSize() - 4);
  1280. if (nextSecID == Sector.Freesect || nextSecID == Sector.Endofchain) break;
  1281. validationCount--;
  1282. if (validationCount < 0)
  1283. {
  1284. Dispose();
  1285. throw new InvalidDataException("DIFAT sectors count mismatched. Corrupted compound file");
  1286. }
  1287. s = sectors[nextSecID];
  1288. if (s == null)
  1289. sectors[nextSecID] = s = new Sector(GetSectorSize(), sourceStream) { Id = nextSecID };
  1290. result.Add(s);
  1291. }
  1292. }
  1293. return result;
  1294. }
  1295. private List<Sector> GetFatSectorChain()
  1296. {
  1297. int N_HEADER_FAT_ENTRY = 109;
  1298. List<Sector> result = new List<Sector>();
  1299. int nextSecID = Sector.Endofchain;
  1300. List<Sector> difatSectors = GetDifatSectorChain();
  1301. int idx = 0;
  1302. while (idx < header.FATSectorsNumber && idx < N_HEADER_FAT_ENTRY)
  1303. {
  1304. nextSecID = header.DIFAT[idx];
  1305. Sector s = sectors[nextSecID];
  1306. if (s == null)
  1307. {
  1308. sectors[nextSecID] = s = new Sector(GetSectorSize(), sourceStream)
  1309. {
  1310. Id = nextSecID,
  1311. Type = SectorType.FAT
  1312. };
  1313. }
  1314. result.Add(s);
  1315. ++idx;
  1316. }
  1317. if (difatSectors.Count > 0)
  1318. {
  1319. var difatStream = new StreamView(difatSectors, GetSectorSize(),
  1320. header.FATSectorsNumber > N_HEADER_FAT_ENTRY ? (header.FATSectorsNumber - N_HEADER_FAT_ENTRY) * 4 : 0,
  1321. null, sourceStream);
  1322. byte[] nextDIFATSectorBuffer = new byte[4];
  1323. difatStream.Read(nextDIFATSectorBuffer, 0, 4);
  1324. nextSecID = BitConverter.ToInt32(nextDIFATSectorBuffer, 0);
  1325. int i = 0;
  1326. int nFat = N_HEADER_FAT_ENTRY;
  1327. while (nFat < header.FATSectorsNumber)
  1328. {
  1329. if (difatStream.Position == ((GetSectorSize() - 4) + i * GetSectorSize()))
  1330. {
  1331. difatStream.Seek(4, SeekOrigin.Current);
  1332. ++i;
  1333. continue;
  1334. }
  1335. Sector s = sectors[nextSecID];
  1336. if (s == null)
  1337. {
  1338. sectors[nextSecID] = s = new Sector(GetSectorSize(), sourceStream)
  1339. {
  1340. Type = SectorType.FAT,
  1341. Id = nextSecID
  1342. };
  1343. }
  1344. result.Add(s);
  1345. difatStream.Read(nextDIFATSectorBuffer, 0, 4);
  1346. nextSecID = BitConverter.ToInt32(nextDIFATSectorBuffer, 0);
  1347. nFat++;
  1348. }
  1349. }
  1350. return result;
  1351. }
  1352. private List<Sector> GetNormalSectorChain(int secID)
  1353. {
  1354. List<Sector> result = new List<Sector>();
  1355. int nextSecID = secID;
  1356. List<Sector> fatSectors = GetFatSectorChain();
  1357. var fatStream = new StreamView(fatSectors, GetSectorSize(), fatSectors.Count * GetSectorSize(), null, sourceStream);
  1358. while (true)
  1359. {
  1360. if (nextSecID == Sector.Endofchain) break;
  1361. if (nextSecID < 0)
  1362. throw new InvalidDataException(String.Format("Next Sector ID reference is below zero. NextID : {0}", nextSecID));
  1363. if (nextSecID >= sectors.Count)
  1364. throw new InvalidDataException(String.Format("Next Sector ID reference an out of range sector. NextID : {0} while sector count {1}", nextSecID, sectors.Count));
  1365. Sector s = sectors[nextSecID];
  1366. if (s == null)
  1367. {
  1368. sectors[nextSecID] = s = new Sector(GetSectorSize(), sourceStream)
  1369. {
  1370. Id = nextSecID,
  1371. Type = SectorType.Normal
  1372. };
  1373. }
  1374. result.Add(s);
  1375. fatStream.Seek(nextSecID * 4, SeekOrigin.Begin);
  1376. int next = fatStream.ReadInt32();
  1377. if (next != nextSecID)
  1378. nextSecID = next;
  1379. else
  1380. throw new InvalidDataException("Cyclic sector chain found. File is corrupted");
  1381. }
  1382. return result;
  1383. }
  1384. private List<Sector> GetMiniSectorChain(int secID)
  1385. {
  1386. List<Sector> result = new List<Sector>();
  1387. if (secID != Sector.Endofchain)
  1388. {
  1389. int nextSecID = secID;
  1390. List<Sector> miniFAT = GetNormalSectorChain(header.FirstMiniFATSectorID);
  1391. List<Sector> miniStream = GetNormalSectorChain(RootEntry.StartSetc);
  1392. StreamView miniFATView = new StreamView(miniFAT, GetSectorSize(), header.MiniFATSectorsNumber * Sector.MinisectorSize, null, sourceStream);
  1393. StreamView miniStreamView = new StreamView(miniStream, GetSectorSize(), RootStorage.Size, null, sourceStream);
  1394. BinaryReader miniFATReader = new BinaryReader(miniFATView);
  1395. nextSecID = secID;
  1396. while (true)
  1397. {
  1398. if (nextSecID == Sector.Endofchain)
  1399. break;
  1400. Sector ms = new Sector(Sector.MinisectorSize, sourceStream);
  1401. byte[] temp = new byte[Sector.MinisectorSize];
  1402. ms.Id = nextSecID;
  1403. ms.Type = SectorType.Mini;
  1404. miniStreamView.Seek(nextSecID * Sector.MinisectorSize, SeekOrigin.Begin);
  1405. miniStreamView.Read(ms.GetData(), 0, Sector.MinisectorSize);
  1406. result.Add(ms);
  1407. miniFATView.Seek(nextSecID * 4, SeekOrigin.Begin);
  1408. nextSecID = miniFATReader.ReadInt32();
  1409. }
  1410. }
  1411. return result;
  1412. }
  1413. internal List<Sector> GetSectorChain(int secID, SectorType chainType)
  1414. {
  1415. switch (chainType)
  1416. {
  1417. case SectorType.DIFAT:
  1418. return GetDifatSectorChain();
  1419. case SectorType.FAT:
  1420. return GetFatSectorChain();
  1421. case SectorType.Normal:
  1422. return GetNormalSectorChain(secID);
  1423. case SectorType.Mini:
  1424. return GetMiniSectorChain(secID);
  1425. default:
  1426. throw new Exception("Unsupproted chain type");
  1427. }
  1428. }
  1429. public CFStorage RootStorage { get; private set; }
  1430. public int Version => this.header.MajorVersion;
  1431. internal RBTree CreateNewTree()
  1432. {
  1433. RBTree bst = new RBTree();
  1434. return bst;
  1435. }
  1436. internal RBTree GetChildrenTree(int sid)
  1437. {
  1438. RBTree bst = new RBTree();
  1439. DoLoadChildren(bst, directoryEntries[sid]);
  1440. return bst;
  1441. }
  1442. private RBTree DoLoadChildrenTrusted(DirectoryEntry de)
  1443. {
  1444. RBTree bst = null;
  1445. if (de.Child != DirectoryEntry.NOSTREAM)
  1446. bst = new RBTree(directoryEntries[de.Child]);
  1447. return bst;
  1448. }
  1449. private void DoLoadChildren(RBTree bst, DirectoryEntry de)
  1450. {
  1451. if (de.Child != DirectoryEntry.NOSTREAM)
  1452. {
  1453. if (directoryEntries[de.Child].StgType == StgType.StgInvalid) return;
  1454. LoadSiblings(bst, directoryEntries[de.Child]);
  1455. NullifyChildNodes(directoryEntries[de.Child]);
  1456. bst.Insert(directoryEntries[de.Child]);
  1457. }
  1458. }
  1459. private void NullifyChildNodes(DirectoryEntry de)
  1460. {
  1461. de.Parent = null;
  1462. de.Left = null;
  1463. de.Right = null;
  1464. }
  1465. private readonly List<int> _levelSiDs = new List<int>();
  1466. private void LoadSiblings(RBTree bst, DirectoryEntry de)
  1467. {
  1468. _levelSiDs.Clear();
  1469. if (de.LeftSibling != DirectoryEntry.NOSTREAM)
  1470. DoLoadSiblings(bst, directoryEntries[de.LeftSibling]);
  1471. if (de.RightSibling != DirectoryEntry.NOSTREAM)
  1472. {
  1473. _levelSiDs.Add(de.RightSibling);
  1474. DoLoadSiblings(bst, directoryEntries[de.RightSibling]);
  1475. }
  1476. }
  1477. private void DoLoadSiblings(RBTree bst, DirectoryEntry de)
  1478. {
  1479. if (ValidateSibling(de.LeftSibling))
  1480. {
  1481. _levelSiDs.Add(de.LeftSibling);
  1482. DoLoadSiblings(bst, directoryEntries[de.LeftSibling]);
  1483. }
  1484. if (ValidateSibling(de.RightSibling))
  1485. {
  1486. _levelSiDs.Add(de.RightSibling);
  1487. DoLoadSiblings(bst, directoryEntries[de.RightSibling]);
  1488. }
  1489. NullifyChildNodes(de);
  1490. bst.Insert(de);
  1491. }
  1492. private bool ValidateSibling(int sid)
  1493. {
  1494. if (sid != DirectoryEntry.NOSTREAM)
  1495. {
  1496. if (sid >= directoryEntries.Count)
  1497. return false;
  1498. if (directoryEntries[sid].StgType == StgType.StgInvalid)
  1499. return false;
  1500. if (!Enum.IsDefined(typeof(StgType), directoryEntries[sid].StgType))
  1501. return false;
  1502. if (_levelSiDs.Contains(sid))
  1503. throw new InvalidDataException("Cyclic reference of directory item");
  1504. return true;
  1505. }
  1506. return false;
  1507. }
  1508. private void LoadDirectories()
  1509. {
  1510. List<Sector> directoryChain = GetSectorChain(header.FirstDirectorySectorID, SectorType.Normal);
  1511. if (header.FirstDirectorySectorID == Sector.Endofchain)
  1512. header.FirstDirectorySectorID = directoryChain[0].Id;
  1513. StreamView dirReader = new StreamView(directoryChain, GetSectorSize(), directoryChain.Count * GetSectorSize(), null, sourceStream);
  1514. while (dirReader.Position < directoryChain.Count * GetSectorSize())
  1515. {
  1516. DirectoryEntry de = DirectoryEntry.New(String.Empty, StgType.StgInvalid, directoryEntries);
  1517. de.Read(dirReader, this.Version);
  1518. }
  1519. }
  1520. private void CheckFileLength() => throw new NotImplementedException();
  1521. internal int ReadData(CFStream cFStream, long position, byte[] buffer, int count)
  1522. {
  1523. if (count > buffer.Length)
  1524. throw new ArgumentException("count parameter exceeds buffer size");
  1525. DirectoryEntry de = cFStream.DirEntry;
  1526. count = (int)Math.Min(de.Size - position, count);
  1527. StreamView sView = null;
  1528. if (de.Size < header.MinSizeStandardStream)
  1529. sView = new StreamView(GetSectorChain(de.StartSetc, SectorType.Mini), Sector.MinisectorSize, de.Size, null, sourceStream);
  1530. else
  1531. sView = new StreamView(GetSectorChain(de.StartSetc, SectorType.Normal), GetSectorSize(), de.Size, null, sourceStream);
  1532. sView.Seek(position, SeekOrigin.Begin);
  1533. int result = sView.Read(buffer, 0, count);
  1534. return result;
  1535. }
  1536. internal int ReadData(CFStream cFStream, long position, byte[] buffer, int offset, int count)
  1537. {
  1538. DirectoryEntry de = cFStream.DirEntry;
  1539. count = (int)Math.Min(de.Size - offset, count);
  1540. StreamView sView = null;
  1541. if (de.Size < header.MinSizeStandardStream)
  1542. sView = new StreamView(GetSectorChain(de.StartSetc, SectorType.Mini), Sector.MinisectorSize, de.Size, null, sourceStream);
  1543. else
  1544. sView = new StreamView(GetSectorChain(de.StartSetc, SectorType.Normal), GetSectorSize(), de.Size, null, sourceStream);
  1545. sView.Seek(position, SeekOrigin.Begin);
  1546. int result = sView.Read(buffer, offset, count);
  1547. return result;
  1548. }
  1549. internal byte[] GetData(CFStream cFStream)
  1550. {
  1551. AssertDisposed();
  1552. byte[] result = null;
  1553. DirectoryEntry de = cFStream.DirEntry;
  1554. if (de.Size < header.MinSizeStandardStream)
  1555. {
  1556. var miniView = new StreamView(GetSectorChain(de.StartSetc, SectorType.Mini), Sector.MinisectorSize, de.Size, null, sourceStream);
  1557. using (BinaryReader br = new BinaryReader(miniView))
  1558. result = br.ReadBytes((int)de.Size);
  1559. }
  1560. else
  1561. {
  1562. var sView = new StreamView(GetSectorChain(de.StartSetc, SectorType.Normal), GetSectorSize(), de.Size, null, sourceStream);
  1563. result = new byte[(int)de.Size];
  1564. sView.Read(result, 0, result.Length);
  1565. }
  1566. return result;
  1567. }
  1568. public byte[] GetDataBySID(int sid)
  1569. {
  1570. AssertDisposed();
  1571. if (sid < 0)
  1572. return null;
  1573. byte[] result = null;
  1574. try
  1575. {
  1576. DirectoryEntry de = directoryEntries[sid];
  1577. if (de.Size < header.MinSizeStandardStream)
  1578. {
  1579. var miniView = new StreamView(GetSectorChain(de.StartSetc, SectorType.Mini), Sector.MinisectorSize, de.Size, null, sourceStream);
  1580. BinaryReader br = new BinaryReader(miniView);
  1581. result = br.ReadBytes((int)de.Size);
  1582. br.Dispose();
  1583. }
  1584. else
  1585. {
  1586. var sView = new StreamView(GetSectorChain(de.StartSetc, SectorType.Normal), GetSectorSize(), de.Size, null, sourceStream);
  1587. result = new byte[(int)de.Size];
  1588. sView.Read(result, 0, result.Length);
  1589. }
  1590. }
  1591. catch
  1592. {
  1593. throw new Exception("Cannot get data for SID");
  1594. }
  1595. return result;
  1596. }
  1597. public Guid GetGuidBySID(int sid)
  1598. {
  1599. AssertDisposed();
  1600. if (sid < 0)
  1601. throw new Exception("Invalid SID");
  1602. DirectoryEntry de = directoryEntries[sid];
  1603. return de.StorageCLSID;
  1604. }
  1605. public Guid GetGuidForStream(int sid)
  1606. {
  1607. AssertDisposed();
  1608. if (sid < 0)
  1609. throw new Exception("Invalid SID");
  1610. Guid g = new Guid("00000000000000000000000000000000");
  1611. for (int i = sid - 1; i >= 0; i--)
  1612. {
  1613. if (directoryEntries[i].StorageCLSID != g && directoryEntries[i].StgType == StgType.StgStorage)
  1614. return directoryEntries[i].StorageCLSID;
  1615. }
  1616. return g;
  1617. }
  1618. private static int Ceiling(double d) => (int)Math.Ceiling(d);
  1619. private static int LowSaturation(int i) => i > 0 ? i : 0;
  1620. private bool closeStream = true;
  1621. internal bool IsClosed => _disposed;
  1622. #region IDisposable Members
  1623. private bool _disposed;//false
  1624. private object lockObject = new Object();
  1625. public void Dispose()
  1626. {
  1627. try
  1628. {
  1629. if (!_disposed)
  1630. {
  1631. lock (lockObject)
  1632. {
  1633. if (sectors != null)
  1634. {
  1635. sectors.Clear();
  1636. sectors = null;
  1637. }
  1638. this.RootStorage = null;
  1639. this.header = null;
  1640. this.directoryEntries.Clear();
  1641. this.directoryEntries = null;
  1642. this.fileName = null;
  1643. }
  1644. if (this.sourceStream != null && closeStream && !Configuration.HasFlag(CFSConfiguration.LeaveOpen))
  1645. this.sourceStream.Dispose();
  1646. }
  1647. }
  1648. finally
  1649. {
  1650. _disposed = true;
  1651. }
  1652. GC.SuppressFinalize(this);
  1653. }
  1654. #endregion IDisposable Members
  1655. private List<DirectoryEntry> directoryEntries = new List<DirectoryEntry>();
  1656. internal IList<DirectoryEntry> GetDirectories() => directoryEntries;
  1657. internal DirectoryEntry RootEntry => directoryEntries[0];
  1658. private IList<DirectoryEntry> FindDirectoryEntries(String entryName)
  1659. {
  1660. List<DirectoryEntry> result = new List<DirectoryEntry>();
  1661. foreach (DirectoryEntry d in directoryEntries)
  1662. if (d.GetEntryName() == entryName && d.StgType != StgType.StgInvalid)
  1663. result.Add(d);
  1664. return result;
  1665. }
  1666. public IList<CFItem> GetAllNamedEntries(String entryName)
  1667. {
  1668. IList<DirectoryEntry> r = FindDirectoryEntries(entryName);
  1669. List<CFItem> result = new List<CFItem>();
  1670. foreach (DirectoryEntry id in r)
  1671. if (id.GetEntryName() == entryName && id.StgType != StgType.StgInvalid)
  1672. result.Add(id.StgType == StgType.StgStorage ? new CFStorage(this, id) : new CFStream(this, id));
  1673. return result;
  1674. }
  1675. public int GetNumDirectories()
  1676. {
  1677. AssertDisposed();
  1678. return directoryEntries.Count;
  1679. }
  1680. public string GetNameDirEntry(int id)
  1681. {
  1682. AssertDisposed();
  1683. if (id < 0)
  1684. throw new Exception("Invalid Storage ID");
  1685. return directoryEntries[id].Name;
  1686. }
  1687. public StgType GetStorageType(int id)
  1688. {
  1689. AssertDisposed();
  1690. if (id < 0)
  1691. throw new Exception("Invalid Storage ID");
  1692. return directoryEntries[id].StgType;
  1693. }
  1694. private void AssertDisposed()
  1695. {
  1696. if (_disposed)
  1697. throw new ObjectDisposedException("Compound File closed: cannot access data");
  1698. }
  1699. }
  1700. #endregion Modified OpenMCDF
  1701. }