using Mono.Cecil; using Mono.Cecil.Cil; using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; namespace IPA.Patcher { class PatchedModule { private static readonly string[] ENTRY_TYPES = { "Input", "Display" }; private FileInfo _File; private ModuleDefinition _Module; internal struct PatchData { public bool IsPatched; public Version Version; } public static PatchedModule Load(string engineFile) { return new PatchedModule(engineFile); } private PatchedModule(string engineFile) { _File = new FileInfo(engineFile); LoadModules(); } private void LoadModules() { var resolver = new DefaultAssemblyResolver(); resolver.AddSearchDirectory(_File.DirectoryName); var parameters = new ReaderParameters { AssemblyResolver = resolver, }; _Module = ModuleDefinition.ReadModule(_File.FullName, parameters); } public PatchData Data { get { var IIdata = new PatchData { IsPatched = false, Version = null }; foreach (var @ref in _Module.AssemblyReferences) { if (@ref.Name == "IllusionInjector") IIdata = new PatchData { IsPatched = true, Version = new Version(0, 0, 0, 0) }; if (@ref.Name == "IllusionPlugin") IIdata = new PatchData { IsPatched = true, Version = new Version(0, 0, 0, 0) }; if (@ref.Name == "IPA.Injector") return new PatchData { IsPatched = true, Version = @ref.Version }; } return IIdata; } } public void Patch(Version v) { // First, let's add the reference var nameReference = new AssemblyNameReference("IPA.Injector", v); var injectorPath = Path.Combine(_File.DirectoryName, "IPA.Injector.dll"); var injector = ModuleDefinition.ReadModule(injectorPath); bool hasIPAInjector = false; for (int i = 0; i < _Module.AssemblyReferences.Count; i++) { if (_Module.AssemblyReferences[i].Name == "IllusionInjector") _Module.AssemblyReferences.RemoveAt(i--); if (_Module.AssemblyReferences[i].Name == "IllusionPlugin") _Module.AssemblyReferences.RemoveAt(i--); if (_Module.AssemblyReferences[i].Name == "IPA.Injector") { hasIPAInjector = true; _Module.AssemblyReferences[i].Version = v; } } if (!hasIPAInjector) { _Module.AssemblyReferences.Add(nameReference); int patched = 0; foreach (var type in FindEntryTypes()) { if (PatchType(type, injector)) { patched++; } } if (patched > 0) { _Module.Write(_File.FullName); } else { throw new Exception("Could not find any entry type!"); } } else { _Module.Write(_File.FullName); } } private bool PatchType(TypeDefinition targetType, ModuleDefinition injector) { var targetMethod = targetType.Methods.FirstOrDefault(m => m.IsConstructor && m.IsStatic); if (targetMethod != null) { var methodReference = _Module.Import(injector.GetType("IPA.Injector.Injector").Methods.First(m => m.Name == "Inject")); targetMethod.Body.Instructions.Insert(0, Instruction.Create(OpCodes.Call, methodReference)); return true; } return false; } private IEnumerable FindEntryTypes() { return _Module.GetTypes().Where(m => ENTRY_TYPES.Contains(m.Name)); } } }