ManifestTools.cs 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349
  1. using System;
  2. using System.IO;
  3. using System.Collections;
  4. using System.Collections.Generic;
  5. using UnityEngine;
  6. namespace YooAsset
  7. {
  8. internal static class ManifestTools
  9. {
  10. /// <summary>
  11. /// 序列化(JSON文件)
  12. /// </summary>
  13. public static void SerializeToJson(string savePath, PackageManifest manifest)
  14. {
  15. string json = JsonUtility.ToJson(manifest, true);
  16. FileUtility.WriteAllText(savePath, json);
  17. }
  18. /// <summary>
  19. /// 序列化(二进制文件)
  20. /// </summary>
  21. public static void SerializeToBinary(string savePath, PackageManifest manifest)
  22. {
  23. using (FileStream fs = new FileStream(savePath, FileMode.Create))
  24. {
  25. // 创建缓存器
  26. BufferWriter buffer = new BufferWriter(ManifestDefine.FileMaxSize);
  27. // 写入文件标记
  28. buffer.WriteUInt32(ManifestDefine.FileSign);
  29. // 写入文件版本
  30. buffer.WriteUTF8(manifest.FileVersion);
  31. // 写入文件头信息
  32. buffer.WriteBool(manifest.EnableAddressable);
  33. buffer.WriteBool(manifest.LocationToLower);
  34. buffer.WriteBool(manifest.IncludeAssetGUID);
  35. buffer.WriteInt32(manifest.OutputNameStyle);
  36. buffer.WriteInt32(manifest.BuildBundleType);
  37. buffer.WriteUTF8(manifest.BuildPipeline);
  38. buffer.WriteUTF8(manifest.PackageName);
  39. buffer.WriteUTF8(manifest.PackageVersion);
  40. buffer.WriteUTF8(manifest.PackageNote);
  41. // 写入资源列表
  42. buffer.WriteInt32(manifest.AssetList.Count);
  43. for (int i = 0; i < manifest.AssetList.Count; i++)
  44. {
  45. var packageAsset = manifest.AssetList[i];
  46. buffer.WriteUTF8(packageAsset.Address);
  47. buffer.WriteUTF8(packageAsset.AssetPath);
  48. buffer.WriteUTF8(packageAsset.AssetGUID);
  49. buffer.WriteUTF8Array(packageAsset.AssetTags);
  50. buffer.WriteInt32(packageAsset.BundleID);
  51. buffer.WriteInt32Array(packageAsset.DependBundleIDs);
  52. }
  53. // 写入资源包列表
  54. buffer.WriteInt32(manifest.BundleList.Count);
  55. for (int i = 0; i < manifest.BundleList.Count; i++)
  56. {
  57. var packageBundle = manifest.BundleList[i];
  58. buffer.WriteUTF8(packageBundle.BundleName);
  59. buffer.WriteUInt32(packageBundle.UnityCRC);
  60. buffer.WriteUTF8(packageBundle.FileHash);
  61. buffer.WriteUTF8(packageBundle.FileCRC);
  62. buffer.WriteInt64(packageBundle.FileSize);
  63. buffer.WriteBool(packageBundle.Encrypted);
  64. buffer.WriteUTF8Array(packageBundle.Tags);
  65. buffer.WriteInt32Array(packageBundle.DependBundleIDs);
  66. }
  67. // 写入文件流
  68. buffer.WriteToStream(fs);
  69. fs.Flush();
  70. }
  71. }
  72. /// <summary>
  73. /// 反序列化(JSON文件)
  74. /// </summary>
  75. public static PackageManifest DeserializeFromJson(string jsonContent)
  76. {
  77. return JsonUtility.FromJson<PackageManifest>(jsonContent);
  78. }
  79. /// <summary>
  80. /// 反序列化(二进制文件)
  81. /// </summary>
  82. public static PackageManifest DeserializeFromBinary(byte[] binaryData)
  83. {
  84. // 创建缓存器
  85. BufferReader buffer = new BufferReader(binaryData);
  86. // 读取文件标记
  87. uint fileSign = buffer.ReadUInt32();
  88. if (fileSign != ManifestDefine.FileSign)
  89. throw new Exception("Invalid manifest file !");
  90. // 读取文件版本
  91. string fileVersion = buffer.ReadUTF8();
  92. if (fileVersion != ManifestDefine.FileVersion)
  93. throw new Exception($"The manifest file version are not compatible : {fileVersion} != {ManifestDefine.FileVersion}");
  94. PackageManifest manifest = new PackageManifest();
  95. {
  96. // 读取文件头信息
  97. manifest.FileVersion = fileVersion;
  98. manifest.EnableAddressable = buffer.ReadBool();
  99. manifest.LocationToLower = buffer.ReadBool();
  100. manifest.IncludeAssetGUID = buffer.ReadBool();
  101. manifest.OutputNameStyle = buffer.ReadInt32();
  102. manifest.BuildBundleType = buffer.ReadInt32();
  103. manifest.BuildPipeline = buffer.ReadUTF8();
  104. manifest.PackageName = buffer.ReadUTF8();
  105. manifest.PackageVersion = buffer.ReadUTF8();
  106. manifest.PackageNote = buffer.ReadUTF8();
  107. // 检测配置
  108. if (manifest.EnableAddressable && manifest.LocationToLower)
  109. throw new Exception("Addressable not support location to lower !");
  110. // 读取资源列表
  111. int packageAssetCount = buffer.ReadInt32();
  112. CreateAssetCollection(manifest, packageAssetCount);
  113. for (int i = 0; i < packageAssetCount; i++)
  114. {
  115. var packageAsset = new PackageAsset();
  116. packageAsset.Address = buffer.ReadUTF8();
  117. packageAsset.AssetPath = buffer.ReadUTF8();
  118. packageAsset.AssetGUID = buffer.ReadUTF8();
  119. packageAsset.AssetTags = buffer.ReadUTF8Array();
  120. packageAsset.BundleID = buffer.ReadInt32();
  121. packageAsset.DependBundleIDs = buffer.ReadInt32Array();
  122. FillAssetCollection(manifest, packageAsset);
  123. }
  124. // 读取资源包列表
  125. int packageBundleCount = buffer.ReadInt32();
  126. CreateBundleCollection(manifest, packageBundleCount);
  127. for (int i = 0; i < packageBundleCount; i++)
  128. {
  129. var packageBundle = new PackageBundle();
  130. packageBundle.BundleName = buffer.ReadUTF8();
  131. packageBundle.UnityCRC = buffer.ReadUInt32();
  132. packageBundle.FileHash = buffer.ReadUTF8();
  133. packageBundle.FileCRC = buffer.ReadUTF8();
  134. packageBundle.FileSize = buffer.ReadInt64();
  135. packageBundle.Encrypted = buffer.ReadBool();
  136. packageBundle.Tags = buffer.ReadUTF8Array();
  137. packageBundle.DependBundleIDs = buffer.ReadInt32Array();
  138. FillBundleCollection(manifest, packageBundle);
  139. }
  140. }
  141. // 初始化资源清单
  142. InitManifest(manifest);
  143. return manifest;
  144. }
  145. #region 解析资源清单辅助方法
  146. public static void InitManifest(PackageManifest manifest)
  147. {
  148. // 填充资源包内包含的主资源列表
  149. foreach (var packageAsset in manifest.AssetList)
  150. {
  151. int bundleID = packageAsset.BundleID;
  152. if (bundleID >= 0 && bundleID < manifest.BundleList.Count)
  153. {
  154. var packageBundle = manifest.BundleList[bundleID];
  155. packageBundle.IncludeMainAssets.Add(packageAsset);
  156. }
  157. else
  158. {
  159. throw new Exception($"Invalid bundle id : {bundleID} Asset path : {packageAsset.AssetPath}");
  160. }
  161. }
  162. // 填充资源包引用关系
  163. for (int index = 0; index < manifest.BundleList.Count; index++)
  164. {
  165. var sourceBundle = manifest.BundleList[index];
  166. foreach (int dependIndex in sourceBundle.DependBundleIDs)
  167. {
  168. var dependBundle = manifest.BundleList[dependIndex];
  169. dependBundle.AddReferenceBundleID(index);
  170. }
  171. }
  172. }
  173. public static void CreateAssetCollection(PackageManifest manifest, int assetCount)
  174. {
  175. manifest.AssetList = new List<PackageAsset>(assetCount);
  176. manifest.AssetDic = new Dictionary<string, PackageAsset>(assetCount);
  177. if (manifest.EnableAddressable)
  178. manifest.AssetPathMapping1 = new Dictionary<string, string>(assetCount * 3);
  179. else
  180. manifest.AssetPathMapping1 = new Dictionary<string, string>(assetCount * 2);
  181. if (manifest.IncludeAssetGUID)
  182. manifest.AssetPathMapping2 = new Dictionary<string, string>(assetCount);
  183. else
  184. manifest.AssetPathMapping2 = new Dictionary<string, string>();
  185. }
  186. public static void FillAssetCollection(PackageManifest manifest, PackageAsset packageAsset)
  187. {
  188. // 添加到列表集合
  189. manifest.AssetList.Add(packageAsset);
  190. // 注意:我们不允许原始路径存在重名
  191. string assetPath = packageAsset.AssetPath;
  192. if (manifest.AssetDic.ContainsKey(assetPath))
  193. throw new System.Exception($"AssetPath have existed : {assetPath}");
  194. else
  195. manifest.AssetDic.Add(assetPath, packageAsset);
  196. // 填充AssetPathMapping1
  197. {
  198. string location = packageAsset.AssetPath;
  199. if (manifest.LocationToLower)
  200. location = location.ToLower();
  201. // 添加原生路径的映射
  202. if (manifest.AssetPathMapping1.ContainsKey(location))
  203. throw new System.Exception($"Location have existed : {location}");
  204. else
  205. manifest.AssetPathMapping1.Add(location, packageAsset.AssetPath);
  206. // 添加无后缀名路径的映射
  207. string locationWithoutExtension = Path.ChangeExtension(location, null);
  208. if (ReferenceEquals(location, locationWithoutExtension) == false)
  209. {
  210. if (manifest.AssetPathMapping1.ContainsKey(locationWithoutExtension))
  211. YooLogger.Warning($"Location have existed : {locationWithoutExtension}");
  212. else
  213. manifest.AssetPathMapping1.Add(locationWithoutExtension, packageAsset.AssetPath);
  214. }
  215. }
  216. // 添加可寻址地址
  217. if (manifest.EnableAddressable)
  218. {
  219. string location = packageAsset.Address;
  220. if (string.IsNullOrEmpty(location) == false)
  221. {
  222. if (manifest.AssetPathMapping1.ContainsKey(location))
  223. throw new System.Exception($"Location have existed : {location}");
  224. else
  225. manifest.AssetPathMapping1.Add(location, packageAsset.AssetPath);
  226. }
  227. }
  228. // 填充AssetPathMapping2
  229. if (manifest.IncludeAssetGUID)
  230. {
  231. if (manifest.AssetPathMapping2.ContainsKey(packageAsset.AssetGUID))
  232. throw new System.Exception($"AssetGUID have existed : {packageAsset.AssetGUID}");
  233. else
  234. manifest.AssetPathMapping2.Add(packageAsset.AssetGUID, packageAsset.AssetPath);
  235. }
  236. }
  237. public static void CreateBundleCollection(PackageManifest manifest, int bundleCount)
  238. {
  239. manifest.BundleList = new List<PackageBundle>(bundleCount);
  240. manifest.BundleDic1 = new Dictionary<string, PackageBundle>(bundleCount);
  241. manifest.BundleDic2 = new Dictionary<string, PackageBundle>(bundleCount);
  242. manifest.BundleDic3 = new Dictionary<string, PackageBundle>(bundleCount);
  243. }
  244. public static void FillBundleCollection(PackageManifest manifest, PackageBundle packageBundle)
  245. {
  246. // 初始化资源包
  247. packageBundle.InitBundle(manifest);
  248. // 添加到列表集合
  249. manifest.BundleList.Add(packageBundle);
  250. manifest.BundleDic1.Add(packageBundle.BundleName, packageBundle);
  251. manifest.BundleDic2.Add(packageBundle.FileName, packageBundle);
  252. manifest.BundleDic3.Add(packageBundle.BundleGUID, packageBundle);
  253. }
  254. #endregion
  255. /// <summary>
  256. /// 注意:该类拷贝自编辑器
  257. /// </summary>
  258. private enum EFileNameStyle
  259. {
  260. /// <summary>
  261. /// 哈希值名称
  262. /// </summary>
  263. HashName = 0,
  264. /// <summary>
  265. /// 资源包名称(不推荐)
  266. /// </summary>
  267. BundleName = 1,
  268. /// <summary>
  269. /// 资源包名称 + 哈希值名称
  270. /// </summary>
  271. BundleName_HashName = 2,
  272. }
  273. /// <summary>
  274. /// 获取资源文件的后缀名
  275. /// </summary>
  276. public static string GetRemoteBundleFileExtension(string bundleName)
  277. {
  278. string fileExtension = Path.GetExtension(bundleName);
  279. return fileExtension;
  280. }
  281. /// <summary>
  282. /// 获取远端的资源文件名
  283. /// </summary>
  284. public static string GetRemoteBundleFileName(int nameStyle, string bundleName, string fileExtension, string fileHash)
  285. {
  286. if (nameStyle == (int)EFileNameStyle.HashName)
  287. {
  288. return StringUtility.Format("{0}{1}", fileHash, fileExtension);
  289. }
  290. else if (nameStyle == (int)EFileNameStyle.BundleName)
  291. {
  292. return bundleName;
  293. }
  294. else if (nameStyle == (int)EFileNameStyle.BundleName_HashName)
  295. {
  296. if (string.IsNullOrEmpty(fileExtension))
  297. {
  298. return StringUtility.Format("{0}_{1}", bundleName, fileHash);
  299. }
  300. else
  301. {
  302. string fileName = bundleName.Remove(bundleName.LastIndexOf('.'));
  303. return StringUtility.Format("{0}_{1}{2}", fileName, fileHash, fileExtension);
  304. }
  305. }
  306. else
  307. {
  308. throw new NotImplementedException($"Invalid name style : {nameStyle}");
  309. }
  310. }
  311. }
  312. }