public static void LoadModZIP(string archive) { if (!File.Exists(archive)) { // Probably a mod in the mod directory archive = Path.Combine(ModsDirectory, archive); } if (!File.Exists(archive)) { // It just doesn't exist. return; } ModLogger.Log("loader", $"Loading mod .zip: {archive}"); GameModMetadata meta = null; Assembly asm = null; using (ZipFile zip = ZipFile.Read(archive)) { Texture2D icon = null; // First read the metadata, ... foreach (ZipEntry entry in zip.Entries) { if (entry.FileName == "metadata.yaml") { using (MemoryStream ms = new MemoryStream()) { entry.Extract(ms); ms.Seek(0, SeekOrigin.Begin); using (StreamReader reader = new StreamReader(ms)) meta = GameModMetadata.Parse(archive, "", reader); } continue; } if (entry.FileName == "icon.png") { icon = new Texture2D(2, 2); icon.name = "icon"; using (MemoryStream ms = new MemoryStream()) { entry.Extract(ms); ms.Seek(0, SeekOrigin.Begin); icon.LoadImage(ms.GetBuffer()); } icon.filterMode = FilterMode.Point; continue; } } if (meta != null) { // In case the icon appears before the metadata in the .zip if (icon != null) { meta.Icon = icon; } // ... then check if the mod runs on this profile ... if (meta.ProfileID > ModAPI.Profile.Id) { ModLogger.Log("loader", "Mod meant for an in-dev YLMAPI version!"); return; } // ... then check if the dependencies are loaded ... foreach (GameModMetadata dep in meta.Dependencies) { if (!DependencyLoaded(dep)) { ModLogger.Log("loader", $"Dependency {dep} of mod {meta} not loaded!"); return; } } // ... then add an AssemblyResolve handler for all the .zip-ped libraries AppDomain.CurrentDomain.AssemblyResolve += meta._GenerateModAssemblyResolver(); } // ... then the patch (if any) ... if (!string.IsNullOrEmpty(meta?.PatchDLL)) { foreach (ZipEntry entry in zip.Entries) { string entryName = entry.FileName.Replace("\\", "/"); if (entryName != meta.PatchDLL) { continue; } using (MemoryStream ms = new MemoryStream()) { entry.Extract(ms); ms.Seek(0, SeekOrigin.Begin); ModRuntimePatcher.LoadPatch(ms); } break; } } // ... then everything else foreach (ZipEntry entry in zip.Entries) { string entryName = entry.FileName.Replace("\\", "/"); if (meta != null && entryName == meta.DLL) { using (MemoryStream ms = new MemoryStream()) { entry.Extract(ms); ms.Seek(0, SeekOrigin.Begin); if (meta.Prelinked) { asm = Assembly.Load(ms.GetBuffer()); } else { asm = meta.GetRelinkedAssembly(ms); } } } ModContent.AddMapping(entryName, new AssetMetadata(archive, entryName) { AssetType = entry.IsDirectory ? typeof(AssetTypeDirectory) : null }); } } if (meta != null && asm != null) { LoadMod(meta, asm); } }
public static Assembly GetRelinkedAssembly(this GameModMetadata meta, Stream stream, MissingDependencyResolver depResolver = null) { string name = Path.GetFileName(meta.DLL); string cachedName = meta.Name + "." + name.Substring(0, name.Length - 3) + "dll"; string cachedPath = Path.Combine(ModLoader.ModsCacheDirectory, cachedName); string cachedChecksumPath = Path.Combine(ModLoader.ModsCacheDirectory, cachedName + ".sum"); string[] checksums = new string[2]; using (MD5 md5 = MD5.Create()) { if (GameChecksum == null) { using (FileStream fs = File.OpenRead(Assembly.GetAssembly(typeof(ModRelinker)).Location)) GameChecksum = md5.ComputeHash(fs).ToHexadecimalString(); } checksums[0] = GameChecksum; string modPath = meta.Archive; if (modPath.Length == 0) { modPath = meta.DLL; } using (FileStream fs = File.OpenRead(modPath)) checksums[1] = md5.ComputeHash(fs).ToHexadecimalString(); } if (File.Exists(cachedPath) && File.Exists(cachedChecksumPath) && checksums.ChecksumsEqual(File.ReadAllLines(cachedChecksumPath))) { return(Assembly.LoadFrom(cachedPath)); } if (depResolver == null) { depResolver = _GenerateModDependencyResolver(meta); } using (MonoModder modder = new MonoModder() { Input = stream, OutputPath = cachedPath, CleanupEnabled = false, RelinkModuleMap = AssemblyRelinkMap, DependencyDirs = { ManagedDirectory }, MissingDependencyResolver = depResolver, RelinkMap = ModRuntimePatcher.Detourer.RelinkMap }) try { modder.ReaderParameters.ReadSymbols = false; modder.WriterParameters.WriteSymbols = false; modder.WriterParameters.SymbolWriterProvider = null; modder.Read(); modder.MapDependencies(); modder.AutoPatch(); modder.Write(); } catch (Exception e) { ModLogger.Log("relinker", $"Failed relinking {meta}: {e}"); return(null); } return(Assembly.LoadFrom(cachedPath)); }
public static void LoadModDir(string dir) { if (!Directory.Exists(dir)) { // Probably a mod in the mod directory dir = Path.Combine(ModsDirectory, dir); } if (!Directory.Exists(dir)) { // It just doesn't exist. return; } ModLogger.Log("loader", $"Loading mod directory: {dir}"); GameModMetadata meta = null; Assembly asm = null; // First read the metadata, ... string metaPath = Path.Combine(dir, "metadata.yaml"); if (File.Exists(metaPath)) { using (StreamReader reader = new StreamReader(metaPath)) meta = GameModMetadata.Parse("", dir, reader); } if (meta != null) { // ... then check if the mod runs on this profile ... if (meta.ProfileID > ModAPI.Profile.Id) { ModLogger.Log("loader", "Mod meant for an in-dev YLMAPI version!"); return; } // ... then check if the dependencies are loaded ... foreach (GameModMetadata dep in meta.Dependencies) { if (!DependencyLoaded(dep)) { ModLogger.Log("loader", $"Dependency {dep} of mod {meta} not loaded!"); return; } } // ... then add an AssemblyResolve handler for all the .zip-ped libraries AppDomain.CurrentDomain.AssemblyResolve += meta._GenerateModAssemblyResolver(); } // ... then everything else ModContent.Crawl(null, dir); if (meta == null || !File.Exists(meta.DLL)) { return; } if (!string.IsNullOrEmpty(meta.PatchDLL) && File.Exists(meta.PatchDLL)) { using (Stream stream = File.OpenRead(meta.PatchDLL)) ModRuntimePatcher.LoadPatch(stream); } if (meta.Prelinked) { asm = Assembly.LoadFrom(meta.DLL); } else { using (FileStream fs = File.OpenRead(meta.DLL)) asm = meta.GetRelinkedAssembly(fs); } if (asm != null) { LoadMod(meta, asm); } }