UnityBinFile.cs 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124
  1. using System.Collections;
  2. using System.Collections.Generic;
  3. using System.IO;
  4. using UnityEngine;
  5. using System.Text;
  6. using System.Reflection;
  7. using System;
  8. using System.Linq;
  9. namespace UnityFS
  10. {
  11. /// <summary>
  12. /// Unity 生成的二进制文件(本代码不支持5.x之前的版本)
  13. /// </summary>
  14. public unsafe class UnityBinFile
  15. {
  16. /*
  17. * MonoManager: idx: 6;
  18. * type: metaData.types[objects[6].typeID]
  19. */
  20. public const int kMonoManagerIdx = 6;
  21. public FileHeader header;
  22. public MetaData metaData;
  23. public ScriptsData scriptsData;
  24. private Stream _originStream;
  25. public void LoadFromStream(Stream source)
  26. {
  27. _originStream = source;
  28. using (var br = new BinaryReader(source, Encoding.UTF8, true))
  29. {
  30. header.LoadFromStream(br);
  31. // 按理说 metaData 应该新开一个buffer来避免加载时的对齐逻辑问题,但由于 sizeof(Header) = 20,已经对齐到4了,所以可以连续读
  32. metaData.LoadFromStream(br, header.dataOffset);
  33. scriptsData = metaData.GetScriptData(br);
  34. }
  35. }
  36. public void Load(string path)
  37. {
  38. LoadFromStream(new MemoryStream(File.ReadAllBytes(path)));
  39. }
  40. public void AddScriptingAssemblies(List<string> assemblies)
  41. {
  42. foreach (string name in assemblies)
  43. {
  44. if (!scriptsData.dllNames.Contains(name))
  45. {
  46. scriptsData.dllNames.Add(name);
  47. scriptsData.dllTypes.Add(16); // user dll type
  48. Debug.Log($"[PatchScriptAssembliesJson] add dll:{name} to globalgamemanagers");
  49. }
  50. }
  51. }
  52. public byte[] CreatePatchedBytes()
  53. {
  54. var fsR = _originStream;
  55. fsR.Position = 0;
  56. var brR = new BinaryReader(fsR, Encoding.UTF8, true);
  57. var ms = new MemoryStream((int)(header.fileSize * 1.5f));
  58. var bw = new BinaryWriter(ms, Encoding.UTF8, true);
  59. /*
  60. * 开始写入data
  61. * dll名称列表存储于 data 区段,修改其数据并不会影响 MetaData 大小,因此 dataOffset 不会改变
  62. */
  63. ms.Position = header.dataOffset;
  64. Dictionary<long, ObjectInfo> newObjInfos = new Dictionary<long, ObjectInfo>();
  65. foreach (var kv in metaData.objects)
  66. {
  67. long objID = kv.Key;
  68. ObjectInfo objInfo = kv.Value;
  69. byte[] buff = new byte[objInfo.size];
  70. fsR.Position = objInfo.realPos;
  71. brR.Read(buff, 0, buff.Length);
  72. {// unity 的数据偏移貌似会对齐到 8
  73. int newPos = (((int)ms.Position + 7) >> 3) << 3;
  74. int gapSize = newPos - (int)ms.Position;
  75. for (int i = 0; i < gapSize; i++)
  76. bw.Write((byte)0);
  77. objInfo.dataPos = (uint)ms.Position - header.dataOffset; // 重定位数据偏移
  78. }
  79. if (objID != kMonoManagerIdx)
  80. bw.Write(buff, 0, buff.Length);
  81. else
  82. objInfo.size = (uint)scriptsData.SaveToStream(bw);
  83. newObjInfos.Add(objID, objInfo);
  84. }
  85. metaData.objects = newObjInfos;
  86. header.fileSize = (uint)ms.Position;
  87. ms.Position = 0;
  88. header.SaveToStream(bw);
  89. metaData.SaveToStream(bw);
  90. brR.Close();
  91. // 写入新文件
  92. ms.Position = 0;
  93. return ms.ToArray();
  94. }
  95. public void Save(string newPath)
  96. {
  97. byte[] patchedBytes = CreatePatchedBytes();
  98. File.WriteAllBytes(newPath, patchedBytes);
  99. }
  100. }
  101. }