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.

148 lines
4.1 KiB

  1. using Mono.Cecil;
  2. using System;
  3. using System.IO;
  4. using System.Reflection;
  5. namespace IPA.Injector
  6. {
  7. internal class VirtualizedModule : IDisposable
  8. {
  9. private readonly FileInfo file;
  10. private ModuleDefinition module;
  11. public static VirtualizedModule Load(string engineFile)
  12. {
  13. return new VirtualizedModule(engineFile);
  14. }
  15. private VirtualizedModule(string assemblyFile)
  16. {
  17. file = new FileInfo(assemblyFile);
  18. LoadModules();
  19. }
  20. private void LoadModules()
  21. {
  22. module = ModuleDefinition.ReadModule(file.FullName, new ReaderParameters
  23. {
  24. ReadWrite = false,
  25. InMemory = true,
  26. ReadingMode = ReadingMode.Immediate
  27. });
  28. }
  29. public void Virtualize(AssemblyName selfName, Action beforeChangeCallback = null)
  30. {
  31. var changed = false;
  32. var virtualize = true;
  33. foreach (var r in module.AssemblyReferences)
  34. {
  35. if (r.Name == selfName.Name)
  36. {
  37. virtualize = false;
  38. if (r.Version != selfName.Version)
  39. {
  40. r.Version = selfName.Version;
  41. changed = true;
  42. }
  43. }
  44. }
  45. if (virtualize)
  46. {
  47. changed = true;
  48. module.AssemblyReferences.Add(new AssemblyNameReference(selfName.Name, selfName.Version));
  49. foreach (var type in module.Types)
  50. {
  51. VirtualizeType(type);
  52. }
  53. }
  54. if (changed)
  55. {
  56. beforeChangeCallback?.Invoke();
  57. module.Write(file.FullName);
  58. }
  59. }
  60. private void VirtualizeType(TypeDefinition type)
  61. {
  62. if(type.IsSealed)
  63. {
  64. // Unseal
  65. type.IsSealed = false;
  66. }
  67. if (type.IsInterface) return;
  68. if (type.IsAbstract) return;
  69. // These two don't seem to work.
  70. if (type.Name == "SceneControl" || type.Name == "ConfigUI") return;
  71. // Take care of sub types
  72. foreach (var subType in type.NestedTypes)
  73. {
  74. VirtualizeType(subType);
  75. }
  76. foreach (var method in type.Methods)
  77. {
  78. if (method.IsManaged
  79. && method.IsIL
  80. && !method.IsStatic
  81. && !method.IsVirtual
  82. && !method.IsAbstract
  83. && !method.IsAddOn
  84. && !method.IsConstructor
  85. && !method.IsSpecialName
  86. && !method.IsGenericInstance
  87. && !method.HasOverrides)
  88. {
  89. method.IsVirtual = true;
  90. method.IsPublic = true;
  91. method.IsPrivate = false;
  92. method.IsNewSlot = true;
  93. method.IsHideBySig = true;
  94. }
  95. }
  96. foreach (var field in type.Fields)
  97. {
  98. if (field.IsPrivate) field.IsFamily = true;
  99. }
  100. }
  101. #region IDisposable Support
  102. private bool disposedValue = false; // To detect redundant calls
  103. protected virtual void Dispose(bool disposing)
  104. {
  105. if (!disposedValue)
  106. {
  107. if (disposing)
  108. {
  109. module.Dispose();
  110. }
  111. disposedValue = true;
  112. }
  113. }
  114. ~VirtualizedModule()
  115. {
  116. // Do not change this code. Put cleanup code in Dispose(bool disposing) above.
  117. Dispose(false);
  118. }
  119. // This code added to correctly implement the disposable pattern.
  120. public void Dispose()
  121. {
  122. // Do not change this code. Put cleanup code in Dispose(bool disposing) above.
  123. Dispose(true);
  124. GC.SuppressFinalize(this);
  125. }
  126. #endregion
  127. }
  128. }