using System; using System.Collections.Generic; using System.Linq; using System.Reflection; using Mono.Cecil; using Mono.Cecil.Cil; using UnityEngine; namespace COM3D2.NeighUncensor.Patcher { public static class NeighUncensorPatcher { public static readonly string[] TargetAssemblyNames = { "Assembly-CSharp.dll", "UnityEngine.dll" }; private static Dictionary> patches = new Dictionary> { ["Assembly-CSharp"] = PatchAssemblyCSharp, ["UnityEngine"] = PatchUnityEngine }; 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); } 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; 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)); } } private static void PatchAssemblyCSharp(AssemblyDefinition ad, TypeDefinition hooks) { var tBody = ad.MainModule.GetType("TBody"); var importCM = ad.MainModule.GetType("ImportCM"); var fixMaskFlag = tBody.Methods.FirstOrDefault(m => m.Name == "FixMaskFlag"); PatchPostfix(fixMaskFlag, hooks.Methods.FirstOrDefault(m => m.Name == nameof(Hooks.PostFixMaskFlag)), true); var loadSkinMeshR = importCM.Methods.FirstOrDefault(m => m.Name == "LoadSkinMesh_R"); PatchPostfix(loadSkinMeshR, hooks.Methods.FirstOrDefault(m => m.Name == nameof(Hooks.PostLoadMesh))); } private static void PatchUnityEngine(AssemblyDefinition ad, TypeDefinition hooks) { var resources = ad.MainModule.GetType("UnityEngine.Resources"); var load = resources.Methods.FirstOrDefault( m => m.Name == "Load" && m.Parameters.Count == 1 && m.ReturnType.FullName == "UnityEngine.Object"); PatchPostfix(load, hooks.Methods.FirstOrDefault(m => m.Name == nameof(Hooks.OnResourceLoad))); } } public static class Hooks { private static object diffuse; public static void PostFixMaskFlag(TBody tbody) { tbody.goSlot[(int)TBody.hashSlotName["moza"]].boVisible = false; } public static GameObject PostLoadMesh(GameObject result) { TryUncensor(result); return result; } public static UnityEngine.Object OnResourceLoad(UnityEngine.Object result) { if (result is GameObject go) TryUncensor(go); return result; } private static void TryUncensor(GameObject go) { if (go == null) return; var smr = go.GetComponentInChildren(); if (smr == null || smr.materials == null) return; foreach (var mm in smr.materials) { if (mm.shader?.name.ToLowerInvariant().Contains("mosaic") ?? false) { if (diffuse == null) diffuse = Shader.Find("CM3D2/Toony_Lighted_Trans"); mm.shader = diffuse as Shader; mm.color += new Color(0f, 0f, 0f, -1f); } } } } }