Exemple #1
0
 /// <summary>
 /// Create a single instance initialized with default values
 /// </summary>
 /// <param name="bigBlockSize"></param>
 /// <param name="properties">the properties to be inserted</param>
 /// <param name="offset">the offset into the properties array</param>
 protected PropertyBlock(POIFSBigBlockSize bigBlockSize, Property[] properties, int offset) : base(bigBlockSize)
 {
     _properties = new Property[bigBlockSize.GetPropertiesPerBlock()];
     for (int j = 0; j < _properties.Length; j++)
     {
         _properties[j] = properties[j + offset];
     }
 }
        public SmallDocumentBlock(POIFSBigBlockSize bigBlockSize, byte[] data, int index)
        {
            _bigBlockSize         = bigBlockSize;
            _blocks_per_big_block = GetBlocksPerBigBlock(bigBlockSize);
            _data = new byte[_block_size];

            System.Array.Copy(data, index * _block_size, _data, 0, _block_size);
        }
Exemple #3
0
 internal BigBlockStore(POIFSBigBlockSize bigBlockSize, POIFSDocumentPath path, string name, int size, POIFSWriterListener writer)
 {
     this.bigBlockSize = bigBlockSize;
     this.bigBlocks    = new DocumentBlock[0];
     this.path         = path;
     this.name         = name;
     this.size         = size;
     this.writer       = writer;
 }
Exemple #4
0
 internal BigBlockStore(POIFSBigBlockSize bigBlockSize, DocumentBlock[] blocks)
 {
     this.bigBlockSize = bigBlockSize;
     bigBlocks         = (DocumentBlock[])blocks.Clone();
     path   = null;
     name   = null;
     size   = -1;
     writer = null;
 }
Exemple #5
0
 internal SmallBlockStore(POIFSBigBlockSize bigBlockSize, SmallDocumentBlock[] blocks)
 {
     this.bigBlockSize = bigBlockSize;
     smallBlocks       = (SmallDocumentBlock[])blocks.Clone();
     this.path         = null;
     this.name         = null;
     this.size         = -1;
     this.writer       = null;
 }
Exemple #6
0
        public static BATBlockAndIndex GetSBATBlockAndIndex(int offset, HeaderBlock header, List <BATBlock> sbats)
        {
            POIFSBigBlockSize bigBlockSize = header.BigBlockSize;

            int whichSBAT = (int)Math.Floor(1.0 * offset / bigBlockSize.GetBATEntriesPerBlock());
            int index     = offset % bigBlockSize.GetBATEntriesPerBlock();

            return(new BATBlockAndIndex(index, sbats[whichSBAT]));
        }
Exemple #7
0
        public void TestUsedSectors()
        {
            POIFSBigBlockSize b512  = POIFSConstants.SMALLER_BIG_BLOCK_SIZE_DETAILS;
            POIFSBigBlockSize b4096 = POIFSConstants.LARGER_BIG_BLOCK_SIZE_DETAILS;

            // Try first with 512 block sizes, which can hold 128 entries
            BATBlock block512 = BATBlock.CreateEmptyBATBlock(b512, false);

            Assert.AreEqual(true, block512.HasFreeSectors);
            Assert.AreEqual(0, block512.GetUsedSectors(false));
            // Allocate a few
            block512.SetValueAt(0, 42);
            block512.SetValueAt(10, 42);
            block512.SetValueAt(20, 42);
            Assert.AreEqual(true, block512.HasFreeSectors);
            Assert.AreEqual(3, block512.GetUsedSectors(false));

            // Allocate all
            for (int i = 0; i < b512.GetBATEntriesPerBlock(); i++)
            {
                block512.SetValueAt(i, 82);
            }
            // Check
            Assert.AreEqual(false, block512.HasFreeSectors);
            Assert.AreEqual(128, block512.GetUsedSectors(false));
            Assert.AreEqual(127, block512.GetUsedSectors(true));

            // Release one
            block512.SetValueAt(10, POIFSConstants.UNUSED_BLOCK);
            Assert.AreEqual(true, block512.HasFreeSectors);
            Assert.AreEqual(127, block512.GetUsedSectors(false));
            Assert.AreEqual(126, block512.GetUsedSectors(true));


            // Now repeat with 4096 block sizes
            BATBlock block4096 = BATBlock.CreateEmptyBATBlock(b4096, false);

            Assert.AreEqual(true, block4096.HasFreeSectors);
            Assert.AreEqual(0, block4096.GetUsedSectors(false));

            block4096.SetValueAt(0, 42);
            block4096.SetValueAt(10, 42);
            block4096.SetValueAt(20, 42);
            Assert.AreEqual(true, block4096.HasFreeSectors);
            Assert.AreEqual(3, block4096.GetUsedSectors(false));

            // Allocate all
            for (int i = 0; i < b4096.GetBATEntriesPerBlock(); i++)
            {
                block4096.SetValueAt(i, 82);
            }
            // Check
            Assert.AreEqual(false, block4096.HasFreeSectors);
            Assert.AreEqual(1024, block4096.GetUsedSectors(false));
            Assert.AreEqual(1023, block4096.GetUsedSectors(true));
        }
Exemple #8
0
        /// <summary>
        /// Makes the empty small document block.
        /// </summary>
        /// <returns></returns>
        private static SmallDocumentBlock MakeEmptySmallDocumentBlock(POIFSBigBlockSize bigBlockSize)
        {
            SmallDocumentBlock block = new SmallDocumentBlock(bigBlockSize);

            for (int i = 0; i < block._data.Length; i++)
            {
                block._data[i] = _default_fill;
            }
            return(block);
        }
Exemple #9
0
        ///**
        // * Creates a single BATBlock, with all the values set to empty.
        // */
        public static BATBlock CreateEmptyBATBlock(POIFSBigBlockSize bigBlockSize, bool isXBAT)
        {
            BATBlock block = new BATBlock(bigBlockSize);

            if (isXBAT)
            {
                block.SetXBATChain(bigBlockSize, POIFSConstants.END_OF_CHAIN);
            }
            return(block);
        }
Exemple #10
0
 private static BlockAllocationTableReader prepareReader(
     POIFSBigBlockSize bigBlockSize,
     RawDataBlockList blockList, BlockList list,
     RootProperty root, int sbatStart)
 {
     // Process the SBAT and blocks
     return(new BlockAllocationTableReader(bigBlockSize,
                                           blockList.FetchBlocks(sbatStart, -1),
                                           list));
 }
        /**
         * Read and process the PropertiesTable and the
         *  FAT / XFAT blocks, so that we're Ready to
         *  work with the file
         */
        private void ReadCoreContents()
        {
            // Grab the block size
            bigBlockSize = _header.BigBlockSize;

            // Each block should only ever be used by one of the
            //  FAT, XFAT or Property Table. Ensure it does
            ChainLoopDetector loopDetector = GetChainLoopDetector();

            // Read the FAT blocks
            foreach (int fatAt in _header.BATArray)
            {
                ReadBAT(fatAt, loopDetector);
            }

            // Now read the XFAT blocks, and the FATs within them
            BATBlock xfat;
            int nextAt = _header.XBATIndex;
            for (int i = 0; i < _header.XBATCount; i++)
            {
                loopDetector.Claim(nextAt);
                ByteBuffer fatData = GetBlockAt(nextAt);
                xfat = BATBlock.CreateBATBlock(bigBlockSize, fatData);
                xfat.OurBlockIndex = nextAt;
                nextAt = xfat.GetValueAt(bigBlockSize.GetXBATEntriesPerBlock());
                _xbat_blocks.Add(xfat);

                for (int j = 0; j < bigBlockSize.GetXBATEntriesPerBlock(); j++)
                {
                    int fatAt = xfat.GetValueAt(j);
                    if (fatAt == POIFSConstants.UNUSED_BLOCK) break;
                    ReadBAT(fatAt, loopDetector);
                }
            }

            // We're now able to load steams
            // Use this to read in the properties
            _property_table = new NPropertyTable(_header, this);

            // Finally read the Small Stream FAT (SBAT) blocks
            BATBlock sfat;
            List<BATBlock> sbats = new List<BATBlock>();
            _mini_store = new NPOIFSMiniStore(this, _property_table.Root, sbats, _header);
            nextAt = _header.SBATStart;
            for (int i = 0; i < _header.SBATCount; i++)
            {
                loopDetector.Claim(nextAt);
                ByteBuffer fatData = GetBlockAt(nextAt);
                sfat = BATBlock.CreateBATBlock(bigBlockSize, fatData);
                sfat.OurBlockIndex = nextAt;
                sbats.Add(sfat);
                nextAt = GetNextBlock(nextAt);
            }
        }
Exemple #12
0
        /// <summary>
        /// fetch the small document block list from an existing file
        /// </summary>
        /// <param name="blockList">the raw data from which the small block table will be extracted</param>
        /// <param name="root">the root property (which contains the start block and small block table size)</param>
        /// <param name="sbatStart">the start block of the SBAT</param>
        /// <returns>the small document block list</returns>
        public static BlockList GetSmallDocumentBlocks(POIFSBigBlockSize bigBlockSize,
                                                       RawDataBlockList blockList, RootProperty root,
                                                       int sbatStart)
        {
            BlockList list =
                new SmallDocumentBlockList(
                    SmallDocumentBlock.Extract(bigBlockSize, blockList.FetchBlocks(root.StartBlock, -1)));

            new BlockAllocationTableReader(bigBlockSize, blockList.FetchBlocks(sbatStart, -1), list);
            return(list);
        }
Exemple #13
0
        /// <summary>
        /// fetch the small document block list from an existing file, normally
        /// needed for debugging and low level dumping. You should typically call
        /// </summary>
        /// <param name="bigBlockSize">the poifs bigBlockSize</param>
        /// <param name="blockList">the raw data from which the small block table will be extracted</param>
        /// <param name="root">the root property (which contains the start block and small block table size)</param>
        /// <param name="sbatStart">the start block of the SBAT</param>
        /// <returns>the small document block reader</returns>
        public static BlockAllocationTableReader _getSmallDocumentBlockReader(
            POIFSBigBlockSize bigBlockSize,
            RawDataBlockList blockList, RootProperty root,
            int sbatStart)
        {
            BlockList list = prepareSmallDocumentBlocks(
                bigBlockSize, blockList, root, sbatStart);

            return(prepareReader(
                       bigBlockSize, blockList, list, root, sbatStart));
        }
Exemple #14
0
        /**
         * Calculates the maximum size of a file which is addressable given the
         *  number of FAT (BAT) sectors specified. (We don't care if those BAT
         *  blocks come from the 109 in the header, or from header + XBATS, it
         *  won't affect the calculation)
         *
         * The actual file size will be between [size of fatCount-1 blocks] and
         *   [size of fatCount blocks].
         *  For 512 byte block sizes, this means we may over-estimate by up to 65kb.
         *  For 4096 byte block sizes, this means we may over-estimate by up to 4mb
         */
        public static long CalculateMaximumSize(POIFSBigBlockSize bigBlockSize,
                                                int numBATs)
        {
            long size = 1; // Header isn't FAT addressed

            // The header has up to 109 BATs, and extra ones are referenced
            //  from XBATs
            // However, all BATs can contain 128/1024 blocks
            size += (numBATs * bigBlockSize.GetBATEntriesPerBlock());

            // So far we've been in sector counts, turn into bytes
            return(size * bigBlockSize.GetBigBlockSize());
        }
Exemple #15
0
        /// <summary>
        /// fill out a List of SmallDocumentBlocks so that it fully occupies
        /// a Set of big blocks
        /// </summary>
        /// <param name="bigBlockSize"></param>
        /// <param name="blocks">the List to be filled out.</param>
        /// <returns>number of big blocks the list encompasses</returns>
        public static int Fill(POIFSBigBlockSize bigBlockSize, IList <SmallDocumentBlock> blocks)
        {
            int _blocks_per_big_block = GetBlocksPerBigBlock(bigBlockSize);
            int count           = blocks.Count;
            int big_block_count = (count + _blocks_per_big_block - 1) / _blocks_per_big_block;
            int full_count      = big_block_count * _blocks_per_big_block;

            for (; count < full_count; count++)
            {
                blocks.Add(MakeEmptySmallDocumentBlock(bigBlockSize));
            }
            return(big_block_count);
        }
Exemple #16
0
        protected BATBlock(POIFSBigBlockSize bigBlockSize)
            : base(bigBlockSize)
        {
            int _entries_per_block = bigBlockSize.GetBATEntriesPerBlock();

            _values           = new int[_entries_per_block];
            _has_free_sectors = true;

            for (int i = 0; i < _values.Length; i++)
            {
                _values[i] = POIFSConstants.UNUSED_BLOCK;
            }
        }
Exemple #17
0
        public void PrivateHeaderBlock(byte[] data)
        {
            _data = data;

            long signature = LittleEndian.GetLong(_data, _signature_offset);

            if (signature != _signature)
            {
                byte[] OOXML_FILE_HEADER = POIFSConstants.OOXML_FILE_HEADER;
                if (_data[0] == OOXML_FILE_HEADER[0] &&
                    _data[1] == OOXML_FILE_HEADER[1] &&
                    _data[2] == OOXML_FILE_HEADER[2] &&
                    _data[3] == OOXML_FILE_HEADER[3])
                {
                    throw new OfficeXmlFileException("The supplied data appears to be in the Office 2007+ XML. You are calling the part of POI that deals with OLE2 Office Documents. You need to call a different part of POI to process this data (eg XSSF instead of HSSF)");
                }
                if ((signature & unchecked ((long)0xFF8FFFFFFFFFFFFFL)) == 0x0010000200040009L)
                {
                    throw new ArgumentException("The supplied data appears to be in BIFF2 format.  "
                                                + "POI only supports BIFF8 format");
                }

                // Give a generic error if the OLE2 signature isn't found
                throw new IOException("Invalid header signature; read "
                                      + LongToHex(signature) + ", expected "
                                      + LongToHex(_signature) + " - Your file appears "
                                      + "not to be a valid OLE2 document");
            }

            if (_data[30] == 12)
            {
                bigBlockSize = POIFSConstants.LARGER_BIG_BLOCK_SIZE_DETAILS;
            }
            else if (_data[30] == 9)
            {
                bigBlockSize = POIFSConstants.SMALLER_BIG_BLOCK_SIZE_DETAILS;
            }
            else
            {
                throw new IOException("Unsupported blocksize  (2^" + _data[30] + "). Expected 2^9 or 2^12.");
            }

            // Setup the fields to read and write the counts and starts
            _bat_count      = new IntegerField(HeaderBlockConstants._bat_count_offset, _data).Value;
            _property_start = new IntegerField(HeaderBlockConstants._property_start_offset, _data).Value;
            _sbat_start     = new IntegerField(HeaderBlockConstants._sbat_start_offset, _data).Value;
            _sbat_count     = new IntegerField(HeaderBlockConstants._sbat_block_count_offset, _data).Value;
            _xbat_start     = new IntegerField(HeaderBlockConstants._xbat_start_offset, _data).Value;
            _xbat_count     = new IntegerField(HeaderBlockConstants._xbat_count_offset, _data).Value;
        }
Exemple #18
0
        /**
         * Create a single instance initialized (perhaps partially) with entries
         *
         * @param entries the array of block allocation table entries
         * @param start_index the index of the first entry to be written
         *                    to the block
         * @param end_index the index, plus one, of the last entry to be
         *                  written to the block (writing is for all index
         *                  k, start_index &lt;= k &lt; end_index)
         */

        protected BATBlock(POIFSBigBlockSize bigBlockSize, int[] entries,
                           int start_index, int end_index)
            : this(bigBlockSize)
        {
            for (int k = start_index; k < end_index; k++)
            {
                _values[k - start_index] = entries[k];
            }

            // Do we have any free sectors?
            if (end_index - start_index == _values.Length)
            {
                RecomputeFree();
            }
        }
Exemple #19
0
        private static BlockList prepareSmallDocumentBlocks(
            POIFSBigBlockSize bigBlockSize,
            RawDataBlockList blockList, RootProperty root,
            int sbatStart)
        {
            // Fetch the blocks which hold the Small Blocks stream
            ListManagedBlock[]
            smallBlockBlocks =
                blockList.FetchBlocks(root.StartBlock, -1);

            // Turn that into a list
            BlockList list = new SmallDocumentBlockList(
                SmallDocumentBlock.Extract(bigBlockSize, smallBlockBlocks));

            return(list);
        }
Exemple #20
0
        /// <summary>
        /// create a list of SmallDocumentBlock's from raw data
        /// </summary>
        /// <param name="bigBlockSize"></param>
        /// <param name="blocks">the raw data containing the SmallDocumentBlock</param>
        /// <returns>a List of SmallDocumentBlock's extracted from the input</returns>
        public static List <SmallDocumentBlock> Extract(POIFSBigBlockSize bigBlockSize, ListManagedBlock [] blocks)
        {
            int _blocks_per_big_block      = GetBlocksPerBigBlock(bigBlockSize);
            List <SmallDocumentBlock> sdbs = new List <SmallDocumentBlock>();

            for (int j = 0; j < blocks.Length; j++)
            {
                byte[] data = blocks[j].Data;

                for (int k = 0; k < _blocks_per_big_block; k++)
                {
                    sdbs.Add(new SmallDocumentBlock(bigBlockSize, data, k));
                }
            }
            return(sdbs);
        }
Exemple #21
0
        /**
         * Create a single BATBlock from the byte buffer, which must hold at least
         *  one big block of data to be read.
         */
        public static BATBlock CreateBATBlock(POIFSBigBlockSize bigBlockSize, BinaryReader data)
        {
            // Create an empty block
            BATBlock block = new BATBlock(bigBlockSize);

            // Fill it
            byte[] buffer = new byte[LittleEndianConsts.INT_SIZE];
            for (int i = 0; i < block._values.Length; i++)
            {
                data.Read(buffer, 0, buffer.Length);
                block._values[i] = LittleEndian.GetInt(buffer);
            }
            block.RecomputeFree();

            // All done
            return(block);
        }
Exemple #22
0
 public POIFSDocument(string name, int size, POIFSBigBlockSize bigBlockSize, POIFSDocumentPath path, POIFSWriterListener writer)
 {
     _size              = size;
     _bigBigBlockSize   = bigBlockSize;
     _property          = new DocumentProperty(name, _size);
     _property.Document = this;
     if (_property.ShouldUseSmallBlocks)
     {
         _small_store = new SmallBlockStore(_bigBigBlockSize, path, name, size, writer);
         _big_store   = new BigBlockStore(_bigBigBlockSize, EMPTY_BIG_BLOCK_ARRAY);
     }
     else
     {
         _small_store = new SmallBlockStore(_bigBigBlockSize, EMPTY_SMALL_BLOCK_ARRAY);
         _big_store   = new BigBlockStore(_bigBigBlockSize, path, name, size, writer);
     }
 }
        public static DataInputBlock GetDataInputBlock(DocumentBlock[] blocks, int offset)
        {
            if (blocks == null || blocks.Length == 0)
            {
                return(null);
            }

            POIFSBigBlockSize bigBlockSize = blocks[0].bigBlockSize;
            int BLOCK_SHIFT = bigBlockSize.GetHeaderValue();
            int BLOCK_SIZE  = bigBlockSize.GetBigBlockSize();
            int BLOCK_MASK  = BLOCK_SIZE - 1;

            int firstBlockIndex  = offset >> BLOCK_SHIFT;
            int firstBlockOffset = offset & BLOCK_MASK;

            return(new DataInputBlock(blocks[firstBlockIndex]._data, firstBlockOffset));
        }
Exemple #24
0
        public POIFSDocument(string name, SmallDocumentBlock[] blocks, int length)
        {
            _size = length;
            if (blocks.Length == 0)
            {
                _bigBigBlockSize = POIFSConstants.SMALLER_BIG_BLOCK_SIZE_DETAILS;
            }
            else
            {
                _bigBigBlockSize = blocks[0].BigBlockSize;
            }

            _big_store         = new BigBlockStore(_bigBigBlockSize, EMPTY_BIG_BLOCK_ARRAY);
            _property          = new DocumentProperty(name, _size);
            _small_store       = new SmallBlockStore(_bigBigBlockSize, blocks);
            _property.Document = this;
        }
Exemple #25
0
        /// <summary>
        /// Create an array of BATBlocks from an array of int block
        /// allocation table entries
        /// </summary>
        /// <param name="bigBlockSize">the poifs bigBlockSize</param>
        /// <param name="entries">the array of int entries</param>
        /// <returns>the newly created array of BATBlocks</returns>
        public static BATBlock[] CreateBATBlocks(POIFSBigBlockSize bigBlockSize, int[] entries)
        {
            int block_count = CalculateStorageRequirements(entries.Length);

            BATBlock[] blocks    = new BATBlock[block_count];
            int        index     = 0;
            int        remaining = entries.Length;

            for (int j = 0; j < entries.Length; j += _entries_per_block)
            {
                blocks[index++] = new BATBlock(bigBlockSize, entries, j,
                                               (remaining > _entries_per_block)
                                                 ? j + _entries_per_block
                                                 : entries.Length);
                remaining -= _entries_per_block;
            }
            return(blocks);
        }
Exemple #26
0
        public POIFSDocument(string name, POIFSBigBlockSize bigBlockSize, ListManagedBlock[] blocks, int length)
        {
            _size              = length;
            _bigBigBlockSize   = bigBlockSize;
            _property          = new DocumentProperty(name, _size);
            _property.Document = this;

            if (Property.IsSmall(_size))
            {
                _big_store   = new BigBlockStore(bigBlockSize, EMPTY_BIG_BLOCK_ARRAY);
                _small_store = new SmallBlockStore(bigBlockSize, ConvertRawBlocksToSmallBlocks(blocks));
            }
            else
            {
                _big_store   = new BigBlockStore(bigBlockSize, ConvertRawBlocksToBigBlocks(blocks));
                _small_store = new SmallBlockStore(bigBlockSize, EMPTY_SMALL_BLOCK_ARRAY);
            }
        }
Exemple #27
0
        public POIFSDocument(string name, RawDataBlock[] blocks, int length)
        {
            _size = length;
            if (blocks.Length == 0)
            {
                _bigBigBlockSize = POIFSConstants.SMALLER_BIG_BLOCK_SIZE_DETAILS;
            }
            else
            {
                _bigBigBlockSize = (blocks[0].BigBlockSize == POIFSConstants.SMALLER_BIG_BLOCK_SIZE ?
                                    POIFSConstants.SMALLER_BIG_BLOCK_SIZE_DETAILS : POIFSConstants.LARGER_BIG_BLOCK_SIZE_DETAILS);
            }

            _big_store         = new BigBlockStore(_bigBigBlockSize, ConvertRawBlocksToBigBlocks(blocks));
            _property          = new DocumentProperty(name, _size);
            _small_store       = new SmallBlockStore(_bigBigBlockSize, EMPTY_SMALL_BLOCK_ARRAY);
            _property.Document = this;
        }
Exemple #28
0
        /// <summary>
        /// Create a OPOIFSFileSystem from an Stream. Normally the stream is Read until
        /// EOF.  The stream is always Closed.  In the unlikely case that the caller has such a stream and
        /// needs to use it after this constructor completes, a work around is to wrap the
        /// stream in order to trap the Close() call.
        /// </summary>
        /// <param name="stream">the Streamfrom which to Read the data</param>
        public OPOIFSFileSystem(Stream stream)
            : this()
        {
            bool success = false;

            HeaderBlock      header_block_reader;
            RawDataBlockList data_blocks;

            try
            {
                // Read the header block from the stream
                header_block_reader = new HeaderBlock(stream);
                bigBlockSize        = header_block_reader.BigBlockSize;

                // Read the rest of the stream into blocks
                data_blocks = new RawDataBlockList(stream, bigBlockSize);
                success     = true;
            }
            finally
            {
                CloseInputStream(stream, success);
            }


            // Set up the block allocation table (necessary for the
            // data_blocks to be manageable
            new BlockAllocationTableReader(header_block_reader.BigBlockSize,
                                           header_block_reader.BATCount,
                                           header_block_reader.BATArray,
                                           header_block_reader.XBATCount,
                                           header_block_reader.XBATIndex,
                                           data_blocks);

            // Get property table from the document
            PropertyTable properties = new PropertyTable(header_block_reader, data_blocks);

            // init documents
            ProcessProperties(SmallBlockTableReader.GetSmallDocumentBlocks(bigBlockSize, data_blocks, properties.Root, header_block_reader.SBATStart),
                              data_blocks, properties.Root.Children, null, header_block_reader.PropertyStart);

            // For whatever reason CLSID of root is always 0.
            Root.StorageClsid = (properties.Root.StorageClsid);
        }
        public void Test4KBlocks()
        {
            Stream inp = _samples.OpenResourceAsStream("BlockSize4096.zvi");

            try
            {
                // First up, check that we can process the header properly
                HeaderBlock       header_block = new HeaderBlock(inp);
                POIFSBigBlockSize bigBlockSize = header_block.BigBlockSize;
                Assert.AreEqual(4096, bigBlockSize.GetBigBlockSize());

                // Check the fat info looks sane
                Assert.AreEqual(1, header_block.BATArray.Length);
                Assert.AreEqual(1, header_block.BATCount);
                Assert.AreEqual(0, header_block.XBATCount);

                // Now check we can get the basic fat
                RawDataBlockList data_blocks = new RawDataBlockList(inp, bigBlockSize);
                Assert.AreEqual(15, data_blocks.BlockCount());

                // Now try and open properly
                POIFSFileSystem fs = new POIFSFileSystem(
                    _samples.OpenResourceAsStream("BlockSize4096.zvi")
                    );
                Assert.IsTrue(fs.Root.EntryCount > 3);

                // Check we can get at all the contents
                CheckAllDirectoryContents(fs.Root);


                // Finally, check we can do a similar 512byte one too
                fs = new POIFSFileSystem(
                    _samples.OpenResourceAsStream("BlockSize512.zvi")
                    );
                Assert.IsTrue(fs.Root.EntryCount > 3);
                CheckAllDirectoryContents(fs.Root);
            }
            finally
            {
                inp.Close();
            }
        }
        /// <summary>
        /// Set BAT block parameters. Assumes that all BAT blocks are
        /// contiguous. Will construct XBAT blocks if necessary and return
        /// the array of newly constructed XBAT blocks.
        /// </summary>
        /// <param name="blockCount">count of BAT blocks</param>
        /// <param name="startBlock">index of first BAT block</param>
        /// <returns>array of XBAT blocks; may be zero Length, will not be
        /// null</returns>
        public BATBlock[] SetBATBlocks(int blockCount, int startBlock)
        {
            BATBlock[] rvalue;

            POIFSBigBlockSize bigBlockSize = _header_block.BigBlockSize;

            _header_block.BATCount = blockCount;

            int limit = Math.Min(blockCount, _max_bats_in_header);

            int[] bat_blocks = new int[limit];
            for (int j = 0; j < limit; j++)
            {
                bat_blocks[j] = startBlock + j;
            }

            _header_block.BATArray = bat_blocks;

            if (blockCount > _max_bats_in_header)
            {
                int   excess_blocks      = blockCount - _max_bats_in_header;
                int[] excess_block_array = new int[excess_blocks];

                for (int j = 0; j < excess_blocks; j++)
                {
                    excess_block_array[j] = startBlock + j + _max_bats_in_header;
                }

                rvalue = BATBlock.CreateXBATBlocks(bigBlockSize, excess_block_array,
                                                   startBlock + blockCount);

                _header_block.XBATStart = startBlock + blockCount;
            }
            else
            {
                rvalue = BATBlock.CreateXBATBlocks(bigBlockSize, new int[0], 0);
                _header_block.XBATStart = POIFSConstants.END_OF_CHAIN;
            }

            _header_block.XBATCount = rvalue.Length;
            return(rvalue);
        }