private uint _standardStreamMinBytes; //in bytes #endregion Fields #region Constructors /// <summary> /// Initializes a new, blank, Ole2Document object. /// </summary> public Ole2Document() { SetDefaults(); _header = new Header(this); _msat = new Msat(this); _sat = new Sat(this); _ssat = new Ssat(this); _directory = new Directory(this); _streams = new Streams(this); _streams.AddNamed(new Bytes(), Directory.RootName); }
/// <summary> /// Loads this Ole2Document object from the provided stream (e.g. a FileStream to load /// from a File). This is only preliminarily supported and tested for Excel /// files. /// </summary> /// <param name="stream">Stream to load the document from.</param> public void Load(System.IO.Stream stream) { if (stream.Length == 0) { throw new Exception("No data (or zero-length) found!"); } if (stream.Length < 512) { throw new Exception(string.Format("File length {0} < 512 bytes", stream.Length)); } byte[] head = new byte[512]; stream.Read(head, 0, 512); bool isLE = false; if (head[28] == 254 && head[29] == 255) { isLE = true; } if (!isLE) { throw new NotSupportedException("File is not Little-Endian"); } _isLittleEndian = isLE; ushort sectorSize = BitConverter.ToUInt16(MidByteArray(head, 30, 2), 0); if (sectorSize < 7 || sectorSize > 32) { throw new Exception(string.Format("Invalid Sector Size [{0}] (should be 7 <= sectorSize <= 32", sectorSize)); } _sectorSize = sectorSize; ushort shortSectorSize = BitConverter.ToUInt16(MidByteArray(head, 32, 2), 0); if (shortSectorSize > sectorSize) { throw new Exception( string.Format("Invalid Short Sector Size [{0}] (should be < sectorSize; {1})", shortSectorSize, sectorSize)); } _shortSectorSize = shortSectorSize; //if (readError) ExitFunction; uint satSectorCount = BitConverter.ToUInt32(MidByteArray(head, 44, 4), 0); if (satSectorCount < 0) { throw new Exception(string.Format("Invalid SAT Sector Count [{0}] (should be > 0)", satSectorCount)); } int dirSID0 = BitConverter.ToInt32(MidByteArray(head, 48, 4), 0); if (dirSID0 < 0) { throw new Exception(string.Format("Invalid Directory SID0 [{0}] (should be > 0)", dirSID0)); } uint minStandardStreamSize = BitConverter.ToUInt32(MidByteArray(head, 56, 4), 0); if ((minStandardStreamSize < (Math.Pow(2, sectorSize))) || (minStandardStreamSize % (Math.Pow(2, sectorSize)) > 0)) { throw new Exception(string.Format("Invalid MinStdStreamSize [{0}] (should be multiple of (2^SectorSize)", minStandardStreamSize)); } _standardStreamMinBytes = minStandardStreamSize; int ssatSID0 = BitConverter.ToInt32(MidByteArray(head, 60, 4), 0); uint ssatSectorCount = BitConverter.ToUInt32(MidByteArray(head, 64, 4), 0); if (ssatSID0 < 0 && ssatSID0 != -2) { throw new Exception(string.Format("Invalid SSAT SID0 [{0}] (must be >=0 or -2", ssatSID0)); } if (ssatSectorCount > 0 && ssatSID0 < 0) { throw new Exception( string.Format("Invalid SSAT SID0 [{0}] (must be >=0 when SSAT Sector Count > 0)", ssatSID0)); } if (ssatSectorCount < 0) { throw new Exception(string.Format("Invalid SSAT Sector Count [{0}] (must be >= 0)", ssatSectorCount)); } int msatSID0 = BitConverter.ToInt32(MidByteArray(head, 68, 4), 0); if (msatSID0 < 1 && msatSID0 != -2) { throw new Exception(string.Format("Invalid MSAT SID0 [{0}]", msatSID0)); } uint msatSectorCount = BitConverter.ToUInt32(MidByteArray(head, 72, 4), 0); if (msatSectorCount < 0) { throw new Exception(string.Format("Invalid MSAT Sector Count [{0}]", msatSectorCount)); } else if (msatSectorCount == 0 && msatSID0 != -2) { throw new Exception(string.Format("Invalid MSAT SID0 [{0}] (should be -2)", msatSID0)); } int i = 0; int k = ((int)Math.Pow(2, sectorSize) / 4) - 1; int[] msat = new int[108 + (k * msatSectorCount) + 1]; //add 1 compared to VBScript version due to C#/VBS array declaration diff for (int j = 0; j < 109; j++) { msat[j] = BitConverter.ToInt32(MidByteArray(head, 76 + (j * 4), 4), 0); } int msatSidNext = msatSID0; while (i < msatSectorCount) { Bytes sector = GetSector(stream, sectorSize, msatSidNext); if (sector.Length == 0) { throw new Exception(string.Format("MSAT SID Chain broken - SID [{0}] not found / EOF reached", msatSidNext)); } for (int j = 0; j < k; j++) { msat[109 + (i * k) + j] = BitConverter.ToInt32(sector.Get(j * 4, 4).ByteArray, 0); } msatSidNext = BitConverter.ToInt32(sector.Get(k * 4, 4).ByteArray, 0); i++; } //if (re) Exit Function; //Find number of Sectors in SAT --> i i = msat.Length; while (msat[i - 1] < 0) { i--; } //Size and fill SAT SID array int[] sat = new int[(uint)(i * (Math.Pow(2, sectorSize) / 4))]; int m = (int)(Math.Pow(2, sectorSize) / 4); for (int j = 0; j < i; j++) { Bytes sector = GetSector(stream, sectorSize, msat[j]); if (sector.Length == 0) { throw new Exception(string.Format("SAT SID Chain broken - SAT Sector SID{0} not found / EOF reached", msat[j])); } for (k = 0; k < m; k++) { sat[(j * m) + k] = BitConverter.ToInt32(sector.Get(k * 4, 4).ByteArray, 0); } } //Size and fill SSAT SID array i = 0; int ssatSidNext = ssatSID0; // m = (int) (Math.Pow(2, sectorSize) / 4); //Dictionary<int, int> ssat = new Dictionary<int, int>(); int[] ssat = new int[(ssatSectorCount + 1) * m]; while (ssatSidNext > -2) { Bytes sector = GetSector(stream, sectorSize, ssatSidNext); if (sector.Length == 0) { throw new Exception(string.Format("SSAT Sector SID{0} not found", ssatSidNext)); } for (int j = 0; j < m; j++) { ssat[(i * m) + j] = BitConverter.ToInt32(sector.Get(j * 4, 4).ByteArray, 0); } ssatSidNext = sat[ssatSidNext]; i++; } if (i < ssatSectorCount) { throw new Exception(string.Format("SSAT Sector chain broken: {0} found, header indicates {1}", i, ssatSectorCount)); } //Size and fill Directory byte array array int dirSectorCount = 0; int dirSidNext = dirSID0; m = (int)(Math.Pow(2, sectorSize) / 128); Dictionary <int, byte[]> dir = new Dictionary <int, byte[]>(); while (dirSidNext > -2) { Bytes sector = GetSector(stream, sectorSize, dirSidNext); if (sector.Length == 0) { throw new Exception(string.Format("Directory Sector SID{0} not found", dirSidNext)); } for (int j = 0; j < m; j++) { dir[(dirSectorCount * m) + j] = sector.Get(j * 128, 128).ByteArray; } dirSidNext = sat[dirSidNext]; dirSectorCount++; } for (i = 0; i < dir.Count; i++) { byte[] dirEntry = dir[i]; int nameLength = BitConverter.ToInt16(MidByteArray(dirEntry, 64, 2), 0); byte[] docStreamName = MidByteArray(dirEntry, 0, nameLength); bool overwrite = false; if (Bytes.AreEqual(docStreamName, Directory.RootName)) { overwrite = true; } Bytes docStream = GetStream(stream, i, dir, sectorSize, sat, shortSectorSize, ssat, minStandardStreamSize); if (docStreamName.Length == 0 && docStream.Length == 0) { continue; //don't add streams for directory padding entries } Streams.AddNamed(docStream, docStreamName, overwrite); } }