Пример #1
0
        private void writeFiles(UnityBinaryWriter writer)
        {
            // calc total size
            int totalsize = Files.Sum(f => f.Data.Length);

            // teardown into blocks
            // 1.File is not aligned to block boundary (Simple concatation)
            // 2.Maximum block size is BLOCK_SIZE

            // Calculate block count
            var totalbytes = Files.Sum(f => f.Data.Length);
            var blockcount = totalbytes / BLOCK_SIZE + (totalbytes % BLOCK_SIZE != 0 ? 1 : 0);

            // Build blockinfo
            BlockInfo[] blockinfos = new BlockInfo[blockcount];
            short       blockflag  = EnableCompression ? (short)2 : (short)0;

            for (int i = 0; i < blockcount; i++)
            {
                blockinfos[i].uncompressedSize = BLOCK_SIZE;
                blockinfos[i].compressedSize   = BLOCK_SIZE;
                blockinfos[i].flag             = blockflag;
            }
            if (totalbytes % BLOCK_SIZE != 0)
            {
                blockinfos[blockcount - 1].uncompressedSize = totalbytes % BLOCK_SIZE;
                blockinfos[blockcount - 1].compressedSize   = totalbytes % BLOCK_SIZE;
            }

            // Seek Writer (Skip Info)
            //int infoheadersize = 4 + 4 + 4;
            int blockinfosize = 0x10 + 4 + (4 + 4 + 2) * blockinfos.Length;
            int fileinfosize  = 4 + Files.Sum(f => f.CalcInfoSize());
            int info_offset   = writer.Position;

            // Write Blocks

            // If no compression required, just copy all files
            if (!EnableCompression)
            {
                // Write Header

                // Info Header
                writer.WriteIntBE(blockinfosize + fileinfosize);
                writer.WriteIntBE(blockinfosize + fileinfosize);
                writer.WriteIntBE(0x40);
                writer.Position += 0x10;
                // BlockInfo
                writer.WriteIntBE(blockcount);
                blockinfos.Write(writer);
                // FileInfo
                writer.WriteIntBE(Files.Length);
                int curoffset = 0;
                for (int i = 0; i < Files.Length; i++)
                {
                    writer.WriteLongBE(curoffset);
                    writer.WriteLongBE(Files[i].Data.LongLength);
                    writer.WriteIntBE(4);
                    writer.WriteStringToNull(Files[i].Name);
                    curoffset += Files[i].Data.Length;
                }

                // Write Files
                for (int i = 0; i < Files.Length; i++)
                {
                    writer.WriteBytes(Files[i].Data);
                }
            }
            // In compression mode, try to parallelize the compression
            else
            {
                // First of all, Prepare buffer for compression
                byte[] compbuf = MemoryPool <AssetBundleFile> .GetBuffer(blockcount *BLOCK_SIZE);

                // don't parallelize when block count is small
                if (blockcount < 128)
                {
                    byte[] boundarybuf = MiniMemoryPool <AssetBundleFile> .GetBuffer(BLOCK_SIZE);

                    int remainlength = 0;
                    int curblock     = 0;
                    for (int i = 0; i < Files.Length; i++)
                    {
                        // If previous file has overflow, concat and compress
                        if (remainlength > 0)
                        {
                            Buffer.BlockCopy(Files[i].Data, 0, boundarybuf, remainlength, BLOCK_SIZE - remainlength);
                            blockinfos[curblock].compressedSize = TryLZ4Compress(boundarybuf, 0, compbuf, curblock * BLOCK_SIZE, BLOCK_SIZE);
                            if (blockinfos[curblock].compressedSize == BLOCK_SIZE)
                            {
                                blockinfos[curblock].flag &= ~0x3F;
                            }
                            curblock++;
                        }

                        // update remainlength
                        int blockstart = 0;
                        if (remainlength > 0)
                        {
                            blockstart = BLOCK_SIZE - remainlength;
                        }

                        // compress fullblocks
                        int fullblockcount = (Files[i].Data.Length - blockstart) / BLOCK_SIZE;
                        for (int j = 0; j < fullblockcount; j++, curblock++)
                        {
                            blockinfos[curblock].compressedSize = TryLZ4Compress(Files[i].Data, blockstart + j * BLOCK_SIZE, compbuf, curblock * BLOCK_SIZE, BLOCK_SIZE);
                            if (blockinfos[curblock].compressedSize == BLOCK_SIZE)
                            {
                                blockinfos[curblock].flag &= ~0x3F;
                            }
                        }

                        // If the file has remaindata, buffer them
                        remainlength = (Files[i].Data.Length - blockstart) % BLOCK_SIZE;
                        if (remainlength > 0)
                        {
                            Buffer.BlockCopy(Files[i].Data, Files[i].Data.Length - remainlength, boundarybuf, 0, remainlength);
                        }
                    }
                    if (remainlength > 0)  // Process last block
                    {
                        blockinfos[curblock].compressedSize = TryLZ4Compress(boundarybuf, 0, compbuf, curblock * BLOCK_SIZE, remainlength);
                        if (blockinfos[curblock].compressedSize == remainlength)
                        {
                            blockinfos[curblock].flag &= ~0x3F;
                        }
                    }
                }
                else
                {
                    // Create CompressionInfo & Compress file boundary
                    CompressionInfo[] compinfos   = new CompressionInfo[blockcount];
                    byte[]            boundarybuf = MiniMemoryPool <AssetBundleFile> .GetBuffer(BLOCK_SIZE);

                    int curblock     = 0;
                    int remainlength = 0;
                    for (int i = 0; i < Files.Length; i++)
                    {
                        // If previous file has overflow, concat and compress
                        if (remainlength > 0)
                        {
                            Buffer.BlockCopy(Files[i].Data, 0, boundarybuf, remainlength, BLOCK_SIZE - remainlength);
                            blockinfos[curblock].compressedSize = TryLZ4Compress(boundarybuf, 0, compbuf, curblock * BLOCK_SIZE, BLOCK_SIZE);
                            if (blockinfos[curblock].compressedSize == BLOCK_SIZE)
                            {
                                blockinfos[curblock].flag &= ~0x3F;
                            }
                            curblock++;
                        }

                        int blockstart = 0;
                        if (remainlength > 0)
                        {
                            blockstart = BLOCK_SIZE - remainlength;
                        }

                        int fullblockcount = (Files[i].Data.Length - blockstart) / BLOCK_SIZE;
                        for (int j = 0; j < fullblockcount; j++, curblock++)
                        {
                            compinfos[curblock].data   = Files[i].Data;
                            compinfos[curblock].length = BLOCK_SIZE;
                            compinfos[curblock].offset = blockstart + j * BLOCK_SIZE;
                        }

                        // If the file has remaindata, buffer them
                        remainlength = (Files[i].Data.Length - blockstart) % BLOCK_SIZE;
                        if (remainlength > 0)
                        {
                            Buffer.BlockCopy(Files[i].Data, Files[i].Data.Length - remainlength, boundarybuf, 0, remainlength);
                        }
                    }
                    if (remainlength > 0)   // Process last block
                    {
                        blockinfos[curblock].compressedSize =
                            LZ4.LZ4Codec.Encode(boundarybuf, 0, remainlength,
                                                compbuf, curblock * BLOCK_SIZE, remainlength);
                        // If compression is no use, just copy
                        if (blockinfos[curblock].compressedSize == 0)
                        {
                            blockinfos[curblock].compressedSize = remainlength;
                            blockinfos[curblock].flag          &= ~0x3F;
                            Buffer.BlockCopy(boundarybuf, 0, compbuf, curblock * BLOCK_SIZE, BLOCK_SIZE);
                        }
                    }

                    // Parallelly compress the data
                    Parallel.For(0, blockcount, i => {
                        if (compinfos[i].data == null)
                        {
                            return;
                        }
                        blockinfos[i].compressedSize = TryLZ4Compress(compinfos[i].data, compinfos[i].offset, compbuf, i * BLOCK_SIZE, compinfos[i].length);
                        if (blockinfos[i].compressedSize == BLOCK_SIZE)
                        {
                            blockinfos[i].flag &= ~0x3F;
                        }
                    });
                }

                // Write Headers
                UnityBinaryWriter headerwriter = new UnityBinaryWriter();
                // Info Header
                headerwriter.Position += 0x10;
                // BlockInfo
                headerwriter.WriteIntBE(blockcount);
                blockinfos.Write(headerwriter);
                // FileInfo
                headerwriter.WriteIntBE(Files.Length);
                int curoffset = 0;
                for (int i = 0; i < Files.Length; i++)
                {
                    headerwriter.WriteLongBE(curoffset);
                    headerwriter.WriteLongBE(Files[i].Data.LongLength);
                    headerwriter.WriteIntBE(4);
                    headerwriter.WriteStringToNull(Files[i].Name);
                    curoffset += Files[i].Data.Length;
                }

                // Compress and write header
                writer.Position += 4 + 4 + 4;
                int header_compsize = writer.WriteLZ4Data(headerwriter.ToBytes());
                int final_pos       = writer.Position;
                writer.Position = info_offset;
                writer.WriteIntBE(header_compsize);
                writer.WriteIntBE(blockinfosize + fileinfosize);
                writer.WriteIntBE(0x42);
                writer.Position = final_pos;

                // Write Blocks
                for (int i = 0; i < blockcount; i++)
                {
                    writer.WriteBytes(compbuf, i * BLOCK_SIZE, blockinfos[i].compressedSize);
                }
            }
        }