You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

129 lines
4.2 KiB

5 years ago
5 years ago
5 years ago
5 years ago
  1. using Mono.Cecil;
  2. using Mono.Cecil.Cil;
  3. using System;
  4. using System.Collections.Generic;
  5. using System.IO;
  6. using System.Linq;
  7. using System.Text;
  8. namespace IPA.Patcher
  9. {
  10. class PatchedModule
  11. {
  12. private static readonly string[] ENTRY_TYPES = { "Input", "Display" };
  13. private FileInfo _File;
  14. private ModuleDefinition _Module;
  15. internal struct PatchData {
  16. public bool IsPatched;
  17. public Version Version;
  18. }
  19. public static PatchedModule Load(string engineFile)
  20. {
  21. return new PatchedModule(engineFile);
  22. }
  23. private PatchedModule(string engineFile)
  24. {
  25. _File = new FileInfo(engineFile);
  26. LoadModules();
  27. }
  28. private void LoadModules()
  29. {
  30. var resolver = new DefaultAssemblyResolver();
  31. resolver.AddSearchDirectory(_File.DirectoryName);
  32. var parameters = new ReaderParameters
  33. {
  34. AssemblyResolver = resolver,
  35. };
  36. _Module = ModuleDefinition.ReadModule(_File.FullName, parameters);
  37. }
  38. public PatchData Data
  39. {
  40. get
  41. {
  42. var IIdata = new PatchData { IsPatched = false, Version = null };
  43. foreach (var @ref in _Module.AssemblyReferences) {
  44. if (@ref.Name == "IllusionInjector") IIdata = new PatchData { IsPatched = true, Version = new Version(0, 0, 0, 0) };
  45. if (@ref.Name == "IllusionPlugin") IIdata = new PatchData { IsPatched = true, Version = new Version(0, 0, 0, 0) };
  46. if (@ref.Name == "IPA.Injector") return new PatchData { IsPatched = true, Version = @ref.Version };
  47. }
  48. return IIdata;
  49. }
  50. }
  51. public void Patch(Version v)
  52. {
  53. // First, let's add the reference
  54. var nameReference = new AssemblyNameReference("IPA.Injector", v);
  55. var injectorPath = Path.Combine(_File.DirectoryName, "IPA.Injector.dll");
  56. var injector = ModuleDefinition.ReadModule(injectorPath);
  57. bool hasIPAInjector = false;
  58. for (int i = 0; i < _Module.AssemblyReferences.Count; i++)
  59. {
  60. if (_Module.AssemblyReferences[i].Name == "IllusionInjector")
  61. _Module.AssemblyReferences.RemoveAt(i--);
  62. if (_Module.AssemblyReferences[i].Name == "IllusionPlugin")
  63. _Module.AssemblyReferences.RemoveAt(i--);
  64. if (_Module.AssemblyReferences[i].Name == "IPA.Injector")
  65. {
  66. hasIPAInjector = true;
  67. _Module.AssemblyReferences[i].Version = v;
  68. }
  69. }
  70. if (!hasIPAInjector)
  71. {
  72. _Module.AssemblyReferences.Add(nameReference);
  73. int patched = 0;
  74. foreach (var type in FindEntryTypes())
  75. {
  76. if (PatchType(type, injector))
  77. {
  78. patched++;
  79. }
  80. }
  81. if (patched > 0)
  82. {
  83. _Module.Write(_File.FullName);
  84. }
  85. else
  86. {
  87. throw new Exception("Could not find any entry type!");
  88. }
  89. }
  90. else
  91. {
  92. _Module.Write(_File.FullName);
  93. }
  94. }
  95. private bool PatchType(TypeDefinition targetType, ModuleDefinition injector)
  96. {
  97. var targetMethod = targetType.Methods.FirstOrDefault(m => m.IsConstructor && m.IsStatic);
  98. if (targetMethod != null)
  99. {
  100. var methodReference = _Module.Import(injector.GetType("IPA.Injector.Injector").Methods.First(m => m.Name == "Inject"));
  101. targetMethod.Body.Instructions.Insert(0, Instruction.Create(OpCodes.Call, methodReference));
  102. return true;
  103. }
  104. return false;
  105. }
  106. private IEnumerable<TypeDefinition> FindEntryTypes()
  107. {
  108. return _Module.GetTypes().Where(m => ENTRY_TYPES.Contains(m.Name));
  109. }
  110. }
  111. }