ObbUtil.cs 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231
  1. #if UNITY_ANDROID
  2. //using ICSharpCode.SharpZipLib.Zip;
  3. using System;
  4. using System.IO;
  5. using System.Text;
  6. using System.Text.RegularExpressions;
  7. using UnityEditor;
  8. using UnityEngine;
  9. namespace XGame.Editor.Build
  10. {
  11. public static class ObbUtil
  12. {
  13. public static void GenerateObb(string pathToBuiltProject)
  14. {
  15. var outputPath = Path.GetDirectoryName(pathToBuiltProject).Replace("\\", "/");
  16. var obbRoot = $"{outputPath}/obb/";
  17. var obbAssets = $"{obbRoot}assets";
  18. //Debug.Log($"directory:{outputPath} obb:{obbRoot}");
  19. if (!Directory.Exists(obbAssets))
  20. {
  21. return;
  22. }
  23. string apkFolder = null;
  24. string manifestPath = null;
  25. if (EditorUserBuildSettings.exportAsGoogleAndroidProject)
  26. {
  27. manifestPath = $"{pathToBuiltProject}/unityLibrary/src/main/AndroidManifest.xml";
  28. }
  29. else
  30. {
  31. //解压apk
  32. apkFolder = UnCompressApk(pathToBuiltProject);
  33. if (!string.IsNullOrEmpty(apkFolder))
  34. {
  35. manifestPath = $"{apkFolder}/AndroidManifest.xml";
  36. }
  37. }
  38. if (!string.IsNullOrEmpty(manifestPath))
  39. {
  40. //取出Unity的build-id
  41. Regex regex = new Regex(@"<meta-data android:name=""unity\.build-id"" android:value=""(.+?)""(\s*)/>", RegexOptions.Multiline);
  42. var manifest = File.ReadAllText(manifestPath);
  43. var match = regex.Match(manifest);
  44. if (match.Success)
  45. {
  46. BuildLog.Log($"unity.build-id:{match.Groups[1].Value}");
  47. File.WriteAllText($"{obbAssets}/{match.Groups[1].Value}", "", Encoding.UTF8);
  48. }
  49. else
  50. {
  51. BuildLog.Error($"找不到unity.build-id ManifestPath:{manifestPath}");
  52. return;
  53. }
  54. try
  55. {
  56. //生成新的obb
  57. var zip = $"{outputPath}/main.{PlayerSettings.Android.bundleVersionCode}.{PlayerSettings.applicationIdentifier}.obb";
  58. ProcessCommand(SevenZipExePath, "-tZip a " + zip + " " + obbAssets + " -mx0");
  59. //using (var zipFile = File.Create($"{outputPath}/main.{PlayerSettings.Android.bundleVersionCode}.{PlayerSettings.applicationIdentifier}.obb"))
  60. //{
  61. // using (ZipOutputStream zipOutStream = new ZipOutputStream(zipFile))
  62. // {
  63. // zipOutStream.SetLevel(0);// 0 - store only to 9 - means best compression
  64. // ZipCrc32 crc = new ZipCrc32();
  65. // ZipDirectory(obbRoot, obbAssets, zipOutStream, crc);
  66. // }
  67. //}
  68. }
  69. catch (Exception ex)
  70. {
  71. BuildLog.Error("生成obb文件失败");
  72. BuildLog.Exception(ex);
  73. }
  74. //删除obb文件夹
  75. UnityEditor.FileUtil.DeleteFileOrDirectory(obbRoot);
  76. //删除解压的apk文件夹
  77. if (!string.IsNullOrEmpty(apkFolder))
  78. {
  79. UnityEditor.FileUtil.DeleteFileOrDirectory(apkFolder);
  80. }
  81. }
  82. }
  83. #region 解压apk
  84. //[MenuItem("Build/UnCompressApk")]
  85. //public static void _UnCompressApk()
  86. //{
  87. // UnCompressApk("D:/WorkSpaces/XGameBuild/output/xbuild.apk");
  88. //}
  89. private static string UnCompressApk(string apkPath)
  90. {
  91. var apkToolPath = ApkToolPath;
  92. if (string.IsNullOrEmpty(apkToolPath))
  93. {
  94. BuildLog.Error("找不到Apktool!!!!!!!!!");
  95. return string.Empty;
  96. }
  97. //apktool固定解压到工程主目录下
  98. var apkFolder = Path.GetFullPath($"{Application.dataPath}/../{Path.GetFileNameWithoutExtension(apkPath)}").Replace("\\", "/");
  99. //先删除旧文件夹
  100. if (Directory.Exists(apkFolder))
  101. {
  102. BuildLog.Log($"存在apk文件夹,先删除。Path:{apkFolder}");
  103. UnityEditor.FileUtil.DeleteFileOrDirectory(apkFolder);
  104. }
  105. ProcessCommand("java.exe", "-jar " + apkToolPath + " d " + apkPath);
  106. return apkFolder;
  107. }
  108. private static readonly string _apktoolRelativePath = "Build/Editor/BuildPlayer/apktool/apktool.jar";
  109. private static string ApkToolPath
  110. {
  111. get
  112. {
  113. var guids = AssetDatabase.FindAssets("apktool");
  114. foreach (var guid in guids)
  115. {
  116. var assetPath = AssetDatabase.GUIDToAssetPath(guid);
  117. if (assetPath.EndsWith(_apktoolRelativePath))
  118. {
  119. var path = Path.GetFullPath(assetPath).Replace("\\", "/");
  120. BuildLog.Log($"ApkToolPath:{path}");
  121. return path;
  122. }
  123. }
  124. return string.Empty;
  125. }
  126. }
  127. private static readonly string _zipExeRelativePath = "Build/Editor/BuildPlayer/7z/sevenZip.exe";
  128. private static string SevenZipExePath
  129. {
  130. get
  131. {
  132. var guids = AssetDatabase.FindAssets("sevenZip");
  133. foreach (var guid in guids)
  134. {
  135. var assetPath = AssetDatabase.GUIDToAssetPath(guid);
  136. if (assetPath.EndsWith(_zipExeRelativePath))
  137. {
  138. var path = Path.GetFullPath(assetPath).Replace("\\", "/");
  139. BuildLog.Log($"SevenZipExePath:{path}");
  140. return path;
  141. }
  142. }
  143. return string.Empty;
  144. }
  145. }
  146. private static void ProcessCommand(string command, string argument, bool shell = true)
  147. {
  148. var start = new System.Diagnostics.ProcessStartInfo(command)
  149. {
  150. Arguments = argument,
  151. CreateNoWindow = false,
  152. ErrorDialog = true,
  153. UseShellExecute = true
  154. };
  155. if (start.UseShellExecute)
  156. {
  157. start.RedirectStandardOutput = false;
  158. start.RedirectStandardError = false;
  159. start.RedirectStandardInput = false;
  160. }
  161. else
  162. {
  163. start.RedirectStandardOutput = true;
  164. start.RedirectStandardError = true;
  165. start.RedirectStandardInput = true;
  166. start.StandardOutputEncoding = Encoding.UTF8;
  167. start.StandardErrorEncoding = Encoding.UTF8;
  168. }
  169. var process = System.Diagnostics.Process.Start(start);
  170. if (!start.UseShellExecute)
  171. {
  172. BuildLog.Log(process.StandardOutput.ReadToEnd());
  173. BuildLog.Error(process.StandardError.ReadToEnd());
  174. }
  175. process.WaitForExit();
  176. process.Close();
  177. }
  178. #endregion
  179. #region 打包obb
  180. /// <summary>
  181. /// 递归遍历目录
  182. /// </summary>
  183. //private static void ZipDirectory(string root, string directoryToZip, ZipOutputStream zipOutput, ZipCrc32 crc)
  184. //{
  185. // var filenames = Directory.GetFileSystemEntries(directoryToZip);
  186. // foreach (string file in filenames)// 遍历所有的文件和目录
  187. // {
  188. // if (Directory.Exists(file))// 先当作目录处理如果存在这个目录就递归Copy该目录下面的文件
  189. // {
  190. // ZipDirectory(root, file, zipOutput, crc);
  191. // }
  192. // else // 否则直接压缩文件
  193. // {
  194. // //打开压缩文件
  195. // using (FileStream fileStream = File.OpenRead(file))
  196. // {
  197. // var buffer = new byte[fileStream.Length];
  198. // fileStream.Read(buffer, 0, buffer.Length);
  199. // //string fileName = parentPath + file.Substring(file.LastIndexOf("\\") + 1);
  200. // var entry = new ZipEntry(file.Replace("\\", "/").Replace(root, ""))
  201. // {
  202. // DateTime = DateTime.Now,
  203. // Size = fileStream.Length
  204. // };
  205. // fileStream.Close();
  206. // crc.Reset();
  207. // crc.Update(buffer);
  208. // entry.Crc = crc.Value;
  209. // zipOutput.PutNextEntry(entry);
  210. // zipOutput.Write(buffer, 0, buffer.Length);
  211. // }
  212. // }
  213. // }
  214. //}
  215. #endregion
  216. }
  217. }
  218. #endif