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); }
private static void PostProcess(MonoModder modder) { // GameControl.ReceiveEvent { MethodDefinition md = GetMethod(nameof(GameControl), nameof(GameControl.ReceiveEvent)); var cursor = new ILCursor(new ILContext(md)); cursor.GotoNext ( MoveType.After, x => x.MatchLdcI4(4), x => x.MatchStfld <GameControl>("inputPlayerNumber") ); // Move the cursor out of the else if, one instruction into the sequence after // This way we're after the ldarg.1 (e) in `e.Key`, so the branches of our if // has it point at our code first. cursor.Index += 1; // Instance for stfld. cursor.Emit(OpCodes.Ldarg_0); // InputEvent for delegate cursor.Emit(OpCodes.Ldarg_1); cursor.Emit(OpCodes.Call, GetMethod("GameControl", "GetPlayerNumber")); cursor.Emit <GameControl>(OpCodes.Stfld, "inputPlayerNumber"); } }
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 static void RelinkModule(string from, string toName) { MonoModder self = Modder; from = from.Inject(MonoModExt.SharedData); toName = toName.Inject(MonoModExt.SharedData); bool retrying = false; ModuleDefinition to = null; RETRY: if (toName + ".dll" == self.Module.Name) { to = self.Module; } else if (self.DependencyCache.TryGetValue(toName, out to)) { } else if (!retrying) { self.MapDependency(self.Module, toName); retrying = true; goto RETRY; } if (to != null) { self.Log($"[MonoModRules] RelinkModule: {from} -> {toName}"); self.RelinkModuleMap[from] = to; } }
private static void PostProcessType(MonoModder modder, TypeDefinition type) { foreach (FieldDefinition fdef in type.Fields) { PostProcessField(modder, fdef); } foreach (MethodDefinition mDef in type.Methods) { PostProcessMethod(modder, mDef); } foreach (TypeDefinition nested in type.NestedTypes) { PostProcessType(modder, nested); } foreach (PropertyDefinition pDef in type.Properties) { PostProcessProperty(modder, pDef); } if (type.IsNested) { type.IsNestedPublic = true; } else { type.IsPublic = true; } }
public DebugILGenerator(MonoModder modder) { Modder = modder; OutputPath = Path.GetFullPath(modder.OutputPath); modder.OutputPath = Path.Combine(OutputPath, Path.GetFileName(modder.InputPath)); }
public static void PostProcessor(MonoModder modder) { foreach (TypeDefinition type in modder.Module.Types) { PostProcessType(modder, type); } }
public static void RelinkType(string from, string to) { MonoModder self = Modder; self.Log($"[MonoModRules] RelinkType: {from} -> {to}"); self.RelinkMap[from] = to; }
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(); } }
public static void RelinkMember(string from, string toType, string toMember) { MonoModder self = Modder; self.Log($"[MonoModRules] RelinkMember: {from} -> {toType}::{toMember}"); self.RelinkMap[from] = new RelinkMapEntry(toType, toMember); }
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; } }
private static void _FixBGswitch(MonoModder modder) { // The broken code is inside of Celeste.BGModeToggle::Setup TypeDefinition t_BGModeToggle = modder.Module.GetType("Celeste.BGModeToggle"); if (t_BGModeToggle == null) { return; } ILContext il = new ILContext(t_BGModeToggle.FindMethod("Setup")); ILCursor c = new ILCursor(il); // newobj Grid::.ctor(System.Single,System.Single,System.Boolean[,]) -> newobj Grid::.ctor(System.Single,System.Single,System.Boolean[0...,0...]) c.Index = 0; while (c.TryGotoNext(i => i.MatchNewobj <Grid>())) { MethodReference ctor = c.Next.Operand as MethodReference; if (ctor == null) { continue; } ArrayType param = (ArrayType)ctor.Parameters[2].ParameterType; param.Dimensions.Clear(); param.Dimensions.Add(new ArrayDimension(0, null)); param.Dimensions.Add(new ArrayDimension(0, null)); } }
public static void RelinkType(MonoModder self, string from, string to) { from = from.Inject(MonoModder.Data); to = to.Inject(MonoModder.Data); self.Log($"[MonoModRules] RelinkType: {from} -> {to}"); self.RelinkMap[from] = to; }
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); } }
public static void RelinkMember(MonoModder self, string from, string toType, string toMember) { from = from.Inject(MonoModder.Data); toType = toType.Inject(MonoModder.Data); toMember = toMember.Inject(MonoModder.Data); self.Log($"[MonoModRules] RelinkMember: {from} -> {toType}::{toMember}"); self.RelinkMap[from] = Tuple.Create(toType, toMember); }
public DebugILGenerator(MonoModder modder) { Modder = modder; OutputPath = Path.GetFullPath(modder.OutputPath); modder.OutputPath = Path.Combine(OutputPath, Path.GetFileName(modder.InputPath)); Relative = Environment.GetEnvironmentVariable("MONOMOD_DEBUGIL_RELATIVE") == "1"; }
public static void Register(MonoModder self) { bool firstTime; ModderMap[ModderIdGen.GetId(self, out firstTime)] = new WeakReference(self); if (!firstTime) { throw new InvalidOperationException("MonoModder instance already registered in MMILProxyManager"); } }
public static long GetId(MonoModder self) { WeakReference weak = new WeakReference(self); if (!IDMap.TryGetValue(weak, out long id)) { throw new InvalidOperationException("MonoModder instance wasn't registered in MMILProxyManager"); } return(id); }
public static void ParseMMILAccessCtorHead( MonoModder self, MethodBody body, MethodReference callCtor, ref int instri, out TypeReference type_, out IMetadataTokenProvider member_ ) { TypeReference type = null; IMetadataTokenProvider member = null; if (callCtor.DeclaringType.IsGenericInstance) { type = self.Relink(((GenericInstanceType)callCtor.DeclaringType).GenericArguments[0], body.Method); } if (callCtor.Parameters.Count >= 2 && callCtor.Parameters[callCtor.Parameters.Count - 2].Name == "type") { type = self.FindTypeDeep((string)body.Instructions[instri - 2].Operand); body.Instructions.RemoveAt(instri - 2); instri--; } else if (callCtor.Parameters.Count == 1 && callCtor.Parameters[0].Name == "type") { type = self.FindTypeDeep((string)body.Instructions[instri - 1].Operand); body.Instructions.RemoveAt(instri - 1); instri--; } TypeDefinition typeDef = type.Resolve(); if (callCtor.Parameters.Count >= 1 && callCtor.Parameters[callCtor.Parameters.Count - 1].Name == "name") { string memberName = (string)body.Instructions[instri - 1].Operand; body.Instructions.RemoveAt(instri - 1); instri--; if (memberName.StartsWith("field:")) { member = typeDef.FindField(memberName.Substring(6).Trim()); } else if (memberName.StartsWith("method:")) { member = typeDef.FindMethod(memberName.Substring(7).Trim()); } else { member = typeDef.FindField(memberName) ?? (IMetadataTokenProvider)typeDef.FindMethod(memberName); } member.SetPublic(true); } // Remove the newobj constructor call body.Instructions.RemoveAt(instri); type_ = type; member_ = member; }
public static long GetId(MonoModder self) { bool firstTime; long id = ModderIdGen.GetId(self, out firstTime); if (firstTime) { throw new InvalidOperationException("MonoModder instance wasn't registered in MMILProxyManager"); } return(id); }
/// <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); } } }
public static void RelinkMember(string from, string toType, string toMember) { MonoModder self = Modder; from = from.Inject(MonoModExt.SharedData); toType = toType.Inject(MonoModExt.SharedData); toMember = toMember.Inject(MonoModExt.SharedData); self.Log($"[MonoModRules] RelinkMember: {from} -> {toType}::{toMember}"); self.RelinkMap[from] = new RelinkMapEntry(toType, toMember); }
private static void PostProcessProperty(MonoModder modder, PropertyDefinition pDef) { if (pDef.GetMethod != null) { pDef.GetMethod.IsPublic = true; } if (pDef.SetMethod != null) { pDef.SetMethod.IsPublic = true; } }
private static void ApplyModHackfixes(MonoModder modder) { if (_Relinking == null && !( // Some mods require additional special care. _Relinking.Name == "AdventureHelper" // Don't check the version for this mod as the hackfix is harmless. )) { return; // No hackfixes necessary. } void CrawlMethod(MethodDefinition method) { string methodID = method.GetID(); if (_ModHackfixNoAtlasFallback.Contains(methodID)) { using (ILContext ctx = new ILContext(method)) { ctx.Invoke(ctx => { ILCursor c = new ILCursor(ctx); c.Emit(OpCodes.Ldsfld, typeof(GFX).GetField("Game")); c.Emit(OpCodes.Ldnull); c.Emit(OpCodes.Callvirt, typeof(patch_Atlas).GetMethod("PushFallback")); while (c.TryGotoNext(MoveType.AfterLabel, i => i.MatchRet())) { c.Emit(OpCodes.Ldsfld, typeof(GFX).GetField("Game")); c.Emit(OpCodes.Callvirt, typeof(patch_Atlas).GetMethod("PopFallback")); c.Emit(OpCodes.Pop); c.Index++; } }); } } } void CrawlType(TypeDefinition type) { foreach (MethodDefinition method in type.Methods) { CrawlMethod(method); } foreach (TypeDefinition nested in type.NestedTypes) { CrawlType(nested); } } foreach (TypeDefinition type in modder.Module.Types) { CrawlType(type); } }
public static void PostProcessor(MonoModder modder) { // Patch previously registered AreaCompleteCtors and LevelExitRoutines _in that order._ foreach (MethodDefinition method in AreaCompleteCtors) { PatchAreaCompleteCtor(method); } foreach (MethodDefinition method in LevelExitRoutines) { PatchLevelExitRoutine(method); } }
private static void PostProcessType(MonoModder modder, TypeDefinition type) { foreach (MethodDefinition method in type.Methods) { PostProcessMethod(modder, method); } foreach (TypeDefinition nested in type.NestedTypes) { PostProcessType(modder, nested); } }
public static void Register(MonoModder self) { WeakReference weak = new WeakReference(self); if (IDMap.ContainsKey(weak)) { throw new InvalidOperationException("MonoModder instance already registered in MMILProxyManager"); } long id = IDMap[weak] = PrevID++; ModderMap[id] = weak; }
/// <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 static void Patch(MonoModder self, string id, bool patch) { id = id.Inject(MonoModder.Data); self.Log($"[MonoModRules] Patch: {id}: {patch}"); if (patch && self.SkipList.Contains(id)) { self.SkipList.Remove(id); } else if (!patch && !self.SkipList.Contains(id)) { self.SkipList.Add(id); } }