113 lines
4.4 KiB
C#
113 lines
4.4 KiB
C#
|
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}");
|
|||
|
}
|
|||
|
}
|
|||
|
}
|