public static void StartLoadRuntimeManifest() { _RuntimeRawManifest = null; _RuntimeManifest = null; _RuntimeManifestReady.Reset(); _RuntimeManifestTaskIdle.Reset(); PlatDependant.RunBackground(LoadRuntimeManifest); }
public static void CheckRuntimeManifest() { _RuntimeManifestReady.WaitOne(); if (_RuntimeRawManifest == null) { StartLoadRuntimeManifest(); _RuntimeManifestReady.WaitOne(); } _RuntimeManifest = MergeAndCollapseRuntimeManifest(_RuntimeRawManifest); }
private static void MergeRuntimeManifestInMod(CapsResManifest target, CapsResManifestNode root) { var flags = ResManager.GetValidDistributeFlags(); if (root.Children != null) { CapsResManifestNode modNode; if (root.Children.TryGetValue("mod", out modNode)) { if (modNode != null && modNode.Children != null) { var modChildren = modNode.Children; // merge - critical mod for (int i = 0; i < modChildren.Count; ++i) { var modChild = modChildren.Values[i]; var mod = modChild.PPath; var moddesc = ResManager.GetDistributeDesc(mod); if (moddesc == null || (moddesc.InMain && !moddesc.IsOptional)) { CapsResManifest.MergeManifestNode(target.Root, modChild, true); } } // merge - opt mod for (int i = 0; i < flags.Length; ++i) { var flag = flags[i]; CapsResManifestNode modChild; if (modChildren.TryGetValue(flag, out modChild)) { if (modChild != null) { CapsResManifest.MergeManifestNode(target.Root, modChild, true); } } } } } } }
public static void SaveManifest(CapsResManifest mani, string file) { var tmpfile = file + ".tmp"; using (var sw = PlatDependant.OpenWriteText(tmpfile)) { if (mani != null && mani.Root != null) { Stack <Pack <int, CapsResManifestNode> > candis = new Stack <Pack <int, CapsResManifestNode> >(); candis.Push(new Pack <int, CapsResManifestNode>(0, mani.Root)); while (candis.Count > 0) { var ppair = candis.Pop(); var plvl = ppair.t1; var parent = ppair.t2; for (int i = 0; i < plvl; ++i) { sw.Write("*"); } sw.WriteLine(parent.PPath ?? ""); var children = parent.Children; if (children != null) { var clvl = plvl + 1; for (int i = children.Count - 1; i >= 0; --i) { var child = children.Values[i]; candis.Push(new Pack <int, CapsResManifestNode>(clvl, child)); } } } } } PlatDependant.MoveFile(tmpfile, file); }
public static IEnumerator GenerateBuildWorkAsync(Dictionary <string, CapsResBuildWork> result, IList <string> assets, IEditorWorkProgressShower winprog, IList <IResBuilderEx> runOnceExBuilder) { var logger = new EditorWorkProgressLogger() { Shower = winprog }; logger.Log("(Start) Generate Build Work."); if (winprog != null && AsyncWorkTimer.Check()) { yield return(null); } if (result == null) { logger.Log("(Error) You have to provide container to retrive the result."); yield break; } result.Clear(); if (assets == null) { logger.Log("(Option) Get All Assets."); assets = AssetDatabase.GetAllAssetPaths(); if (winprog != null && AsyncWorkTimer.Check()) { yield return(null); } } if (assets != null) { List <IResBuilderEx> allExBuilders = new List <IResBuilderEx>(ResBuilderEx); if (runOnceExBuilder != null) { allExBuilders.AddRange(runOnceExBuilder); } Dictionary <string, Dictionary <string, List <string> > > mod2build = new Dictionary <string, Dictionary <string, List <string> > >(); Dictionary <string, Dictionary <string, CapsResManifest> > mod2mani = new Dictionary <string, Dictionary <string, CapsResManifest> >(); for (int i = 0; i < assets.Count; ++i) { if (winprog != null && AsyncWorkTimer.Check()) { yield return(null); } var asset = assets[i]; logger.Log(asset); if (string.IsNullOrEmpty(asset)) { logger.Log("Empty Path."); continue; } if (System.IO.Directory.Exists(asset)) { logger.Log("Folder."); continue; } if (CapsResInfoEditor.IsAssetScript(asset)) { logger.Log("Script."); continue; } string mod = null; string opmod = null; string dist = null; string norm = asset; bool inPackage = false; if (asset.StartsWith("Assets/Mods/") || (inPackage = asset.StartsWith("Packages/"))) { string sub; if (inPackage) { sub = asset.Substring("Packages/".Length); } else { sub = asset.Substring("Assets/Mods/".Length); } var index = sub.IndexOf('/'); if (index < 0) { logger.Log("Cannot Parse Module."); continue; } mod = sub.Substring(0, index); if (inPackage) { mod = CapsModEditor.GetPackageModName(mod); } if (string.IsNullOrEmpty(mod)) { logger.Log("Empty Module."); continue; } sub = sub.Substring(index + 1); if (!sub.StartsWith("CapsRes/")) { logger.Log("Should Ignore This Asset."); continue; } var moddesc = ResManager.GetDistributeDesc(mod); bool isMainPackage = inPackage && !CapsModEditor.ShouldTreatPackageAsMod(CapsModEditor.GetPackageName(mod)); if (moddesc == null || moddesc.InMain || isMainPackage) { mod = ""; if (moddesc != null && moddesc.IsOptional && !isMainPackage) { opmod = moddesc.Mod; } } sub = sub.Substring("CapsRes/".Length); norm = sub; if (sub.StartsWith("dist/")) { sub = sub.Substring("dist/".Length); index = sub.IndexOf('/'); if (index > 0) { dist = sub.Substring(0, index); norm = sub.Substring(index + 1); } } } else { if (asset.StartsWith("Assets/CapsRes/")) { mod = ""; var sub = asset.Substring("Assets/CapsRes/".Length); norm = sub; if (sub.StartsWith("dist/")) { sub = sub.Substring("dist/".Length); var index = sub.IndexOf('/'); if (index > 0) { dist = sub.Substring(0, index); norm = sub.Substring(index + 1); } } } else { logger.Log("Should Ignore This Asset."); continue; } } if (string.IsNullOrEmpty(norm)) { logger.Log("Normallized Path Empty."); continue; } mod = mod ?? ""; dist = dist ?? ""; logger.Log("Mod " + mod + "; Dist " + dist + "; Norm " + norm); Dictionary <string, List <string> > builds; if (!mod2build.TryGetValue(mod, out builds)) { builds = new Dictionary <string, List <string> >(); mod2build[mod] = builds; } Dictionary <string, CapsResManifest> manis; if (!mod2mani.TryGetValue(opmod ?? mod, out manis)) { manis = new Dictionary <string, CapsResManifest>(); mod2mani[opmod ?? mod] = manis; } CapsResManifest mani; if (!manis.TryGetValue(dist, out mani)) { mani = new CapsResManifest(); mani.MFlag = opmod ?? mod; mani.DFlag = dist; if (opmod != null) { mani.InMain = true; } manis[dist] = mani; } string bundle = null; bool shouldWriteBRef = false; for (int j = 0; j < allExBuilders.Count; ++j) { bundle = allExBuilders[j].FormatBundleName(asset, opmod ?? mod, dist, norm); if (bundle != null) { break; } } if (bundle == null) { bundle = FormatBundleName(asset, opmod ?? mod, dist, norm); } else { shouldWriteBRef = true; } List <string> build; if (!builds.TryGetValue(bundle, out build)) { build = new List <string>(); builds[bundle] = build; } build.Add(asset); var node = mani.AddOrGetItem(asset); for (int j = 0; j < allExBuilders.Count; ++j) { if (allExBuilders[j].CreateItem(node)) { break; } } if (node.Item == null) { var item = new CapsResManifestItem(node); if (asset.EndsWith(".prefab")) { item.Type = (int)CapsResManifestItemType.Prefab; } else if (asset.EndsWith(".unity")) { item.Type = (int)CapsResManifestItemType.Scene; } else { item.Type = (int)CapsResManifestItemType.Normal; } if (shouldWriteBRef) { item.BRef = bundle; } node.Item = item; } for (int j = 0; j < allExBuilders.Count; ++j) { allExBuilders[j].ModifyItem(node.Item); } } if (winprog != null && AsyncWorkTimer.Check()) { yield return(null); } logger.Log("(Phase) Combine the final result."); foreach (var kvpbuild in mod2build) { var mod = kvpbuild.Key; var builds = kvpbuild.Value; CapsResBuildWork work = new CapsResBuildWork(); if (mod == "") { List <CapsResManifest> manis = new List <CapsResManifest>(mod2mani[mod].Values); foreach (var kvpmm in mod2mani) { if (!mod2build.ContainsKey(kvpmm.Key)) { manis.AddRange(kvpmm.Value.Values); } } work.Manifests = manis.ToArray(); } else { work.Manifests = mod2mani[mod].Values.ToArray(); } work.ABs = new AssetBundleBuild[builds.Count]; int index = 0; foreach (var kvpbundle in builds) { var bundleName = kvpbundle.Key; var bundleAssets = kvpbundle.Value; AssetBundleBuild build = new AssetBundleBuild(); build.assetBundleName = kvpbundle.Key; build.assetNames = kvpbundle.Value.ToArray(); for (int j = 0; j < allExBuilders.Count; ++j) { allExBuilders[j].GenerateBuildWork(bundleName, bundleAssets, ref build, work, index); } work.ABs[index++] = build; } result[mod] = work; } } logger.Log("(Done) Generate Build Work."); }
public static IEnumerator BuildResAsync(IList <string> assets, IEditorWorkProgressShower winprog, IList <IResBuilderEx> runOnceExBuilder) { bool isDefaultBuild = assets == null; var logger = new EditorWorkProgressLogger() { Shower = winprog }; bool shouldCreateBuildingParams = BuildingParams == null; BuildingParams = BuildingParams ?? ResBuilderParams.Create(); var timetoken = BuildingParams.timetoken; var makezip = BuildingParams.makezip; string outputDir = "Latest"; if (!isDefaultBuild) { outputDir = timetoken + "/build"; } outputDir = "EditorOutput/Build/" + outputDir; System.IO.StreamWriter swlog = null; try { System.IO.Directory.CreateDirectory(outputDir + "/log/"); swlog = new System.IO.StreamWriter(outputDir + "/log/ResBuildLog.txt", false, System.Text.Encoding.UTF8); } catch (Exception e) { Debug.Log(e); } EditorApplication.LockReloadAssemblies(); List <IResBuilderEx> allExBuilders = new List <IResBuilderEx>(ResBuilderEx); if (runOnceExBuilder != null) { allExBuilders.AddRange(runOnceExBuilder); } Application.LogCallback LogToFile = (message, stack, logtype) => { swlog.WriteLine(message); swlog.Flush(); }; if (swlog != null) { Application.logMessageReceived += LogToFile; } for (int i = 0; i < allExBuilders.Count; ++i) { allExBuilders[i].Prepare(outputDir); } bool cleanupDone = false; Action BuilderCleanup = () => { if (!cleanupDone) { logger.Log("(Phase) Build Res Cleaup."); cleanupDone = true; for (int i = 0; i < allExBuilders.Count; ++i) { allExBuilders[i].Cleanup(); } logger.Log("(Done) Build Res Cleaup."); if (swlog != null) { Application.logMessageReceived -= LogToFile; swlog.Flush(); swlog.Dispose(); if (isDefaultBuild) { var logdir = "EditorOutput/Build/" + timetoken + "/log/"; System.IO.Directory.CreateDirectory(logdir); System.IO.File.Copy(outputDir + "/log/ResBuildLog.txt", logdir + "ResBuildLog.txt", true); } } if (shouldCreateBuildingParams) { BuildingParams = null; } EditorApplication.UnlockReloadAssemblies(); } }; if (winprog != null) { winprog.OnQuit += BuilderCleanup; } try { logger.Log("(Start) Build Res."); if (winprog != null && AsyncWorkTimer.Check()) { yield return(null); } //logger.Log("(Phase) Ex Full Build System."); //for (int i = 0; i < allExBuilders.Count; ++i) //{ // IEnumerator exwork = allExBuilders[i].CustomBuild(); // if (exwork != null) // { // while (exwork.MoveNext()) // { // if (winprog != null) // { // yield return exwork.Current; // } // } // } // if (winprog != null && AsyncWorkTimer.Check()) yield return null; //} // Generate Build Work Dictionary <string, CapsResBuildWork> works = new Dictionary <string, CapsResBuildWork>(); var work = GenerateBuildWorkAsync(works, assets, winprog, runOnceExBuilder); while (work.MoveNext()) { if (winprog != null) { yield return(work.Current); } } logger.Log("(Phase) Write Manifest."); var managermod = CapsEditorUtils.__MOD__; var manidir = "Assets/Mods/" + managermod + "/Build/"; System.IO.Directory.CreateDirectory(manidir); List <AssetBundleBuild> listManiBuilds = new List <AssetBundleBuild>(); HashSet <string> maniFileNames = new HashSet <string>(); foreach (var kvp in works) { foreach (var mani in kvp.Value.Manifests) { var mod = mani.MFlag; var dist = mani.DFlag; if (winprog != null && AsyncWorkTimer.Check()) { yield return(null); } logger.Log("Mod " + mod + "; Dist " + dist); var dmani = CapsResManifest.Save(mani); var filename = "m-" + mod + "-d-" + dist; var manipath = manidir + filename + ".m.asset"; AssetDatabase.CreateAsset(dmani, manipath); maniFileNames.Add(filename.ToLower()); listManiBuilds.Add(new AssetBundleBuild() { assetBundleName = filename + ".m.ab", assetNames = new[] { manipath } }); } } logger.Log("(Phase) Build Manifest."); if (winprog != null && AsyncWorkTimer.Check()) { yield return(null); } var buildopt = BuildAssetBundleOptions.DeterministicAssetBundle | BuildAssetBundleOptions.ChunkBasedCompression; BuildTarget buildtar = EditorUserBuildSettings.activeBuildTarget; var outmanidir = outputDir + "/res/mani"; System.IO.Directory.CreateDirectory(outmanidir); BuildPipeline.BuildAssetBundles(outmanidir, listManiBuilds.ToArray(), buildopt, buildtar); logger.Log("(Phase) Delete Unused Manifest."); if (winprog != null && AsyncWorkTimer.Check()) { yield return(null); } var manifiles = PlatDependant.GetAllFiles(outmanidir); for (int i = 0; i < manifiles.Length; ++i) { var file = manifiles[i]; if (file.EndsWith(".m.ab")) { var filename = file.Substring(outmanidir.Length + 1, file.Length - outmanidir.Length - 1 - ".m.ab".Length); if (!maniFileNames.Contains(filename)) { PlatDependant.DeleteFile(file); PlatDependant.DeleteFile(file + ".manifest"); } } } logger.Log("(Phase) Real Build."); foreach (var kvp in works) { var mod = kvp.Key; var abs = kvp.Value.ABs; logger.Log("Mod " + mod); if (winprog != null && AsyncWorkTimer.Check()) { yield return(null); } var dest = outputDir + "/res"; if (!string.IsNullOrEmpty(mod)) { dest += "/mod/" + mod; } System.IO.Directory.CreateDirectory(dest); // delete the unused ab HashSet <string> buildFiles = new HashSet <string>(); for (int i = 0; i < abs.Length; ++i) { if (!kvp.Value.ForceRefreshABs.Contains(i)) { if (string.IsNullOrEmpty(abs[i].assetBundleVariant)) { buildFiles.Add(abs[i].assetBundleName.ToLower()); } else { buildFiles.Add(abs[i].assetBundleName.ToLower() + "." + abs[i].assetBundleVariant.ToLower()); } } } var files = System.IO.Directory.GetFiles(dest); for (int i = 0; i < files.Length; ++i) { var file = files[i]; if (!file.EndsWith(".ab")) { var sub = System.IO.Path.GetFileName(file); var split = sub.LastIndexOf(".ab."); if (split < 0) { continue; } var ext = sub.Substring(split + ".ab.".Length); if (ext.Contains(".")) { continue; } if (ext == "manifest") { continue; } } { var fileName = System.IO.Path.GetFileName(file); if (!buildFiles.Contains(fileName)) { PlatDependant.DeleteFile(file); PlatDependant.DeleteFile(file + ".manifest"); } } } BuildPipeline.BuildAssetBundles(dest, abs, buildopt, buildtar); } logger.Log("(Phase) Delete Mod Folder Not Built."); var outmoddir = outputDir + "/res/mod/"; if (System.IO.Directory.Exists(outmoddir)) { var builtMods = new HashSet <string>(works.Keys); var allModFolders = System.IO.Directory.GetDirectories(outmoddir); int deletedModFolderCnt = 0; for (int i = 0; i < allModFolders.Length; ++i) { if (winprog != null && AsyncWorkTimer.Check()) { yield return(null); } var modfolder = allModFolders[i]; logger.Log(modfolder); var mod = modfolder.Substring(outmoddir.Length); if (!builtMods.Contains(mod)) { System.IO.Directory.Delete(modfolder, true); ++deletedModFolderCnt; } } if (deletedModFolderCnt == allModFolders.Length) { System.IO.Directory.Delete(outmoddir, true); } } if (isDefaultBuild) { logger.Log("(Phase) Write Version."); var outverdir = outputDir + "/res/version.txt"; int version = GetResVersion(); System.IO.File.WriteAllText(outverdir, version.ToString()); } logger.Log("(Phase) Copy."); var outresdir = outputDir + "/res/"; var allbuildfiles = PlatDependant.GetAllFiles(outresdir); if (System.IO.Directory.Exists("Assets/StreamingAssets/res/")) { logger.Log("Delete old."); var allexistfiles = PlatDependant.GetAllFiles("Assets/StreamingAssets/res/"); for (int i = 0; i < allexistfiles.Length; ++i) { if (winprog != null && AsyncWorkTimer.Check()) { yield return(null); } PlatDependant.DeleteFile(allexistfiles[i]); } } for (int i = 0; i < allbuildfiles.Length; ++i) { if (winprog != null && AsyncWorkTimer.Check()) { yield return(null); } var srcfile = allbuildfiles[i]; if (srcfile.EndsWith(".manifest")) { continue; } var part = srcfile.Substring(outresdir.Length); if (part == "mani/mani") { continue; } logger.Log(part); var destfile = "Assets/StreamingAssets/res/" + part; PlatDependant.CreateFolder(System.IO.Path.GetDirectoryName(destfile)); System.IO.File.Copy(srcfile, destfile); } if (System.IO.Directory.Exists("Assets/StreamingAssets/res/mod/")) { logger.Log("(Phase) Delete StreamingAssets Mod Folder Not Built."); var builtMods = new HashSet <string>(works.Keys); var allModFolders = System.IO.Directory.GetDirectories("Assets/StreamingAssets/res/mod/"); int deletedModFolderCnt = 0; for (int i = 0; i < allModFolders.Length; ++i) { if (winprog != null && AsyncWorkTimer.Check()) { yield return(null); } var modfolder = allModFolders[i]; logger.Log(modfolder); var mod = modfolder.Substring("Assets/StreamingAssets/res/mod/".Length); if (!builtMods.Contains(mod)) { System.IO.Directory.Delete(modfolder, true); ++deletedModFolderCnt; } } if (deletedModFolderCnt == allModFolders.Length) { System.IO.Directory.Delete("Assets/StreamingAssets/res/mod/", true); } } if (isDefaultBuild && makezip) { logger.Log("(Phase) Zip."); List <Pack <string, string, IList <string> > > zips = new List <Pack <string, string, IList <string> > >(); var outzipdir = "EditorOutput/Build/" + timetoken + "/whole/res/"; System.IO.Directory.CreateDirectory(outzipdir); foreach (var kvp in works) { var mod = kvp.Key; var manis = kvp.Value.Manifests; for (int i = 0; i < manis.Length; ++i) { var opmod = manis[i].MFlag; var dist = manis[i].DFlag; if (winprog != null && AsyncWorkTimer.Check()) { yield return(null); } logger.Log("Mod " + opmod + "; Dist " + dist); List <string> entries = new List <string>(); // abs var abdir = outputDir + "/res"; if (!string.IsNullOrEmpty(mod)) { abdir += "/mod/" + mod; } if (System.IO.Directory.Exists(abdir)) { try { var files = System.IO.Directory.GetFiles(abdir); for (int j = 0; j < files.Length; ++j) { var file = files[j]; if (!file.EndsWith(".ab")) { var sub = System.IO.Path.GetFileName(file); var split = sub.LastIndexOf(".ab."); if (split < 0) { continue; } var ext = sub.Substring(split + ".ab.".Length); if (ext.Contains(".")) { continue; } if (ext == "manifest") { continue; } } { var bundle = file.Substring(abdir.Length + 1); if (IsBundleInModAndDist(bundle, opmod, dist)) { var entry = file.Substring(outputDir.Length + 1); entries.Add(entry); entries.Add(entry + ".manifest"); } } } } catch (Exception e) { logger.Log("(Error)(Not Critical)"); logger.Log(e.ToString()); } } if (entries.Count > 0) { // unity build mani var umani = abdir + "/" + (string.IsNullOrEmpty(mod) ? "res" : mod); umani = umani.Substring(outputDir.Length + 1); entries.Add(umani); entries.Add(umani + ".manifest"); // mani var mani = "m-" + opmod.ToLower() + "-d-" + dist.ToLower() + ".m.ab"; mani = "res/mani/" + mani; entries.Add(mani); entries.Add(mani + ".manifest"); entries.Add("res/mani/mani"); entries.Add("res/mani/mani.manifest"); // version entries.Add("res/version.txt"); var zipfile = outzipdir + "m-" + opmod + "-d-" + dist + ".zip"; zips.Add(new Pack <string, string, IList <string> >(zipfile, outputDir, entries)); //var workz = MakeZipAsync(zipfile, outputDir, entries, winprog); //while (workz.MoveNext()) //{ // if (winprog != null) // { // yield return workz.Current; // } //} } } } if (zips.Count > 0) { var workz = CapsResBuilder.MakeZipsBackground(zips, winprog); while (workz.MoveNext()) { if (winprog != null) { yield return(workz.Current); } } } } for (int i = 0; i < allExBuilders.Count; ++i) { allExBuilders[i].OnSuccess(); } } finally { BuilderCleanup(); logger.Log("(Done) Build Res."); } }
// This will judge whether a mod is optional, so this should be called in UnityMain thread. private static CapsResManifest MergeAndCollapseRuntimeManifest(CapsResManifest rawmani) { var root = rawmani.Root; var rmani = new CapsResManifest(); if (root != null) { rmani.Root = new CapsResManifestNode(rmani); CapsResManifestNode tmpNode = new CapsResManifestNode(rawmani); CapsResManifestNode archNode = null; if (root.Children != null) { tmpNode.Children = new SortedList <string, CapsResManifestNode>(); for (int i = 0; i < root.Children.Count; ++i) { var child = root.Children.Values[i]; if (child.PPath == "mod") { continue; } else if (child.PPath == "@64") { if (Environment.Is64BitProcess) { archNode = child; } continue; } else if (child.PPath == "@32") { if (!Environment.Is64BitProcess) { archNode = child; } continue; } tmpNode.Children[child.PPath] = child; } } // merge - no mod CapsResManifest.MergeManifestNode(rmani.Root, tmpNode, true); // merge - no mod on arch if (archNode != null) { tmpNode = new CapsResManifestNode(rawmani); if (archNode.Children != null) { tmpNode.Children = new SortedList <string, CapsResManifestNode>(); for (int i = 0; i < archNode.Children.Count; ++i) { var child = archNode.Children.Values[i]; if (child.PPath == "mod") { continue; } tmpNode.Children[child.PPath] = child; } } CapsResManifest.MergeManifestNode(rmani.Root, tmpNode, true); } // merge - mod MergeRuntimeManifestInMod(rmani, root); // merge - mod on arch if (archNode != null) { MergeRuntimeManifestInMod(rmani, archNode); } // Collapse var flags = ResManager.GetValidDistributeFlags(); rmani.CollapseManifest(flags); rmani.TrimExcess(); } return(rmani); }
private static void LoadRuntimeManifest(TaskProgress progress) { try { var maniPath = ThreadSafeValues.UpdatePath + "/spt/manifest.m.txt"; if (PlatDependant.IsFileExist(maniPath)) { _RuntimeRawManifest = LoadManifest(maniPath); } else { CapsResManifest mani = new CapsResManifest(); // load from update path var sptfolder = ThreadSafeValues.UpdatePath + "/spt/"; try { var files = PlatDependant.GetAllFiles(sptfolder); if (files != null && files.Length > 0) { for (int i = 0; i < files.Length; ++i) { var file = files[i]; var part = file.Substring(sptfolder.Length).Replace('\\', '/'); var node = mani.AddOrGetItem(part); if (node.Item == null) { CapsResManifestItem item; item = new CapsResManifestItem(node); node.Item = item; } } } } catch (Exception e) { PlatDependant.LogError(e); } // load from package if (ThreadSafeValues.AppStreamingAssetsPath.Contains("://")) { if (ThreadSafeValues.AppPlatform == RuntimePlatform.Android.ToString() && ResManager.LoadAssetsFromApk) { // Obb if (ResManager.LoadAssetsFromObb && ResManager.ObbZipArchive != null) { sptfolder = "spt/"; int retryTimes = 10; int entryindex = 0; for (int i = 0; i < retryTimes; ++i) { Exception error = null; do { ZipArchive za = ResManager.ObbZipArchive; if (za == null) { PlatDependant.LogError("Obb Archive Cannot be read."); break; } try { var entries = za.Entries; while (entryindex < entries.Count) { var entry = entries[entryindex]; var fullname = entry.FullName; if (fullname.StartsWith(sptfolder)) { var part = fullname.Substring(sptfolder.Length); var node = mani.AddOrGetItem(part); if (node.Item == null) { CapsResManifestItem item; item = new CapsResManifestItem(node); node.Item = item; } } ++entryindex; } } catch (Exception e) { error = e; break; } } while (false); if (error != null) { if (i == retryTimes - 1) { PlatDependant.LogError(error); } else { PlatDependant.LogError(error); PlatDependant.LogInfo("Need Retry " + i); } } else { break; } } } // Apk //if (true) { sptfolder = "assets/spt/"; int retryTimes = 10; int entryindex = 0; for (int i = 0; i < retryTimes; ++i) { Exception error = null; do { ZipArchive za = ResManager.AndroidApkZipArchive; if (za == null) { PlatDependant.LogError("Apk Archive Cannot be read."); break; } try { var entries = za.Entries; while (entryindex < entries.Count) { var entry = entries[entryindex]; var fullname = entry.FullName; if (fullname.StartsWith(sptfolder)) { var part = fullname.Substring(sptfolder.Length); var node = mani.AddOrGetItem(part); if (node.Item == null) { CapsResManifestItem item; item = new CapsResManifestItem(node); node.Item = item; } } ++entryindex; } } catch (Exception e) { error = e; break; } } while (false); if (error != null) { if (i == retryTimes - 1) { PlatDependant.LogError(error); } else { PlatDependant.LogError(error); PlatDependant.LogInfo("Need Retry " + i); } } else { break; } } } } } else { sptfolder = ThreadSafeValues.AppStreamingAssetsPath + "/spt/"; try { var files = PlatDependant.GetAllFiles(sptfolder); if (files != null && files.Length > 0) { for (int i = 0; i < files.Length; ++i) { var file = files[i]; var part = file.Substring(sptfolder.Length).Replace('\\', '/'); var node = mani.AddOrGetItem(part); if (node.Item == null) { CapsResManifestItem item; item = new CapsResManifestItem(node); node.Item = item; } } } } catch (Exception e) { PlatDependant.LogError(e); } } mani.TrimExcess(); _RuntimeRawManifest = mani; _RuntimeManifestReady.Set(); SaveManifest(mani, maniPath); } } finally { _RuntimeManifestReady.Set(); _RuntimeManifestTaskIdle.Set(); } }
public static CapsResManifest LoadManifest(string file) { CapsResManifest mani = new CapsResManifest(); if (PlatDependant.IsFileExist(file)) { using (var sr = PlatDependant.OpenReadText(file)) { if (sr != null) { List <CapsResManifestNode> nodeStack = new List <CapsResManifestNode>(); var root = new CapsResManifestNode(mani); mani.Root = root; nodeStack.Add(root); int nxtChar = -1; while ((nxtChar = sr.Peek()) > 0) { int lvl = 0; while (nxtChar == '*') { sr.Read(); ++lvl; nxtChar = sr.Peek(); } string ppath = sr.ReadLine(); if (string.IsNullOrEmpty(ppath)) { continue; } if (nodeStack.Count > lvl) { var last = nodeStack[nodeStack.Count - 1]; if (last.Children == null || last.Children.Count <= 0) { CapsResManifestItem item; item = new CapsResManifestItem(last); last.Item = item; } nodeStack.RemoveRange(lvl, nodeStack.Count - lvl); } { var last = nodeStack[nodeStack.Count - 1]; if (last.Children == null) { last.Children = new SortedList <string, CapsResManifestNode>(); } var child = new CapsResManifestNode(last, ppath); last.Children[ppath] = child; nodeStack.Add(child); } } if (nodeStack.Count > 1) { var last = nodeStack[nodeStack.Count - 1]; CapsResManifestItem item; item = new CapsResManifestItem(last); last.Item = item; } mani.TrimExcess(); } } } return(mani); }
public static bool BuildResUpdate(string oldz, string newz, string diff) { LinkedList<IDisposable> lstToDispose = new LinkedList<IDisposable>(); try { ZipArchive olda = null, newa = null, diffa = null; try { var olds = File.OpenRead(oldz); lstToDispose.AddFirst(olds); olda = new ZipArchive(olds, ZipArchiveMode.Read); lstToDispose.AddFirst(olda); } catch { } try { var news = File.OpenRead(newz); lstToDispose.AddFirst(news); newa = new ZipArchive(news, ZipArchiveMode.Read); lstToDispose.AddFirst(newa); } catch { } HashSet<string> diffb = new HashSet<string>(); if (newa == null) { return false; } else if (Path.GetFileNameWithoutExtension(oldz) != Path.GetFileNameWithoutExtension(newz)) { PlatDependant.LogError("Build update diff error - the old zip and new zip have different names."); return false; } else { string mentry = "res/mani/" + Path.GetFileNameWithoutExtension(newz).ToLower() + ".m.ab"; CapsResManifest mold = null; CapsResManifest mnew = null; // get mani of old try { if (olda != null) { var oldme = olda.GetEntry(mentry); if (oldme != null) { using (var stream = oldme.Open()) { using (var mems = new MemoryStream()) { stream.CopyTo(mems); var mab = UnityEngine.AssetBundle.LoadFromMemory(mems.ToArray()); if (mab) { var allassets = mab.LoadAllAssets<CapsResOnDiskManifest>(); if (allassets != null && allassets.Length > 0) { mold = CapsResManifest.Load(allassets[0]); } mab.Unload(true); } } } } } } catch { } // get mani of new try { var newme = newa.GetEntry(mentry); if (newme != null) { using (var stream = newme.Open()) { using (var mems = new MemoryStream()) { stream.CopyTo(mems); var mab = UnityEngine.AssetBundle.LoadFromMemory(mems.ToArray()); if (mab) { var allassets = mab.LoadAllAssets<CapsResOnDiskManifest>(); if (allassets != null && allassets.Length > 0) { mnew = CapsResManifest.Load(allassets[0]); } mab.Unload(true); } } } } } catch { } string abrootold = "res/"; string umpathold = "res/res"; if (mold != null && !string.IsNullOrEmpty(mold.MFlag)) { abrootold += "mod/" + mold.MFlag + "/"; umpathold = abrootold + mold.MFlag; } string abrootnew = "res/"; string umpathnew = "res/res"; if (mnew != null && !string.IsNullOrEmpty(mnew.MFlag)) { abrootnew += "mod/" + mnew.MFlag + "/"; umpathnew = abrootnew + mnew.MFlag; } // parse old manifest UnityEngine.AssetBundleManifest maniold = null; try { if (olda != null) { var emani = olda.GetEntry(umpathold); if (emani != null) { using (var smani = emani.Open()) { using (var mems = new MemoryStream()) { smani.CopyTo(mems); var resab = UnityEngine.AssetBundle.LoadFromMemory(mems.ToArray()); if (resab) { var allassets = resab.LoadAllAssets<UnityEngine.AssetBundleManifest>(); if (allassets != null && allassets.Length > 0) { maniold = allassets[0]; if (maniold) { maniold = Object.Instantiate(maniold); } } resab.Unload(true); } } } } } } catch { } // parse new manifest UnityEngine.AssetBundleManifest maninew = null; try { var emani = newa.GetEntry(umpathnew); if (emani != null) { using (var smani = emani.Open()) { using (var mems = new MemoryStream()) { smani.CopyTo(mems); var resab = UnityEngine.AssetBundle.LoadFromMemory(mems.ToArray()); if (resab) { var allassets = resab.LoadAllAssets<UnityEngine.AssetBundleManifest>(); if (allassets != null && allassets.Length > 0) { maninew = allassets[0]; if (maninew != null) { maninew = Object.Instantiate(maninew); } } resab.Unload(true); } } } } } catch { } // both manifest found? if (maninew == null) { File.Copy(newz, diff, true); return true; } // parse diff assets and bundles if (maninew != null) { var allbundles = maninew.GetAllAssetBundles(); foreach (var bundle in allbundles) { var newe = newa.GetEntry(abrootnew + bundle); if (newe == null) { continue; } if (maniold != null && maninew.GetAssetBundleHash(bundle) == maniold.GetAssetBundleHash(bundle)) { if (olda != null) { var olde = olda.GetEntry(abrootold + bundle); if (olde != null) { if (olde.Length == newe.Length) { // TODO: sometimes the AssetBundleHash may be same (example: we deleted a sprite-atlas). // we can diff these from: AssetBundle TypeTreeHash. we should load bundle.manifest and parse it use YAML. it's so difficult. // or we can get the ab file's change time. but this information is not recorded to zip // or we can get the crc of the entry. but it's private. // or we can get md5 of the entry. but need full read. // so we choose use length. In this condition, the ab file length's diff is almost a must. continue; } } } } diffb.Add(bundle); } } // create update zip if (diffb.Count > 0) { try { var streama = PlatDependant.OpenWrite(diff); if (streama != null) { lstToDispose.AddFirst(streama); diffa = new ZipArchive(streama, ZipArchiveMode.Create); if (diffa != null) { lstToDispose.AddFirst(diffa); // each bundle foreach (var bundle in diffb) { try { var ename = abrootnew + bundle; var entryn = newa.GetEntry(ename); if (entryn != null) { var entryd = diffa.CreateEntry(ename, CompressionLevel.Optimal); if (entryd != null) { using (var streamn = entryn.Open()) { using (var streamd = entryd.Open()) { streamn.CopyTo(streamd); } } } } } catch { } } // mani / unity manifest / version.txt string[] rawcopyentries = new[] { mentry, umpathnew, "res/version.txt" }; for (int i = 0; i < rawcopyentries.Length; ++i) { var ename = rawcopyentries[i]; try { var entrys = newa.GetEntry(ename); if (entrys != null) { var entryd = diffa.CreateEntry(ename, CompressionLevel.Optimal); if (entryd != null) { using (var streams = entrys.Open()) { using (var streamd = entryd.Open()) { streams.CopyTo(streamd); } } } } } catch { } } } } } catch { } return true; } } } catch { } finally { foreach (var dis in lstToDispose) { if (dis != null) { dis.Dispose(); } } } return false; }