private int ReadMultiUnitFile(byte[] buffer, BlockTableEntry blockEntry)
        {
            var sectorCount = blockEntry.FileSize / SectorSize + 1;

            //if (blockEntry.FileSize%SectorSize > 0)
            //sectorCount++;

            var hasCRC = (blockEntry.Flags & BlockFlags.HasChecksums) != 0;

            if (hasCRC)
            {
                sectorCount++;
            }

            var sectorTable = new int[sectorCount + 1];

            for (var i = 0; i < sectorTable.Length; i++)
            {
                sectorTable[i] = _reader.ReadInt32();
            }

            var resultPosition = 0;

            long left = blockEntry.FileSize;

            for (var i = 0; i < sectorCount - (hasCRC ? 1 : 0); i++)
            {
                var position = sectorTable[i];
                var length   = sectorTable[i + 1] - position;

                SeekToArchiveOffset(position + blockEntry.BlockOffset);
                var tempBuffer = ArrayPool <byte> .Shared.Rent(length);

                var decompressedBuffer = tempBuffer;
                int decompressedLength = length;
                var sectorData         = _reader.Read(tempBuffer, 0, length);
                Debug.Assert(sectorData == length);

                if (blockEntry.IsCompressed && left > length)
                {
                    var compressionFlags = (CompressionFlags)tempBuffer[0];

                    if (compressionFlags == CompressionFlags.Bzip2)
                    {
                        decompressedBuffer = Compression.BZip2Decompress(tempBuffer, 1, length - 1);
                        decompressedLength = decompressedBuffer.Length;
                        Array.Copy(decompressedBuffer, 0, buffer, resultPosition, decompressedLength);
                    }
                    else if (compressionFlags == CompressionFlags.Deflated)
                    {
                        decompressedLength = Compression.DeflateTo(tempBuffer, 1, length - 1, buffer.AsSpan(resultPosition));
                    }
                    else
                    {
                        throw new NotSupportedException(
                                  "Currenlty only Bzip2 and Deflate compression is supported by Nmpq. Compression flags: " + compressionFlags);
                    }
                }
                else
                {
                    Array.Copy(decompressedBuffer, 0, buffer, resultPosition, decompressedLength);
                }

                ArrayPool <byte> .Shared.Return(tempBuffer);

                resultPosition += decompressedLength;
                left           -= decompressedLength;
            }

            return((int)blockEntry.FileSize);
        }
        // todos: support more decompression algorithms?
        public int?ReadFile(byte[] buffer, string path)
        {
            if (path == null)
            {
                throw new ArgumentNullException("path");
            }

            var blockEntry = FindBlockTableEntry(path);

            if (blockEntry == null)
            {
                return(null);
            }

            if (!blockEntry.Value.IsFile)
            {
                throw new NotSupportedException("Non-file blocks are not currently supported by Nmpq.");
            }

            if (blockEntry.Value.IsEncrypted)
            {
                throw new NotSupportedException("Encrypted files are not currently supported by Nmpq.");
            }

            if (blockEntry.Value.IsImploded)
            {
                throw new NotSupportedException("Imploded files are not currently supported by Nmpq.");
            }

            SeekToArchiveOffset(blockEntry.Value.BlockOffset);

            if (!blockEntry.Value.IsFileSingleUnit)
            {
                return(ReadMultiUnitFile(buffer, blockEntry.Value));
            }

            // file is only compressed if the block size is smaller than the file size.
            //	per docs at (http://wiki.devklog.net/index.php?title=MPQ_format_specification)
            var compressed = blockEntry.Value.IsCompressed && blockEntry.Value.BlockSize < blockEntry.Value.FileSize;

            if (!compressed)
            {
                return(_reader.Read(buffer, 0, (int)blockEntry.Value.BlockSize));
            }

            // first byte of each compressed block is a set of flags indicating which
            //	compression algorithm(s) to use
            var compressionFlags = (CompressionFlags)_reader.ReadByte();

            // compression flags don't count toward the data size, but does toward the block size
            var dataSize       = (int)(blockEntry.Value.BlockSize - 1);
            var compressedData = ArrayPool <byte> .Shared.Rent((int)dataSize);

            _reader.Read(compressedData, 0, (int)dataSize);
            int decompressedLength;

            if (compressionFlags == CompressionFlags.Bzip2)
            {
                decompressedLength = Compression.BZip2Decompress(compressedData, 0, dataSize, buffer);
            }
            else if (compressionFlags == CompressionFlags.Deflated)
            {
                decompressedLength = Compression.Deflate(compressedData, 0, dataSize, buffer);
            }
            else
            {
                throw new NotSupportedException("Currenlty only Bzip2 and Deflate compression is supported by Nmpq. Compression flags: " + compressionFlags);
            }

            ArrayPool <byte> .Shared.Return(compressedData);

            return(decompressedLength);
        }