using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEditor; using Game.Config; using System; using System.Reflection; using System.IO; using System.IO.Compression; using System.Security.Cryptography; using System.Text; using System.Linq; public class CheckHotUpdateRes : EditorWindow { public class VersionInfoData:GameData { public string FullName; public string MD5; public ulong Size; public VersionInfoData() { FullName = ""; MD5 = ""; Size = 0; } private static void OnCsvLoad(CsvReader csvReader) { VersionInfoData.Onload(csvReader); } } [MenuItem("AssetBundle/创建最大Version")] public static void CreateVersion() { #if UNITY_IOS FileHelper.WirteStringToFile(Application.dataPath+ "/StreamingAssets/unityRes/afivs", "99.99.99.99"); #else FileHelper.WirteStringToFile(Application.dataPath + "/StreamingAssets/AssetsAndroid/Version", "99.99.99.99"); #endif } [MenuItem("AssetBundle/热更相关")] public static void OpenCheckHotUpdateWindow() { CheckHotUpdateRes window = GetWindow("热更相关"); window.maxSize = new Vector2(800, 600); window.minSize = new Vector2(600, 400); window.Init(); window.Show(); } private string oldPath = ""; private string newPath = ""; private CheckResWindowInfo info; private ResInfo curResInfo; private List needResList; private GameDataFormatInfo downloadFormatInfo; public void Init() { string path = "Assets/Editor/AssetBundle/CheckHotResWindownInfo.asset"; //Debug.Log(); info = AssetDatabase.LoadAssetAtPath(path); info.InitTags(); curResInfo = info.GetResInfo(); if (curResInfo == null) { curResInfo = new ResInfo(); } #if UNITY_IOS info.VersionFileName = "afivs"; info.mainfestFileName = "afimft"; info.NewPath = Application.dataPath + "/StreamingAssets/unityRes"; //#else // versionFileName = "Version"; // assetsFileName = "mainfest"; #endif needResList = new List(); } private void OnGUI() { DrowSelectFolder("旧版本文件", "选择文件路径", "选择旧版本资源MD5文件路径", ref curResInfo.OldPath); DrowSelectFolder("新版本文件", "选择文件路径", "选择新版本资源MD5文件路径", ref curResInfo.NewPath); DrowSelectFolder("需跟新文件输出路径", "选择文件路径", "选择需跟新文件输出路径", ref curResInfo.OutputPath); DrowSelectFolder("ab包名基础名字文件路径", "选择文件路径", "选择ab包名基础名字文件路径", ref curResInfo.BaseNameFileInfoPath); DrawInfo(); DrawCheckBtn(); } private void DrowSelectFolder(string name, string btnName, string savepath, ref string path) { GUILayout.Box("", GUILayout.Width(790)); GUILayout.BeginHorizontal(); GUILayout.Space(10); GUILayout.Label(name, GUILayout.Width(120)); GUILayout.Space(10); path = GUILayout.TextField(path, GUILayout.Width(360)); if (GUILayout.Button(btnName, GUILayout.Width(100))) { string getstr = EditorUtility.SaveFolderPanel(savepath, path, ""); if(!string.IsNullOrEmpty(getstr)) { path = getstr; } } GUILayout.Space(10); GUILayout.EndHorizontal(); } private void DrawInfo() { GUILayout.Box("", GUILayout.Width(790)); GUILayout.BeginHorizontal(); GUILayout.Label("平台", GUILayout.Width(65)); int TagIndex = EditorGUILayout.Popup(info.buildTag, info.Tags, GUILayout.Width(100)); if (TagIndex != info.buildTag) { info.buildTag = TagIndex; ResInfo ri = info.GetResInfo(); if (ri != null) { curResInfo = ri; } } GUILayout.EndHorizontal(); GUILayout.Box("", GUILayout.Width(790)); GUILayout.BeginHorizontal(); GUILayout.Space(10); GUILayout.Label("版本文件名:", GUILayout.Width(65)); GUILayout.Space(10); curResInfo.VersionFileName = GUILayout.TextField(curResInfo.VersionFileName, GUILayout.Width(160)); GUILayout.Space(60); GUILayout.Label("版本文件信息文件名:", GUILayout.Width(120)); GUILayout.Space(10); curResInfo.mainfestFileName = GUILayout.TextField(curResInfo.mainfestFileName, GUILayout.Width(160)); GUILayout.EndHorizontal(); GUILayout.Box("", GUILayout.Width(790)); GUILayout.BeginHorizontal(); GUILayout.Space(10); GUILayout.Label("资源版本:", GUILayout.Width(65)); GUILayout.Space(10); curResInfo.ResVersion = GUILayout.TextField(curResInfo.ResVersion, GUILayout.Width(160)); GUILayout.Space(10); if (GUILayout.Button("读取版本", GUILayout.Width(100))) { curResInfo.ResVersion = File.ReadAllText($"{curResInfo.OldPath}/{curResInfo.VersionFileName}"); } GUILayout.Space(10); if (GUILayout.Button("版本+1", GUILayout.Width(100))) { VersionCode lVersionCode = curResInfo.ResVersion; lVersionCode.patch += 1; curResInfo.ResVersion = lVersionCode.ToString(); } GUILayout.Space(10); GUILayout.EndHorizontal(); GUILayout.BeginHorizontal(); GUILayout.Label("语言:", GUILayout.Width(65)); GUILayout.Space(10); curResInfo.Language = (BuildLanguage)EditorGUILayout.EnumPopup(curResInfo.Language, GUILayout.Width(100)); GUILayout.Space(10); GUILayout.EndHorizontal(); } private void DrawCheckBtn() { GUILayout.Box("", GUILayout.Width(790)); GUILayout.BeginHorizontal(); GUILayout.Space(10); if (GUILayout.Button("生成MD5资源版本文件")) { BundleBuilderZ.CreateMD5File(curResInfo.ResVersion); } GUILayout.Space(60); if (GUILayout.Button("对比新旧资源")) { CheckRes(); } GUILayout.EndHorizontal(); GUILayout.Box("", GUILayout.Width(790)); if (GUILayout.Button("复制所以资源到输出路径")) { CopyFiles(); } if (GUILayout.Button("复制图片资源到输出路径")) { CopyTexture(); } GUILayout.Box("", GUILayout.Width(790)); GUILayout.BeginHorizontal(); GUILayout.Space(60); curResInfo.IsIgnore =GUILayout.Toggle(curResInfo.IsIgnore, "是否开启忽略某些文件") ; GUILayout.Space(60); if (GUILayout.Button("制作混淆信息")) { MakeAbHxNameAssets(); } GUILayout.EndHorizontal(); } private List SerizlizeResList(byte[] data) { CsvReader csvReader = new CsvReader(curResInfo.mainfestFileName, data); if (downloadFormatInfo == null) { downloadFormatInfo = new GameDataFormatInfo(csvReader.Fields(), csvReader.Types()); } Type type = typeof(VersionInfoData); MethodInfo methodInfo = type.GetMethod("OnCsvLoad", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static); methodInfo?.Invoke(null, new object[] { csvReader }); List resList = VersionInfoData.AllData(); VersionInfoData.Clear(); return resList; } private void CheckRes() { needResList.Clear(); oldPath = $"{curResInfo.OldPath}/{ curResInfo.mainfestFileName}"; List Oldlist = GetInfoDatas(oldPath); newPath = $"{curResInfo.NewPath}/{ curResInfo.mainfestFileName}"; List newlist = GetInfoDatas(newPath); ChangeResMD5(newlist); Dictionary abNamedic = null; if (curResInfo.IsHx) { abNamedic= GetCurInfoAbNameInfos(); } if (Oldlist != null && newlist != null) { int size = newlist.Count; for (int i = 0; i < size; i++) { if (newlist[i].FullName == curResInfo.mainfestFileName || newlist[i].FullName == curResInfo.VersionFileName) { continue; } bool isignore = false; string baseName = newlist[i].FullName; ; RG_Ignore_Info rG_Ignore_Info = null; if (curResInfo.IsHx && abNamedic.ContainsKey(newlist[i].FullName)) { baseName = abNamedic[newlist[i].FullName]; } rG_Ignore_Info = curResInfo.Rg_Essential_Infos.FindFirst(it => !it.IsIgnore && baseName.Contains(it.Field)); bool isEssential = rG_Ignore_Info != null; if (curResInfo.IsIgnore && !isEssential) { string igstr = curResInfo.IgnoreFiles.FindFirst(it => it == baseName); rG_Ignore_Info = curResInfo.rG_Ignore_Infos.FindFirst(it => it.IsIgnore && baseName.Contains( it.Field)); if (!string.IsNullOrEmpty(igstr) || rG_Ignore_Info != null) { isignore = true; Debug.Log("=========忽略=============" + baseName); } } VersionInfoData ores = Oldlist.FindFirst(it=> it.FullName == newlist[i].FullName); if (ores != null ) { if (isignore) { newlist[i].MD5 = ores.MD5; newlist[i].Size = ores.Size; } else if (newlist[i].MD5 != ores.MD5) { Debug.Log($"新 {newlist[i].FullName} MD5=[{newlist[i].MD5}] Base = [{baseName}]"); Debug.Log($"旧 {ores.FullName} MD5=[{ores.MD5}]"); needResList.Add(newlist[i]); } } else { if (isignore) { continue; } Debug.Log($"新 {newlist[i].FullName} MD5=[{newlist[i].MD5}] Base = [{baseName}]"); needResList.Add(newlist[i]); } } } CsvWriter csvWriter = new CsvWriter(newPath, "", newlist, downloadFormatInfo); csvWriter.Write(); CopyFile(); string FiletempPath = $"{curResInfo.OutputPath}/../ZipTempPath"; if(Directory.Exists(FiletempPath)) Directory.Delete(FiletempPath,true); Directory.CreateDirectory(FiletempPath); CopyFile(FiletempPath); string zipName = $"{curResInfo.CN_Name}-热更资源---{curResInfo.ResVersion}---{DateTime.Now.Month}.{DateTime.Now.Day}--{DateTime.Now.Hour}.{DateTime.Now.Minute}.zip"; ZipFile.CreateFromDirectory(FiletempPath,$"{curResInfo.OutputPath}/{zipName}"); Directory.Delete(FiletempPath,true); needResList.Clear(); Debug.Log($"资源检查完成 输出路径 : 【{curResInfo.OutputPath}】【{zipName}】"); } private void CopyFile(string outP = "") { if (needResList == null) { return; } if (string.IsNullOrEmpty(outP)) { outP = curResInfo.OutputPath; } int size = needResList.Count; string datapath = $"{curResInfo.NewPath}/"; for (int i = 0; i < size; i++) { if (needResList[i].FullName == "Version") { continue; } byte[] fdatas = File.ReadAllBytes(datapath + needResList[i].FullName); FileHelper.WirteToFile( $"{outP}/{needResList[i].FullName}" ,fdatas); } FileHelper.WirteStringToFile($"{outP}/{curResInfo.VersionFileName}",curResInfo.ResVersion); byte[] mdatas = File.ReadAllBytes(datapath + curResInfo.mainfestFileName); FileHelper.WirteToFile($"{outP}/{curResInfo.mainfestFileName}", mdatas); } private List GetInfoDatas(string path) { List list = null; byte[] datas = null; if (FileSystem.Exists(path)) { datas = File.ReadAllBytes(path); } if (datas != null) { list = SerizlizeResList(datas); } else { Debug.LogError($"请选择正确路径;[{path}]文件不存在!!!!"); } return list; } private void ChangeResMD5(List datas) { int[] ids = GenerateRandomArray(curResInfo.ChangeNum, 1, datas.Count); for (int i = 0; i < ids.Length; i++) { Debug.Log($"{ datas[ids[i]].FullName} 改===={ datas[ids[i]].MD5}====="); datas[ids[i]].MD5 = datas[ids[i]].MD5.Substring(0,6); //Debug.Log($"{ datas[ids[i]].FullName} 改===={ datas[ids[i]].MD5}=====eeee"); } } int[] GenerateRandomArray(int size, int min, int max) { List array = new List(size); if (size >= max - min) { for (int i = min; i < max; i++) { array.Add(i); } } else { while (size > 0) { int a = UnityEngine.Random.Range(min, max); if (!array.Contains(a)) { array.Add(a); size--; } } } return array.ToArray(); } private void CopyFiles() { string[] files1 = FileHelper.GetAllFileNmae(curResInfo.NewPath, "meta"); int size = files1.Length; string datapath = $"{curResInfo.NewPath}/"; for (int i = 0; i < size; i++) { byte[] fdatas = File.ReadAllBytes(datapath + files1[i]); FileHelper.WirteToFile($"{curResInfo.OutputPath}/{files1[i]}", fdatas); } } private void CopyTexture() { string datapath = $"{Application.dataPath}/Content/Icons/"; FileHelper.CopyDir(datapath, curResInfo.OutputPath, "meta"); } public static string GetABFileName(string abName,string s_ObscureKey) { try { using (var md5 = new MD5CryptoServiceProvider()) { UTF8Encoding encoding = new UTF8Encoding(false); byte[] bytes = encoding.GetBytes((abName + s_ObscureKey).ToLower()); bytes = md5.ComputeHash(bytes); StringBuilder sb = new StringBuilder(); for (int i = 0; i < bytes.Length; i++) { sb.Append(bytes[i].ToString("x2")); } return sb.ToString(); } } catch (Exception e) { Debug.LogException(e); } return abName; } private void MakeAbHxNameAssets() { List baseData = GetInfoDatas(curResInfo.BaseNameFileInfoPath); AbNameHxInfo hxInfo = curResInfo.abNameHxInfo == null? new AbNameHxInfo():curResInfo.abNameHxInfo; if (hxInfo.Infos == null) hxInfo.Infos = new List(); foreach (var item in baseData) { string hxName = GetABFileName(item.FullName,curResInfo.hxKey); abNameInfo nameInfo = new abNameInfo() { BaseName = item.FullName, HxName = hxName, }; hxInfo.Infos.Add(nameInfo); } curResInfo.abNameHxInfo = hxInfo; } public Dictionary GetCurInfoAbNameInfos() { Dictionary abinfos = new Dictionary(); foreach (var item in curResInfo.abNameHxInfo.Infos) { abinfos.Add(item.HxName,item.BaseName); } return abinfos; } }