public override void Awake() { if (!settings.loadMods) { return; } var needRescan = false; needRescan |= CanonizeZip(); needRescan |= CanonizeCsv(); needRescan |= CopyABdata(); if (needRescan) { print("VFS changed; rescanning"); LoadedAssetBundle.GCBundles(); Vfs.Rescan(true); Vfs.Save(); } }
public bool CanonizeZip() { var target = Dir.mod; print("Unzipping mods into " + Path.GetFullPath(target)); var zipdir = Dir.root + "mods"; bool needRescan = false; LoadedAssetBundle.GCBundles(); foreach (var zipfn in Directory.GetFiles(zipdir, "*.zip", SearchOption.AllDirectories)) { var modname = Path.GetFileNameWithoutExtension(zipfn); //print("@ " + modname); if (Directory.Exists(target + modname)) { continue; } needRescan = true; print("Extracting " + Path.GetFileName(zipfn)); using (var fs = File.Open(zipfn, FileMode.Open, FileAccess.Read, FileShare.Read)) { var zip = new ZipFile(fs); var guid = modname; // pick guid from manifest if its there var manifest = zip.GetEntry("manifest.xml"); if (manifest != null) { XmlDocument doc = new XmlDocument(); doc.LoadXml(Encoding.UTF8.GetString(entry2bytes(zip, manifest)).StripBOM()); guid = doc.SelectSingleNode("//guid").InnerText; } foreach (ZipEntry entry in zip) { if (!entry.IsFile) { continue; } var efn = entry.Name; if (efn.ToLower().EndsWith("manifest.xml")) { continue; } if (efn.StartsWith("abdata/")) { efn = efn.Substring(7); } var prefix = "list/characustom/"; var basename = Path.GetFileNameWithoutExtension(efn); var bytes = entry2bytes(zip, entry); // make the cat manifest csv canonical if (efn.StartsWith(prefix) && efn.EndsWith(".csv")) { var str = Encoding.UTF8.GetString(bytes); var ex = ScriptableObject.CreateInstance <ExcelData>().Import(CSV.ParseCSV(str)); var firstrow = ex.list.First().list; var cat = -1; var dist = 0; if (firstrow.Count == 1) { try { cat = int.Parse(ex[0, 0].Trim()); dist = int.Parse(ex[1, 0].Trim()); ex.list.RemoveRange(0, 3); } catch (System.Exception exc) { print(exc.Message); print(ex[0, 0]); print(ex[1, 0]); }; } else { try { cat = int.Parse(basename.Split('_')[0]); } catch { }; } // if we can't figure out the category, bail on this csv if (cat == -1) { print(zipfn + " unzip failed"); continue; } efn = $"{prefix}{dist:D2}_{guid}/{cat}_{basename}.csv"; bytes = ex.Marshal().AddBOM().ToBytes(); } // if it is a hardmod, check that the contents of the bundle fully match the file overriden. var hardab = Dir.abdata + efn; var oldefn = efn; int nmissing = 0; efn = target + modname + "/" + efn; Directory.CreateDirectory(Path.GetDirectoryName(efn)); bool nukecab = true; if (efn.EndsWith(".unity3d") && File.Exists(hardab)) { // if it overwrites in its entirety, do not nuke the cab as deps amy point to it nukecab = false; var ab = AssetBundle.LoadFromMemory(bytes); if (ab == null) { print($"WARNING: {efn} failed to load, skipping."); continue; } var abnames = new HashSet <string>(ab.GetAllAssetNames().Select((x) => Path.GetFileNameWithoutExtension(x)).ToList()); ab.Unload(true); var origab = AssetBundle.LoadFromFile(hardab); if (origab == null) { goto skip2; } // Now load the original and check the replacement has all its assets foreach (var ass in origab.GetAllAssetNames()) { var shass = Path.GetFileNameWithoutExtension(ass); if (!abnames.Contains(shass)) { //print($"WARNING: {Path.GetFileName(efn)}: {shass} is missing {oldefn}"); nmissing++; } } origab.Unload(true); // missing assets; nuke cab if (nmissing > 0) { nukecab = true; } } skip2: //print(".. Extracted " + efn); if (nmissing != 0) { efn = Directory.GetParent(efn).FullName + "/+" + Path.GetFileName(efn); } if (efn.EndsWith(".unity3d")) { using (var fo = File.Create(efn)) if (Vfs.Repack(new MemoryStream(bytes), fo, nukecab | sideloader_compat)) { bytes = null; } } if (bytes != null) { File.WriteAllBytes(efn, bytes); } } } } print("Done"); return(needRescan); }
public static void dumpassets() { print("Dumping all text serializable assets"); var dumpdir = Dir.mod + "!base/"; var mainass = AppDomain.CurrentDomain.GetAssemblies().First(a => a.GetName().Name == "Assembly-CSharp"); var dumpables = mainass.GetExportedTypes().Where(t => typeof(IDumpable).IsAssignableFrom(t)); var basedir = Dir.abdata; print($"Found {dumpables.Count()} types."); LoadedAssetBundle.GCBundles(); foreach (var bpath in Directory.GetFiles(Dir.abdata + subdir, "*.unity3d", SearchOption.AllDirectories)) { var bundle = bpath.Replace("\\", "/"); var abname = bundle.Substring(Dir.abdata.Length); if (abname.Contains("--")) { continue; } var lab = AssetBundle.LoadFromFile(bundle); if (lab == null) { continue; } foreach (var longname in lab.GetAllAssetNames()) { var dir = bundle.Remove(bundle.LastIndexOf('.')).Substring(Dir.abdata.Length); byte[] bytes = null; var name = Path.GetFileName(longname).ToLower(); var bname = Path.GetFileNameWithoutExtension(name); string ext = name.Substring(bname.Length).ToLower(); if (ext != ".asset" && ext != ".txt" && ext != ".bytes" && ext != "") { //print("skip"); continue; } var ass = lab.LoadAsset(bname); if (ass == null) { print("Failed to load ", ass); continue; } var ta = ass as UnityEngine.TextAsset; var da = ass as IDumpable; //print(ass.GetType()); if (ta != null) { if (bname.StartsWith("cf_anmshape")) { print(name); var si = new AnimationKeyInfo(); si.LoadInfo(new MemoryStream(ta.bytes)); bytes = si.Marshal().ToBytes(); ext = ".csv"; } else if (abname.Contains("list/characustom/")) { var chd = MessagePackSerializer.Deserialize <ChaListData>(ta.bytes); bytes = chd.Marshal().ToBytes(); bname = chd.categoryNo + "_" + bname; var disa = $"{chd.distributionNo:00}"; var disb = Path.GetFileName(dir); dir = dir.Remove(dir.LastIndexOf('/') + 1) + disa + ((disa != disb)?("_" + disb):""); ext = ".csv"; } else if (name.ToLower().EndsWith(".txt")) { bytes = ta.text.StripBOM().ToBytes(); ext = ".lst"; } else { var test = ta.text.Replace("\r", "").Split('\n'); if (test.Length > 2 && (test[0].Split('\t').Length == test[1].Split('\t').Length) && test[0].Split('\t').Length >= 2) { bytes = ta.text.StripBOM().ToBytes(); ext = ".lst"; } else { if (abname.Contains("h/list") && bname.StartsWith("kh") && ext == ".bytes") { var ikd = new MotionIKData(); ikd.Read(new MemoryStream(ta.bytes)); bytes = ikd.Marshal().ToBytes(); ext = "." + ikd.GetFileExt(); } else { print("Uknown " + bname); } } } } else if (ass is IDumpable) { ext = "." + da.GetFileExt(); try { bytes = da.Marshal().ToBytes(); } catch (Exception ex) { print("Something went wrong"); print(bpath); print(bname); print(ex); } } if (bytes != null) { var ddir = dumpdir + dir; Directory.CreateDirectory(ddir); var dst = ddir + "/" + bname + ext; File.WriteAllBytes(dst, bytes); //print("Dumping " + dst.Substring(Dir.root.Length) + " of type " + ass.GetType().Name); } } lab.Unload(true); } }