BundleFileWriter.cs 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112
  1. using LZ4;
  2. using System;
  3. using System.Collections.Generic;
  4. using System.IO;
  5. using System.Linq;
  6. using System.Text;
  7. using System.Threading.Tasks;
  8. using UnityEngine;
  9. namespace UnityFS
  10. {
  11. public class BundleFileWriter
  12. {
  13. private readonly BundleFileInfo _bundle;
  14. private readonly List<Node> _files = new List<Node>();
  15. private readonly List<StorageBlock> _blocks = new List<StorageBlock>();
  16. private readonly EndianBinaryWriter _blockDirectoryMetadataStream = new EndianBinaryWriter(new MemoryStream());
  17. private byte[] _blockBytes;
  18. public BundleFileWriter(BundleFileInfo bundle)
  19. {
  20. _bundle = bundle;
  21. }
  22. public void Write(EndianBinaryWriter output)
  23. {
  24. InitBlockAndDirectories();
  25. output.WriteNullEndString(_bundle.signature);
  26. output.Write(_bundle.version);
  27. output.WriteNullEndString(_bundle.unityVersion);
  28. output.WriteNullEndString(_bundle.unityRevision);
  29. BuildBlockDirectoryMetadata();
  30. long sizePos = output.Position;
  31. output.Write(0L);
  32. output.Write((uint)_blockDirectoryMetadataStream.Length);
  33. output.Write((uint)_blockDirectoryMetadataStream.Length);
  34. ArchiveFlags flags = ArchiveFlags.BlocksAndDirectoryInfoCombined | (uint)CompressionType.None;
  35. output.Write((uint)flags);
  36. if (_bundle.version >= 7)
  37. {
  38. output.AlignStream(16);
  39. }
  40. byte[] metadataBytes = _blockDirectoryMetadataStream.BaseStream.ReadAllBytes();
  41. output.Write(metadataBytes, 0, metadataBytes.Length);
  42. byte[] dataBytes = _blockBytes;
  43. output.Write(dataBytes, 0, dataBytes.Length);
  44. output.Position = sizePos;
  45. output.Write(output.Length);
  46. }
  47. private void InitBlockAndDirectories()
  48. {
  49. var dataStream = new MemoryStream();
  50. foreach(var file in _bundle.files)
  51. {
  52. byte[] data = file.data;
  53. _files.Add(new Node { path = file.file, flags = 0, offset = dataStream.Length, size = data.LongLength });
  54. dataStream.Write(data, 0, data.Length);
  55. }
  56. byte[] dataBytes = dataStream.ToArray();
  57. var compressedBlockStream = new MemoryStream(dataBytes.Length / 2);
  58. int blockByteSize = 128 * 1024;
  59. long dataSize = dataBytes.Length;
  60. byte[] tempCompressBlock = new byte[blockByteSize * 2];
  61. for(long i = 0, blockNum = (dataSize + blockByteSize - 1) / blockByteSize; i < blockNum; i++)
  62. {
  63. long curBlockSize = Math.Min(dataSize, blockByteSize);
  64. dataSize -= curBlockSize;
  65. int compressedSize = LZ4Codec.Encode(dataBytes, (int)(i * blockByteSize), (int)curBlockSize, tempCompressBlock, 0, tempCompressBlock.Length);
  66. compressedBlockStream.Write(tempCompressBlock, 0, compressedSize);
  67. _blocks.Add(new StorageBlock { flags = (StorageBlockFlags)(int)CompressionType.Lz4, compressedSize = (uint)compressedSize, uncompressedSize = (uint)curBlockSize });
  68. //Debug.Log($"== block[{i}] uncompressedSize:{curBlockSize} compressedSize:{compressedSize} totalblocksize:{compressedBlockStream.Length}");
  69. }
  70. _blockBytes = compressedBlockStream.ToArray();
  71. }
  72. private void BuildBlockDirectoryMetadata()
  73. {
  74. var hash = new byte[16];
  75. _blockDirectoryMetadataStream.Write(hash, 0, 16);
  76. _blockDirectoryMetadataStream.Write((uint)_blocks.Count);
  77. foreach(var b in _blocks)
  78. {
  79. _blockDirectoryMetadataStream.Write(b.uncompressedSize);
  80. _blockDirectoryMetadataStream.Write(b.compressedSize);
  81. _blockDirectoryMetadataStream.Write((ushort)b.flags);
  82. }
  83. _blockDirectoryMetadataStream.Write((uint)_files.Count);
  84. foreach(var f in _files)
  85. {
  86. _blockDirectoryMetadataStream.Write(f.offset);
  87. _blockDirectoryMetadataStream.Write(f.size);
  88. _blockDirectoryMetadataStream.Write(f.flags);
  89. _blockDirectoryMetadataStream.WriteNullEndString(f.path);
  90. }
  91. //Debug.Log($"block and directory metadata size:{_blockDirectoryMetadataStream.Length}");
  92. }
  93. }
  94. }