三国卡牌客户端基础资源仓库
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
113
114
115
116
117
118
119
120
121
122
123
124
using System.Collections;
using System.Collections.Generic;
using System.IO;
using UnityEngine;
using System.Text;
using System.Reflection;
using System;
using System.Linq;
 
namespace UnityFS
{
    /// <summary>
    /// Unity 生成的二进制文件(本代码不支持5.x之前的版本)
    /// </summary>
    public unsafe class UnityBinFile
    {
        /*
         * MonoManager: idx: 6;
         * type: metaData.types[objects[6].typeID]
         */
        public const int kMonoManagerIdx = 6;
 
        public FileHeader header;
        public MetaData metaData;
        public ScriptsData scriptsData;
 
        private Stream _originStream;
 
        public void LoadFromStream(Stream source)
        {
            _originStream = source;
            using (var br = new BinaryReader(source, Encoding.UTF8, true))
            {
                header.LoadFromStream(br);
                // 按理说 metaData 应该新开一个buffer来避免加载时的对齐逻辑问题,但由于 sizeof(Header) = 20,已经对齐到4了,所以可以连续读
                metaData.LoadFromStream(br, header.dataOffset);
                scriptsData = metaData.GetScriptData(br);
            }
        }
 
        public void Load(string path)
        {
            LoadFromStream(new MemoryStream(File.ReadAllBytes(path)));
        }
 
        public void AddScriptingAssemblies(List<string> assemblies)
        {
            foreach (string name in assemblies)
            {
                if (!scriptsData.dllNames.Contains(name))
                {
                    scriptsData.dllNames.Add(name);
                    scriptsData.dllTypes.Add(16); // user dll type
                    Debug.Log($"[PatchScriptAssembliesJson] add dll:{name} to globalgamemanagers");
                }
            }
        }
 
        public byte[] CreatePatchedBytes()
        {
            var fsR = _originStream;
            fsR.Position = 0;
            var brR = new BinaryReader(fsR, Encoding.UTF8, true);
 
            var ms = new MemoryStream((int)(header.fileSize * 1.5f));
            var bw = new BinaryWriter(ms, Encoding.UTF8, true);
 
            /*
             * 开始写入data
             * dll名称列表存储于 data 区段,修改其数据并不会影响 MetaData 大小,因此 dataOffset 不会改变
             */
            ms.Position = header.dataOffset;
 
            Dictionary<long, ObjectInfo> newObjInfos = new Dictionary<long, ObjectInfo>();
            foreach (var kv in metaData.objects)
            {
                long objID = kv.Key;
                ObjectInfo objInfo = kv.Value;
 
                byte[] buff = new byte[objInfo.size];
                fsR.Position = objInfo.realPos;
                brR.Read(buff, 0, buff.Length);
 
 
                {// unity 的数据偏移貌似会对齐到 8
                    int newPos = (((int)ms.Position + 7) >> 3) << 3;
                    int gapSize = newPos - (int)ms.Position;
 
                    for (int i = 0; i < gapSize; i++)
                        bw.Write((byte)0);
 
                    objInfo.dataPos = (uint)ms.Position - header.dataOffset; // 重定位数据偏移
                }
 
                if (objID != kMonoManagerIdx)
                    bw.Write(buff, 0, buff.Length);
                else
                    objInfo.size = (uint)scriptsData.SaveToStream(bw);
 
                newObjInfos.Add(objID, objInfo);
            }
 
            metaData.objects = newObjInfos;
            header.fileSize = (uint)ms.Position;
 
            ms.Position = 0;
            header.SaveToStream(bw);
            metaData.SaveToStream(bw);
 
            brR.Close();
 
            // 写入新文件
            ms.Position = 0;
            return ms.ToArray();
        }
 
        public void Save(string newPath)
        {
            byte[] patchedBytes = CreatePatchedBytes();
            File.WriteAllBytes(newPath, patchedBytes);
        }
    }
 
}