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 HookGen(string inputPath, string outputPath) { using var mm = new MonoModder { InputPath = inputPath, OutputPath = outputPath, ReadingMode = ReadingMode.Deferred, DependencyDirs = { Environment.GetFolderPath(Environment.SpecialFolder.ProgramFilesX86) + @"\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.5", Path.Combine(libsPath, "Common") }, MissingDependencyThrow = false, }; mm.Read(); mm.MapDependencies(); var gen = new HookGenerator(mm, "TerrariaHooks") { HookPrivate = true, }; gen.Generate(); RemoveModLoaderTypes(gen.OutputModule); gen.OutputModule.Write(outputPath); }
static void GenHooks(string input, string output) { Console.WriteLine($"Hooking: {input} -> {output}"); using (MonoModder mm = new MonoModder() { InputPath = input, OutputPath = output, ReadingMode = ReadingMode.Deferred, MissingDependencyThrow = false, }) { mm.Read(); mm.MapDependencies(); if (File.Exists(output)) { File.Delete(output); } HookGenerator gen = new HookGenerator(mm, Path.GetFileName(output)) { HookPrivate = true, }; gen.Generate(); gen.OutputModule.Write(output); } }
public void RunMonoMod() { Environment.SetEnvironmentVariable("MONOMOD_DEPENDENCY_MISSING_THROW", "0"); if (this.verbosity > 0) { Environment.SetEnvironmentVariable("MONOMOD_LOG_VERBOSE", "1"); } using (MonoModder mm = new MonoModder() { InputPath = this.assemblyPath, OutputPath = AssemblyTmpPath, }) { // read assembly mm.Read(); // read Seshat.dll mm.ReadMod(this.seshatPath); mm.MapDependencies(); // autopatch mm.AutoPatch(); // write assembly mm.Write(); } }
static HookgenPreloader() { Logging.LogMessage($"BepInEx-Partiality-Wrapper initializing HOOKS..."); if (!File.Exists(AsmCSharpFilePath)) { Logging.LogMessage($"Could not find 'Assembly-CSharp.dll' file, aborting HOOKS generatiion."); return; } if (File.Exists(HooksAsmFilePath)) { // if HOOKS file is older than the Assembly-Csharp file... if (File.GetLastWriteTime(HooksAsmFilePath) < File.GetLastWriteTime(AsmCSharpFilePath)) { Logging.LogMessage($"HOOKS file is outdated, deleting..."); File.Delete(HooksAsmFilePath); } else { Logging.LogMessage($"HOOKS file is up to date!"); return; } } Logging.LogMessage("Generating new HOOKS file..."); try { using (var modder = new MonoModder { InputPath = AsmCSharpFilePath, OutputPath = HooksAsmFilePath, PublicEverything = true, DependencyDirs = new List <string> { Paths.ManagedPath, HookgenPatcherFolder } }) { modder.Read(); modder.MapDependencies(); var generator = new HookGenerator(modder, Path.GetFileName(HooksAsmFilePath)); using (ModuleDefinition module = generator.OutputModule) { generator.HookPrivate = true; generator.Generate(); module.Write(HooksAsmFilePath); } } Logging.LogMessage("Done!"); } catch (Exception ex) { Logging.LogWarning($"Exception running HOOKS generation!"); Logging.LogMessage(ex); } }
/// <summary> /// Perform the actual patching of Assembly-CSharp.dll in the Risk of Rain 2 managed folder /// </summary> /// <param name="modPath">The path to the Assembly-CSharp.HexiDave.mm.dll file</param> private static void PatchWithMod(string modPath) { // Ensure that we have an Assembly-CSharp.dll.original file to work from FileUtilities.EnsureBackup(); // Get a temporary file to write the new assembly to var outputPath = Path.GetTempFileName(); // Setup MonoModder to patch the original Assembly-CSharp.dll using (var monoModder = new MonoModder { InputPath = FileUtilities.BackupAssemblyPath, OutputPath = outputPath }) { // Read the assembly monoModder.Read(); // Read the patch monoModder.ReadMod(modPath); // Ensure all the assembly references are still set monoModder.MapDependencies(); // Re-write assembly with patch functions monoModder.AutoPatch(); // Spit the file out monoModder.Write(); } // Clear the assembly in RoR2's managed folder File.Delete(FileUtilities.AssemblyPath); // Move the patched assembly into place File.Move(outputPath, FileUtilities.AssemblyPath); // Make sure any dependencies are moved // TODO: Maybe remove this, but still tinkering var filesToInclude = new[] { "Mono.Cecil.dll" }; foreach (var fileName in filesToInclude) { var moveToPath = $@"{FileUtilities.ManagedPath}\{fileName}"; if (!File.Exists(moveToPath)) { File.Move(fileName, moveToPath); } } }
/// <summary> /// Patches an assembly located in Managed. /// </summary> /// <param name="pathToPatch"></param> /// <param name="assemblyName"></param> public void PatchAssembly(string pathToPatch, string assemblyName, string[] dependencies) { string assemblyPath = FindAssemblyByName(assemblyName); if (assemblyPath == string.Empty) { return; } string outputPath = Path.Combine(Directory.GetParent(assemblyPath).FullName, Path.GetFileNameWithoutExtension(assemblyPath) + "-NEW.dll"); Console.WriteLine(assemblyPath); Console.WriteLine(outputPath); using (MonoModder mm = new MonoModder() { InputPath = assemblyPath, OutputPath = outputPath }) { mm.LogVerboseEnabled = true; mm.Read(); //Force everything to be public mm.PublicEverything = true; //Read in the patch mm.ReadMod(pathToPatch); mm.MapDependencies(); mm.DependencyDirs.Add(Directory.GetParent(Assembly.GetExecutingAssembly().FullName).FullName); mm.AutoPatch(); mm.Write(); } File.Delete(assemblyPath); File.Copy(outputPath, assemblyPath); File.Delete(outputPath); string managedPath = Directory.GetParent(assemblyPath).FullName; foreach (string s in dependencies) { string dependencyDestination = Path.Combine(managedPath, Path.GetFileName(s)); if (File.Exists(dependencyDestination)) { File.Delete(dependencyDestination); } File.Copy(s, dependencyDestination); } }
/// <summary> /// Call Monomod hookgen. /// </summary> /// <param name="input">Input assembly</param> /// <param name="mmhookFolder">MMHOOK output folder</param> /// <returns></returns> public static bool GenerateMMHook(string input, string mmhookFolder, string md5, string ValheimPath, TaskLoggingHelper Log) { string output = Path.Combine(mmhookFolder, $"{JotunnBuildTask.Mmhook}_{Path.GetFileName(input)}"); Log.LogMessage(MessageImportance.High, $"Generating MMHOOK of {input}."); MonoModder modder = new MonoModder(); modder.InputPath = input; modder.OutputPath = output; modder.ReadingMode = ReadingMode.Deferred; ((BaseAssemblyResolver)modder.AssemblyResolver)?.AddSearchDirectory(Path.Combine(Environment.CurrentDirectory, "bin", "Debug")); ((BaseAssemblyResolver)modder.AssemblyResolver)?.AddSearchDirectory(Path.GetDirectoryName(Assembly.GetExecutingAssembly().CodeBase.Replace("file:///home", "/home").Replace("file:///", ""))); if (Directory.Exists(Path.Combine(ValheimPath, JotunnBuildTask.ValheimData, JotunnBuildTask.Managed))) { ((BaseAssemblyResolver)modder.AssemblyResolver)?.AddSearchDirectory(Path.Combine(ValheimPath, JotunnBuildTask.ValheimData, JotunnBuildTask.Managed)); } if (Directory.Exists(Path.Combine(ValheimPath, JotunnBuildTask.ValheimServerData, JotunnBuildTask.Managed))) { ((BaseAssemblyResolver)modder.AssemblyResolver)?.AddSearchDirectory(Path.Combine(ValheimPath, JotunnBuildTask.ValheimServerData, JotunnBuildTask.Managed)); } ((BaseAssemblyResolver)modder.AssemblyResolver)?.AddSearchDirectory(Path.Combine(ValheimPath, JotunnBuildTask.UnstrippedCorlib)); modder.Read(); modder.MapDependencies(); if (File.Exists(output)) { Log.LogMessage(MessageImportance.High, $"Clearing {output}"); File.Delete(output); } HookGenerator hookGenerator = new HookGenerator(modder, Path.GetFileName(output)); hookGenerator.HookPrivate = true; using (ModuleDefinition mOut = hookGenerator.OutputModule) { hookGenerator.Generate(); mOut.Types.Add(new TypeDefinition("BepHookGen", "hash" + md5, TypeAttributes.AutoClass)); mOut.Write(output); } Log.LogMessage(MessageImportance.High, $"Finished writing {output}"); return(true); }
public void TestHookGenRun() { string outputPath = Path.Combine(Environment.CurrentDirectory, "testdump", "MonoMod.UnitTest.Hooks.dll"); try { string dir = Path.GetDirectoryName(outputPath); if (!Directory.Exists(dir)) { Directory.CreateDirectory(dir); } if (File.Exists(outputPath)) { File.SetAttributes(outputPath, FileAttributes.Normal); File.Delete(outputPath); } } catch (Exception e) { Console.WriteLine("Couldn't create testdump."); Console.WriteLine(e); } using (MonoModder mm = new MonoModder { InputPath = typeof(HookGenRunTest).Assembly.Location, ReadingMode = ReadingMode.Deferred, MissingDependencyThrow = false, }) { mm.Read(); mm.MapDependencies(); HookGenerator gen = new HookGenerator(mm, "MonoMod.UnitTest.Hooks") { HookPrivate = true, }; using (ModuleDefinition mOut = gen.OutputModule) { gen.Generate(); if (outputPath != null) { mOut.Write(outputPath); } else { using (MemoryStream ms = new MemoryStream()) mOut.Write(ms); } } } }
private void CheckHooks() { string asmPath = Path.Combine(Paths.ManagedPath, "Assembly-CSharp.dll"); string hooksPath = Path.Combine(PluginFolder, "HOOKS-Assembly-CSharp.dll"); if (File.Exists(hooksPath)) { // if HOOKS file is older than the Assembly-Csharp file... if (File.GetLastWriteTime(hooksPath) < File.GetLastWriteTime(asmPath)) { File.Delete(hooksPath); } else { return; } } Logger.Log(LogLevel.Message, "Generating new HOOKS file..."); using (var modder = new MonoModder { InputPath = asmPath, OutputPath = hooksPath, PublicEverything = true, DependencyDirs = new List <string> { Paths.ManagedPath, PluginFolder } }) { modder.Read(); modder.MapDependencies(); var generator = new HookGenerator(modder, Path.GetFileName(hooksPath)); using (ModuleDefinition module = generator.OutputModule) { generator.HookPrivate = true; generator.Generate(); module.Write(hooksPath); } } Assembly.Load(File.ReadAllBytes(hooksPath)); }
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 void HookGen(string inputPath, string outputPath, string refsDir) { using (var mm = new MonoModder { InputPath = inputPath, OutputPath = outputPath, ReadingMode = ReadingMode.Deferred, MissingDependencyThrow = false, }) { mm.Read(); mm.MapDependencies(); mm.DependencyCache["MonoMod.RuntimeDetour"] = ModuleDefinition.ReadModule(Path.Combine(refsDir, "MonoMod.RuntimeDetour.dll")); var gen = new HookGenerator(mm, "TerrariaHooks") { HookPrivate = true, }; gen.Generate(); gen.OutputModule.Write(outputPath); } }
private void _Relink(string input, string output) { using (var modder = new MonoModder() { InputPath = input, OutputPath = output }) { modder.CleanupEnabled = false; modder.RelinkModuleMap = AssemblyRelinkMap; modder.ReaderParameters.ReadSymbols = false; modder.WriterParameters.WriteSymbols = false; modder.WriterParameters.SymbolWriterProvider = null; modder.Read(); modder.MapDependencies(); modder.AutoPatch(); modder.Write(); } }
public void Patch() { // Load the assembly to check if it needs to be patched first string assemblyPath = Path.Combine(_assemblyDirectory, AssemblyName); string assemblyBackupPath = assemblyPath.Replace(AssemblyName, AssemblyBackupName); if (!IsPatchingNeeded(assemblyPath)) { Console.WriteLine("Game is already patched, no further action required."); return; } // Copy the required assemblies to the game directory foreach (var assemblyName in PatchAssemblies) { File.Copy(assemblyName, Path.Combine(_assemblyDirectory, assemblyName), true); } File.Copy(assemblyPath, assemblyBackupPath, true); // Run MonoMod Console.WriteLine("Patching game..."); using (MonoModder mm = new MonoModder() { InputPath = assemblyBackupPath, OutputPath = assemblyPath + ".tmp" }) { Environment.SetEnvironmentVariable("MONOMOD_DEPENDENCY_MISSING_THROW", "0"); mm.Read(); mm.ReadMod(Path.Combine(_assemblyDirectory, AssemblyPatchName)); mm.MapDependencies(); mm.AutoPatch(); mm.Write(); } // Copy the resultant assembly File.Delete(assemblyPath); File.Move(assemblyPath + ".tmp", assemblyPath); }
/// <summary>Generates the HOOKS-Assembly-CSharp.dll the same as Partiality</summary> void GenerateHooks() { string pathIn = Path.Combine(Paths.ManagedPath, "Assembly-CSharp.dll"); string pathOut = Path.Combine(Path.GetDirectoryName(Info.Location), "HOOKS-Assembly-CSharp.dll"); if (File.Exists(pathOut)) { // Only Regenerate if Managed is newer than HOOKS if (File.GetLastWriteTime(pathOut) > File.GetLastWriteTime(pathIn)) { return; } File.Delete(pathOut); } using (MonoModder mm = new MonoModder { InputPath = pathIn, OutputPath = pathOut, PublicEverything = true, DependencyDirs = new List <string>() { Paths.ManagedPath, Paths.BepInExAssemblyDirectory }, }) { mm.Read(); mm.MapDependencies(); mm.Log("[HookGen] Starting HookGenerator"); HookGenerator gen = new HookGenerator(mm, Path.GetFileName(pathOut)); using (ModuleDefinition mOut = gen.OutputModule) { gen.HookPrivate = true; gen.Generate(); mOut.Write(pathOut); } mm.Log("[HookGen] Done."); } }
public static void Main(string[] args) { // Backup the assembly before setup FileUtilities.EnsureBackup(); // Rebuild the GamePath.targets file with correct game path ReplaceGamePathTarget(); // Directory for storing 'public'-ified assembly var libsDir = $@"{EnvHelper.SolutionDir}\libs"; // Ensure the path exists for output Directory.CreateDirectory(libsDir); // Output path for the assembly var publicAssemblyPath = $@"{libsDir}\{FileUtilities.AssemblyFileName}"; // Remove the old one, if it exists if (File.Exists(publicAssemblyPath)) { File.Delete(publicAssemblyPath); } // Create the all-public assembly for import using (var monoMod = new MonoModder { InputPath = FileUtilities.BackupAssemblyPath, OutputPath = publicAssemblyPath }) { monoMod.Read(); monoMod.PublicEverything = true; monoMod.MapDependencies(); monoMod.AutoPatch(); monoMod.Write(); } }
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)); }
static void Main(string[] args) { Console.WriteLine("MonoMod.RuntimeDetour.HookGen " + typeof(Program).Assembly.GetName().Version); Console.WriteLine("using MonoMod " + typeof(MonoModder).Assembly.GetName().Version); Console.WriteLine("using MonoMod.RuntimeDetour " + typeof(Detour).Assembly.GetName().Version); if (args.Length == 0) { Console.WriteLine("No valid arguments (assembly path) passed."); if (System.Diagnostics.Debugger.IsAttached) // Keep window open when running in IDE { Console.ReadKey(); } return; } string pathIn; string pathOut; int pathInI = 0; for (int i = 0; i < args.Length; i++) { if (args[i] == "--namespace" && i + 2 < args.Length) { i++; Environment.SetEnvironmentVariable("MONOMOD_HOOKGEN_NAMESPACE", args[i]); } else if (args[i] == "--orig") { Environment.SetEnvironmentVariable("MONOMOD_HOOKGEN_ORIG", "1"); } else if (args[i] == "--private") { Environment.SetEnvironmentVariable("MONOMOD_HOOKGEN_PRIVATE", "1"); } else { pathInI = i; break; } } if (pathInI >= args.Length) { Console.WriteLine("No assembly path passed."); if (System.Diagnostics.Debugger.IsAttached) // Keep window open when running in IDE { Console.ReadKey(); } return; } pathIn = args[pathInI]; pathOut = args.Length != 1 && pathInI != args.Length - 1 ? args[args.Length - 1] : null; pathOut = pathOut ?? Path.Combine(Path.GetDirectoryName(pathIn), "MMHOOK_" + Path.ChangeExtension(Path.GetFileName(pathIn), "dll")); using (MonoModder mm = new MonoModder() { InputPath = pathIn, OutputPath = pathOut }) { mm.Read(); mm.MapDependencies(); if (File.Exists(pathOut)) { mm.Log($"[HookGen] Clearing {pathOut}"); File.Delete(pathOut); } mm.Log("[HookGen] Starting HookGenerator"); HookGenerator gen = new HookGenerator(mm, Path.GetFileName(pathOut)); using (ModuleDefinition mOut = gen.OutputModule) { gen.Generate(); mOut.Write(pathOut); } mm.Log("[HookGen] Done."); } if (System.Diagnostics.Debugger.IsAttached) // Keep window open when running in IDE { Console.ReadKey(); } }
/// <summary> /// Relink a .dll to point towards the game's assembly at runtime, then load it. /// </summary> /// <param name="meta">The mod metadata, used for caching, among other things.</param> /// <param name="stream">The stream to read the .dll from.</param> /// <param name="depResolver">An optional dependency resolver.</param> /// <param name="checksumsExtra">Any optional checksums. If you're running this at runtime, pass at least Relinker.GetChecksum(Metadata)</param> /// <param name="prePatch">An optional step executed before patching, but after MonoMod has loaded the input assembly.</param> /// <returns>The loaded, relinked assembly.</returns> public static Assembly GetRelinkedAssembly(ModMetadata meta, Stream stream, MissingDependencyResolver depResolver = null, string[] checksumsExtra = null, Action <MonoModder> prePatch = null) { string cachedPath = GetCachedPath(meta); string cachedChecksumPath = cachedPath.Substring(0, cachedPath.Length - 4) + ".sum"; string[] checksums = new string[2 + (checksumsExtra?.Length ?? 0)]; if (GameChecksum == null) { GameChecksum = GetChecksum(Assembly.GetAssembly(typeof(Utils.Relinker)).Location); } checksums[0] = GameChecksum; checksums[1] = GetChecksum(meta); if (checksumsExtra != null) { for (int i = 0; i < checksumsExtra.Length; i++) { checksums[i + 2] = checksumsExtra[i]; } } if (File.Exists(cachedPath) && File.Exists(cachedChecksumPath) && ChecksumsEqual(checksums, File.ReadAllLines(cachedChecksumPath))) { Logger.Log(LogLevel.Verbose, "relinker", $"Loading cached assembly for {meta}"); try { return(Assembly.LoadFrom(cachedPath)); } catch (Exception e) { Logger.Log(LogLevel.Warn, "relinker", $"Failed loading {meta}"); e.LogDetailed(); return(null); } } if (depResolver == null) { depResolver = GenerateModDependencyResolver(meta); } try { MonoModder modder = Modder; modder.Input = stream; modder.OutputPath = cachedPath; modder.MissingDependencyResolver = depResolver; string symbolPath; modder.ReaderParameters.SymbolStream = meta.OpenStream(out symbolPath, meta.DLL.Substring(0, meta.DLL.Length - 4) + ".pdb", meta.DLL + ".mdb"); modder.ReaderParameters.ReadSymbols = modder.ReaderParameters.SymbolStream != null; if (modder.ReaderParameters.SymbolReaderProvider != null && modder.ReaderParameters.SymbolReaderProvider is RelinkerSymbolReaderProvider) { ((RelinkerSymbolReaderProvider)modder.ReaderParameters.SymbolReaderProvider).Format = string.IsNullOrEmpty(symbolPath) ? DebugSymbolFormat.Auto : symbolPath.EndsWith(".mdb") ? DebugSymbolFormat.MDB : symbolPath.EndsWith(".pdb") ? DebugSymbolFormat.PDB : DebugSymbolFormat.Auto; } modder.Read(); modder.ReaderParameters.ReadSymbols = false; if (modder.ReaderParameters.SymbolReaderProvider != null && modder.ReaderParameters.SymbolReaderProvider is RelinkerSymbolReaderProvider) { ((RelinkerSymbolReaderProvider)modder.ReaderParameters.SymbolReaderProvider).Format = DebugSymbolFormat.Auto; } modder.MapDependencies(); if (RuntimeRuleContainer != null) { modder.ParseRules(RuntimeRuleContainer); RuntimeRuleContainer = null; } prePatch?.Invoke(modder); modder.AutoPatch(); modder.Write(); } catch (Exception e) { Logger.Log(LogLevel.Warn, "relinker", $"Failed relinking {meta}"); e.LogDetailed(); return(null); } finally { Modder.ClearCaches(moduleSpecific: true); Modder.Module.Dispose(); Modder.Module = null; Modder.ReaderParameters.SymbolStream?.Dispose(); } if (File.Exists(cachedChecksumPath)) { File.Delete(cachedChecksumPath); } File.WriteAllLines(cachedChecksumPath, checksums); Logger.Log(LogLevel.Verbose, "relinker", $"Loading assembly for {meta}"); try { return(Assembly.LoadFrom(cachedPath)); } catch (Exception e) { Logger.Log(LogLevel.Warn, "relinker", $"Failed loading {meta}"); e.LogDetailed(); return(null); } }
public void Install(Installer installer, bool leave_mmdlls = false) { var managed = installer.ManagedDir; if (HasPluginsDir) { _InstallPlugins(installer.PluginsDir); } if (RequiresPatchedExe) { installer.PatchExe(); } using (StreamWriter patches_info_writer = new StreamWriter(File.OpenWrite(installer.PatchesInfoFile))) { patches_info_writer.WriteLine($"{Name} {VersionName}"); } _Install("assembly", Assemblies, managed); _Install("MonoMod patch DLL", PatchDLLs, managed); _Install("file", OtherFiles, managed, subdir: true); _Install("directory", Dirs, managed, subdir: true); foreach (var patch_target in Metadata?.OrderedTargets ?? installer.Downloader.GungeonMetadata.ViablePatchTargets) { var patch_target_dll = Path.Combine(managed, $"{patch_target}.dll"); var patch_target_tmp = Path.Combine(managed, $"{patch_target}{TMP_PATCH_SUFFIX}"); var modder = new MonoModder { InputPath = patch_target_dll, OutputPath = patch_target_tmp }; if (Metadata != null && Metadata.RelinkMap != null) { Dictionary <string, string> rmap; if (Metadata.RelinkMap.TryGetValue(patch_target, out rmap)) { _Logger.Info($"Reading component relink map for target {patch_target}"); foreach (var pair in rmap) { ModuleDefinition module; if (!modder.DependencyCache.TryGetValue(pair.Value, out module)) { var path = Path.Combine(managed, pair.Value); _Logger.Debug($"Dependency not in cache: {pair.Value} ({path})"); module = modder.DependencyCache[pair.Value] = ModuleDefinition.ReadModule(path); } _Logger.Debug($"Mapping {pair.Key} => {pair.Value}"); modder.RelinkModuleMap[pair.Key] = module; } } } modder.Read(); var found_mods = false; foreach (var dll in PatchDLLs) { if (File.Exists(Path.Combine(managed, dll)) && dll.StartsWith($"{patch_target}.", StringComparison.InvariantCulture)) { found_mods = true; _Logger.Debug($"Using patch DLL: {dll}"); modder.ReadMod(Path.Combine(managed, dll)); } } if (!found_mods) { _Logger.Info($"Not patching {patch_target} because this component has no patches for it"); continue; } _Logger.Info($"Patching target: {patch_target}"); modder.MapDependencies(); modder.AutoPatch(); modder.Write(); modder.Dispose(); _Logger.Debug($"Replacing original ({patch_target_tmp} => {patch_target_dll})"); if (File.Exists(patch_target_dll)) { File.Delete(patch_target_dll); } File.Move(patch_target_tmp, patch_target_dll); } if (!leave_mmdlls) { foreach (var dll in PatchDLLs) { if (!File.Exists(Path.Combine(managed, dll))) { continue; } _Logger.Debug($"Cleaning up patch DLL {dll}"); File.Delete(Path.Combine(managed, dll)); } } _Logger.Debug($"Cleaning up patched DLL MDB/PDBs"); foreach (var ent in Directory.GetFileSystemEntries(managed)) { if (ent.EndsWith($"{TMP_PATCH_SUFFIX}.mdb", StringComparison.InvariantCulture)) { File.Delete(ent); } } }
public static bool Mod(this InstallerWindow ins, string file) { string inPath = Path.Combine(ins.MainModDir, file); string outPath = Path.Combine(ins.MainModDir, file + ".tmp.dll"); MonoModder monomod = new MonoModder() { InputPath = inPath, OutputPath = outPath }; monomod.SetupETGModder(); using (FileStream fileStream = File.Open(LogPath, FileMode.Append)) { using (StreamWriter streamWriter = new StreamWriter(fileStream)) { monomod.Logger = (string s) => ins.OnActivity(); monomod.Logger += (string s) => streamWriter.WriteLine(s); // Unity wants .mdbs // monomod.WriterParameters.SymbolWriterProvider = new Mono.Cecil.Mdb.MdbWriterProvider(); string db = Path.ChangeExtension(inPath, "pdb"); string dbTmp = Path.ChangeExtension(outPath, "pdb"); if (!File.Exists(db)) { db = inPath + ".mdb"; dbTmp = outPath + ".mdb"; } #if !DEBUG RETRY: try { #endif monomod.Read(); // Read main module first monomod.ReadMod(Directory.GetParent(inPath).FullName); // ... then mods monomod.MapDependencies(); // ... then all dependencies monomod.AutoPatch(); // Patch, monomod.Write(); // and write. monomod.Dispose(); // Finally, dispose, because file access happens now. File.Delete(inPath); File.Move(outPath, inPath); if (File.Exists(db)) { File.Delete(db); } if (File.Exists(dbTmp)) { File.Move(dbTmp, db); } return(true); #if !DEBUG } catch (ArgumentException e) { monomod.Dispose(); if (File.Exists(db)) { File.Delete(db); if (File.Exists(dbTmp)) { File.Delete(dbTmp); } goto RETRY; } ins.LogLine(e.ToString()); throw; return(false); } catch (Exception e) { monomod.Dispose(); ins.LogLine(e.ToString()); throw; return(false); } #endif } } }
/// <summary> /// Relink a .dll to point towards Celeste.exe and FNA / XNA properly at runtime, then load it. /// </summary> /// <param name="meta">The mod metadata, used for caching, among other things.</param> /// <param name="stream">The stream to read the .dll from.</param> /// <param name="depResolver">An optional dependency resolver.</param> /// <param name="checksumsExtra">Any optional checksums. If you're running this at runtime, pass at least Everest.Relinker.GetChecksum(Metadata)</param> /// <param name="prePatch">An optional step executed before patching, but after MonoMod has loaded the input assembly.</param> /// <returns>The loaded, relinked assembly.</returns> public static Assembly GetRelinkedAssembly(Stream stream, string modName) { string cachedPath = GetCachedPath(modName); MissingDependencyResolver depResolver = GenerateModDependencyResolver(); try { MonoModder modder = Modder; modder.Input = stream; modder.OutputPath = cachedPath; modder.MissingDependencyResolver = depResolver; modder.ReaderParameters.ReadSymbols = false; if (modder.ReaderParameters.SymbolReaderProvider is RelinkerSymbolReaderProvider) { ((RelinkerSymbolReaderProvider)modder.ReaderParameters.SymbolReaderProvider).Format = DebugSymbolFormat.Auto; } modder.Read(); modder.ReaderParameters.ReadSymbols = false; if (modder.ReaderParameters.SymbolReaderProvider != null && modder.ReaderParameters.SymbolReaderProvider is RelinkerSymbolReaderProvider) { ((RelinkerSymbolReaderProvider)modder.ReaderParameters.SymbolReaderProvider).Format = DebugSymbolFormat.Auto; } modder.MapDependencies(); modder.AutoPatch(); modder.Write(); } catch (Exception e) { Logger.LogError($"[API] Failed relinking\n{e}"); e.LogDetailed(); return(null); } finally { Modder.ClearCaches(moduleSpecific: true); Modder.Module.Dispose(); Modder.Module = null; Modder.ReaderParameters.SymbolStream?.Dispose(); } Logger.LogDebug($"[API] Loading assembly for {modName}"); try { return(Assembly.LoadFrom(cachedPath)); } catch (Exception e) { Logger.LogError($"[API] Failed loading\n{e}"); e.LogDetailed(); return(null); } }
public static Assembly GetRelinkedAssembly(EverestModuleMetadata meta, Stream stream, MissingDependencyResolver depResolver = null, string[] checksumsExtra = null, Action <MonoModder> prePatch = null) { string cachedPath = GetCachedPath(meta); string cachedChecksumPath = cachedPath.Substring(0, cachedPath.Length - 4) + ".sum"; string[] checksums = new string[2 + (checksumsExtra?.Length ?? 0)]; if (GameChecksum == null) { GameChecksum = GetChecksum(Assembly.GetAssembly(typeof(Relinker)).Location); } checksums[0] = GameChecksum; checksums[1] = GetChecksum(meta); if (checksumsExtra != null) { for (int i = 0; i < checksumsExtra.Length; i++) { checksums[i + 2] = checksumsExtra[i]; } } if (File.Exists(cachedPath) && File.Exists(cachedChecksumPath) && ChecksumsEqual(checksums, File.ReadAllLines(cachedChecksumPath))) { return(Assembly.LoadFrom(cachedPath)); } if (depResolver == null) { depResolver = GenerateModDependencyResolver(meta); } try { MonoModder modder = Modder; modder.Input = stream; modder.OutputPath = cachedPath; modder.MissingDependencyResolver = depResolver; modder.Read(); modder.MapDependencies(); prePatch?.Invoke(modder); modder.AutoPatch(); modder.Write(); } catch (Exception e) { Logger.Log("relinker", $"Failed relinking {meta}: {e}"); return(null); } finally { Modder.ClearCaches(moduleSpecific: true); Modder.Module.Dispose(); Modder.Module = null; } if (File.Exists(cachedChecksumPath)) { File.Delete(cachedChecksumPath); } File.WriteAllLines(cachedChecksumPath, checksums); return(Assembly.LoadFrom(cachedPath)); }
public static void Main(string[] args) { Console.WriteLine("MonoMod " + System.Reflection.Assembly.GetExecutingAssembly().GetName().Version); if (args.Length == 0) { Console.WriteLine("No valid arguments (assembly path) passed."); if (System.Diagnostics.Debugger.IsAttached) // Keep window open when running in IDE { Console.ReadKey(); } return; } string pathIn; string pathOut; if (args.Length > 1 && args[0] == "--generate-debug-il" || args[0] == "--gen-dbg-il") { Console.WriteLine("[DbgILGen] Generating debug hierarchy and debug data (pdb / mdb)."); pathIn = args[1]; pathOut = args.Length != 2 ? args[args.Length - 1] : Path.Combine(Path.GetDirectoryName(pathIn), "MMDBGIL_" + Path.GetFileName(pathIn)); using (MonoModder mm = new MonoModder() { InputPath = pathIn, OutputPath = pathOut }) { mm.Read(false); mm.Log("[DbgILGen] DebugILGenerator.Generate(mm);"); DebugILGenerator.Generate(mm); mm.Write(); mm.Log("[DbgILGen] Done."); } if (System.Diagnostics.Debugger.IsAttached) // Keep window open when running in IDE { Console.ReadKey(); } return; } int pathInI = 0; for (int i = 0; i < args.Length; i++) { if (args[i] == "--dependency-missing-throw=0" || args[i] == "--lean-dependencies") { Environment.SetEnvironmentVariable("MONOMOD_DEPENDENCY_MISSING_THROW", "0"); pathInI = i + 1; } else if (args[i] == "--cleanup=0" || args[i] == "--skip-cleanup") { Environment.SetEnvironmentVariable("MONOMOD_CLEANUP", "0"); pathInI = i + 1; } else if (args[i] == "--cleanup-all=1" || args[i] == "--cleanup-all") { Environment.SetEnvironmentVariable("MONOMOD_CLEANUP_ALL", "1"); pathInI = i + 1; } else if (args[i] == "--verbose=1" || args[i] == "--verbose" || args[i] == "-v") { Environment.SetEnvironmentVariable("MONOMOD_VERBOSE", "1"); pathInI = i + 1; } } if (pathInI >= args.Length) { Console.WriteLine("No assembly path passed."); if (System.Diagnostics.Debugger.IsAttached) // Keep window open when running in IDE { Console.ReadKey(); } return; } pathIn = args[pathInI]; pathOut = args.Length != 1 && pathInI != args.Length - 1 ? args[args.Length - 1] : Path.Combine(Path.GetDirectoryName(pathIn), "MONOMODDED_" + Path.GetFileName(pathIn)); if (File.Exists(pathOut)) { File.Delete(pathOut); } try { using (MonoModder mm = new MonoModder() { InputPath = pathIn, OutputPath = pathOut, Verbose = Environment.GetEnvironmentVariable("MONOMOD_VERBOSE") == "1" }) { mm.Read(false); if (args.Length <= 2) { mm.Log("[Main] Scanning for mods in directory."); mm.ReadMod(Directory.GetParent(pathIn).FullName); } else { mm.Log("[Main] Reading mods list from arguments."); for (int i = pathInI + 1; i < args.Length - 2; i++) { mm.ReadMod(args[i]); } } mm.Read(true); mm.Log("[Main] mm.AutoPatch();"); mm.AutoPatch(); mm.Write(); mm.Log("[Main] Done."); } } catch (Exception e) { Console.WriteLine(e); } if (System.Diagnostics.Debugger.IsAttached) // Keep window open when running in IDE { Console.ReadKey(); } }
public static void PatchGame() { string executablePath = Assembly.GetEntryAssembly().Location; string executableDirectory = Directory.GetParent(executablePath).FullName; string monoModPath = Path.Combine(executableDirectory, "MonoMod.exe"); string hookGenPath = Path.Combine(executableDirectory, "MonoMod.RuntimeDetour.HookGen.exe"); string runtimeDetourDLL = "MonoMod.RuntimeDetour.dll"; string mmUtilsDLL = "MonoMod.Utils.dll"; string jsonDLL = "YamlDotNet.dll"; string gameDirectory = Directory.GetParent(GameManager.exePath).FullName; string hashesFolder = Path.Combine(gameDirectory, "DustDevilHashes"); string modDependencies = Path.Combine(gameDirectory, "ModDependencies"); string dataDirectory = Path.Combine(gameDirectory, Path.GetFileNameWithoutExtension(GameManager.exePath) + "_Data"); string managedFolder = Path.Combine(dataDirectory, "Managed"); string codeDll = Path.Combine(managedFolder, "Assembly-CSharp.dll"); string hookGenDLL = Path.Combine(managedFolder, "HOOKS-Assembly-CSharp.dll"); string engineDll = Path.Combine(managedFolder, "UnityEngine.dll"); string coreModuleDLL = Path.Combine(managedFolder, "UnityEngine.CoreModule.dll"); string backupFolder = managedFolder + "_backup"; Process currentProcess = Process.GetCurrentProcess(); Process monomodProcess = new Process(); monomodProcess.StartInfo.FileName = monoModPath; //monomodProcess.StartInfo.CreateNoWindow = true; monomodProcess.StartInfo.UseShellExecute = false; monomodProcess.StartInfo.RedirectStandardOutput = true; //Create backup if there isn't one if (!Directory.Exists(backupFolder)) { Directory.CreateDirectory(backupFolder); CopyFilesRecursively(managedFolder, backupFolder); } //Install the default patch for Partiality { string engineDLLName = "UnityEngine.dll"; if (File.Exists(coreModuleDLL)) { engineDLLName = "UnityEngine.CoreModule.dll"; engineDll = coreModuleDLL; } string moddedDLL = Path.Combine(Path.GetDirectoryName(engineDll), "patched" + engineDLLName); string defaultPatchLocation = Path.Combine(executableDirectory, "PartialityPatch.dll"); string partialityModLocation = Path.Combine(Path.GetDirectoryName(engineDll), "Partiality.dll"); bool shouldPatch = false; if (!File.Exists(Path.Combine(hashesFolder, "ENGINEHASH.hash"))) { shouldPatch = true; } else { shouldPatch = !ModMetadata.CompareHashes(defaultPatchLocation, Path.Combine(hashesFolder, "ENGINEHASH.hash")); } //Delete mod if it exists if (File.Exists(partialityModLocation)) { File.Delete(partialityModLocation); } //Copy mod to folder with assembly-chsharp.dll File.Copy(Path.Combine(executableDirectory, "Partiality.dll"), partialityModLocation); if (shouldPatch) { //Restore backup File.Delete(engineDll); File.Copy(Path.Combine(backupFolder, engineDLLName), engineDll); //Set monomod arguments to "[UnityEngine.dll] [PartialityPatch.dll] [patched_UnityEngine.dll]" monomodProcess.StartInfo.Arguments = ('"' + engineDll + '"') + " " + ('"' + defaultPatchLocation + '"') + " " + ('"' + moddedDLL + '"'); monomodProcess.Start(); string mmoutput = monomodProcess.StandardOutput.ReadToEnd(); Console.WriteLine(mmoutput); monomodProcess.WaitForExit(); int exitCode = monomodProcess.ExitCode; Console.WriteLine("MMEC:" + exitCode); Console.WriteLine(mmoutput); //Replace file if (File.Exists(moddedDLL)) { //Move modded .dll over original .dll File.Delete(engineDll); File.Copy(moddedDLL, engineDll); File.Delete(moddedDLL); } byte[] newHash = ChecksumHasher.ComputeHash(File.ReadAllBytes(defaultPatchLocation)); File.WriteAllBytes(Path.Combine(hashesFolder, "ENGINEHASH.hash"), newHash); } } //Install custom patches { string[] files = Directory.GetFiles(modDependencies); //Copy mod dependencies foreach (string dependency in files) { string fileName = Path.GetFileName(dependency); //Delete the dependency if it already exists if (File.Exists(Path.Combine(managedFolder, fileName))) { File.Delete(Path.Combine(managedFolder, fileName)); } //Copy the file File.Copy(dependency, Path.Combine(managedFolder, fileName)); } bool shouldPatch = false; string moddedDLL = Path.Combine(Path.GetDirectoryName(engineDll), "patched_Assembly-CSharp.dll"); string epListLocation = Path.Combine(hashesFolder, "ENABLEDPATCHES.enp"); //Check if we have the same enabled/disabled mods as last time, if we do, then { string totalEnabledPatches = "Partiality+"; foreach (ModMetadata md in GameManager.modMetas) { if ((md.isStandalone || md.isPatch) && md.isEnabled) { totalEnabledPatches += Path.GetFileNameWithoutExtension(md.modPath) + "+"; } } DebugLogger.Log(totalEnabledPatches); if (File.Exists(epListLocation)) { string getList = File.ReadAllText(epListLocation); shouldPatch = getList != totalEnabledPatches; if (shouldPatch) { File.WriteAllText(epListLocation, totalEnabledPatches); } } else { shouldPatch = true; File.WriteAllText(epListLocation, totalEnabledPatches); } } //If all the same mods are enabled, check if any mods are dirty. If they are, we gotta re-patch. if (!shouldPatch) { foreach (ModMetadata md in GameManager.modMetas) { if (md.isDirty) { shouldPatch = true; break; } } } if (shouldPatch) { DebugLogger.Log("Patching Assembly-CSharp"); string backupDll = Path.Combine(backupFolder, "Assembly-CSharp.dll"); foreach (ModMetadata md in GameManager.modMetas) { if (md.isStandalone && md.isEnabled) { backupDll = md.modPath; } } //Restore backup File.Delete(codeDll); File.Copy(backupDll, codeDll); List <string> failedPatches = new List <string>(); foreach (ModMetadata md in GameManager.modMetas) { if (md.isPatch && md.isEnabled) { monomodProcess.StartInfo.Arguments = ('"' + codeDll + '"') + " " + ('"' + md.modPath + '"') + " " + ('"' + moddedDLL + '"'); monomodProcess.Start(); string mmoutput = monomodProcess.StandardOutput.ReadToEnd(); monomodProcess.WaitForExit(); int exitCode = monomodProcess.ExitCode; DebugLogger.Log("MMEC:" + exitCode); DebugLogger.Log(mmoutput); if (exitCode != 0) { failedPatches.Add(Path.GetFileNameWithoutExtension(md.modPath)); } //Replace file if (File.Exists(moddedDLL)) { //Move modded .dll over original .dll File.Delete(codeDll); File.Copy(moddedDLL, codeDll); File.Delete(moddedDLL); } } } if (failedPatches.Count > 0) { Eto.Forms.MessageBox.Show("Some mods failed to apply correctly! Please send your LOG.txt (in the Partiality folder) to someone who can help, probably from the people who made the mod."); } //Set mods to all not be dirty, and save them. foreach (ModMetadata md in GameManager.modMetas) { md.isDirty = false; } try { GameManager.SaveAllMetadata(); } catch (System.Exception e) { DebugLogger.Log(e); } } } //HookGen stuff { //Delete Legacy DLL if (File.Exists(Path.Combine(managedFolder, "HOOKS-Assembly-CSharp.dll"))) { File.Delete(Path.Combine(managedFolder, "HOOKS-Assembly-CSharp.dll")); } if (File.Exists(hookGenDLL)) { File.Delete(hookGenDLL); } //Delete files if they existed, so we can update them. if (File.Exists(Path.Combine(managedFolder, runtimeDetourDLL))) { File.Delete(Path.Combine(managedFolder, runtimeDetourDLL)); } if (File.Exists(Path.Combine(managedFolder, mmUtilsDLL))) { File.Delete(Path.Combine(managedFolder, mmUtilsDLL)); } if (File.Exists(Path.Combine(managedFolder, jsonDLL))) { File.Delete(Path.Combine(managedFolder, jsonDLL)); } //Copy files File.Copy(Path.Combine(executableDirectory, runtimeDetourDLL), Path.Combine(managedFolder, runtimeDetourDLL)); File.Copy(Path.Combine(executableDirectory, mmUtilsDLL), Path.Combine(managedFolder, mmUtilsDLL)); File.Copy(Path.Combine(executableDirectory, jsonDLL), Path.Combine(managedFolder, jsonDLL)); string pathIn = codeDll; string pathOut = hookGenDLL; using (MonoModder mm = new MonoModder { InputPath = pathIn, OutputPath = pathOut }) { mm.Read(); mm.MapDependencies(); if (File.Exists(pathOut)) { mm.Log(string.Format("Clearing {0}", pathOut)); File.Delete(pathOut); } mm.Log("[HookGen] Starting HookGenerator"); HookGenerator gen = new HookGenerator(mm, Path.GetFileName(pathOut)); using (ModuleDefinition mOut = gen.OutputModule) { gen.HookPrivate = true; gen.Generate(); mOut.Write(pathOut); } mm.Log("[HookGen] Done."); } } //File.WriteAllText( gameDirectory + "\\PARTIALITY_OUTPUT.txt", ); }
/// <summary> /// Relink a .dll to point towards Celeste.exe and FNA / XNA properly at runtime, then load it. /// </summary> /// <param name="meta">The mod metadata, used for caching, among other things.</param> /// <param name="stream">The stream to read the .dll from.</param> /// <param name="depResolver">An optional dependency resolver.</param> /// <param name="checksumsExtra">Any optional checksums</param> /// <param name="prePatch">An optional step executed before patching, but after MonoMod has loaded the input assembly.</param> /// <returns>The loaded, relinked assembly.</returns> public static Assembly GetRelinkedAssembly(EverestModuleMetadata meta, string asmname, Stream stream, MissingDependencyResolver depResolver = null, string[] checksumsExtra = null, Action <MonoModder> prePatch = null) { if (!Flags.SupportRelinkingMods) { Logger.Log(LogLevel.Warn, "relinker", "Relinker disabled!"); return(null); } string cachedPath = GetCachedPath(meta, asmname); string cachedChecksumPath = cachedPath.Substring(0, cachedPath.Length - 4) + ".sum"; string[] checksums = new string[2 + (checksumsExtra?.Length ?? 0)]; if (GameChecksum == null) { GameChecksum = Everest.GetChecksum(Assembly.GetAssembly(typeof(Relinker)).Location).ToHexadecimalString(); } checksums[0] = GameChecksum; checksums[1] = Everest.GetChecksum(ref stream).ToHexadecimalString(); if (checksumsExtra != null) { for (int i = 0; i < checksumsExtra.Length; i++) { checksums[i + 2] = checksumsExtra[i]; } } if (File.Exists(cachedPath) && File.Exists(cachedChecksumPath) && ChecksumsEqual(checksums, File.ReadAllLines(cachedChecksumPath))) { Logger.Log(LogLevel.Verbose, "relinker", $"Loading cached assembly for {meta} - {asmname}"); try { Assembly asm = Assembly.LoadFrom(cachedPath); _RelinkedAssemblies.Add(asm); return(asm); } catch (Exception e) { Logger.Log(LogLevel.Warn, "relinker", $"Failed loading {meta} - {asmname}"); e.LogDetailed(); return(null); } } if (depResolver == null) { depResolver = GenerateModDependencyResolver(meta); } bool temporaryASM = false; try { MonoModder modder = Modder; modder.Input = stream; modder.OutputPath = cachedPath; modder.MissingDependencyResolver = depResolver; string symbolPath; modder.ReaderParameters.SymbolStream = OpenStream(meta, out symbolPath, meta.DLL.Substring(0, meta.DLL.Length - 4) + ".pdb", meta.DLL + ".mdb"); modder.ReaderParameters.ReadSymbols = modder.ReaderParameters.SymbolStream != null; if (modder.ReaderParameters.SymbolReaderProvider != null && modder.ReaderParameters.SymbolReaderProvider is RelinkerSymbolReaderProvider) { ((RelinkerSymbolReaderProvider)modder.ReaderParameters.SymbolReaderProvider).Format = string.IsNullOrEmpty(symbolPath) ? DebugSymbolFormat.Auto : symbolPath.EndsWith(".mdb") ? DebugSymbolFormat.MDB : symbolPath.EndsWith(".pdb") ? DebugSymbolFormat.PDB : DebugSymbolFormat.Auto; } try { modder.ReaderParameters.ReadSymbols = true; modder.Read(); } catch { modder.ReaderParameters.SymbolStream?.Dispose(); modder.ReaderParameters.SymbolStream = null; modder.ReaderParameters.ReadSymbols = false; stream.Seek(0, SeekOrigin.Begin); modder.Read(); } if (modder.ReaderParameters.SymbolReaderProvider != null && modder.ReaderParameters.SymbolReaderProvider is RelinkerSymbolReaderProvider) { ((RelinkerSymbolReaderProvider)modder.ReaderParameters.SymbolReaderProvider).Format = DebugSymbolFormat.Auto; } modder.MapDependencies(); if (!RuntimeRulesParsed) { RuntimeRulesParsed = true; InitMMSharedData(); string rulesPath = Path.Combine( Path.GetDirectoryName(typeof(Celeste).Assembly.Location), Path.GetFileNameWithoutExtension(typeof(Celeste).Assembly.Location) + ".Mod.mm.dll" ); if (!File.Exists(rulesPath)) { // Fallback if someone renamed Celeste.exe rulesPath = Path.Combine( Path.GetDirectoryName(typeof(Celeste).Assembly.Location), "Celeste.Mod.mm.dll" ); } if (File.Exists(rulesPath)) { ModuleDefinition rules = ModuleDefinition.ReadModule(rulesPath, new ReaderParameters(ReadingMode.Immediate)); modder.ParseRules(rules); rules.Dispose(); // Is this safe? } } prePatch?.Invoke(modder); modder.ParseRules(modder.Module); modder.AutoPatch(); RetryWrite: try { modder.WriterParameters.WriteSymbols = true; modder.Write(); } catch { try { modder.WriterParameters.WriteSymbols = false; modder.Write(); } catch when(!temporaryASM) { temporaryASM = true; long stamp = DateTime.Now.Ticks / TimeSpan.TicksPerMillisecond; cachedPath = Path.Combine(Path.GetTempPath(), $"Everest.Relinked.{Path.GetFileNameWithoutExtension(cachedPath)}.{stamp}.dll"); modder.Module.Name += "." + stamp; modder.Module.Assembly.Name.Name += "." + stamp; modder.OutputPath = cachedPath; modder.WriterParameters.WriteSymbols = true; goto RetryWrite; } } } catch (Exception e) { Logger.Log(LogLevel.Warn, "relinker", $"Failed relinking {meta} - {asmname}"); e.LogDetailed(); return(null); } finally { Modder.ReaderParameters.SymbolStream?.Dispose(); if (SharedModder) { Modder.ClearCaches(moduleSpecific: true); Modder.Module.Dispose(); Modder.Module = null; } else { Modder.Dispose(); Modder = null; } } if (File.Exists(cachedChecksumPath)) { File.Delete(cachedChecksumPath); } if (!temporaryASM) { File.WriteAllLines(cachedChecksumPath, checksums); } Logger.Log(LogLevel.Verbose, "relinker", $"Loading assembly for {meta} - {asmname}"); try { Assembly asm = Assembly.LoadFrom(cachedPath); _RelinkedAssemblies.Add(asm); return(asm); } catch (Exception e) { Logger.Log(LogLevel.Warn, "relinker", $"Failed loading {meta} - {asmname}"); e.LogDetailed(); return(null); } }
public static int Main(string[] args) { Console.WriteLine("MonoMod " + typeof(Program).GetTypeInfo().Assembly.GetName().Version); if (args.Length == 0) { Console.WriteLine("No valid arguments (assembly path) passed."); if (System.Diagnostics.Debugger.IsAttached) // Keep window open when running in IDE { Console.ReadKey(); } return(0); } string pathIn; string pathOut; int pathInI = 0; for (int i = 0; i < args.Length; i++) { if (args[i] == "--dependency-missing-throw=0" || args[i] == "--lean-dependencies") { Environment.SetEnvironmentVariable("MONOMOD_DEPENDENCY_MISSING_THROW", "0"); pathInI = i + 1; } else if (args[i] == "--cleanup=0" || args[i] == "--skip-cleanup") { Environment.SetEnvironmentVariable("MONOMOD_CLEANUP", "0"); pathInI = i + 1; } else if (args[i] == "--cleanup-all=1" || args[i] == "--cleanup-all") { Environment.SetEnvironmentVariable("MONOMOD_CLEANUP_ALL", "1"); pathInI = i + 1; } else if (args[i] == "--verbose=1" || args[i] == "--verbose" || args[i] == "-v") { Environment.SetEnvironmentVariable("MONOMOD_LOG_VERBOSE", "1"); pathInI = i + 1; } else if (args[i] == "--cache=0" || args[i] == "--uncached") { Environment.SetEnvironmentVariable("MONOMOD_RELINKER_CACHED", "0"); pathInI = i + 1; } } if (pathInI >= args.Length) { Console.WriteLine("No assembly path passed."); if (System.Diagnostics.Debugger.IsAttached) // Keep window open when running in IDE { Console.ReadKey(); } return(0); } pathIn = args[pathInI]; pathOut = args.Length != 1 && pathInI != args.Length - 1 ? args[args.Length - 1] : null; pathOut = pathOut ?? Path.Combine(Path.GetDirectoryName(pathIn), "MONOMODDED_" + Path.GetFileName(pathIn)); if (File.Exists(pathOut)) { File.Delete(pathOut); } #if !DEBUG try { #endif using (MonoModder mm = new MonoModder() { InputPath = pathIn, OutputPath = pathOut }) { mm.Read(); if (args.Length <= 2) { mm.Log("[Main] Scanning for mods in directory."); mm.ReadMod(Directory.GetParent(pathIn).FullName); } else { mm.Log("[Main] Reading mods list from arguments."); for (int i = pathInI + 1; i < args.Length - 1; i++) { mm.ReadMod(args[i]); } } mm.MapDependencies(); mm.Log("[Main] mm.AutoPatch();"); mm.AutoPatch(); mm.Write(); mm.Log("[Main] Done."); } #if !DEBUG } catch (Exception e) { Console.WriteLine(e); if (System.Diagnostics.Debugger.IsAttached) // Keep window open when running in IDE { Console.ReadKey(); } return(-1); } #endif if (System.Diagnostics.Debugger.IsAttached) // Keep window open when running in IDE { Console.ReadKey(); } return(0); }
/// <summary> /// Relink a .dll to point towards Celeste.exe and FNA / XNA properly at runtime, then load it. /// </summary> /// <param name="meta">The mod metadata, used for caching, among other things.</param> /// <param name="stream">The stream to read the .dll from.</param> /// <param name="depResolver">An optional dependency resolver.</param> /// <param name="checksumsExtra">Any optional checksums. If you're running this at runtime, pass at least Everest.Relinker.GetChecksum(Metadata)</param> /// <param name="prePatch">An optional step executed before patching, but after MonoMod has loaded the input assembly.</param> /// <returns>The loaded, relinked assembly.</returns> public static Assembly GetRelinkedAssembly(EverestModuleMetadata meta, Stream stream, MissingDependencyResolver depResolver = null, string[] checksumsExtra = null, Action <MonoModder> prePatch = null) { if (!Flags.SupportRelinkingMods) { Logger.Log(LogLevel.Warn, "relinker", "Relinker disabled!"); return(null); } string cachedPath = GetCachedPath(meta); string cachedChecksumPath = cachedPath.Substring(0, cachedPath.Length - 4) + ".sum"; string[] checksums = new string[2 + (checksumsExtra?.Length ?? 0)]; if (GameChecksum == null) { GameChecksum = Everest.GetChecksum(Assembly.GetAssembly(typeof(Relinker)).Location).ToHexadecimalString(); } checksums[0] = GameChecksum; checksums[1] = Everest.GetChecksum(meta).ToHexadecimalString(); if (checksumsExtra != null) { for (int i = 0; i < checksumsExtra.Length; i++) { checksums[i + 2] = checksumsExtra[i]; } } if (File.Exists(cachedPath) && File.Exists(cachedChecksumPath) && ChecksumsEqual(checksums, File.ReadAllLines(cachedChecksumPath))) { Logger.Log(LogLevel.Verbose, "relinker", $"Loading cached assembly for {meta}"); try { return(Assembly.LoadFrom(cachedPath)); } catch (Exception e) { Logger.Log(LogLevel.Warn, "relinker", $"Failed loading {meta}"); e.LogDetailed(); return(null); } } if (depResolver == null) { depResolver = GenerateModDependencyResolver(meta); } try { MonoModder modder = Modder; modder.Input = stream; modder.OutputPath = cachedPath; modder.MissingDependencyResolver = depResolver; string symbolPath; modder.ReaderParameters.SymbolStream = OpenStream(meta, out symbolPath, meta.DLL.Substring(0, meta.DLL.Length - 4) + ".pdb", meta.DLL + ".mdb"); modder.ReaderParameters.ReadSymbols = modder.ReaderParameters.SymbolStream != null; if (modder.ReaderParameters.SymbolReaderProvider != null && modder.ReaderParameters.SymbolReaderProvider is RelinkerSymbolReaderProvider) { ((RelinkerSymbolReaderProvider)modder.ReaderParameters.SymbolReaderProvider).Format = string.IsNullOrEmpty(symbolPath) ? DebugSymbolFormat.Auto : symbolPath.EndsWith(".mdb") ? DebugSymbolFormat.MDB : symbolPath.EndsWith(".pdb") ? DebugSymbolFormat.PDB : DebugSymbolFormat.Auto; } modder.Read(); modder.ReaderParameters.ReadSymbols = false; if (modder.ReaderParameters.SymbolReaderProvider != null && modder.ReaderParameters.SymbolReaderProvider is RelinkerSymbolReaderProvider) { ((RelinkerSymbolReaderProvider)modder.ReaderParameters.SymbolReaderProvider).Format = DebugSymbolFormat.Auto; } modder.MapDependencies(); if (!RuntimeRulesParsed) { RuntimeRulesParsed = true; InitMMSharedData(); string rulesPath = Path.Combine( Path.GetDirectoryName(typeof(Celeste).Assembly.Location), Path.GetFileNameWithoutExtension(typeof(Celeste).Assembly.Location) + ".Mod.mm.dll" ); if (!File.Exists(rulesPath)) { // Fallback if someone renamed Celeste.exe rulesPath = Path.Combine( Path.GetDirectoryName(typeof(Celeste).Assembly.Location), "Celeste.Mod.mm.dll" ); } if (File.Exists(rulesPath)) { ModuleDefinition rules = ModuleDefinition.ReadModule(rulesPath, new ReaderParameters(ReadingMode.Immediate)); modder.ParseRules(rules); rules.Dispose(); // Is this safe? } // Fix old mods built against HookIL instead of ILContext. _Modder.RelinkMap["MonoMod.RuntimeDetour.HookGen.ILManipulator"] = "MonoMod.Cil.ILContext/Manipulator"; _Modder.RelinkMap["MonoMod.RuntimeDetour.HookGen.HookIL"] = "MonoMod.Cil.ILContext"; _Modder.RelinkMap["MonoMod.RuntimeDetour.HookGen.HookILCursor"] = "MonoMod.Cil.ILCursor"; _Modder.RelinkMap["MonoMod.RuntimeDetour.HookGen.HookILLabel"] = "MonoMod.Cil.ILLabel"; _Modder.RelinkMap["MonoMod.RuntimeDetour.HookGen.HookExtensions"] = "MonoMod.Cil.ILPatternMatchingExt"; _Shim("MonoMod.Utils.ReflectionHelper", typeof(MonoModUpdateShim._ReflectionHelper)); _Shim("MonoMod.Cil.ILCursor", typeof(MonoModUpdateShim._ILCursor)); // If no entry for MonoMod.Utils exists already, add one. modder.MapDependency(_Modder.Module, "MonoMod.Utils"); } prePatch?.Invoke(modder); modder.AutoPatch(); modder.Write(); } catch (Exception e) { Logger.Log(LogLevel.Warn, "relinker", $"Failed relinking {meta}"); e.LogDetailed(); return(null); } finally { Modder.ClearCaches(moduleSpecific: true); Modder.Module.Dispose(); Modder.Module = null; Modder.ReaderParameters.SymbolStream?.Dispose(); } if (File.Exists(cachedChecksumPath)) { File.Delete(cachedChecksumPath); } File.WriteAllLines(cachedChecksumPath, checksums); Logger.Log(LogLevel.Verbose, "relinker", $"Loading assembly for {meta}"); try { return(Assembly.LoadFrom(cachedPath)); } catch (Exception e) { Logger.Log(LogLevel.Warn, "relinker", $"Failed loading {meta}"); e.LogDetailed(); return(null); } }
public static void Main(string[] args) { #if CECIL0_9 throw new NotSupportedException(); #else Console.WriteLine("MonoMod.DebugIL " + typeof(Program).Assembly.GetName().Version); if (args.Length == 0) { Console.WriteLine("No valid arguments (assembly path) passed."); if (System.Diagnostics.Debugger.IsAttached) // Keep window open when running in IDE { Console.ReadKey(); } return; } string pathIn; string pathOut; int pathInI = 0; for (int i = 0; i < args.Length; i++) { if (args[i] == "--relative") { Environment.SetEnvironmentVariable("MONOMOD_DEBUGIL_RELATIVE", "1"); pathInI = i + 1; } else if (args[i] == "--skip-maxstack") { Environment.SetEnvironmentVariable("MONOMOD_DEBUGIL_SKIP_MAXSTACK", "1"); pathInI = i + 1; } } if (pathInI >= args.Length) { Console.WriteLine("No assembly path passed."); if (System.Diagnostics.Debugger.IsAttached) // Keep window open when running in IDE { Console.ReadKey(); } return; } pathIn = args[pathInI]; pathOut = args.Length != 1 && pathInI != args.Length - 1 ? args[args.Length - 1] : null; pathOut = pathOut ?? Path.Combine(Path.GetDirectoryName(pathIn), "MMDBGIL_" + Path.GetFileName(pathIn)); using (MonoModder mm = new MonoModder() { InputPath = pathIn, OutputPath = pathOut }) { mm.Read(); mm.Log("[DbgILGen] DebugILGenerator.Generate(mm);"); DebugILGenerator.Generate(mm); mm.Write(); mm.Log("[DbgILGen] Done."); } if (System.Diagnostics.Debugger.IsAttached) // Keep window open when running in IDE { Console.ReadKey(); } #endif }
/// <summary> /// Runs HookGen on an assembly located in Managed /// </summary> /// <param name="assemblyName"></param> public void RunHookGen(string assemblyName) { string assemblyPath = FindAssemblyByName(assemblyName); if (assemblyPath == string.Empty) { return; } string hooksPath = Path.Combine(Directory.GetParent(assemblyPath).FullName, Path.GetFileNameWithoutExtension(assemblyPath) + "-HOOKS.dll"); using (MonoModder mm = new MonoModder() { InputPath = assemblyPath, OutputPath = hooksPath, ReadingMode = ReadingMode.Deferred }) { mm.Read(); mm.MapDependencies(); if (File.Exists(hooksPath)) { File.Delete(hooksPath); } HookGenerator gen = new HookGenerator(mm, Path.GetFileName(hooksPath)); #if !CECIL0_9 using (ModuleDefinition mOut = gen.OutputModule) { #else ModuleDefinition mOut = gen.OutputModule; { #endif gen.Generate(); mOut.Write(hooksPath); } } string managedPath = Directory.GetParent(assemblyPath).FullName; string currentDirectory = Directory.GetParent(Assembly.GetExecutingAssembly().FullName).FullName; string[] filesToCopy = new string[] { "MonoMod.dll", "MonoMod.RuntimeDetour.dll", "MonoMod.Utils.dll", }; foreach (string s in filesToCopy) { string fromPath = Path.Combine(currentDirectory, s); string toPath = Path.Combine(managedPath, s); if (File.Exists(fromPath)) { File.Delete(fromPath); } File.Copy(fromPath, toPath); } }