public static void Patch(string platform)
        {
            var p        = Application.dataPath + DevCacheDirectory + platform + AssetBundlePath.kSlash + AssetBundlePath.kPackCfg;
            var jsonData = File.ReadAllText(p);
            var cfg      = new PackageCfg();

            EditorJsonUtility.FromJsonOverwrite(jsonData, cfg);

            var tmpDir = Application.dataPath + "/tempDir";

            if (Directory.Exists(tmpDir))
            {
                Directory.Delete(tmpDir, true);
            }
            var patchCfg = new PackageCfg();

            patchCfg.PatchVersion = PackEditorWin.GetCfg().PatchVersion;
            var parentPath = Application.dataPath + DevCacheDirectory + platform;

            var codePatchFile = parentPath + AssetBundlePath.kSlash + AssetBundlePath.kCodePatchFile;

            if (File.Exists(codePatchFile))
            {
                cfg.Files.Add(new FileCfg(CommonTool.CalFileMD5(codePatchFile), AssetBundlePath.kCodePatchFile));
            }
            for (int i = 0; i < cfg.Files.Count; ++i)
            {
                var f   = cfg.Files[i];
                var md5 = CommonTool.CalFileMD5(parentPath + AssetBundlePath.kSlash + f.Path);
                if (Path.GetExtension(f.Path) == AssetBundleMgr.instance.kPatchFileExt || md5 != f.MD5 && Path.GetExtension(f.Path) != AssetBundlePath.kPackCfgSuffix)
                {
                    patchCfg.Files.Add(new FileCfg(md5, f.Path));

                    var t = tmpDir + AssetBundlePath.kSlash + f.Path;
                    if (!Directory.Exists(Path.GetDirectoryName(t)))
                    {
                        Directory.CreateDirectory(Path.GetDirectoryName(t));
                    }
                    File.Copy(parentPath + AssetBundlePath.kSlash + f.Path, t);
                }
            }
            if (patchCfg.Files.Count != 0)
            {
                using (var sw = File.CreateText(tmpDir + AssetBundlePath.kSlash + AssetBundlePath.kPatchCfg))
                {
                    sw.Write(EditorJsonUtility.ToJson(patchCfg));
                }
                ZipHelper.ZipDirectoryDirect(tmpDir, Application.dataPath + DiffPatchDirectory + platform + AssetBundlePath.kPatchZipRes);
                TimeLogger.LogYellow(platform + "差异包生成成功");

                if (Directory.Exists(tmpDir))
                {
                    Directory.Delete(tmpDir, true);
                }
            }
            else
            {
                Debug.LogError("no different patch");
            }
        }
        public static void GeneratePackageCfg(string directoryPath)
        {
            var root = Path.GetFileNameWithoutExtension(directoryPath).ToLower();

            var packEditorCfg = PackEditorWin.GetCfg();

            var packCfg = new PackageCfg();

            packCfg.CurVersion         = packEditorCfg.CurVersion;
            packCfg.PatchVersion       = packEditorCfg.PatchVersion;
            packCfg.ForceUpdateVersion = packEditorCfg.ForceUpdateVersion;

            TraverseDirectory(directoryPath, packCfg, root);

            var filePath = directoryPath + AssetBundlePath.kSlash + AssetBundlePath.kPackCfg;

            if (File.Exists(filePath))
            {
                File.Delete(filePath);
            }
            using (var sw = File.CreateText(filePath))
            {
                sw.Write(EditorJsonUtility.ToJson(packCfg));
            }
        }
        public static PackageCfg GetCfg()
        {
            var cfg = new PackageCfg();

            var d = EditorPrefs.GetString(SaveKey, "");

            if (!string.IsNullOrEmpty(d))
            {
                EditorJsonUtility.FromJsonOverwrite(d, cfg);
            }
            return(cfg);
        }
        public static void TraverseDirectory(string dirPath, PackageCfg pcfg, string root)
        {
            var files = Directory.GetFiles(dirPath);

            foreach (var file in files)
            {
                var correctFileName = file.Replace(@"\", @"/").ToLower();

                var ext = Path.GetExtension(correctFileName);
                var ret = AssetBundleMgr.instance.CompressExts.FirstOrDefault(c => c == ext);
                if (!string.IsNullOrEmpty(ret))
                {
                    pcfg.Files.Add(new FileCfg(CommonTool.CalFileMD5(correctFileName), GetRelativePath(correctFileName, root)));
                }
            }
            var dirs = Directory.GetDirectories(dirPath);

            foreach (var dir in dirs)
            {
                TraverseDirectory(dir, pcfg, root);
            }
        }
        IEnumerator PackCoroutineEnter()
        {
            EventMgr.instance.SendEvent<float>(EventDefine.UpdateProgress, 0f);

            yield return StartCoroutine(UncompressPackRes(false));

            var mainPackCfg = new PackageCfg();
            var mainPackCfgPath = Application.persistentDataPath + AssetBundlePath.kSlash + AssetBundleMgr.instance.ZipFolder + AssetBundlePath.kSlash + AssetBundlePath.kPackCfg;
            var jsonData = File.ReadAllText(mainPackCfgPath);
            JsonUtility.FromJsonOverwrite(jsonData, mainPackCfg);

            EventMgr.instance.SendEvent<float>(EventDefine.UpdateProgress, 0.05f);

            //覆盖安装检测(之前已经有了缓存文件)
            var InternalCfg = Resources.Load<VersionCfg>(Path.GetFileNameWithoutExtension(AssetBundlePath.kVersionCfg));
            if (mainPackCfg.CurVersion != InternalCfg.CurVersion)
            {
                yield return StartCoroutine(UncompressPackRes(true));
                jsonData = File.ReadAllText(mainPackCfgPath);
                JsonUtility.FromJsonOverwrite(jsonData, mainPackCfg);
            }

            EventMgr.instance.SendEvent<float>(EventDefine.UpdateProgress, 0.2f);

            //todo: 向服务器请求版本更新信息
            var updateUri = "192.168.1.79/" + "UpdateJson";
            var patchUri = "file://" + Application.dataPath + "/DiffPatch/" + AssetBundleMgr.instance.PatchZip;
            using (var updateReq = UnityWebRequest.Get(updateUri))
            {
                EventMgr.instance.SendEvent<float>(EventDefine.UpdateProgress, 0.3f);

                yield return updateReq.SendWebRequest();
                if (updateReq.isHttpError || updateReq.isNetworkError)
                {
                    Debug.LogError(updateReq.error);
                    yield break;
                }
                EventMgr.instance.SendEvent<float>(EventDefine.UpdateProgress, 0.4f);

                var serverCfg = new PackageCfg();
                jsonData = System.Text.Encoding.UTF8.GetString(updateReq.downloadHandler.data);
                JsonUtility.FromJsonOverwrite(jsonData, serverCfg);
                if (mainPackCfg.CurVersion < serverCfg.ForceUpdateVersion)
                {
                    //todo:展示强更界面
                    yield break;
                }
                EventMgr.instance.SendEvent<float>(EventDefine.UpdateProgress, 0.5f);

                //处理补丁包
                var mainRoot = Application.persistentDataPath + AssetBundlePath.kSlash + AssetBundleMgr.instance.ZipFolder + AssetBundlePath.kSlash;
                if (mainPackCfg.PatchVersion < serverCfg.PatchVersion)
                {
                    if (mainPackCfg.PatchVersion != 0)
                    {
                        yield return StartCoroutine(UncompressPackRes(true));
                    }
                    using (var patchReq = UnityWebRequest.Get(patchUri))
                    {
                        EventMgr.instance.SendEvent<float>(EventDefine.UpdateProgress, 0.6f);

                        yield return patchReq.SendWebRequest();
                        if (patchReq.isNetworkError || patchReq.isHttpError)
                        {
                            Debug.LogError(updateReq.error);
                            yield break;
                        }
                        EventMgr.instance.SendEvent<float>(EventDefine.UpdateProgress, 0.7f);

                        var patchZipPath = Application.persistentDataPath + AssetBundlePath.kSlash + AssetBundleMgr.instance.PatchZip;
                        if (File.Exists(patchZipPath))
                        {
                            File.Delete(patchZipPath);
                        }
                        File.WriteAllBytes(patchZipPath, patchReq.downloadHandler.data);

                        ForceUncompressLocalRes(patchZipPath, Application.persistentDataPath + AssetBundlePath.kSlash + AssetBundlePath.kPatchDir);
                    }

                    EventMgr.instance.SendEvent<float>(EventDefine.UpdateProgress, 0.8f);

                    //合并主包与补丁包配置
                    var mainPackDict = new Dictionary<string, FileCfg>();
                    foreach (var mf in mainPackCfg.Files)
                    {
                        mainPackDict[mf.Path] = mf;
                    }
                    var patchRoot = Application.persistentDataPath + AssetBundlePath.kSlash + AssetBundlePath.kPatchDir + AssetBundlePath.kSlash;
                    var patchCfg = new PackageCfg();
                    var patchJsonData = File.ReadAllText(patchRoot + AssetBundlePath.kPatchCfg);
                    JsonUtility.FromJsonOverwrite(patchJsonData, patchCfg);
                    foreach (var f in patchCfg.Files)
                    {
                        var isPatchFile = Path.GetExtension(f.Path) == AssetBundleMgr.instance.kPatchFileExt;
                        if (!isPatchFile)
                        {
                            if (!mainPackDict.ContainsKey(f.Path))
                            {
                                Debug.LogError("patch file no mainPack file:" + f.Path);
                                yield break;
                            }
                            if (mainPackDict[f.Path].MD5 == f.MD5)
                            {
                                Debug.LogError("patchfile same as mainPack file:" + f.Path);
                                yield break;
                            }
                        }
                        if (File.Exists(mainRoot + f.Path))
                        {
                            File.Delete(mainRoot + f.Path);
                        }
                        File.Copy(patchRoot + f.Path, mainRoot + f.Path);
                        mainPackDict[f.Path] = f;
                    }
                    mainPackCfg.PatchVersion = patchCfg.PatchVersion;
                    mainPackCfg.Files = mainPackDict.Values.ToList();
                    var mergeCfgJsonData = JsonUtility.ToJson(mainPackCfg);
                    if (File.Exists(mainPackCfgPath))
                    {
                        File.Delete(mainPackCfgPath);
                    }
                    File.WriteAllText(mainPackCfgPath, mergeCfgJsonData);

                    Directory.Delete(patchRoot, true);
                }
                EventMgr.instance.SendEvent<float>(EventDefine.UpdateProgress, 0.9f);

                //进行文件完整性校验
                var md5 = "";
                foreach(var f in mainPackCfg.Files)
                {
                    if (!File.Exists(mainRoot + f.Path))
                    {
                        Debug.LogError("mainPack file not exist " + f.Path);
                        yield break;
                    }
                    if (Path.GetExtension(f.Path) == AssetBundlePath.kPackCfgSuffix)
                    {
                        continue;
                    }
                    md5 = CommonTool.CalFileMD5(mainRoot + f.Path);
                    if (md5 != f.MD5)
                    {
                        Debug.LogError(string.Format("mainPack file MD5 check error! cfg md5:{0} real md5:{1}", f.MD5, md5));
                        yield break;
                    }
                }

                //如果存在代码补丁就加载
                var codePatchFile = mainRoot + AssetBundlePath.kCodePatchFile;
                if (File.Exists(codePatchFile))
                {
                    PatchManager.Load(new MemoryStream(File.ReadAllBytes(codePatchFile)));
                }
                EventMgr.instance.SendEvent<float>(EventDefine.UpdateProgress, 1f);
            }
            EventMgr.instance.SendEvent(EventDefine.UpdateFinish);
        }