Browse Source

Rewrite to get rid of any material manipulation

ghorsington 4 years ago
parent
commit
cd0aeca09f
2 changed files with 69 additions and 75 deletions
  1. 2 0
      COM3D2.NeighUncensor.Patcher.csproj
  2. 67 75
      NeighUncensorPatcher.cs

+ 2 - 0
COM3D2.NeighUncensor.Patcher.csproj

@@ -29,6 +29,7 @@
     <DefineConstants>TRACE</DefineConstants>
     <ErrorReport>prompt</ErrorReport>
     <WarningLevel>4</WarningLevel>
+    <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
   </PropertyGroup>
   <ItemGroup>
     <Reference Include="Assembly-CSharp">
@@ -37,6 +38,7 @@
     <Reference Include="Mono.Cecil">
       <HintPath>..\lib\Mono.Cecil.dll</HintPath>
     </Reference>
+    <Reference Include="System" />
     <Reference Include="System.Core" />
     <Reference Include="UnityEngine">
       <HintPath>..\lib\UnityEngine.dll</HintPath>

+ 67 - 75
NeighUncensorPatcher.cs

@@ -1,115 +1,107 @@
 using System;
 using System.Collections.Generic;
+using System.Diagnostics;
+using System.IO;
 using System.Linq;
 using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
 using Mono.Cecil;
 using Mono.Cecil.Cil;
 using UnityEngine;
-using Object = UnityEngine.Object;
 
 namespace COM3D2.NeighUncensor.Patcher
 {
     public static class NeighUncensorPatcher
     {
-        public static readonly string[] TargetAssemblyNames = { "Assembly-CSharp.dll", "UnityEngine.dll" };
-
-        private static readonly Dictionary<string, Action<AssemblyDefinition, TypeDefinition>> patches =
-            new Dictionary<string, Action<AssemblyDefinition, TypeDefinition>>
-            {
-                ["Assembly-CSharp"] = PatchAssemblyCSharp, ["UnityEngine"] = PatchUnityEngine
-            };
+        public static readonly string[] TargetAssemblyNames = { "Assembly-CSharp.dll" };
 
         public static void Patch(AssemblyDefinition ad)
         {
             var hookAd = AssemblyDefinition.ReadAssembly(Assembly.GetExecutingAssembly().Location);
             var hooks = hookAd.MainModule.GetType("COM3D2.NeighUncensor.Patcher.Hooks");
-
-            if (patches.TryGetValue(ad.Name.Name, out var patch))
-                patch(ad, hooks);
+            var gameMain = ad.MainModule.GetType("GameMain");
+            var onInit = gameMain.Methods.FirstOrDefault(m => m.Name == "OnInitialize");
+            var ins = onInit.Body.Instructions.First();
+            var il = onInit.Body.GetILProcessor();
+            il.InsertBefore(
+                ins,
+                il.Create(OpCodes.Call,
+                          ad.MainModule.ImportReference(
+                              hooks.Methods.FirstOrDefault(m => m.Name == nameof(Hooks.FixShaders)))));
         }
+    }
 
-        private static void PatchPostfix(MethodDefinition method, MethodReference postfix, bool passInstance = false)
-        {
-            var il = method.Body.GetILProcessor();
-            var mRef = method.Module.ImportReference(postfix);
-            foreach (var ins in method.Body.Instructions.ToList())
-            {
-                if (ins.OpCode != OpCodes.Ret)
-                    continue;
+    public static class Hooks
+    {
+        // List of shaders the RQ of which to zero
+        private static readonly string[] ShadersToCleanup = { "CM3D2/Mosaic", "CM3D2/Mosaic_en" };
 
-                ins.OpCode = OpCodes.Nop;
-                il.InsertAfter(ins, il.Create(OpCodes.Ret));
-                il.InsertAfter(ins, il.Create(OpCodes.Call, mRef));
-                if (passInstance)
-                    il.InsertAfter(ins, il.Create(OpCodes.Ldarg_0));
-            }
-        }
+        // We need to keep at least one instance of the shader alive so that it won't get unloaded
+        private static readonly List<object> ShadersCache = new List<object>();
 
-        private static void PatchAssemblyCSharp(AssemblyDefinition ad, TypeDefinition hooks)
+        public static void FixShaders()
         {
-            var bgMgr = ad.MainModule.GetType("BgMgr");
-            var importCM = ad.MainModule.GetType("ImportCM");
+            try
+            {
+                var modules = Process.GetCurrentProcess().Modules;
 
-            var createAssetBundle = bgMgr.Methods.FirstOrDefault(m => m.Name == "CreateAssetBundle");
-            PatchPostfix(createAssetBundle,
-                         hooks.Methods.FirstOrDefault(m => m.Name == nameof(Hooks.PostCreateAssetBundle)));
+                ProcessModule mono = null;
+                foreach (ProcessModule module in modules)
+                {
+                    if (!module.ModuleName.ToLowerInvariant().Contains("mono"))
+                        continue;
 
-            var readMaterial = importCM.Methods.FirstOrDefault(m => m.Name == "ReadMaterial");
-            PatchPostfix(readMaterial, hooks.Methods.FirstOrDefault(m => m.Name == nameof(Hooks.PostReadMaterial)));
-        }
+                    mono = module;
+                }
 
-        private static void PatchUnityEngine(AssemblyDefinition ad, TypeDefinition hooks)
-        {
-            var resources = ad.MainModule.GetType("UnityEngine.Resources");
+                if (mono == null)
+                    return;
 
-            var load = resources.Methods.FirstOrDefault(
-                m => m.Name == "Load" && m.Parameters.Count == 1 &&
-                     m.ReturnType.FullName == "UnityEngine.Object");
+                var addICall =
+                    Marshal.GetDelegateForFunctionPointer(GetProcAddress(mono.BaseAddress, "mono_add_internal_call"),
+                                                          typeof(AddICallDelegate)) as AddICallDelegate;
+                addICall("COM3D2.NeighUncensor.Patcher.Hooks::FixShader",
+                         Marshal.GetFunctionPointerForDelegate(new FixShaderDelegate(FixShaderImpl)));
 
-            PatchPostfix(load, hooks.Methods.FirstOrDefault(m => m.Name == nameof(Hooks.OnResourceLoad)));
+                CleanupMosaicShaders();
+            }
+            catch (Exception e)
+            {
+                File.WriteAllText("neigh_uncensor_error.log", e.ToString());
+            }
         }
-    }
-
-    public static class Hooks
-    {
-        private static object replacement;
 
-        public static Material PostReadMaterial(Material result)
+        private static void CleanupMosaicShaders()
         {
-            if (result.shader?.name.ToLowerInvariant().Contains("mosaic") ?? false)
+            foreach (string shaderName in ShadersToCleanup)
             {
-                if (replacement == null)
-                    replacement = Shader.Find("Unlit/Transparent");
-                result.shader = (Shader)replacement;
-                result.renderQueue = 0;
-            }
+                var s = Shader.Find(shaderName);
+                if (s == null)
+                    continue;
 
-            return result;
+                ShadersCache.Add(s);
+                FixShader(s);
+            }
         }
 
-        public static GameObject PostCreateAssetBundle(GameObject result)
+        public static unsafe void FixShaderImpl(IntPtr shaderObj)
         {
-            TryUncensor(result);
-            return result;
+            var s = (byte*)shaderObj.ToPointer();
+            var a = *(byte**)(s + 0x10);
+            var b = *(byte**)(a + 0x38);
+            var rq = (uint*)(b + 0x5C);
+            *rq = 0u;
         }
 
-        public static Object OnResourceLoad(Object result)
-        {
-            if (result is GameObject go)
-                TryUncensor(go);
-            return result;
-        }
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern void FixShader(Shader s);
 
-        private static void TryUncensor(GameObject go)
-        {
-            if (go == null)
-                return;
-
-            var smr = go.GetComponentInChildren<Renderer>();
-            if (smr == null || smr.materials == null)
-                return;
-            foreach (var mm in smr.materials)
-                PostReadMaterial(mm);
-        }
+        [DllImport("kernel32", CharSet = CharSet.Ansi, ExactSpelling = true, SetLastError = true)]
+        private static extern IntPtr GetProcAddress(IntPtr hModule, string procName);
+
+        private delegate void FixShaderDelegate(IntPtr shader);
+
+        private delegate void AddICallDelegate([MarshalAs(UnmanagedType.LPStr)] string name, IntPtr addr);
     }
 }