Decompiled source of MapCopyrightSilencer v1.0.2

Mods/MapCopyrightSilencer.dll

Decompiled a day ago
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.Versioning;
using BoneLib;
using BoneLib.BoneMenu;
using HarmonyLib;
using Il2CppInterop.Runtime.InteropTypes;
using Il2CppInterop.Runtime.InteropTypes.Arrays;
using Il2CppSLZ.Bonelab;
using MapCopyrightSilencer;
using MelonLoader;
using MelonLoader.Preferences;
using MelonLoader.Utils;
using Microsoft.CodeAnalysis;
using UnityEngine;
using UnityEngine.SceneManagement;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: MelonInfo(typeof(MainMod), "MapCopyrightSilencer", "1.0.1", "dimastv", null)]
[assembly: MelonGame("Stress Level Zero", "BONELAB")]
[assembly: MelonOptionalDependencies(new string[] { "BoneLib" })]
[assembly: MelonColor(255, 230, 50, 50)]
[assembly: MelonAuthorColor(255, 255, 120, 120)]
[assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = ".NET 6.0")]
[assembly: AssemblyCompany("MapCopyrightSilencer")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("MapCopyrightSilencer")]
[assembly: AssemblyTitle("MapCopyrightSilencer")]
[assembly: AssemblyVersion("1.0.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableAttribute : Attribute
	{
		public readonly byte[] NullableFlags;

		public NullableAttribute(byte P_0)
		{
			NullableFlags = new byte[1] { P_0 };
		}

		public NullableAttribute(byte[] P_0)
		{
			NullableFlags = P_0;
		}
	}
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableContextAttribute : Attribute
	{
		public readonly byte Flag;

		public NullableContextAttribute(byte P_0)
		{
			Flag = P_0;
		}
	}
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace MapCopyrightSilencer
{
	public static class AudioFilter
	{
		public static bool ShouldMute(AudioClip? clip, GameObject? go, out string reason)
		{
			reason = string.Empty;
			if (!ModConfig.Enabled.Value)
			{
				return false;
			}
			if (ModConfig.MuteCustomMapsOnly.Value && !LevelTracker.IsCustomMap)
			{
				return false;
			}
			string text = (((Object)(object)clip != (Object)null) ? ((Object)clip).name : string.Empty);
			string text2 = (((Object)(object)go != (Object)null) ? ((Object)go).name : string.Empty);
			if (IsWhitelisted(text, text2))
			{
				return false;
			}
			if (ModConfig.MuteKeywordsInName.Value)
			{
				if (MatchesBlacklist(text, out string matchedKeyword))
				{
					reason = "Clip name matched blacklist keyword '" + matchedKeyword + "'";
					return true;
				}
				if (MatchesBlacklist(text2, out matchedKeyword))
				{
					reason = "Object name matched blacklist keyword '" + matchedKeyword + "'";
					return true;
				}
				if ((Object)(object)go != (Object)null && (Object)(object)go.transform != (Object)null)
				{
					Transform parent = go.transform.parent;
					while ((Object)(object)parent != (Object)null)
					{
						if (MatchesBlacklist(((Object)parent).name, out matchedKeyword))
						{
							reason = $"Parent '{((Object)parent).name}' matched blacklist keyword '{matchedKeyword}'";
							return true;
						}
						parent = parent.parent;
					}
				}
			}
			if (ModConfig.MuteLongTracks.Value && (Object)(object)clip != (Object)null)
			{
				float length = clip.length;
				int value = ModConfig.MinDurationSeconds.Value;
				if (length >= (float)value)
				{
					reason = $"Duration ({Math.Round(length, 1)}s >= {value}s threshold)";
					return true;
				}
			}
			return false;
		}

		private static bool IsWhitelisted(string clipName, string goName)
		{
			foreach (string whitelistKeyword in ModConfig.WhitelistKeywords)
			{
				if (!string.IsNullOrEmpty(clipName) && clipName.Contains(whitelistKeyword, StringComparison.OrdinalIgnoreCase))
				{
					return true;
				}
				if (!string.IsNullOrEmpty(goName) && goName.Contains(whitelistKeyword, StringComparison.OrdinalIgnoreCase))
				{
					return true;
				}
			}
			return false;
		}

		private static bool MatchesBlacklist(string name, out string matchedKeyword)
		{
			matchedKeyword = string.Empty;
			if (string.IsNullOrEmpty(name))
			{
				return false;
			}
			foreach (string blacklistKeyword in ModConfig.BlacklistKeywords)
			{
				if (name.Contains(blacklistKeyword, StringComparison.OrdinalIgnoreCase))
				{
					matchedKeyword = blacklistKeyword;
					return true;
				}
			}
			return false;
		}
	}
	[HarmonyPatch]
	public static class AudioPatches
	{
		[HarmonyPatch(typeof(AudioSource), "Play", new Type[] { })]
		[HarmonyPrefix]
		public static bool Prefix_AudioSource_Play(AudioSource __instance)
		{
			try
			{
				if ((Object)(object)__instance == (Object)null)
				{
					return true;
				}
				if (AudioFilter.ShouldMute(__instance.clip, ((Component)__instance).gameObject, out string reason))
				{
					AudioSilencer.SilenceSource(__instance, reason);
					if (ModConfig.HardStopInsteadOfMute.Value)
					{
						return false;
					}
				}
			}
			catch (Exception ex)
			{
				MelonLogger.Warning("Error in Prefix_AudioSource_Play: " + ex.Message);
			}
			return true;
		}

		[HarmonyPatch(typeof(AudioSource), "Play", new Type[] { typeof(double) })]
		[HarmonyPrefix]
		public static bool Prefix_AudioSource_Play_Double(AudioSource __instance)
		{
			return Prefix_AudioSource_Play(__instance);
		}

		[HarmonyPatch(typeof(AudioSource), "PlayDelayed", new Type[] { typeof(float) })]
		[HarmonyPrefix]
		public static bool Prefix_AudioSource_PlayDelayed(AudioSource __instance)
		{
			return Prefix_AudioSource_Play(__instance);
		}

		[HarmonyPatch(typeof(AudioSource), "PlayScheduled", new Type[] { typeof(double) })]
		[HarmonyPrefix]
		public static bool Prefix_AudioSource_PlayScheduled(AudioSource __instance)
		{
			return Prefix_AudioSource_Play(__instance);
		}

		[HarmonyPatch(typeof(AudioSource), "PlayOneShot", new Type[] { typeof(AudioClip) })]
		[HarmonyPrefix]
		public static bool Prefix_AudioSource_PlayOneShot1(AudioSource __instance, AudioClip clip)
		{
			return HandleOneShot(__instance, clip);
		}

		[HarmonyPatch(typeof(AudioSource), "PlayOneShot", new Type[]
		{
			typeof(AudioClip),
			typeof(float)
		})]
		[HarmonyPrefix]
		public static bool Prefix_AudioSource_PlayOneShot2(AudioSource __instance, AudioClip clip, float volumeScale)
		{
			return HandleOneShot(__instance, clip);
		}

		private static bool HandleOneShot(AudioSource __instance, AudioClip clip)
		{
			try
			{
				if ((Object)(object)clip != (Object)null && AudioFilter.ShouldMute(clip, ((Object)(object)__instance != (Object)null) ? ((Component)__instance).gameObject : null, out string reason))
				{
					if ((Object)(object)__instance != (Object)null)
					{
						AudioSilencer.SilenceSource(__instance, reason);
					}
					return false;
				}
			}
			catch (Exception ex)
			{
				MelonLogger.Warning("Error in HandleOneShot: " + ex.Message);
			}
			return true;
		}

		[HarmonyPatch(typeof(RadioPlayer), "Start")]
		[HarmonyPostfix]
		public static void Postfix_RadioPlayer_Start(RadioPlayer __instance)
		{
			try
			{
				if (!((Object)(object)__instance == (Object)null) && ModConfig.Enabled.Value && ModConfig.MuteRadios.Value && (!ModConfig.MuteCustomMapsOnly.Value || LevelTracker.IsCustomMap))
				{
					AudioSilencer.SilenceRadio(__instance);
				}
			}
			catch (Exception ex)
			{
				MelonLogger.Warning("Error in Postfix_RadioPlayer_Start: " + ex.Message);
			}
		}

		[HarmonyPatch(typeof(RadioPlayer), "MainTrigger")]
		[HarmonyPrefix]
		public static bool Prefix_RadioPlayer_MainTrigger(RadioPlayer __instance)
		{
			try
			{
				if ((Object)(object)__instance == (Object)null)
				{
					return true;
				}
				if (ModConfig.Enabled.Value && ModConfig.MuteRadios.Value && (!ModConfig.MuteCustomMapsOnly.Value || LevelTracker.IsCustomMap))
				{
					AudioSilencer.SilenceRadio(__instance);
					return false;
				}
			}
			catch (Exception ex)
			{
				MelonLogger.Warning("Error in Prefix_RadioPlayer_MainTrigger: " + ex.Message);
			}
			return true;
		}
	}
	public class TrackedAudio
	{
		public AudioSource? Source { get; set; }

		public string ClipName { get; set; } = string.Empty;

		public string ObjectName { get; set; } = string.Empty;

		public float Duration { get; set; }

		public bool IsMuted { get; set; }

		public float OriginalVolume { get; set; } = 1f;
	}
	public static class AudioSilencer
	{
		private static readonly HashSet<int> SilencedInstanceIds = new HashSet<int>();

		public static readonly List<TrackedAudio> DetectedSources = new List<TrackedAudio>();

		public static bool IsSourceValid(AudioSource? source)
		{
			if ((Object)(object)source == (Object)null)
			{
				return false;
			}
			try
			{
				if (((Il2CppObjectBase)source).WasCollected)
				{
					return false;
				}
				if (((Object)source).m_CachedPtr == IntPtr.Zero)
				{
					return false;
				}
				return (Object)(object)((Component)source).gameObject != (Object)null;
			}
			catch
			{
				return false;
			}
		}

		public static void PurgeInvalidSources()
		{
			DetectedSources.RemoveAll((TrackedAudio t) => !IsSourceValid(t.Source));
		}

		public static void ResetForNewLevel()
		{
			SilencedInstanceIds.Clear();
			DetectedSources.Clear();
			MelonLogger.Msg("[MapCopyrightSilencer] Cleared all audio sources for new level.");
		}

		public static void SilenceSource(AudioSource? source, string reason)
		{
			if ((Object)(object)source == (Object)null)
			{
				return;
			}
			try
			{
				int instanceID = ((Object)source).GetInstanceID();
				AudioClip clip = source.clip;
				string text = (((Object)(object)clip != (Object)null) ? ((Object)clip).name : "(stream / unknown)");
				float num = (((Object)(object)clip != (Object)null) ? clip.length : 0f);
				string text2 = (((Object)(object)((Component)source).gameObject != (Object)null) ? ((Object)((Component)source).gameObject).name : "unknown");
				UpdateTrackedAudio(source, text, text2, num, isMuted: true);
				if (ModConfig.HardStopInsteadOfMute.Value)
				{
					source.Stop();
				}
				else
				{
					source.mute = true;
					source.volume = 0f;
				}
				if (!SilencedInstanceIds.Contains(instanceID))
				{
					SilencedInstanceIds.Add(instanceID);
					MelonLogger.Msg($"[MapCopyrightSilencer] Silenced: '{text2}' | Clip: '{text}' ({Math.Round(num, 1)}s) -> Reason: {reason}");
				}
			}
			catch (Exception ex)
			{
				MelonLogger.Warning("Error while silencing AudioSource: " + ex.Message);
			}
		}

		public static void UnmuteSource(AudioSource? source)
		{
			if ((Object)(object)source == (Object)null)
			{
				return;
			}
			try
			{
				int id = ((Object)source).GetInstanceID();
				SilencedInstanceIds.Remove(id);
				source.mute = false;
				TrackedAudio trackedAudio = DetectedSources.Find((TrackedAudio t) => (Object)(object)t.Source != (Object)null && ((Object)t.Source).GetInstanceID() == id);
				if (trackedAudio != null)
				{
					source.volume = ((trackedAudio.OriginalVolume > 0f) ? trackedAudio.OriginalVolume : 1f);
					trackedAudio.IsMuted = false;
				}
				else
				{
					source.volume = 1f;
				}
				MelonLogger.Msg("[MapCopyrightSilencer] Unmuted AudioSource: '" + ((Object)source).name + "'");
			}
			catch (Exception ex)
			{
				MelonLogger.Warning("Error while unmuting AudioSource: " + ex.Message);
			}
		}

		public static void SweepScene()
		{
			PurgeInvalidSources();
			if (!ModConfig.Enabled.Value || (ModConfig.MuteCustomMapsOnly.Value && !LevelTracker.IsCustomMap))
			{
				return;
			}
			try
			{
				Il2CppArrayBase<AudioSource> val = Object.FindObjectsOfType<AudioSource>(true);
				if (val != null)
				{
					foreach (AudioSource item in val)
					{
						if ((Object)(object)item == (Object)null)
						{
							continue;
						}
						AudioClip clip = item.clip;
						GameObject gameObject = ((Component)item).gameObject;
						int instanceID = ((Object)item).GetInstanceID();
						string reason;
						if (SilencedInstanceIds.Contains(instanceID))
						{
							if (!item.mute || item.volume > 0.001f)
							{
								item.mute = true;
								item.volume = 0f;
							}
						}
						else if (AudioFilter.ShouldMute(clip, gameObject, out reason))
						{
							SilenceSource(item, reason);
						}
						else
						{
							string clipName = (((Object)(object)clip != (Object)null) ? ((Object)clip).name : "(no clip)");
							string goName = (((Object)(object)gameObject != (Object)null) ? ((Object)gameObject).name : "unknown");
							float length = (((Object)(object)clip != (Object)null) ? clip.length : 0f);
							UpdateTrackedAudio(item, clipName, goName, length, isMuted: false);
						}
					}
				}
				if (ModConfig.MuteRadios.Value)
				{
					SweepRadios();
				}
			}
			catch (Exception ex)
			{
				MelonLogger.Warning("Error during SweepScene: " + ex.Message);
			}
		}

		public static void SweepRadios()
		{
			try
			{
				Il2CppArrayBase<RadioPlayer> val = Object.FindObjectsOfType<RadioPlayer>(true);
				if (val == null)
				{
					return;
				}
				foreach (RadioPlayer item in val)
				{
					if (!((Object)(object)item == (Object)null))
					{
						SilenceRadio(item);
					}
				}
			}
			catch (Exception ex)
			{
				MelonLogger.Warning("Error while sweeping radios: " + ex.Message);
			}
		}

		public static void SilenceRadio(RadioPlayer radio)
		{
			try
			{
				radio.mainVolume = 0f;
				radio.announcementVolume = 0f;
				radio.cashedBaseVolume = 0f;
				if ((Object)(object)radio.mainAP != (Object)null && (Object)(object)radio.mainAP.source != (Object)null)
				{
					SilenceSource(radio.mainAP.source, "Map RadioPlayer");
				}
				if ((Object)(object)radio.announcementAP != (Object)null && (Object)(object)radio.announcementAP.source != (Object)null)
				{
					SilenceSource(radio.announcementAP.source, "Map RadioPlayer Announcement");
				}
			}
			catch (Exception ex)
			{
				MelonLogger.Warning("Error muting RadioPlayer: " + ex.Message);
			}
		}

		public static void PanicMuteAll()
		{
			try
			{
				Il2CppArrayBase<AudioSource> val = Object.FindObjectsOfType<AudioSource>(true);
				if (val != null)
				{
					int num = 0;
					foreach (AudioSource item in val)
					{
						if (!((Object)(object)item == (Object)null))
						{
							item.mute = true;
							item.volume = 0f;
							SilencedInstanceIds.Add(((Object)item).GetInstanceID());
							num++;
						}
					}
					MelonLogger.Msg($"[MapCopyrightSilencer] PANIC: Force-muted {num} AudioSources!");
				}
				SweepRadios();
			}
			catch (Exception value)
			{
				MelonLogger.Error($"Error in PanicMuteAll: {value}");
			}
		}

		public static void RestoreAll()
		{
			try
			{
				Il2CppArrayBase<AudioSource> val = Object.FindObjectsOfType<AudioSource>(true);
				if (val != null)
				{
					foreach (AudioSource item in val)
					{
						if (!((Object)(object)item == (Object)null))
						{
							item.mute = false;
							item.volume = 1f;
						}
					}
				}
				SilencedInstanceIds.Clear();
				foreach (TrackedAudio detectedSource in DetectedSources)
				{
					detectedSource.IsMuted = false;
				}
				Il2CppArrayBase<RadioPlayer> val2 = Object.FindObjectsOfType<RadioPlayer>(true);
				if (val2 != null)
				{
					foreach (RadioPlayer item2 in val2)
					{
						if (!((Object)(object)item2 == (Object)null))
						{
							item2.mainVolume = 1f;
							item2.announcementVolume = 1f;
							item2.cashedBaseVolume = 1f;
						}
					}
				}
				MelonLogger.Msg("[MapCopyrightSilencer] Restored all audio sources.");
			}
			catch (Exception value)
			{
				MelonLogger.Error($"Error in RestoreAll: {value}");
			}
		}

		private static void UpdateTrackedAudio(AudioSource source, string clipName, string goName, float length, bool isMuted)
		{
			int id = ((Object)source).GetInstanceID();
			TrackedAudio trackedAudio = DetectedSources.Find((TrackedAudio t) => (Object)(object)t.Source != (Object)null && ((Object)t.Source).GetInstanceID() == id);
			if (trackedAudio != null)
			{
				trackedAudio.ClipName = clipName;
				trackedAudio.ObjectName = goName;
				trackedAudio.Duration = length;
				trackedAudio.IsMuted = isMuted;
				return;
			}
			if (DetectedSources.Count > 60)
			{
				DetectedSources.RemoveAt(0);
			}
			DetectedSources.Add(new TrackedAudio
			{
				Source = source,
				ClipName = clipName,
				ObjectName = goName,
				Duration = length,
				IsMuted = isMuted,
				OriginalVolume = ((source.volume > 0.001f) ? source.volume : 1f)
			});
		}
	}
	internal static class BoneMenuIntegration
	{
		private static Page? _mainPage;

		private static Page? _audioListPage;

		private static Page? _blacklistPage;

		private static readonly List<Page> _audioItemSubPages = new List<Page>();

		private static readonly List<Page> _blacklistItemSubPages = new List<Page>();

		[MethodImpl(MethodImplOptions.NoInlining)]
		internal static void SetupMenu()
		{
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_0072: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00da: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f5: Unknown result type (might be due to invalid IL or missing references)
			_mainPage = Page.Root.CreatePage("Copyright Silencer", new Color(0.95f, 0.25f, 0.25f), 0, true);
			_mainPage.CreateBool("Mod Enabled", Color.white, ModConfig.Enabled.Value, (Action<bool>)delegate(bool val)
			{
				ModConfig.Enabled.Value = val;
				ModConfig.PrefCategory.SaveToFile(true);
				if (!val)
				{
					AudioSilencer.RestoreAll();
				}
				else
				{
					AudioSilencer.SweepScene();
				}
				RefreshAudioListPage();
			});
			_mainPage.CreateFunction("Panic: Mute All Audio", Color.red, (Action)delegate
			{
				AudioSilencer.PanicMuteAll();
				RefreshAudioListPage();
			});
			_mainPage.CreateFunction("Unmute All Audio", Color.green, (Action)delegate
			{
				AudioSilencer.RestoreAll();
				RefreshAudioListPage();
			});
			_audioListPage = _mainPage.CreatePage("Sounds On Map", Color.cyan, 0, true);
			_blacklistPage = _mainPage.CreatePage("Blacklist", Color.yellow, 0, true);
			RefreshAudioListPage();
			RefreshBlacklistPage();
		}

		[MethodImpl(MethodImplOptions.NoInlining)]
		public static void ClearAudioList()
		{
			//IL_005f: Unknown result type (might be due to invalid IL or missing references)
			if (_audioListPage == null)
			{
				return;
			}
			foreach (Page audioItemSubPage in _audioItemSubPages)
			{
				try
				{
					Menu.DestroyPage(audioItemSubPage);
				}
				catch
				{
				}
			}
			_audioItemSubPages.Clear();
			_audioListPage.RemoveAll();
			_audioListPage.CreateFunction("(Loading map sounds...)", Color.gray, (Action)delegate
			{
			});
		}

		[MethodImpl(MethodImplOptions.NoInlining)]
		public static void RefreshAudioListPage()
		{
			//IL_005f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0093: Unknown result type (might be due to invalid IL or missing references)
			//IL_014b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0319: Unknown result type (might be due to invalid IL or missing references)
			//IL_031e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0327: Unknown result type (might be due to invalid IL or missing references)
			//IL_030b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0312: Unknown result type (might be due to invalid IL or missing references)
			//IL_035a: Unknown result type (might be due to invalid IL or missing references)
			//IL_03fc: Unknown result type (might be due to invalid IL or missing references)
			//IL_043f: Unknown result type (might be due to invalid IL or missing references)
			//IL_04c1: Unknown result type (might be due to invalid IL or missing references)
			if (_audioListPage == null)
			{
				return;
			}
			foreach (Page audioItemSubPage in _audioItemSubPages)
			{
				try
				{
					Menu.DestroyPage(audioItemSubPage);
				}
				catch
				{
				}
			}
			_audioItemSubPages.Clear();
			_audioListPage.RemoveAll();
			_audioListPage.CreateFunction("Refresh Sounds List", Color.green, (Action)delegate
			{
				AudioSilencer.SweepScene();
				RefreshAudioListPage();
			});
			_audioListPage.CreateFunction("Unmute All Audio", Color.yellow, (Action)delegate
			{
				AudioSilencer.RestoreAll();
				RefreshAudioListPage();
			});
			AudioSilencer.PurgeInvalidSources();
			List<TrackedAudio> list = (from t in AudioSilencer.DetectedSources
				where AudioSilencer.IsSourceValid(t.Source) && (t.IsMuted || t.Duration > 0.5f || t.Source.isPlaying)
				orderby t.IsMuted descending, t.Duration descending
				select t).ToList();
			if (list.Count == 0)
			{
				_audioListPage.CreateFunction("(No sounds detected on map)", Color.gray, (Action)delegate
				{
				});
				return;
			}
			int num = 0;
			foreach (TrackedAudio item in list)
			{
				if (num++ >= 25)
				{
					break;
				}
				TrackedAudio localItem = item;
				if (!AudioSilencer.IsSourceValid(localItem.Source))
				{
					continue;
				}
				string obj2 = (localItem.IsMuted ? "[MUTED]" : (((Object)(object)localItem.Source != (Object)null && localItem.Source.isPlaying) ? "[PLAYING]" : "[PAUSED]"));
				string text = ((!string.IsNullOrEmpty(localItem.ClipName) && localItem.ClipName != "(stream / unknown)" && localItem.ClipName != "(no clip)") ? localItem.ClipName : localItem.ObjectName);
				string text2 = ((localItem.Duration > 0f) ? $" ({Math.Round(localItem.Duration)}s)" : "");
				string text3 = obj2 + " " + text + text2;
				Color val = (localItem.IsMuted ? Color.red : (((Object)(object)localItem.Source != (Object)null && localItem.Source.isPlaying) ? Color.green : Color.white));
				Page val2 = _audioListPage.CreatePage(text3, val, 0, true);
				_audioItemSubPages.Add(val2);
				val2.CreateFunction(localItem.IsMuted ? "Unmute Sound" : "Mute Sound", Color.yellow, (Action)delegate
				{
					if (AudioSilencer.IsSourceValid(localItem.Source))
					{
						if (localItem.IsMuted)
						{
							AudioSilencer.UnmuteSource(localItem.Source);
						}
						else
						{
							AudioSilencer.SilenceSource(localItem.Source, "Manual BoneMenu Mute");
						}
						RefreshAudioListPage();
					}
				});
				string blackcandidate = ((!string.IsNullOrEmpty(localItem.ClipName) && localItem.ClipName != "(stream / unknown)" && localItem.ClipName != "(no clip)") ? localItem.ClipName : localItem.ObjectName);
				if (!string.IsNullOrEmpty(blackcandidate))
				{
					val2.CreateFunction("Add '" + blackcandidate + "' to Blacklist", Color.red, (Action)delegate
					{
						ModConfig.AddToBlacklist(blackcandidate);
						if (AudioSilencer.IsSourceValid(localItem.Source))
						{
							AudioSilencer.SilenceSource(localItem.Source, "Added to blacklist");
						}
						RefreshAudioListPage();
						RefreshBlacklistPage();
					});
				}
				if (!string.IsNullOrEmpty(localItem.ObjectName))
				{
					val2.CreateFunction("Object: " + localItem.ObjectName, Color.gray, (Action)delegate
					{
					});
				}
				if (localItem.Duration > 0f)
				{
					val2.CreateFunction($"Length: {Math.Round(localItem.Duration, 1)}s", Color.gray, (Action)delegate
					{
					});
				}
			}
		}

		[MethodImpl(MethodImplOptions.NoInlining)]
		public static void RefreshBlacklistPage()
		{
			//IL_005f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0093: Unknown result type (might be due to invalid IL or missing references)
			//IL_011b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0174: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a6: Unknown result type (might be due to invalid IL or missing references)
			if (_blacklistPage == null)
			{
				return;
			}
			foreach (Page blacklistItemSubPage in _blacklistItemSubPages)
			{
				try
				{
					Menu.DestroyPage(blacklistItemSubPage);
				}
				catch
				{
				}
			}
			_blacklistItemSubPages.Clear();
			_blacklistPage.RemoveAll();
			_blacklistPage.CreateFunction("Refresh List", Color.green, (Action)delegate
			{
				RefreshBlacklistPage();
			});
			_blacklistPage.CreateFunction("Reload From File", Color.cyan, (Action)delegate
			{
				ModConfig.ReloadKeywords();
				AudioSilencer.SweepScene();
				RefreshBlacklistPage();
				RefreshAudioListPage();
			});
			List<string> list = ModConfig.BlacklistKeywords.OrderBy((string k) => k).ToList();
			_blacklistPage.CreateFunction($"Blacklisted Keywords: {list.Count}", Color.yellow, (Action)delegate
			{
			});
			foreach (string item in list)
			{
				string localKeyword = item;
				Page val = _blacklistPage.CreatePage(localKeyword, Color.white, 0, true);
				_blacklistItemSubPages.Add(val);
				val.CreateFunction("Remove '" + localKeyword + "' From Blacklist", Color.red, (Action)delegate
				{
					ModConfig.RemoveFromBlacklist(localKeyword);
					RefreshBlacklistPage();
					AudioSilencer.SweepScene();
					RefreshAudioListPage();
				});
			}
		}
	}
	public static class LevelTracker
	{
		public static string CurrentBarcode { get; private set; } = string.Empty;

		public static string CurrentTitle { get; private set; } = string.Empty;

		public static string CurrentSceneName { get; private set; } = string.Empty;

		public static bool IsCustomMap { get; private set; } = true;

		public static void OnLevelLoaded(LevelInfo levelInfo)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			CurrentBarcode = levelInfo.barcode ?? string.Empty;
			CurrentTitle = levelInfo.title ?? string.Empty;
			Scene activeScene = SceneManager.GetActiveScene();
			CurrentSceneName = ((Scene)(ref activeScene)).name;
			IsCustomMap = DetermineIfCustom(CurrentBarcode, CurrentSceneName);
			MelonLogger.Msg($"[LevelTracker] Loaded: '{CurrentTitle}' (Barcode: '{CurrentBarcode}', Scene: '{CurrentSceneName}') -> CustomMap: {IsCustomMap}");
		}

		public static void OnSceneLoadedDirect(string sceneName)
		{
			CurrentSceneName = sceneName;
			if (string.IsNullOrEmpty(CurrentBarcode))
			{
				IsCustomMap = DetermineIfCustom(CurrentBarcode, sceneName);
			}
		}

		private static bool DetermineIfCustom(string barcode, string sceneName)
		{
			if (!string.IsNullOrEmpty(barcode))
			{
				if (barcode.StartsWith("slz.", StringComparison.OrdinalIgnoreCase) || barcode.StartsWith("stresslevelzero.", StringComparison.OrdinalIgnoreCase) || barcode.StartsWith("c153", StringComparison.OrdinalIgnoreCase))
				{
					return false;
				}
				return true;
			}
			if (string.IsNullOrEmpty(sceneName))
			{
				return true;
			}
			string text = sceneName.ToLowerInvariant();
			if (text == "mainmenu" || text == "bootstrapper" || text.StartsWith("00_") || text.StartsWith("01_") || text.StartsWith("02_") || text.StartsWith("03_") || text.StartsWith("04_") || text.StartsWith("05_") || text.StartsWith("06_") || text.StartsWith("07_") || text.StartsWith("08_") || text.StartsWith("09_") || text.StartsWith("10_") || text.StartsWith("11_") || text.StartsWith("12_") || text.StartsWith("13_") || text.StartsWith("14_") || text.StartsWith("15_") || text.StartsWith("16_") || text.StartsWith("17_") || text.StartsWith("18_") || text.StartsWith("19_") || text.StartsWith("20_") || text.StartsWith("21_") || text.StartsWith("22_") || text.StartsWith("23_"))
			{
				return false;
			}
			return true;
		}
	}
	public class MainMod : MelonMod
	{
		private float _nextSweepTimer;

		private const float SweepInterval = 1f;

		public override void OnInitializeMelon()
		{
			ModConfig.Initialize();
			MelonLogger.Msg("MapCopyrightSilencer initialized successfully!");
		}

		public override void OnLateInitializeMelon()
		{
			TryInitBoneLib();
		}

		public override void OnSceneWasLoaded(int buildIndex, string sceneName)
		{
			AudioSilencer.ResetForNewLevel();
			LevelTracker.OnSceneLoadedDirect(sceneName);
			BoneMenuIntegration.ClearAudioList();
		}

		public override void OnUpdate()
		{
			if (ModConfig.Enabled.Value && Time.time >= _nextSweepTimer)
			{
				_nextSweepTimer = Time.time + 1f;
				AudioSilencer.SweepScene();
			}
		}

		private void TryInitBoneLib()
		{
			try
			{
				if (AppDomain.CurrentDomain.GetAssemblies().Any((Assembly a) => a.GetName().Name == "BoneLib"))
				{
					HookBoneLibEvents();
					BoneMenuIntegration.SetupMenu();
					MelonLogger.Msg("BoneLib integration & BoneMenu initialized successfully!");
				}
				else
				{
					MelonLogger.Warning("BoneLib not detected. Running in standalone mode.");
				}
			}
			catch (Exception ex)
			{
				MelonLogger.Warning("BoneLib integration failed to load: " + ex.Message);
			}
		}

		private void HookBoneLibEvents()
		{
			Hooking.OnLevelLoading += delegate
			{
				try
				{
					AudioSilencer.ResetForNewLevel();
					BoneMenuIntegration.ClearAudioList();
				}
				catch (Exception ex)
				{
					MelonLogger.Warning("Error on LevelLoading hook: " + ex.Message);
				}
			};
			Hooking.OnLevelUnloaded += delegate
			{
				try
				{
					AudioSilencer.ResetForNewLevel();
					BoneMenuIntegration.ClearAudioList();
				}
				catch (Exception ex)
				{
					MelonLogger.Warning("Error on LevelUnloaded hook: " + ex.Message);
				}
			};
			Hooking.OnLevelLoaded += delegate(LevelInfo levelInfo)
			{
				//IL_0005: Unknown result type (might be due to invalid IL or missing references)
				try
				{
					AudioSilencer.ResetForNewLevel();
					LevelTracker.OnLevelLoaded(levelInfo);
					AudioSilencer.SweepScene();
					BoneMenuIntegration.RefreshAudioListPage();
					BoneMenuIntegration.RefreshBlacklistPage();
				}
				catch (Exception ex)
				{
					MelonLogger.Warning("Error on LevelLoaded hook: " + ex.Message);
				}
			};
		}
	}
	public static class ModConfig
	{
		private static readonly string FolderPath = Path.Combine(MelonEnvironment.UserDataDirectory, "MapCopyrightSilencer");

		private static readonly string BlacklistPath = Path.Combine(FolderPath, "blacklist.txt");

		private static readonly string WhitelistPath = Path.Combine(FolderPath, "whitelist.txt");

		public static MelonPreferences_Category PrefCategory { get; private set; } = null;

		public static MelonPreferences_Entry<bool> Enabled { get; private set; } = null;

		public static MelonPreferences_Entry<bool> MuteCustomMapsOnly { get; private set; } = null;

		public static MelonPreferences_Entry<bool> MuteLongTracks { get; private set; } = null;

		public static MelonPreferences_Entry<int> MinDurationSeconds { get; private set; } = null;

		public static MelonPreferences_Entry<bool> MuteKeywordsInName { get; private set; } = null;

		public static MelonPreferences_Entry<bool> MuteRadios { get; private set; } = null;

		public static MelonPreferences_Entry<bool> HardStopInsteadOfMute { get; private set; } = null;

		public static HashSet<string> BlacklistKeywords { get; private set; } = new HashSet<string>(StringComparer.OrdinalIgnoreCase);

		public static HashSet<string> WhitelistKeywords { get; private set; } = new HashSet<string>(StringComparer.OrdinalIgnoreCase);

		public static void Initialize()
		{
			PrefCategory = MelonPreferences.CreateCategory("MapCopyrightSilencer", "Map Copyright Silencer");
			Enabled = PrefCategory.CreateEntry<bool>("Enabled", true, "Enabled", "Master switch for the mod", false, false, (ValueValidator)null, (string)null);
			MuteCustomMapsOnly = PrefCategory.CreateEntry<bool>("MuteCustomMapsOnly", true, "Custom Maps Only", "Only mute music in custom/modded levels, keeping base campaign music intact", false, false, (ValueValidator)null, (string)null);
			MuteLongTracks = PrefCategory.CreateEntry<bool>("MuteLongTracks", true, "Mute Long Tracks", "Automatically mute audio tracks longer than the threshold duration", false, false, (ValueValidator)null, (string)null);
			MinDurationSeconds = PrefCategory.CreateEntry<int>("MinDurationSeconds", 35, "Min Duration (seconds)", "Minimum audio clip duration to be considered a full song/music", false, false, (ValueValidator)null, (string)null);
			MuteKeywordsInName = PrefCategory.CreateEntry<bool>("MuteKeywordsInName", true, "Mute Blacklist Keywords", "Mute audio if clip or object name matches music/copyright blacklist", false, false, (ValueValidator)null, (string)null);
			MuteRadios = PrefCategory.CreateEntry<bool>("MuteRadios", true, "Mute Radios / Boomboxes", "Silences all RadioPlayer objects in the map", false, false, (ValueValidator)null, (string)null);
			HardStopInsteadOfMute = PrefCategory.CreateEntry<bool>("HardStopInsteadOfMute", false, "Hard Stop Audio", "Stop audio completely instead of muting volume (muting is safer for map triggers)", false, false, (ValueValidator)null, (string)null);
			EnsureFilesAndLoad();
		}

		public static void EnsureFilesAndLoad()
		{
			try
			{
				if (!Directory.Exists(FolderPath))
				{
					Directory.CreateDirectory(FolderPath);
				}
				if (!File.Exists(BlacklistPath))
				{
					string[] contents = new string[36]
					{
						"# Map Copyright Silencer - Blacklist", "# Add words, clip names, or artist names below (one per line, case-insensitive).", "# Any audio clip or GameObject containing these words will be muted.", "", "music", "song", "ost", "soundtrack", "bgm", "radio",
						"jukebox", "boombox", "track", "theme", "dance", "phonk", "rock", "rap", "hiphop", "pop",
						"lofi", "lo-fi", "remix", "vocal", "beat", "instrumental", "vibe", "spotify", "youtube", "cassette",
						"guitar", "synthwave", "dubstep", "trap", "rickroll", "never gonna give you up"
					};
					File.WriteAllLines(BlacklistPath, contents);
				}
				if (!File.Exists(WhitelistPath))
				{
					string[] contents2 = new string[33]
					{
						"# Map Copyright Silencer - Whitelist", "# Add words, clip names, or sound types below that should NEVER be muted (e.g. ambient sfx).", "", "ambient", "ambience", "wind", "rain", "drone", "hum", "vent",
						"fan", "water", "air", "roomtone", "reverb", "engine", "machine", "loop_sfx", "footstep", "walk",
						"jump", "impact", "hit", "bullet", "gun", "weapon", "reload", "explosion", "voice", "dialogue",
						"ui_", "click", "button"
					};
					File.WriteAllLines(WhitelistPath, contents2);
				}
				ReloadKeywords();
			}
			catch (Exception value)
			{
				MelonLogger.Error($"Failed to initialize blacklist/whitelist files: {value}");
			}
		}

		public static void ReloadKeywords()
		{
			BlacklistKeywords.Clear();
			WhitelistKeywords.Clear();
			if (File.Exists(BlacklistPath))
			{
				string[] array = File.ReadAllLines(BlacklistPath);
				for (int i = 0; i < array.Length; i++)
				{
					string text = array[i].Trim();
					if (!string.IsNullOrEmpty(text) && !text.StartsWith("#"))
					{
						BlacklistKeywords.Add(text);
					}
				}
			}
			if (File.Exists(WhitelistPath))
			{
				string[] array = File.ReadAllLines(WhitelistPath);
				for (int i = 0; i < array.Length; i++)
				{
					string text2 = array[i].Trim();
					if (!string.IsNullOrEmpty(text2) && !text2.StartsWith("#"))
					{
						WhitelistKeywords.Add(text2);
					}
				}
			}
			MelonLogger.Msg($"Loaded {BlacklistKeywords.Count} blacklist keywords and {WhitelistKeywords.Count} whitelist keywords.");
		}

		public static void AddToBlacklist(string keyword)
		{
			if (string.IsNullOrWhiteSpace(keyword))
			{
				return;
			}
			keyword = keyword.Trim();
			if (!BlacklistKeywords.Add(keyword))
			{
				return;
			}
			try
			{
				File.AppendAllLines(BlacklistPath, new string[1] { keyword });
				MelonLogger.Msg("Added '" + keyword + "' to blacklist.");
			}
			catch (Exception value)
			{
				MelonLogger.Error($"Failed to write to blacklist: {value}");
			}
		}

		public static void RemoveFromBlacklist(string keyword)
		{
			if (string.IsNullOrWhiteSpace(keyword))
			{
				return;
			}
			keyword = keyword.Trim();
			if (!BlacklistKeywords.Remove(keyword))
			{
				return;
			}
			try
			{
				if (File.Exists(BlacklistPath))
				{
					string[] contents = (from l in File.ReadAllLines(BlacklistPath)
						where !l.Trim().Equals(keyword, StringComparison.OrdinalIgnoreCase)
						select l).ToArray();
					File.WriteAllLines(BlacklistPath, contents);
					MelonLogger.Msg("Removed '" + keyword + "' from blacklist.");
				}
			}
			catch (Exception value)
			{
				MelonLogger.Error($"Failed to remove '{keyword}' from blacklist file: {value}");
			}
		}

		public static void AddToWhitelist(string keyword)
		{
			if (string.IsNullOrWhiteSpace(keyword))
			{
				return;
			}
			keyword = keyword.Trim();
			if (!WhitelistKeywords.Add(keyword))
			{
				return;
			}
			try
			{
				File.AppendAllLines(WhitelistPath, new string[1] { keyword });
				MelonLogger.Msg("Added '" + keyword + "' to whitelist.");
			}
			catch (Exception value)
			{
				MelonLogger.Error($"Failed to write to whitelist: {value}");
			}
		}
	}
}