using System; using System.Collections.Generic; using System.Text; using System.IO; using QiHe.CodeLib; namespace HH.WMS.Utils.ExcelLibrary.CompoundDocumentFormat { public partial class CompoundDocument : IDisposable { internal FileHeader Header; internal int SectorSize; internal int ShortSectorSize; private int TotalSectors; internal MasterSectorAllocation MasterSectorAllocation; internal SectorAllocation SectorAllocation; internal ShortSectorAllocation ShortSectorAllocation; private MemoryStream ShortStreamContainer; private MemoryStream DirectoryStream; private Dictionary DirectoryEntries; public DirectoryEntry RootStorage { get { return DirectoryEntries[0]; } } internal Stream FileStorage; private BinaryReader Reader; private BinaryWriter Writer; internal CompoundDocument(Stream stream, FileHeader header) { this.FileStorage = stream; this.Reader = new BinaryReader(this.FileStorage); if (stream.CanWrite) { this.Writer = new BinaryWriter(this.FileStorage, Encoding.Unicode); } this.Header = header; this.SectorSize = (int)Math.Pow(2, Header.SectorSizeInPot); this.ShortSectorSize = (int)Math.Pow(2, Header.ShortSectorSizeInPot); this.TotalSectors = stream.Length == 0 ? 0 : (int)(stream.Length - 512) / this.SectorSize; this.MasterSectorAllocation = new MasterSectorAllocation(this); this.SectorAllocation = new SectorAllocation(this); this.ShortSectorAllocation = new ShortSectorAllocation(this); } public static CompoundDocument Create(string file) { FileStream stream = File.Open(file, FileMode.Create, FileAccess.ReadWrite, FileShare.Read); return Create(stream); } public static CompoundDocument Open(string file) { FileStream stream = File.Open(file, FileMode.Open, FileAccess.ReadWrite, FileShare.Read); return Open(stream); } public static CompoundDocument Create(Stream stream) { CompoundDocument document = new CompoundDocument(stream, new CompoundFileHeader()); document.WriteHeader(); document.MasterSectorAllocation.AllocateSATSector(); document.InitializeDirectoryEntries(); return document; } public static CompoundDocument Open(Stream stream) { BinaryReader reader = new BinaryReader(stream); FileHeader header = ReadHeader(reader); CompoundDocument document = new CompoundDocument(stream, header); if (!document.CheckHeader()) return null; document.ReadDirectoryEntries(); return document; } public void Save() { SaveDirectoryEntries(); SaveShortStreams(); WriteHeader(); Writer.Flush(); } public void Close() { Dispose(); } public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } ~CompoundDocument() { Dispose(false); } bool disposed = false; protected virtual void Dispose(bool disposing) { if (disposed) { return; } if (disposing) { if (FileStorage != null) { FileStorage.Close(); FileStorage = null; } } disposed = true; } bool CheckHeader() { if (!ArrayEqual(Header.FileTypeIdentifier, CompoundFileHeader.FileTypeIdentifier)) throw new Exception("File header not recognized."); if (!ArrayEqual(Header.ByteOrderMark, ByteOrderMarks.LittleEndian)) throw new Exception("Endian not implemented."); return true; } void InitializeDirectoryEntries() { Header.FirstSectorIDofDirectoryStream = AllocateDataSector(); DirectoryEntries = new Dictionary(); DirectoryEntry root = new DirectoryEntry(this, "Root Entry"); root.EntryType = EntryType.Root; root.NodeColor = NodeColor.Black; root.FirstSectorID = AllocateDataSector(); root.StreamLength = 0; DirectoryEntries.Add(0, root); DirectoryStream = new MemoryStream(); ShortStreamContainer = new MemoryStream(); } void SaveDirectoryEntries() { DirectoryTree.Build(RootStorage); DirectoryStream.Position = 0; BinaryWriter writer = new BinaryWriter(DirectoryStream, Encoding.Unicode); for (int id = 0; id < DirectoryEntries.Count; id++) { WriteDirectoryEntry(writer, DirectoryEntries[id]); } WriteStreamData(Header.FirstSectorIDofDirectoryStream, DirectoryStream.ToArray()); } void SaveShortStreams() { ShortSectorAllocation.Save(); WriteStreamData(RootStorage.FirstSectorID, ShortStreamContainer.ToArray()); } internal int ReadInt32(long position) { FileStorage.Position = position; return Reader.ReadInt32(); } internal int ReadInt32InSector(int secID, int position) { int offset = GetSectorOffset(secID); FileStorage.Position = offset + position; return Reader.ReadInt32(); } internal void Write(long position, int integer) { FileStorage.Position = position; Writer.Write(integer); } internal void WriteInSector(int secID, int position, int integer) { int offset = GetSectorOffset(secID); FileStorage.Position = offset + position; Writer.Write(integer); } internal void WriteInSector(int secID, int position, int[] integers) { int offset = GetSectorOffset(secID); FileStorage.Position = offset + position; WriteArrayOfInt32(Writer, integers); } internal void WriteInSector(int secID, int position, byte[] data, int index, int count) { int offset = GetSectorOffset(secID); FileStorage.Position = offset + position; Writer.Write(data, index, count); } internal int AllocateNewSector() { int secID = TotalSectors; FileStorage.Position = GetSectorOffset(secID); Writer.Write(new byte[SectorSize]); TotalSectors++; return secID; } internal int AllocateDataSector() { return this.SectorAllocation.AllocateSector(); } internal int AllocateDataSectorAfter(int sectorID) { int newSectorID = this.SectorAllocation.AllocateSector(); this.SectorAllocation.LinkSectorID(sectorID, newSectorID); return newSectorID; } internal int AllocateNewSector(int[] sectorData) { int secID = TotalSectors; FileStorage.Position = GetSectorOffset(secID); WriteArrayOfInt32(Writer, sectorData); TotalSectors++; return secID; } internal int AllocateShortSector() { return ShortSectorAllocation.AllocateSector(); } internal void AllocateNewShortSector() { ShortStreamContainer.Position = ShortStreamContainer.Length; byte[] sidData = new byte[ShortSectorSize]; for (int i = 0; i < sidData.Length; i++) { sidData[i] = 0xFF; } ShortStreamContainer.Write(sidData, 0, ShortSectorSize); RootStorage.StreamLength = (int)ShortStreamContainer.Length; } internal static Int32[] ReadArrayOfInt32(BinaryReader reader, int count) { Int32[] data = new Int32[count]; for (int i = 0; i < data.Length; i++) { data[i] = reader.ReadInt32(); } return data; } internal static void WriteArrayOfInt32(BinaryWriter writer, Int32[] data) { for (int i = 0; i < data.Length; i++) { writer.Write(data[i]); } } public static CompoundDocument CreateFromStream(Stream stream) { CompoundDocument document = new CompoundDocument(stream, new CompoundFileHeader()); document.WriteHeader(); document.MasterSectorAllocation.AllocateSATSector(); document.InitializeDirectoryEntries(); return document; } } }