NeighUncensorPatcher.cs 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Reflection;
  5. using Mono.Cecil;
  6. using Mono.Cecil.Cil;
  7. using UnityEngine;
  8. using Object = UnityEngine.Object;
  9. namespace COM3D2.NeighUncensor.Patcher
  10. {
  11. public static class NeighUncensorPatcher
  12. {
  13. public static readonly string[] TargetAssemblyNames = { "Assembly-CSharp.dll", "UnityEngine.dll" };
  14. private static readonly Dictionary<string, Action<AssemblyDefinition, TypeDefinition>> patches =
  15. new Dictionary<string, Action<AssemblyDefinition, TypeDefinition>>
  16. {
  17. ["Assembly-CSharp"] = PatchAssemblyCSharp, ["UnityEngine"] = PatchUnityEngine
  18. };
  19. public static void Patch(AssemblyDefinition ad)
  20. {
  21. var hookAd = AssemblyDefinition.ReadAssembly(Assembly.GetExecutingAssembly().Location);
  22. var hooks = hookAd.MainModule.GetType("COM3D2.NeighUncensor.Patcher.Hooks");
  23. if (patches.TryGetValue(ad.Name.Name, out var patch))
  24. patch(ad, hooks);
  25. }
  26. private static void PatchPostfix(MethodDefinition method, MethodReference postfix, bool passInstance = false)
  27. {
  28. var il = method.Body.GetILProcessor();
  29. var mRef = method.Module.ImportReference(postfix);
  30. foreach (var ins in method.Body.Instructions.ToList())
  31. {
  32. if (ins.OpCode != OpCodes.Ret)
  33. continue;
  34. ins.OpCode = OpCodes.Nop;
  35. il.InsertAfter(ins, il.Create(OpCodes.Ret));
  36. il.InsertAfter(ins, il.Create(OpCodes.Call, mRef));
  37. if (passInstance)
  38. il.InsertAfter(ins, il.Create(OpCodes.Ldarg_0));
  39. }
  40. }
  41. private static void PatchAssemblyCSharp(AssemblyDefinition ad, TypeDefinition hooks)
  42. {
  43. var bgMgr = ad.MainModule.GetType("BgMgr");
  44. var importCM = ad.MainModule.GetType("ImportCM");
  45. var createAssetBundle = bgMgr.Methods.FirstOrDefault(m => m.Name == "CreateAssetBundle");
  46. PatchPostfix(createAssetBundle,
  47. hooks.Methods.FirstOrDefault(m => m.Name == nameof(Hooks.PostCreateAssetBundle)));
  48. var readMaterial = importCM.Methods.FirstOrDefault(m => m.Name == "ReadMaterial");
  49. PatchPostfix(readMaterial, hooks.Methods.FirstOrDefault(m => m.Name == nameof(Hooks.PostReadMaterial)));
  50. }
  51. private static void PatchUnityEngine(AssemblyDefinition ad, TypeDefinition hooks)
  52. {
  53. var resources = ad.MainModule.GetType("UnityEngine.Resources");
  54. var load = resources.Methods.FirstOrDefault(
  55. m => m.Name == "Load" && m.Parameters.Count == 1 &&
  56. m.ReturnType.FullName == "UnityEngine.Object");
  57. PatchPostfix(load, hooks.Methods.FirstOrDefault(m => m.Name == nameof(Hooks.OnResourceLoad)));
  58. }
  59. }
  60. public static class Hooks
  61. {
  62. private static object replacement;
  63. public static Material PostReadMaterial(Material result)
  64. {
  65. if (result.shader?.name.ToLowerInvariant().Contains("mosaic") ?? false)
  66. {
  67. if (replacement == null)
  68. replacement = Shader.Find("Unlit/Transparent");
  69. result.shader = (Shader)replacement;
  70. result.renderQueue = 0;
  71. }
  72. return result;
  73. }
  74. public static GameObject PostCreateAssetBundle(GameObject result)
  75. {
  76. TryUncensor(result);
  77. return result;
  78. }
  79. public static Object OnResourceLoad(Object result)
  80. {
  81. if (result is GameObject go)
  82. TryUncensor(go);
  83. return result;
  84. }
  85. private static void TryUncensor(GameObject go)
  86. {
  87. if (go == null)
  88. return;
  89. var smr = go.GetComponentInChildren<Renderer>();
  90. if (smr == null || smr.materials == null)
  91. return;
  92. foreach (var mm in smr.materials)
  93. PostReadMaterial(mm);
  94. }
  95. }
  96. }