hybridclr_unity/Editor/3rds/UnityFS/BundleFileWriter.cs

113 lines
4.4 KiB
C#
Raw Normal View History

2023-11-28 11:27:58 +08:00
using LZ4;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;
namespace UnityFS
{
public class BundleFileWriter
{
private readonly BundleFileInfo _bundle;
private readonly List<Node> _files = new List<Node>();
private readonly List<StorageBlock> _blocks = new List<StorageBlock>();
private readonly EndianBinaryWriter _blockDirectoryMetadataStream = new EndianBinaryWriter(new MemoryStream());
private byte[] _blockBytes;
public BundleFileWriter(BundleFileInfo bundle)
{
_bundle = bundle;
}
public void Write(EndianBinaryWriter output)
{
InitBlockAndDirectories();
output.WriteNullEndString(_bundle.signature);
output.Write(_bundle.version);
output.WriteNullEndString(_bundle.unityVersion);
output.WriteNullEndString(_bundle.unityRevision);
BuildBlockDirectoryMetadata();
long sizePos = output.Position;
output.Write(0L);
output.Write((uint)_blockDirectoryMetadataStream.Length);
output.Write((uint)_blockDirectoryMetadataStream.Length);
ArchiveFlags flags = ArchiveFlags.BlocksAndDirectoryInfoCombined | (uint)CompressionType.None;
output.Write((uint)flags);
if (_bundle.version >= 7)
{
output.AlignStream(16);
}
byte[] metadataBytes = _blockDirectoryMetadataStream.BaseStream.ReadAllBytes();
output.Write(metadataBytes, 0, metadataBytes.Length);
byte[] dataBytes = _blockBytes;
output.Write(dataBytes, 0, dataBytes.Length);
output.Position = sizePos;
output.Write(output.Length);
}
private void InitBlockAndDirectories()
{
var dataStream = new MemoryStream();
foreach(var file in _bundle.files)
{
byte[] data = file.data;
_files.Add(new Node { path = file.file, flags = 0, offset = dataStream.Length, size = data.LongLength });
dataStream.Write(data, 0, data.Length);
}
byte[] dataBytes = dataStream.ToArray();
var compressedBlockStream = new MemoryStream(dataBytes.Length / 2);
int blockByteSize = 128 * 1024;
long dataSize = dataBytes.Length;
byte[] tempCompressBlock = new byte[blockByteSize * 2];
for(long i = 0, blockNum = (dataSize + blockByteSize - 1) / blockByteSize; i < blockNum; i++)
{
long curBlockSize = Math.Min(dataSize, blockByteSize);
dataSize -= curBlockSize;
int compressedSize = LZ4Codec.Encode(dataBytes, (int)(i * blockByteSize), (int)curBlockSize, tempCompressBlock, 0, tempCompressBlock.Length);
compressedBlockStream.Write(tempCompressBlock, 0, compressedSize);
_blocks.Add(new StorageBlock { flags = (StorageBlockFlags)(int)CompressionType.Lz4, compressedSize = (uint)compressedSize, uncompressedSize = (uint)curBlockSize });
//Debug.Log($"== block[{i}] uncompressedSize:{curBlockSize} compressedSize:{compressedSize} totalblocksize:{compressedBlockStream.Length}");
}
_blockBytes = compressedBlockStream.ToArray();
}
private void BuildBlockDirectoryMetadata()
{
var hash = new byte[16];
_blockDirectoryMetadataStream.Write(hash, 0, 16);
_blockDirectoryMetadataStream.Write((uint)_blocks.Count);
foreach(var b in _blocks)
{
_blockDirectoryMetadataStream.Write(b.uncompressedSize);
_blockDirectoryMetadataStream.Write(b.compressedSize);
_blockDirectoryMetadataStream.Write((ushort)b.flags);
}
_blockDirectoryMetadataStream.Write((uint)_files.Count);
foreach(var f in _files)
{
_blockDirectoryMetadataStream.Write(f.offset);
_blockDirectoryMetadataStream.Write(f.size);
_blockDirectoryMetadataStream.Write(f.flags);
_blockDirectoryMetadataStream.WriteNullEndString(f.path);
}
//Debug.Log($"block and directory metadata size:{_blockDirectoryMetadataStream.Length}");
}
}
}