public static void LoadMod(GameModMetadata meta, Assembly asm) { ModContent.Crawl(null, asm); Type[] types; try { types = asm.GetTypes(); } catch (Exception e) { e.LogDetailed(); ModLogger.Log("loader", $"Failed reading assembly: {e}"); return; } for (int i = 0; i < types.Length; i++) { Type type = types[i]; if (!typeof(GameMod).IsAssignableFrom(type) || type.IsAbstract) { continue; } GameMod mod = (GameMod)type.GetConstructor(_EmptyTypeArray).Invoke(_EmptyObjectArray); mod.Metadata = meta; Mods.Add(mod); _ModuleTypes.Add(type); _ModuleMethods.Add(new FastDictionary <string, MethodInfo>()); } ModLogger.Log("loader", $"Mod {meta} initialized."); }
/// <summary> /// Checks if an dependency is loaded. /// Can be used by mods manually to f.e. activate / disable functionality if an API is (not) existing. /// </summary> /// <param name="dependency">Dependency to check for. Name and Version will be checked.</param> /// <returns></returns> public static bool DependencyLoaded(GameModMetadata dep) { string depName = dep.Name; Version depVersion = dep.Version; if (depName == "YLMAPI") { if (ModAPI.Version.Major != depVersion.Major) { return(false); } if (ModAPI.Version.Minor < depVersion.Minor) { return(false); } return(true); } foreach (GameMod mod in Mods) { GameModMetadata meta = mod.Metadata; if (meta.Name != depName) { continue; } Version version = meta.Version; if (version.Major != depVersion.Major) { return(false); } if (version.Minor < depVersion.Minor) { return(false); } return(true); } return(false); }
private static MissingDependencyResolver _GenerateModDependencyResolver(this GameModMetadata meta) { if (!string.IsNullOrEmpty(meta.Archive)) { return(delegate(MonoModder mod, ModuleDefinition main, string name, string fullName) { string asmName = name + ".dll"; using (ZipFile zip = ZipFile.Read(meta.Archive)) { foreach (ZipEntry entry in zip.Entries) { if (entry.FileName != asmName) { continue; } using (MemoryStream ms = new MemoryStream()) { entry.Extract(ms); ms.Seek(0, SeekOrigin.Begin); return ModuleDefinition.ReadModule(ms, mod.GenReaderParameters(false)); } } } return null; }); } if (!string.IsNullOrEmpty(meta.Directory)) { return(delegate(MonoModder mod, ModuleDefinition main, string name, string fullName) { string asmPath = Path.Combine(meta.Directory, name + ".dll"); if (!File.Exists(asmPath)) { return null; } return ModuleDefinition.ReadModule(asmPath, mod.GenReaderParameters(false, asmPath)); }); } return(null); }
private static ResolveEventHandler _GenerateModAssemblyResolver(this GameModMetadata meta) { if (!string.IsNullOrEmpty(meta.Archive)) { return(delegate(object sender, ResolveEventArgs args) { string asmName = new AssemblyName(args.Name).Name + ".dll"; using (ZipFile zip = ZipFile.Read(meta.Archive)) { foreach (ZipEntry entry in zip.Entries) { if (entry.FileName != asmName) { continue; } using (MemoryStream ms = new MemoryStream()) { entry.Extract(ms); ms.Seek(0, SeekOrigin.Begin); return Assembly.Load(ms.GetBuffer()); } } } return null; }); } if (!string.IsNullOrEmpty(meta.Directory)) { return(delegate(object sender, ResolveEventArgs args) { string asmPath = Path.Combine(meta.Directory, new AssemblyName(args.Name).Name + ".dll"); if (!File.Exists(asmPath)) { return null; } return Assembly.LoadFrom(asmPath); }); } return(null); }
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 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 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); } }