using System; using System.Collections; using System.IO; using System.Runtime.InteropServices; namespace FastReport.Export.BIFF8 { /// /// Provides API to binary stream /// public /*abstract*/ class StreamHelper : MemoryStream { internal void SkipBytes(int count) { base.Position = base.Position + count; } internal ushort ReadUshort() { byte hi, low; low = (byte)ReadByte(); hi = (byte)ReadByte(); return (ushort)((int)low + (int)(hi << 8)); } internal uint ReadUint() { uint b0 = (uint)ReadByte(); uint b1 = (uint)ReadByte(); uint b2 = (uint)ReadByte(); uint b3 = (uint)ReadByte(); uint result = (b3 << 24) + (b2 << 16) + (b1 << 8) + b0; return result; } internal int ReadInt() { int b0 = ReadByte(); int b1 = ReadByte(); int b2 = ReadByte(); int b3 = ReadByte(); int result = (b3 << 24) + (b2 << 16) + (b1 << 8) + b0; return result; } internal double ReadDouble() { double[] result = new double[1]; byte[] bytes = new byte[8]; for (int i = 0; i < 8; i++) bytes[i] = (byte)ReadByte(); IntPtr ptr = Marshal.AllocHGlobal(8); Marshal.Copy(bytes, 0, ptr, 8); Marshal.Copy(ptr, result, 0, 1); Marshal.FreeHGlobal(ptr); return result[0]; } internal string ReadUnicodeString(bool short_len) { string str = ""; int num_rich_runs = 0; UInt16 Char_Count = short_len ? (UInt16)ReadByte() : ReadUshort(); byte options = (byte)ReadByte(); if ((options & 0x8) != 0) num_rich_runs = ReadUshort(); if ((options & 0x1) != 0) { for (int i = 0; i < Char_Count; i++) { char ch = (char)ReadUshort(); str += ch; } } else { for (int i = 0; i < Char_Count; i++) { char ch = (char)ReadByte(); str += ch; } } return str; } internal bool CanCompressString(string s) { int str_len = s.Length; for (int i = 0; i < str_len; i++) if (s[i] > 255) return false; return true; } internal void WriteUnicodeString(string FontName, bool short_len) { WriteUnicodeString(FontName, short_len, false); } internal void WriteUnicodeString(string FontName, bool short_len, bool compress) { int str_len = FontName.Length; if (short_len) { WriteByte((byte)str_len); } else { WriteUshort((ushort)str_len); } WriteByte(compress ? (byte)0 : (byte)0x001); // Uncompressed if (compress) { for (int i = 0; i < str_len; i++) { char ch = FontName[i]; if (ch == 0xd) WriteByte((byte)0xa); else WriteByte((byte)ch); } } else { for (int i = 0; i < str_len; i++) { char ch = FontName[i]; if (ch == 0xd) WriteUshort((ushort)0xa); else WriteUshort((ushort)ch); } } } internal int SizeUnicodeString(string FontName, bool short_len) { return SizeUnicodeString(FontName, short_len, false); } internal int SizeUnicodeString(string FontName, bool short_len, bool compress) { int str_len = FontName.Length; if (!compress) str_len *= 2; return str_len + (short_len ? 2 : 3); } internal byte[] ReadBytes(int count) { byte[] result = new byte[count]; for (int i = 0; i < count; i++) result[i] = unchecked((byte)ReadByte()); return result; } internal ushort[] ReadUshorts(int count) { ushort[] result = new ushort[count]; for (int i = 0; i < count; i++) result[i] = ReadUshort(); return result; } internal int[] ReadInts(int count) { int[] result = new int[count]; for (int i = 0; i < count; i++) result[i] = ReadInt(); return result; } internal void WriteUshort(ushort value) { unchecked { byte lo = (byte)(value); WriteByte(lo); byte hi = (byte)(value >> 8); WriteByte(hi); } } internal void WriteUint(uint value) { unchecked { byte b0 = (byte)value; byte b1 = (byte)(value >> 8); byte b2 = (byte)(value >> 16); byte b3 = (byte)(value >> 24); WriteByte(b0); WriteByte(b1); WriteByte(b2); WriteByte(b3); } } internal void WriteInt(int value) { unchecked { byte b0 = (byte)value; byte b1 = (byte)(value >> 8); byte b2 = (byte)(value >> 16); byte b3 = (byte)(value >> 24); WriteByte(b0); WriteByte(b1); WriteByte(b2); WriteByte(b3); } } internal void WriteDouble(double value) { double[] source = new double[1]; source[0] = value; byte[] bytes = new byte[8]; IntPtr ptr = Marshal.AllocHGlobal(8); Marshal.Copy(source, 0, ptr, 1); Marshal.Copy(ptr, bytes, 0, bytes.Length); Marshal.FreeHGlobal(ptr); for (int i = 0; i < 8; i++) WriteByte(bytes[i]); } internal void WriteBytes(byte[] values) { for (int i = 0; i < values.Length; i++) WriteByte(values[i]); } internal void WriteBytes(byte[] values, int start_index, int count) { for (int i = 0; i < count; i++) WriteByte(values[start_index + i]); } internal void WriteInts(int[] values) { WriteInts(values, 0); } internal void WriteInts(int[] values, int start_index) { for (int i = start_index; i < values.Length; i++) WriteInt(values[i]); } internal void WriteUints(uint[] values) { WriteUints(values, 0); } internal void WriteUints(uint[] values, int start_index) { for (int i = start_index; i < values.Length; i++) WriteUint(values[i]); } } internal class DirectoryEntry { internal enum DirEntryType { // Type of the entry: Empty = 0x00, // 00H = Empty UserStorage = 0x01, // 01H = User storage UseStream = 0x02, // 02H = User stream LockBytes = 0x03, // 03H = LockBytes (unknown) Property = 0x04, // 04H = Property (unknown) RootStorage = 0x05 // 05H = Root storage } #if false // 0 64 // Character array of the name of the entry, always 16-bit Unicode characters, with trailing //zero character (results in a maximum name length of 31 characters) byte[] name = new byte[64]; //64 2 //Size of the used area of the character buffer of the name (not character count), including //the trailing zero character (e.g. 12 for a name with 5 characters: (5+1)∙2 = 12) ushort name_size; #else public string entry_name; #endif // 66 1 // Type of the entry: 00H = Empty 03H = LockBytes (unknown) // 01H = User storage 04H = Property (unknown) // 02H = User stream 05H = Root storage internal DirEntryType type; // 67 1 Node colour of the entry: 00H = Red 01H = Black internal byte colour; // 68 4 DirID of the left child node inside the red-black tree of all direct members of the parent // storage (if this entry is a user storage or stream), –1 if there is no left child internal int leftChildDirID; //72 4 DirID of the right child node inside the red-black tree of all direct members of the parent //storage (if this entry is a user storage or stream), –1 if there is no right child internal int rightChildDirID; // 76 4 DirID of the root node entry of the red-black tree of all storage members (if this entry is a //storage,), –1 otherwise internal int rootDirID; // 80 16 Unique identifier, if this is a storage (not of interest in the following, may be all 0) private byte[] uid = new byte[16]; // 96 4 User flags (not of interest in the following, may be all 0) private uint userFlags; // 100 8 Time stamp of creation of this entry. private byte[] creationTime = new byte[8]; // 108 8 Time stamp of last modification of this entry private byte[] modificationTime = new byte[8]; // 116 4 SecID of first sector or short-sector, if this entry refers to a stream public UInt32 bOF; //120 4 Total stream size in bytes, if this entry refers to a stream public UInt32 size; // 124 4 Not used public void Read(StreamHelper File) { entry_name = ""; ushort name_size; for (int i = 0; i < 32; i++) { char ch = (char)File.ReadUshort(); if (ch != '\0') entry_name += ch; } name_size = File.ReadUshort(); type = (DirEntryType)File.ReadByte(); colour = (byte)File.ReadByte(); leftChildDirID = File.ReadInt(); rightChildDirID = File.ReadInt(); rootDirID = File.ReadInt(); uid = File.ReadBytes(uid.Length); userFlags = File.ReadUint(); creationTime = File.ReadBytes(creationTime.Length); modificationTime = File.ReadBytes(modificationTime.Length); bOF = File.ReadUint(); size = File.ReadUint(); File.SkipBytes(sizeof(UInt32)); } public DirectoryEntry() { colour = 0x00; // Black leftChildDirID = -1; rightChildDirID = -1; rootDirID = -1; uid = new byte[16]; // 96 4 User flags (not of interest in the following, may be all 0) userFlags = 0; // 100 8 Time stamp of creation of this entry. Most implementations do not write a valid // time stamp, but fill up this space with zero bytes. creationTime = new byte[8] { 0, 0, 0, 0, 0, 0, 0, 0 }; // 108 8 Time stamp of last modification of this entry. Most implementations do not write // a valid time stamp, but fill up this space with zero bytes. modificationTime = new byte[8] { 0, 0, 0, 0, 0, 0, 0, 0 }; // Fixed empry values this.bOF = 0; this.size = 0; this.type = DirEntryType.Empty; this.entry_name = ""; } public DirectoryEntry(string name, DirEntryType type) : this() { this.type = type; this.entry_name = name; } internal void Write(StreamHelper File) { int name_len = entry_name.Length; if (name_len > 32) name_len = 32; for (int i = 0; i < name_len; i++) { char ch = entry_name[i]; File.WriteUshort(ch); } for (int i = name_len; i < 32; i++) { File.WriteUshort(0); } File.WriteUshort((ushort)(2 + name_len * 2)); File.WriteByte((byte)type); File.WriteByte(colour); File.WriteInt(leftChildDirID); File.WriteInt(rightChildDirID); File.WriteInt(rootDirID); File.WriteBytes(uid); File.WriteUint(userFlags); File.WriteBytes(creationTime); File.WriteBytes(modificationTime); File.WriteUint(bOF); File.WriteUint(size); File.SkipBytes(sizeof(UInt32)); } } internal class CompoundDocumentHeader : StreamHelper { private byte[] id0 = new byte[8]; // Must be: D0 CF 11 E0 A1 B1 1A E1 private byte[] uid = new byte[16]; private ushort revision; // Might be: 3E private ushort version; // Might be: 03 private ushort byteOrder; // Little-Endian: FE FF; Big-Endian: FF FE private ushort secSize; // Sector size is 2**SecSize bytes private ushort shortSecSize; // Short sector size is 2**ShortSecSize bytes // fixed byte NotUsed1[10]; private uint satCount; // Count of sectors used for the SAT public int dir; // First sector of the directory stream // fixed byte NotUsed2[4]; public UInt32 minStreamSize; // Streams that have sizes less than this value are stored in the short stream private int sSAT; // First sector of the SSAT private uint sSATCount; // Count of sectors used for the SSAT private int mSAT; // First sector of the MSAT private uint mSATCount; // Count of sectors used for the MSAT private int[] mSATSectors = new int[109]; // First 109 SecID values in the MSAT private ArrayList sat = new ArrayList(); private ArrayList shortSAT = new ArrayList(); private ArrayList additional_mSAT = new ArrayList(); private uint totalAllocatedSectors; private uint totalAllocatedShortSectors; internal BIFF8_Container shortStreamContainer; public int SectorSize { get { return 1 << secSize; } } public int ShortSectorSize { get { return 1 << shortSecSize; } } internal int SectorOffset(int index) { int a = 512 + index * SectorSize; return a; } internal int ShortSectorOffset(int index) { int a = index * ShortSectorSize; return a; } internal int NextSector(int index) { return (int)sat[index]; } internal int NextShortSector(int index) { return (int)shortSAT[index]; } internal void Read() { id0 = ReadBytes(id0.Length); uid = ReadBytes(uid.Length); revision = ReadUshort(); version = ReadUshort(); byteOrder = ReadUshort(); secSize = ReadUshort(); shortSecSize = ReadUshort(); SkipBytes(10); satCount = ReadUint(); dir = ReadInt(); SkipBytes(4); minStreamSize = ReadUint(); sSAT = ReadInt(); sSATCount = ReadUint(); mSAT = ReadInt(); mSATCount = ReadUint(); mSATSectors = ReadInts(mSATSectors.Length); // Read SAT into memory for (int i = 0; i < satCount; i++) { if (i >= 109) throw new Exception("Huge SAT not implemented"); int SAT_Sector = mSATSectors[i]; int Position = SectorOffset(SAT_Sector); this.Position = Position; sat.AddRange(new ArrayList(ReadInts(this.SectorSize / sizeof(UInt32)))); } // Read SSAT into memory int SSAT_Sector = sSAT; for (int i = 0; i < sSATCount; i++) { int Position = SectorOffset(SSAT_Sector); this.Position = Position; shortSAT.AddRange(new ArrayList(ReadInts(this.SectorSize / sizeof(UInt32)))); SSAT_Sector = NextSector(SSAT_Sector); } } internal void Write() { this.Position = 0; WriteBytes(id0); WriteBytes(uid); WriteUshort(revision); WriteUshort(version); WriteUshort(byteOrder); WriteUshort(secSize); WriteUshort(shortSecSize); SkipBytes(10); WriteUint(satCount); WriteInt(dir); SkipBytes(4); WriteUint(minStreamSize); WriteInt(sSAT); WriteUint(sSATCount); WriteInt(mSAT); WriteUint(mSATCount); WriteInts(mSATSectors); // Write SSAT to memory int SSATEntriesPerSector = this.SectorSize / sizeof(UInt32); int SSAT_Sector = sSAT; for (int i = 0; i < sSATCount;) { this.Position = SectorOffset(SSAT_Sector); for (int j = 0; j < SSATEntriesPerSector; j++) { int value = (int)shortSAT[i * SSATEntriesPerSector + j]; this.WriteInt(value); } i++; if (i == sSATCount) continue; SSAT_Sector = this.AllocateSector(SSAT_Sector); } // Write SAT to memory for (int i = 0; i < satCount; i++) { int SAT_Sector = 0; if (i >= 109) SAT_Sector = (int)this.additional_mSAT[i - 109];//throw new Exception("Huge SAT not implemented"); else SAT_Sector = this.mSATSectors[i]; this.Position = SectorOffset(SAT_Sector); for (int j = 0; j < this.SectorSize / sizeof(UInt32); j++) { int value = -1; int idx = i * this.SectorSize / sizeof(UInt32) + j; if (idx < sat.Count) value = (int)sat[idx]; this.WriteInt(value); } } // Write MSAT to memory int MSATEntriesPerSector = this.SectorSize / sizeof(Int32); int MSAT_Sector = mSAT; for (int i = 0; i < mSATCount;) { this.Position = SectorOffset(MSAT_Sector); for (int j = 0; j < MSATEntriesPerSector; j++) { int n = i * SSATEntriesPerSector + j; int value = -1; if(n < satCount) value = n >= 109 ? (int)additional_mSAT[n - 109] : mSATSectors[n]; this.WriteInt(value); } i++; if (i == mSATCount) continue; MSAT_Sector = this.AllocateSector(MSAT_Sector); } } internal void Reset() { id0 = new byte[] { 0xD0, 0xCF, 0x11, 0xE0, 0xA1, 0xB1, 0x1A, 0xE1 }; revision = 0x003e; version = 0x0003; byteOrder = 0xfffe; secSize = 0x0009; shortSecSize = 0x0006; satCount = 0; minStreamSize = 0x00001000; mSAT = -2; mSATCount = 0; // Reset SAT table this.sat.Clear(); // Reset SSAT table sSATCount = 0; this.shortSAT.Clear(); // Reset MSAT table for (int i = 0; i < mSATSectors.Length; i++) { mSATSectors[i] = -1; } this.additional_mSAT.Clear(); this.totalAllocatedSectors = 0; this.totalAllocatedShortSectors = 0; this.shortStreamContainer.SetLength(0); dir = AllocateSector(); sSAT = AllocateSector(); mSAT = AllocateSector(); mSATCount = 1; } internal CompoundDocumentHeader(FileStream f) : base() { f.CopyTo(this, (int)f.Length); this.Position = 0; } internal CompoundDocumentHeader() : base() { shortStreamContainer = new BIFF8_Container(); Reset(); this.Position = 0; } internal void FinalWriteToStream(Stream f) { this.Position = 0; this.CopyTo(f, (int)this.Length); } internal void SetCurentSector(int Dir) { base.Position = SectorOffset(Dir); } internal void ReadShortStreamContainer(DirectoryEntry entry) { int StorePosition = (int)this.Position; int SectorOfContainer; shortStreamContainer = new BIFF8_Container(); int idx = 0; for ( SectorOfContainer = (int)entry.bOF; SectorOfContainer >= 0; SectorOfContainer = this.NextSector(SectorOfContainer) ) { this.SetCurentSector(SectorOfContainer); for (int i = 0; i < this.SectorSize && idx < entry.size; i++) { shortStreamContainer.WriteByte((byte)ReadByte()); } } this.Position = StorePosition; } internal int WriteShortStreamContainer() { int StorePosition = (int)this.Position; int FirstSectorOfContainer = this.AllocateSector(); int ContainerSize = (int)shortStreamContainer.Length; shortStreamContainer.Position = 0; for ( int SectorOfContainer = (int)FirstSectorOfContainer; ContainerSize > 0; SectorOfContainer = this.AllocateSector(SectorOfContainer) ) { this.SetCurentSector(SectorOfContainer); for (int i = 0; i < this.SectorSize; i++) { byte value = 0xff; if (shortStreamContainer.Position < shortStreamContainer.Length) { value = (byte)shortStreamContainer.ReadByte(); } this.WriteByte(value); } ContainerSize -= this.SectorSize; if (ContainerSize <= 0) break; } this.Position = StorePosition; return FirstSectorOfContainer; } internal int AllocateSector() { int new_sector; new_sector = sat.IndexOf(-1); if (new_sector == -1) { AllocateSATSector(); new_sector = sat.IndexOf(-1); if (new_sector == -1) { throw new Exception("Unable allocate SAT sector"); } sat[new_sector] = -2; int self = sat.IndexOf(-1); sat[self] = -3; if (satCount >= 109) { //throw new Exception("Huge SAT not implemented"); this.additional_mSAT.Add(self); } else mSATSectors[satCount] = self; satCount++; } totalAllocatedSectors++; sat[new_sector] = -2; return new_sector; } internal int AllocateSector(int PrevSector) { int Sector = AllocateSector(); sat[PrevSector] = Sector; return Sector; } internal int AllocateShortSector() { int new_sector; new_sector = shortSAT.IndexOf(-1); if (new_sector == -1) { AllocateSSATSector(); new_sector = shortSAT.IndexOf(-1); if (new_sector == -1) { throw new Exception("Unable allocate SSAT sector"); } } totalAllocatedShortSectors++; shortSAT[new_sector] = -2; return new_sector; } internal int AllocateShortSector(int PrevSector) { int Sector = AllocateShortSector(); shortSAT[PrevSector] = Sector; return Sector; } private void AllocateSATSector() { if (satCount > 109) { //throw new Exception("Large MSAT does not implemented yet"); } int SATEntriesPerSector = this.ShortSectorSize / sizeof(UInt32); ArrayList sat_new_sectors = new ArrayList(); #if false // BIG DEAL sat_new_sectors.Add(-3); // Sector is used by SAT itself for (int i = 1; i < SATEntriesPerSector; i++) #else for (int i = 0; i < SATEntriesPerSector; i++) #endif { sat_new_sectors.Add(-1); } sat.AddRange(sat_new_sectors); int local = sat.Count / SATEntriesPerSector; // MSATSectors[SATCount] = (int) totalAllocatedSectors++; } private void AllocateSSATSector() { int SSATEntriesPerSector = this.SectorSize / sizeof(UInt32); ArrayList ssat_new_sectors = new ArrayList(); // ssat_new_sectors.Add(-3); // Sector is used by SAT itself for (int i = 0; i < SSATEntriesPerSector; i++) { ssat_new_sectors.Add(-1); } this.shortSAT.AddRange(ssat_new_sectors); int local = shortSAT.Count / SSATEntriesPerSector; sSATCount++; } #if false internal int AllocateStream(int WriteQueueLength, bool non_packing_stream) { int BOF; int Sector; // int WriteQueueLength = Container.Length; if (WriteQueueLength == 0) return -1; if (WriteQueueLength >= this.MinStreamSize || non_packing_stream) { BOF = Sector = this.AllocateSector(); // int SourceCopyIndex = 0; do { int CopyCount = (WriteQueueLength < this.SectorSize) ? WriteQueueLength : this.SectorSize; // this.SetCurentSector(Sector); // this.WriteBytes(Container, SourceCopyIndex, CopyCount); WriteQueueLength -= CopyCount; if (WriteQueueLength == 0) break; Sector = this.AllocateSector(Sector); } while (true); } else { BOF = Sector = AllocateShortSector(); // int SourceCopyIndex = 0; do { int CopyCount = (WriteQueueLength < this.ShortSectorSize) ? WriteQueueLength : this.ShortSectorSize; // this.SetCurentSector(Sector); // this.WriteBytes(Container, SourceCopyIndex, CopyCount); WriteQueueLength -= CopyCount; if (WriteQueueLength == 0) break; Sector = this.AllocateShortSector(Sector); } while (true); } return BOF; } #endif } internal class DirectoryStream : ArrayList { private CompoundDocumentHeader documentHeader; internal new DirectoryEntry this[int index] { get { return base[index] as DirectoryEntry; } } internal void Reset() { for (int i = 0; i < this.Count; i++) { base[i] = 0; } base.Clear(); } internal void Read(int Dir, StreamHelper file) { do { documentHeader.SetCurentSector(Dir); for (int i = 0; i < documentHeader.SectorSize / 128; i++) { DirectoryEntry entry = new DirectoryEntry(); entry.Read(file); Add(entry); } Dir = documentHeader.NextSector(Dir); } while (Dir != -2); } internal void Write(int Dir, StreamHelper file) { int local_directory_index = 0; DirectoryEntry entry; documentHeader.SetCurentSector(Dir); for (int i = 0; i < documentHeader.SectorSize / 128; i++) { if (local_directory_index < this.Count) { entry = this[local_directory_index] as DirectoryEntry; } else { entry = new DirectoryEntry(); } entry.Write(file); local_directory_index++; } } public DirectoryStream(CompoundDocumentHeader header) { documentHeader = header; } internal DirectoryEntry Add( string FileName, BIFF8_Stream stream, int left, int right) { DirectoryEntry entry = stream.streamDirEntry; entry.entry_name = FileName; entry.type = DirectoryEntry.DirEntryType.UseStream; entry.size = (uint)stream.Length; entry.leftChildDirID = left; entry.rightChildDirID = right; entry.colour = 0x01; stream.Position = 0; // byte[] payload = stream.ReadBytes((int) entry.Size); // entry.BOF = (uint)DocumentHeader.AllocateStream( payload.Length, false ); stream.streamDirEntry = entry; this.Add(entry); return entry; } internal DirectoryEntry Add(string FileName) { BIFF8_Container Container = documentHeader.shortStreamContainer; DirectoryEntry entry = new DirectoryEntry(); entry.entry_name = FileName; entry.type = DirectoryEntry.DirEntryType.RootStorage; entry.size = (uint)Container.Length; entry.bOF = (uint)documentHeader.WriteShortStreamContainer(); entry.rootDirID = 3; entry.colour = 0x01; this.Add(entry); return entry; } } /// /// /// internal class BIFF8_Container : StreamHelper { } internal class BIFF8_Stream : StreamHelper { internal DirectoryEntry streamDirEntry; internal CompoundDocumentHeader documentHeader; private int ShortSectorSize { get { return documentHeader.ShortSectorSize; } } internal void Write() { this.Position = 0; int BytesCount = (int)this.Length; if (BytesCount < documentHeader.minStreamSize) { this.streamDirEntry.bOF = (uint)this.documentHeader.AllocateShortSector(); int Sector = (int)this.streamDirEntry.bOF; while (BytesCount > 0) { int chunk_size = (BytesCount >= ShortSectorSize) ? ShortSectorSize : BytesCount; byte[] sector = this.ReadBytes(documentHeader.ShortSectorSize); documentHeader.shortStreamContainer.Position = documentHeader.ShortSectorOffset(Sector); documentHeader.shortStreamContainer.Write(sector, 0, chunk_size); BytesCount -= chunk_size; if (BytesCount <= 0) break; Sector = documentHeader.AllocateShortSector(Sector); } } else { this.streamDirEntry.bOF = (uint)this.documentHeader.AllocateSector(); int Sector = (int)this.streamDirEntry.bOF; while (BytesCount > 0) { documentHeader.SetCurentSector(Sector); byte[] sector = this.ReadBytes(documentHeader.SectorSize); documentHeader.Write(sector, 0, documentHeader.SectorSize); BytesCount -= documentHeader.SectorSize; if (BytesCount <= 0) break; Sector = documentHeader.AllocateSector(Sector); } } } public BIFF8_Stream(CompoundDocumentHeader document_header, DirectoryEntry entry) { this.streamDirEntry = entry; this.documentHeader = document_header; int Sector = (int)entry.bOF; int BytesCount = (int)entry.size; if (BytesCount < documentHeader.minStreamSize) { while (BytesCount > 0) { int chunk_size = (BytesCount >= ShortSectorSize) ? ShortSectorSize : BytesCount; documentHeader.shortStreamContainer.Position = documentHeader.ShortSectorOffset(Sector); byte[] sector_data = documentHeader.shortStreamContainer.ReadBytes(chunk_size); this.Write(sector_data, 0, chunk_size); Sector = documentHeader.NextShortSector(Sector); BytesCount -= chunk_size; } } else { while (BytesCount > 0) { documentHeader.SetCurentSector(Sector); if (BytesCount >= documentHeader.SectorSize) { byte[] sector = documentHeader.ReadBytes(documentHeader.SectorSize); base.Write(sector, 0, documentHeader.SectorSize); BytesCount -= documentHeader.SectorSize; } else { byte[] sector = documentHeader.ReadBytes(BytesCount); base.Write(sector, 0, BytesCount); BytesCount -= documentHeader.SectorSize; } Sector = documentHeader.NextSector(Sector); } } base.Position = 0; } } }