public static IEnumerator MakeZipAsync(string zipFile, string srcDir, IList <string> entries, IEditorWorkProgressShower winprog) { var logger = new EditorWorkProgressLogger() { Shower = winprog }; logger.Log("Zipping: " + zipFile); if (string.IsNullOrEmpty(zipFile) || entries == null || entries.Count == 0 || !System.IO.Directory.Exists(srcDir)) { logger.Log("Nothing to zip"); yield break; } var stream = PlatDependant.OpenWrite(zipFile); if (stream == null) { logger.Log("Cannot create zip file."); yield break; } var zip = new ZipArchive(stream, ZipArchiveMode.Create); try { if (!srcDir.EndsWith("/") && !srcDir.EndsWith("\\")) { srcDir += "/"; } for (int i = 0; i < entries.Count; ++i) { var entry = entries[i]; if (winprog != null && AsyncWorkTimer.Check()) { yield return(null); } logger.Log(entry); if (string.IsNullOrEmpty(entry)) { continue; } var src = srcDir + entry; if (PlatDependant.IsFileExist(src)) { try { using (var srcstream = PlatDependant.OpenRead(src)) { var zentry = zip.CreateEntry(entry.Replace('\\', '/')); using (var dststream = zentry.Open()) { srcstream.CopyTo(dststream); } } } catch (Exception e) { logger.Log("(Error)(Not Critical)"); logger.Log(e.ToString()); } } } } finally { zip.Dispose(); stream.Dispose(); } }
public static TaskProgress MakeZipBackground(string zipFile, string srcDir, IList <string> entries, System.Threading.EventWaitHandle waithandle) { return(PlatDependant.RunBackground(progress => { try { if (string.IsNullOrEmpty(zipFile) || entries == null || entries.Count == 0 || !System.IO.Directory.Exists(srcDir)) { return; } progress.Total = entries.Count; using (var stream = PlatDependant.OpenWrite(zipFile)) { using (var zip = new ZipArchive(stream, ZipArchiveMode.Create)) { if (!srcDir.EndsWith("/") && !srcDir.EndsWith("\\")) { srcDir += "/"; } for (int i = 0; i < entries.Count; ++i) { progress.Length = i; var entry = entries[i]; if (string.IsNullOrEmpty(entry)) { continue; } var src = srcDir + entry; if (PlatDependant.IsFileExist(src)) { try { using (var srcstream = PlatDependant.OpenRead(src)) { var zentry = zip.CreateEntry(entry.Replace('\\', '/')); using (var dststream = zentry.Open()) { srcstream.CopyTo(dststream); } } } catch (Exception e) { PlatDependant.LogError("zip entry FAIL! " + entry); PlatDependant.LogError(e); } } } } } } catch (Exception e) { PlatDependant.LogError("Build zip FAIL! " + zipFile); PlatDependant.LogError(e); } finally { if (waithandle != null) { waithandle.Set(); } } })); }
/// <remarks>not recommend</remarks> public static bool BuildRawUpdate(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 { } if (newa == null) { return false; } else if (olda == null) { File.Copy(newz, diff, true); return true; } else { var entries = newa.Entries; List<string> diffEntries = new List<string>(); for (int i = 0; i < entries.Count; ++i) { var entry = entries[i]; try { var oentry = olda.GetEntry(entry.FullName); if (oentry != null && oentry.Length == entry.Length) { string md5old = ""; string md5new = ""; try { using (var sold = oentry.Open()) { md5old = CapsEditorUtils.GetStreamMD5(sold); } } catch { } try { using (var snew = entry.Open()) { md5new = CapsEditorUtils.GetStreamMD5(snew); } } catch { } if (md5new == md5old) { continue; } } diffEntries.Add(entry.FullName); } catch { } } if (diffEntries.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); for (int i = 0; i < diffEntries.Count; ++i) { var ename = diffEntries[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; }
public static void MakeObb(string dest, params string[] subzips) { if (!string.IsNullOrEmpty(dest) && subzips != null && subzips.Length > 0) { HashSet <string> reskeys = new HashSet <string>(); HashSet <string> sptkeys = new HashSet <string>(); using (var sdest = PlatDependant.OpenWrite(dest)) { using (var zdest = new ZipArchive(sdest, ZipArchiveMode.Create)) { for (int i = 0; i < subzips.Length; ++i) { try { var sfile = subzips[i]; if (PlatDependant.IsFileExist(sfile)) { var key = System.IO.Path.GetFileNameWithoutExtension(sfile).ToLower(); bool isres = false; bool isspt = false; HashSet <string> entrynames = new HashSet <string>(); using (var ssrc = PlatDependant.OpenRead(sfile)) { using (var zsrc = new ZipArchive(ssrc, ZipArchiveMode.Read)) { foreach (var sentry in zsrc.Entries) { var fullname = sentry.FullName; if (fullname.StartsWith("res/")) { isres = true; } else if (fullname.StartsWith("spt/")) { isspt = true; } if (entrynames.Add(fullname)) { var dentry = zdest.CreateEntry(fullname, isres ? CompressionLevel.NoCompression : CompressionLevel.Optimal); using (var ses = sentry.Open()) { using (var des = dentry.Open()) { ses.CopyTo(des); } } } } } } if (isres) { reskeys.Add(key); } if (isspt) { sptkeys.Add(key); } } } catch (Exception e) { PlatDependant.LogError(e); } } if (reskeys.Count > 0) { var resindex = zdest.CreateEntry("res/index.txt", CompressionLevel.Optimal); using (var sindex = resindex.Open()) { using (var swindex = new System.IO.StreamWriter(sindex, System.Text.Encoding.UTF8)) { foreach (var key in reskeys) { swindex.WriteLine(key); } } } } if (sptkeys.Count > 0) { var sptindex = zdest.CreateEntry("spt/index.txt", CompressionLevel.Optimal); using (var sindex = sptindex.Open()) { using (var swindex = new System.IO.StreamWriter(sindex, System.Text.Encoding.UTF8)) { foreach (var key in sptkeys) { swindex.WriteLine(key); } } } } } } } }
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; }