using System; using System.Collections.Generic; using System.Diagnostics; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using HarmonyLib; using Il2CppInterop.Runtime; using Il2CppInterop.Runtime.InteropTypes.Arrays; using MelonLoader; using SledChatUnfilter; using SledChatUnfilter.Util; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: AssemblyTitle("SledChatUnfilter")] [assembly: AssemblyFileVersion("0.1.0")] [assembly: MelonInfo(typeof(Mod), "SledChatUnfilter", "0.1.0", "Natebag", null)] [assembly: MelonGame("The Sledding Corporation", "Sledding Game")] [assembly: MelonColor(255, 0, 255, 102)] [assembly: MelonAuthorColor(255, 80, 160, 255)] [assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = "")] [assembly: AssemblyVersion("0.1.0.0")] namespace SledChatUnfilter { public sealed class Mod : MelonMod { public const string Name = "SledChatUnfilter"; public const string Version = "0.1.0"; public const string Author = "Natebag"; private static bool _filterPatched; private static bool _chatManagerDisabled; private static Type _chatManagerType; private static Type _playerSavedSettingsType; public override void OnInitializeMelon() { //IL_0072: Unknown result type (might be due to invalid IL or missing references) //IL_007e: Expected O, but got Unknown SafeLog.Bind(((MelonBase)this).LoggerInstance); try { _chatManagerType = Refl.FindType("Assembly-CSharp", "_Scripts.Systems.Chat.ChatManager"); if (_chatManagerType == null) { SafeLog.Warn("SledChatUnfilter: ChatManager type not found — will retry in OnUpdate"); } _playerSavedSettingsType = Refl.FindType("Assembly-CSharp", "PlayerSavedSettings"); if (_playerSavedSettingsType == null) { SafeLog.Warn("SledChatUnfilter: PlayerSavedSettings type not found"); } PatchProfanityFilter(); ((MelonEventBase)(object)MelonEvents.OnUpdate).Subscribe(new LemonAction(OnFrame), 0, false); SafeLog.Info("SledChatUnfilter v0.1.0 ready."); } catch (Exception value) { ((MelonBase)this).LoggerInstance.Error($"SledChatUnfilter init failed: {value}"); } } private static void OnFrame() { if (_chatManagerDisabled) { return; } try { if (_chatManagerType == null) { _chatManagerType = Refl.FindType("Assembly-CSharp", "_Scripts.Systems.Chat.ChatManager"); if (_chatManagerType == null) { return; } } PropertyInfo property = _chatManagerType.GetProperty("Instance", BindingFlags.Static | BindingFlags.Public); object obj = null; if (property != null) { obj = property.GetValue(null); } else { MethodInfo method = _chatManagerType.GetMethod("get_Instance", BindingFlags.Static | BindingFlags.Public); if (method != null) { obj = method.Invoke(null, null); } } if (obj != null) { object obj2 = Refl.CastToIl2Cpp(obj, _chatManagerType) ?? obj; MethodInfo method2 = obj2.GetType().GetMethod("SetProfanityFilterEnabled", BindingFlags.Instance | BindingFlags.Public); if (method2 == null) { SafeLog.Warn("SledChatUnfilter: SetProfanityFilterEnabled not found on ChatManager"); _chatManagerDisabled = true; return; } method2.Invoke(obj2, new object[1] { false }); SafeLog.Info("SledChatUnfilter: called SetProfanityFilterEnabled(false) on ChatManager.Instance"); DisableSavedSetting(); _chatManagerDisabled = true; } } catch (Exception ex) { SafeLog.Error("SledChatUnfilter.OnFrame", ex); _chatManagerDisabled = true; } } private static void DisableSavedSetting() { try { if (_playerSavedSettingsType == null) { return; } Il2CppReferenceArray val = Object.FindObjectsOfType(Il2CppType.From(_playerSavedSettingsType)); if (val == null || ((Il2CppArrayBase)(object)val).Length == 0) { SafeLog.Warn("SledChatUnfilter: no PlayerSavedSettings found in scene"); return; } object obj = Refl.CastToIl2Cpp(((Il2CppArrayBase)(object)val)[0], _playerSavedSettingsType) ?? ((Il2CppArrayBase)(object)val)[0]; MethodInfo method = obj.GetType().GetMethod("set_CensorTextChat", BindingFlags.Instance | BindingFlags.Public); if (method == null) { SafeLog.Warn("SledChatUnfilter: set_CensorTextChat not found on PlayerSavedSettings"); return; } method.Invoke(obj, new object[1] { false }); SafeLog.Info("SledChatUnfilter: set PlayerSavedSettings.CensorTextChat = false"); } catch (Exception ex) { SafeLog.Error("SledChatUnfilter.DisableSavedSetting", ex); } } private static void PatchProfanityFilter() { //IL_00b6: Unknown result type (might be due to invalid IL or missing references) //IL_00d5: Unknown result type (might be due to invalid IL or missing references) //IL_00e2: Expected O, but got Unknown if (_filterPatched) { return; } try { Type type = Refl.FindType("Assembly-CSharp", "ProfanityFilter"); if (type == null) { SafeLog.Warn("SledChatUnfilter: ProfanityFilter type not found — outgoing filter will not be bypassed"); return; } MethodInfo method = type.GetMethod("Clean", BindingFlags.Instance | BindingFlags.Public, null, new Type[1] { typeof(string) }, null); if (method == null) { SafeLog.Warn("SledChatUnfilter: ProfanityFilter.Clean not found. Methods:"); MethodInfo[] methods = type.GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); foreach (MethodInfo methodInfo in methods) { SafeLog.Warn(" - " + methodInfo.ReturnType.Name + " " + methodInfo.Name); } } else { Harmony val = new Harmony("Natebag.SledChatUnfilter"); MethodInfo method2 = typeof(Mod).GetMethod("ProfanityFilter_Clean_Postfix", BindingFlags.Static | BindingFlags.NonPublic); val.Patch((MethodBase)method, (HarmonyMethod)null, new HarmonyMethod(method2), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); _filterPatched = true; SafeLog.Info("SledChatUnfilter: Harmony postfix applied to ProfanityFilter.Clean()"); } } catch (Exception ex) { SafeLog.Error("SledChatUnfilter.PatchProfanityFilter", ex); } } private static void ProfanityFilter_Clean_Postfix(string input, ref string __result) { try { __result = input; } catch { } } } } namespace SledChatUnfilter.Util { internal static class Refl { private static readonly Dictionary _typeCache = new Dictionary(); private static bool _loggedAssemblies = false; private static MethodInfo _il2cppCastGeneric; public static Type FindType(string assemblyHint, string fullName) { if (_typeCache.TryGetValue(fullName, out var value)) { return value; } try { string[] array = new string[2] { fullName, "Il2Cpp." + fullName }; string[] array2 = new string[3] { assemblyHint, "Il2Cpp" + assemblyHint, assemblyHint + "-firstpass" }; Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies(); foreach (Assembly assembly in assemblies) { string name = assembly.GetName().Name; string[] array3 = array2; foreach (string text in array3) { if (name != text) { continue; } string[] array4 = array; foreach (string name2 in array4) { Type type = assembly.GetType(name2); if (type != null) { _typeCache[fullName] = type; return type; } } } } assemblies = AppDomain.CurrentDomain.GetAssemblies(); foreach (Assembly assembly2 in assemblies) { string[] array3 = array; foreach (string name3 in array3) { Type type2; try { type2 = assembly2.GetType(name3); } catch { continue; } if (type2 != null) { _typeCache[fullName] = type2; return type2; } } } assemblies = AppDomain.CurrentDomain.GetAssemblies(); foreach (Assembly assembly3 in assemblies) { Type[] types; try { types = assembly3.GetTypes(); } catch (ReflectionTypeLoadException ex) { types = ex.Types; } catch { continue; } string text2 = fullName; int num = fullName.LastIndexOf('.'); if (num >= 0 && num < fullName.Length - 1) { text2 = fullName.Substring(num + 1); } Type[] array5 = types; foreach (Type type3 in array5) { if (!(type3 == null) && (type3.FullName == fullName || type3.FullName == "Il2Cpp." + fullName || (type3.FullName != null && type3.FullName.EndsWith("." + fullName)) || type3.Name == fullName || type3.Name == text2)) { _typeCache[fullName] = type3; SafeLog.Info($"Refl.FindType: '{fullName}' resolved via full scan as '{type3.FullName}' in {assembly3.GetName().Name}"); return type3; } } } if (!_loggedAssemblies) { SafeLog.Warn("Refl.FindType: '" + fullName + "' not found. Loaded assemblies:"); assemblies = AppDomain.CurrentDomain.GetAssemblies(); foreach (Assembly assembly4 in assemblies) { SafeLog.Warn(" - " + assembly4.GetName().Name); } _loggedAssemblies = true; } return null; } catch (Exception ex2) { SafeLog.Error($"Refl.FindType({assemblyHint},{fullName})", ex2); return null; } } public static MethodInfo Method(Type t, string name, params Type[] argTypes) { if (t == null) { return null; } try { BindingFlags bindingAttr = BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic; if (argTypes.Length == 0) { MethodInfo[] methods = t.GetMethods(bindingAttr); foreach (MethodInfo methodInfo in methods) { if (methodInfo.Name == name) { return methodInfo; } } return null; } return t.GetMethod(name, bindingAttr, null, argTypes, null); } catch (Exception ex) { SafeLog.Error($"Refl.Method({t.Name}.{name})", ex); return null; } } public static FieldInfo Field(Type t, string name) { if (t == null) { return null; } try { return t.GetField(name, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); } catch (Exception ex) { SafeLog.Error($"Refl.Field({t.Name}.{name})", ex); return null; } } public static object CastToIl2Cpp(object obj, Type targetType) { if (obj == null || targetType == null) { return null; } if (targetType.IsInstanceOfType(obj)) { return obj; } try { if (_il2cppCastGeneric == null) { Type type = FindType("Il2CppInterop.Runtime", "Il2CppInterop.Runtime.InteropTypes.Il2CppObjectBase"); if (type == null) { type = obj.GetType(); } while (type != null) { MethodInfo method = type.GetMethod("Cast", BindingFlags.Instance | BindingFlags.Public); if (method != null && method.IsGenericMethodDefinition) { _il2cppCastGeneric = method; break; } type = type.BaseType; } } if (_il2cppCastGeneric == null) { return null; } return _il2cppCastGeneric.MakeGenericMethod(targetType).Invoke(obj, null); } catch (Exception ex) { SafeLog.Error("Refl.CastToIl2Cpp(" + targetType.FullName + ")", ex); return null; } } } internal static class SafeLog { private static Instance _logger; private static readonly LinkedList _ring = new LinkedList(); private const int RingMax = 200; public static IEnumerable Recent => _ring; public static void Bind(Instance logger) { _logger = logger; } public static void Info(string msg) { Instance logger = _logger; if (logger != null) { logger.Msg(msg); } Push("INFO " + msg); } public static void Warn(string msg) { Instance logger = _logger; if (logger != null) { logger.Warning(msg); } Push("WARN " + msg); } public static void Error(string msg, Exception ex = null) { if (_logger != null) { if (ex != null) { _logger.Error($"{msg}: {ex.GetType().Name}: {ex.Message}\n{ex.StackTrace}"); } else { _logger.Error(msg); } } Push("ERROR " + ((ex != null) ? (msg + ": " + ex.Message) : msg)); } private static void Push(string line) { _ring.AddLast(line); while (_ring.Count > 200) { _ring.RemoveFirst(); } } } }