三国卡牌客户端基础资源仓库
hch
2025-06-20 4841e82bd5e399c4fc39313bbc93c6fc1bb12b2a
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
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}");
        }
    }
}