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<LemonAction>)(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<Object> val = Object.FindObjectsOfType(Il2CppType.From(_playerSavedSettingsType));
if (val == null || ((Il2CppArrayBase<Object>)(object)val).Length == 0)
{
SafeLog.Warn("SledChatUnfilter: no PlayerSavedSettings found in scene");
return;
}
object obj = Refl.CastToIl2Cpp(((Il2CppArrayBase<Object>)(object)val)[0], _playerSavedSettingsType) ?? ((Il2CppArrayBase<Object>)(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<string, Type> _typeCache = new Dictionary<string, Type>();
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<string> _ring = new LinkedList<string>();
private const int RingMax = 200;
public static IEnumerable<string> 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();
}
}
}
}