Esempio n. 1
0
        private void Read530Blocks(SmartStream dataStream, long headerSize)
        {
            if (Header.Flags.IsMetadataAtTheEnd())
            {
                dataStream.Position = headerSize;
            }

            int  cachedBlock = -1;
            long dataOffset  = dataStream.Position;

            // If MemoryStream has compressed block then we need to create individual streams for each entry and copy its data into it
            bool createIndividualStreams = dataStream.StreamType == SmartStreamType.Memory;

            if (createIndividualStreams)
            {
                // find out if this bundle file has compressed blocks
                foreach (BlockInfo block in Metadata.BlockInfos)
                {
                    if (block.Flags.GetCompression() != BundleCompressType.None)
                    {
                        createIndividualStreams = true;
                        break;
                    }
                }
            }

            using (SmartStream blockStream = SmartStream.CreateNull())
            {
                foreach (BundleFileEntry entry in Metadata.Entries.Values)
                {
                    // find out block offsets
                    long blockCompressedOffset   = 0;
                    long blockDecompressedOffset = 0;
                    int  blockIndex = 0;
                    while (blockDecompressedOffset + Metadata.BlockInfos[blockIndex].DecompressedSize <= entry.Offset)
                    {
                        blockCompressedOffset   += Metadata.BlockInfos[blockIndex].CompressedSize;
                        blockDecompressedOffset += Metadata.BlockInfos[blockIndex].DecompressedSize;
                        blockIndex++;
                    }

                    // if at least one block of this entry is compressed or acording to the rule above
                    // we should copy the data of current entry to a separate stream
                    bool needToCopy = createIndividualStreams;
                    if (!needToCopy)
                    {
                        // check if this entry has compressed blocks
                        long entrySize = 0;
                        for (int bi = blockIndex; entrySize < entry.Size; bi++)
                        {
                            if (Metadata.BlockInfos[bi].Flags.GetCompression() != BundleCompressType.None)
                            {
                                // it does. then we need to create individual stream and decomress its data into it
                                needToCopy = true;
                                break;
                            }
                            entrySize += Metadata.BlockInfos[bi].DecompressedSize;
                        }
                    }

                    long entryOffsetInsideBlock = entry.Offset - blockDecompressedOffset;
                    if (needToCopy)
                    {
                        // well, at leat one block is compressed so we should copy data of current entry to a separate stream
                        using (SmartStream entryStream = CreateStream(entry.Size))
                        {
                            long left = entry.Size;
                            dataStream.Position = dataOffset + blockCompressedOffset;

                            // copy data of all blocks used by current entry to new stream
                            for (int bi = blockIndex; left > 0; bi++)
                            {
                                long      blockOffset = 0;
                                BlockInfo block       = Metadata.BlockInfos[bi];
                                if (cachedBlock == bi)
                                {
                                    // some data of previous entry is in the same block as this one
                                    // so we don't need to unpack it once again. Instead we can use cached stream
                                    dataStream.Position += block.CompressedSize;
                                }
                                else
                                {
                                    BundleCompressType compressType = block.Flags.GetCompression();
                                    switch (compressType)
                                    {
                                    case BundleCompressType.None:
                                        blockOffset = dataOffset + blockCompressedOffset;
                                        blockStream.Assign(dataStream);
                                        break;

                                    case BundleCompressType.LZMA:
                                        blockStream.Move(CreateStream(block.DecompressedSize));
                                        SevenZipHelper.DecompressLZMAStream(dataStream, block.CompressedSize, blockStream, block.DecompressedSize);
                                        break;

                                    case BundleCompressType.LZ4:
                                    case BundleCompressType.LZ4HZ:
                                        blockStream.Move(CreateStream(block.DecompressedSize));
                                        using (Lz4DecodeStream lzStream = new Lz4DecodeStream(dataStream, block.CompressedSize))
                                        {
                                            long read = lzStream.Read(blockStream, block.DecompressedSize);
                                            if (read != block.DecompressedSize)
                                            {
                                                throw new Exception($"Read {read} but expected {block.DecompressedSize}");
                                            }
                                            if (lzStream.IsDataLeft)
                                            {
                                                throw new Exception($"LZ4 stream still has some data");
                                            }
                                        }
                                        break;

                                    default:
                                        throw new NotImplementedException($"Bundle compression '{compressType}' isn't supported");
                                    }
                                    cachedBlock = bi;
                                }

                                // consider next offsets:
                                // 1) block - if it is new stream then offset is 0, otherwise offset of this block in the bundle file
                                // 2) entry - if this is first block for current entry then it is offset of this entry related to this block
                                //			  otherwise 0
                                long fragmentSize = block.DecompressedSize - entryOffsetInsideBlock;
                                blockStream.Position   = blockOffset + entryOffsetInsideBlock;
                                entryOffsetInsideBlock = 0;

                                long size = Math.Min(fragmentSize, left);
                                blockStream.CopyStream(entryStream, size);

                                blockCompressedOffset += block.CompressedSize;
                                left -= size;
                            }
                            if (left < 0)
                            {
                                throw new Exception($"Read more than expected");
                            }

                            FileEntryOffset feOffset = new FileEntryOffset(entryStream.CreateReference(), 0);
                            m_entryStreams.Add(entry, feOffset);
                        }
                    }
                    else
                    {
                        // no compressed blocks was found so we can use original bundle stream
                        // since FileEntry.Offset contains decompressedOffset we need to preliminarily subtract it
                        FileEntryOffset feOffset = new FileEntryOffset(dataStream.CreateReference(), dataOffset + blockCompressedOffset + entryOffsetInsideBlock);
                        m_entryStreams.Add(entry, feOffset);
                    }
                }
            }
        }
Esempio n. 2
0
        private SmartStream Read530Metadata(BundleReader reader, long headerSize)
        {
            if (Header.Flags.IsMetadataAtTheEnd())
            {
                reader.BaseStream.Position = Header.BundleSize - Header.MetadataCompressedSize;
            }

            BundleCompressType metaCompression = Header.Flags.GetCompression();

            switch (metaCompression)
            {
            case BundleCompressType.None:
            {
                Metadata.Read(reader);
                long expectedPosition = Header.Flags.IsMetadataAtTheEnd() ? Header.BundleSize : headerSize + Header.MetadataDecompressedSize;
                if (reader.BaseStream.Position != expectedPosition)
                {
                    throw new Exception($"Read {reader.BaseStream.Position - headerSize} but expected {Header.MetadataDecompressedSize}");
                }
            }
            break;

            case BundleCompressType.LZMA:
            {
                using (MemoryStream stream = new MemoryStream(new byte[Header.MetadataDecompressedSize]))
                {
                    SevenZipHelper.DecompressLZMASizeStream(reader.BaseStream, Header.MetadataCompressedSize, stream);
                    using (BundleReader decompressReader = new BundleReader(stream, reader.EndianType, reader.Generation))
                    {
                        Metadata.Read(decompressReader);
                    }
                    if (stream.Position != Header.MetadataDecompressedSize)
                    {
                        throw new Exception($"Read {stream.Position} but expected {Header.MetadataDecompressedSize}");
                    }
                }
            }
            break;

            case BundleCompressType.LZ4:
            case BundleCompressType.LZ4HZ:
            {
                using (MemoryStream stream = new MemoryStream(new byte[Header.MetadataDecompressedSize]))
                {
                    using (Lz4DecodeStream decodeStream = new Lz4DecodeStream(reader.BaseStream, Header.MetadataCompressedSize))
                    {
                        long read = decodeStream.Read(stream, Header.MetadataDecompressedSize);
                        if (read != Header.MetadataDecompressedSize)
                        {
                            throw new Exception($"Read {read} but expected {Header.MetadataDecompressedSize}");
                        }
                        if (decodeStream.IsDataLeft)
                        {
                            throw new Exception($"LZ4 stream still has some data");
                        }
                    }

                    stream.Position = 0;
                    using (BundleReader decompressReader = new BundleReader(stream, reader.EndianType, reader.Generation))
                    {
                        Metadata.Read(decompressReader);
                    }
                    if (stream.Position != Header.MetadataDecompressedSize)
                    {
                        throw new Exception($"Read {stream.Position} but expected {Header.MetadataDecompressedSize}");
                    }
                }
            }
            break;

            default:
                throw new NotSupportedException($"Bundle compression '{metaCompression}' isn't supported");
            }
            return(m_stream.CreateReference());
        }