public static void Init() { try { // TestRuntimeDetourHelper(); Detourer = new MonoModDetourer() { InputPath = Assembly.GetExecutingAssembly().Location, CleanupEnabled = false, DependencyDirs = { ModRelinker.ManagedDirectory } }; Detourer.ReaderParameters.ReadSymbols = false; // DON'T. The assembly is already patched with the .mm.dlls in there! // Otherwise this code here wouldn't even run... // Modder.ReadMod(ModRelinker.ManagedDirectory); Detourer.Read(); Detourer.MapDependencies(); } catch (Exception e) { ModLogger.Log("rtpatcher", $"Failed initializing: {e}"); return; } }
public static void Init() { try { Modder = new MonoModder() { InputPath = Assembly.GetExecutingAssembly().Location, CleanupEnabled = false, DependencyDirs = { ModRelinker.ManagedDirectory } }; Modder.ReaderParameters.ReadSymbols = false; // DON'T. The assembly is already patched with the .mm.dlls in there! // Otherwise this code here wouldn't even run... // Modder.ReadMod(ModRelinker.ManagedDirectory); Modder.Read(); Modder.MapDependencies(); // Do black magic. HarmonyInstance.DEBUG = true; MMHarmony = new MMHarmonyInstance(Modder, Assembly.GetExecutingAssembly()); } catch (Exception e) { ModLogger.Log("rtpatcher", $"Failed initializing: {e}"); return; } }
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."); }
internal static GameModMetadata Parse(string archive, string directory, StreamReader reader) { GameModMetadata meta; try { meta = YamlHelper.Deserializer.Deserialize <GameModMetadata>(reader); } catch (Exception e) { ModLogger.Log("loader", "Failed parsing metadata.yaml: " + e); return(null); } if (meta == null) { ModLogger.Log("loader", "Failed parsing metadata.yaml: YamlDotNet returned null"); return(null); } meta.Archive = archive; meta.Directory = directory; if (!string.IsNullOrEmpty(directory)) { meta.DLL = Path.Combine(directory, meta.DLL.Replace('/', Path.DirectorySeparatorChar).Replace('\\', Path.DirectorySeparatorChar)); if (!string.IsNullOrEmpty(meta.PatchDLL)) { meta.PatchDLL = Path.Combine(directory, meta.PatchDLL.Replace('/', Path.DirectorySeparatorChar).Replace('\\', Path.DirectorySeparatorChar)); } } // Add dependency to API 1.0 if missing. bool dependsOnAPI = false; foreach (GameModMetadata dep in meta.Dependencies) { if (dep.Name == "API") { dep.Name = "YLMAPI"; dependsOnAPI = true; break; } if (dep.Name == "YLMAPI") { dependsOnAPI = true; break; } } if (!dependsOnAPI) { Debug.Log("WARNING: No dependency to API found in " + meta + "! Adding dependency to API 1.0..."); meta.Dependencies.Insert(0, new GameModMetadata() { Name = "API", Version = new Version(1, 0) }); } return(meta); }
public static void LoadPatch(Stream stream) { try { ModLogger.Log("rtpatcher", "Loading new patch"); Detourer.ReadMod(stream); ModLogger.Log("rtpatcher", $"Applied new patch {Detourer.Mods[Detourer.Mods.Count - 1].Assembly.Name.Name}"); } catch (Exception e) { ModLogger.Log("rtpatcher", $"Failed patching: {e}"); return; } }
public static void LoadMods() { Directory.CreateDirectory(ModsDirectory = Path.Combine(ModAPI.GameDirectory, "Mods")); Directory.CreateDirectory(ModsCacheDirectory = Path.Combine(ModsDirectory, "Cache")); ModsBlacklistFile = Path.Combine(ModsDirectory, "blacklist.txt"); ModLogger.Log("loader", "Loading game mods"); List <string> blacklist = new List <string>(); if (File.Exists(ModsBlacklistFile)) { blacklist = File.ReadAllLines(ModsBlacklistFile).Select(l => (l.StartsWith("#") ? "" : l).Trim()).ToList(); } else { using (StreamWriter writer = File.CreateText(ModsBlacklistFile)) { writer.WriteLine("# This is a blacklist file. Lines starting with # are ignored."); writer.WriteLine("ExampleFolder"); writer.WriteLine("SomeMod.zip"); } } string[] files = Directory.GetFiles(ModsDirectory); for (int i = 0; i < files.Length; i++) { string file = Path.GetFileName(files[i]); if (!file.EndsWith(".zip") || blacklist.Contains(file)) { continue; } LoadModZIP(file); } files = Directory.GetDirectories(ModsDirectory); for (int i = 0; i < files.Length; i++) { string file = Path.GetFileName(files[i]); if (file == "Cache" || blacklist.Contains(file)) { continue; } LoadModDir(file); } }
private static IEnumerator _ListMainScenes() { SceneInfo[] scenes; while ((scenes = ScenesInfo.Instance?.ScenesData?.LookupTable) == null) { yield return(null); } for (int i = 0; i < scenes.Length; i++) { SceneInfo scene = scenes[i]; if (string.IsNullOrEmpty(scene.SceneName)) { ModLogger.Log("main", $"Found nameless scene info: {i} {scene.HashID} {scene.Scene?.name ?? "null"}"); continue; } AddScene(scene.SceneName); yield return(null); } }
public static void LoadPatch(Stream stream) { try { ModLogger.Log("rtpatcher", "Loading new patch"); // Reload main assembly. Modder.Module = null; Modder.Read(); // Add new mod to mods list. Modder.ReadMod(stream); ModRelinker.AssemblyRelinkMap[Modder.Mods[Modder.Mods.Count - 1].Assembly.Name.Name] = ModRelinker.AssemblyRelinkedCache["Assembly-CSharp"]; Modder.MapDependencies(); // Auto-patch and then feed Harmony. ModLogger.Log("rtpatcher", $"Applying new patch {Modder.Mods[Modder.Mods.Count - 1].Assembly.Name.Name}"); Modder.AutoPatch(); MMHarmony.PatchAll(); } catch (Exception e) { ModLogger.Log("rtpatcher", $"Failed patching: {e}"); return; } }
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); } }