From cf54036a6adf6977300c611c50f7daf2445a0589 Mon Sep 17 00:00:00 2001 From: aszlig Date: Mon, 16 Jul 2018 23:43:20 +0200 Subject: monogame-patcher: Add a replace-call subcommand It's a very early version which I did for a Unity3d game and it *desperately* needs a cleanup. An example command line: monogame-patcher replace-call -i Assembly-CSharp.dll \ 'System.String UnityEngine.Application::get_dataPath()' \ 'System.String UnityEngine.Application::get_persistentDataPath()' \ IniParser SaveMeta Right now the replace-call subcommand has UnityEngine.Application hardcoded, so this won't work for other types than UnityEngine.Application. However, I'm going to refactor the whole patcher very soon and add some tests, so this whole mess will be cleaned up. Signed-off-by: aszlig --- .../build-support/monogame-patcher/patcher.cs | 63 +++++++++++++++++++++- 1 file changed, 61 insertions(+), 2 deletions(-) (limited to 'pkgs/games') diff --git a/pkgs/games/build-support/monogame-patcher/patcher.cs b/pkgs/games/build-support/monogame-patcher/patcher.cs index 16878831..c1502ba1 100644 --- a/pkgs/games/build-support/monogame-patcher/patcher.cs +++ b/pkgs/games/build-support/monogame-patcher/patcher.cs @@ -99,11 +99,70 @@ class FixFileStreams : Command { } } +[Verb("replace-call", HelpText="Replace calls to types.")] +class ReplaceCallCmd : GenericOptions { + [Value(0, Min=2, Max=2, HelpText="Call to replace.")] + public IEnumerable replaceCall { get; set; } + + [Value(2, Required=true, MetaName = "type", HelpText = "Types to patch.")] + public IEnumerable typesToPatch { get; set; } + +}; + +class ReplaceCall : Command { + private string search; + private string replace; + + public ReplaceCall(ReplaceCallCmd options) : base(options) { + this.search = options.replaceCall.ToList()[0]; + this.replace = options.replaceCall.ToList()[1]; + + var filtered = this.module.Types + .Where(p => options.typesToPatch.Contains(p.Name)); + + foreach (var toPatch in filtered) + patch_type(toPatch); + + this.save(); + } + + private void patch_method(MethodDefinition md) { + var il = md.Body.GetILProcessor(); + + var found = md.Body.Instructions + .Where(i => i.OpCode == OpCodes.Call) + .Where(i => i.Operand.ToString() == this.search); + + var ctorType = this.module.AssemblyReferences.Select( + x => new { + // XXX: Don't hardcode UnityEngine.Application! + type = this.module.AssemblyResolver.Resolve(x) + .MainModule.GetType("UnityEngine.Application") + } + ).Where(x => x.type != null).Select(x => x.type).First(); + + var newMethod = ctorType.GetMethods() + .Single(x => x.ToString() == this.replace); + var refMethod = this.module.ImportReference(newMethod); + + foreach (Instruction i in found.ToList()) + il.Replace(i, il.Create(OpCodes.Call, refMethod)); + } + + private void patch_type(TypeDefinition td) { + foreach (var nested in td.NestedTypes) patch_type(nested); + foreach (MethodDefinition md in td.Methods) patch_method(md); + } +} + public class patcher { public static int Main(string[] args) { - Parser.Default.ParseArguments(args) - .WithParsed(opts => new FixFileStreams(opts)); + Parser.Default.ParseArguments< + GenericOptions, FixFileStreamsCmd, ReplaceCallCmd + >(args) + .WithParsed(opts => new FixFileStreams(opts)) + .WithParsed(opts => new ReplaceCall(opts)); return 0; } } -- cgit 1.4.1