Decompiled source of MithrixChimeraMusic v1.2.2

MithrixChimeraMusic.dll

Decompiled 2 weeks ago
using System;
using System.Collections;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Media;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Text;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using EntityStates.Missions.BrotherEncounter;
using Microsoft.CodeAnalysis;
using On.EntityStates.Missions.BrotherEncounter;
using On.RoR2;
using RoR2;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: AssemblyCompany("MithrixChimeraMusic")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("MithrixChimeraMusic")]
[assembly: AssemblyTitle("MithrixChimeraMusic")]
[assembly: AssemblyVersion("1.0.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.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 MithrixChimeraMusic
{
	[BepInPlugin("com.bungo.mithrixchimeramusic", "Mithrix Chimera Music", "1.2.2")]
	public class Plugin : BaseUnityPlugin
	{
		[Serializable]
		[CompilerGenerated]
		private sealed class <>c
		{
			public static readonly <>c <>9 = new <>c();

			public static hook_OnEnter <>9__16_0;

			public static hook_OnEnter <>9__16_1;

			public static hook_OnEnter <>9__16_2;

			public static hook_OnEnter <>9__16_3;

			public static hook_BeginGameOver <>9__16_4;

			public static hook_BeginStage <>9__16_5;

			public static Func<Type, bool> <>9__22_0;

			internal void <Awake>b__16_0(orig_OnEnter orig, Phase1 self)
			{
				orig.Invoke(self);
				OnPhaseEnter("Phase1");
			}

			internal void <Awake>b__16_1(orig_OnEnter orig, Phase2 self)
			{
				orig.Invoke(self);
				OnPhaseEnter("Phase2");
			}

			internal void <Awake>b__16_2(orig_OnEnter orig, Phase3 self)
			{
				orig.Invoke(self);
				OnPhaseEnter("Phase3");
			}

			internal void <Awake>b__16_3(orig_OnEnter orig, Phase4 self)
			{
				orig.Invoke(self);
				OnPhaseEnter("Phase4");
			}

			internal void <Awake>b__16_4(orig_BeginGameOver orig, Run self, GameEndingDef ending)
			{
				StopSong();
				currentPhase = null;
				orig.Invoke(self, ending);
			}

			internal void <Awake>b__16_5(orig_BeginStage orig, Run self)
			{
				orig.Invoke(self);
				if (debugPlayOnStageStart.Value)
				{
					Log.LogInfo((object)"[Debug] Stage started, triggering song");
					StartSong();
				}
			}

			internal bool <PostWwiseEvent>b__22_0(Type t)
			{
				return t.Name == "AkSoundEngine";
			}
		}

		public const string GUID = "com.bungo.mithrixchimeramusic";

		public const string NAME = "Mithrix Chimera Music";

		public const string VERSION = "1.2.2";

		private const string ResourceName = "MithrixChimeraMusic.intermission.wav";

		internal static ManualLogSource Log;

		private static SoundPlayer player;

		private static ConfigEntry<string> phaseChoice;

		private static ConfigEntry<float> volume;

		private static ConfigEntry<bool> muteGameMusic;

		private static ConfigEntry<string> pauseEventName;

		private static ConfigEntry<string> resumeEventName;

		private static ConfigEntry<bool> debugPlayOnStageStart;

		private static MethodInfo akPostEvent;

		private static bool akLookupAttempted;

		private static string currentPhase;

		private static GameObject akTarget;

		private void Awake()
		{
			//IL_00fc: Unknown result type (might be due to invalid IL or missing references)
			//IL_0101: Unknown result type (might be due to invalid IL or missing references)
			//IL_0107: Expected O, but got Unknown
			//IL_0120: Unknown result type (might be due to invalid IL or missing references)
			//IL_0125: Unknown result type (might be due to invalid IL or missing references)
			//IL_012b: Expected O, but got Unknown
			//IL_0144: Unknown result type (might be due to invalid IL or missing references)
			//IL_0149: Unknown result type (might be due to invalid IL or missing references)
			//IL_014f: Expected O, but got Unknown
			//IL_0168: Unknown result type (might be due to invalid IL or missing references)
			//IL_016d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0173: Expected O, but got Unknown
			//IL_018c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0191: Unknown result type (might be due to invalid IL or missing references)
			//IL_0197: Expected O, but got Unknown
			//IL_01b0: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b5: Unknown result type (might be due to invalid IL or missing references)
			//IL_01bb: Expected O, but got Unknown
			Log = ((BaseUnityPlugin)this).Logger;
			phaseChoice = ((BaseUnityPlugin)this).Config.Bind<string>("General", "PhaseToPlayDuring", "Phase2", "Which Mithrix phase to play during. Valid: Phase1, Phase2, Phase3, Phase4.");
			volume = ((BaseUnityPlugin)this).Config.Bind<float>("General", "Volume", 0.7f, "0.0 to 1.0. Requires game restart to apply.");
			muteGameMusic = ((BaseUnityPlugin)this).Config.Bind<bool>("Audio", "MuteGameMusic", true, "Pause Wwise music while the song plays.");
			pauseEventName = ((BaseUnityPlugin)this).Config.Bind<string>("Audio", "PauseEventName", "Pause_Music_System", "Wwise event posted on phase enter.");
			resumeEventName = ((BaseUnityPlugin)this).Config.Bind<string>("Audio", "ResumeEventName", "Resume_Music_System", "Wwise event posted on phase exit.");
			debugPlayOnStageStart = ((BaseUnityPlugin)this).Config.Bind<bool>("Debug", "PlayOnStageStart", false, "Play the song at the start of every stage.");
			((MonoBehaviour)this).StartCoroutine(LoadCoroutine());
			object obj = <>c.<>9__16_0;
			if (obj == null)
			{
				hook_OnEnter val = delegate(orig_OnEnter orig, Phase1 self)
				{
					orig.Invoke(self);
					OnPhaseEnter("Phase1");
				};
				<>c.<>9__16_0 = val;
				obj = (object)val;
			}
			Phase1.OnEnter += (hook_OnEnter)obj;
			object obj2 = <>c.<>9__16_1;
			if (obj2 == null)
			{
				hook_OnEnter val2 = delegate(orig_OnEnter orig, Phase2 self)
				{
					orig.Invoke(self);
					OnPhaseEnter("Phase2");
				};
				<>c.<>9__16_1 = val2;
				obj2 = (object)val2;
			}
			Phase2.OnEnter += (hook_OnEnter)obj2;
			object obj3 = <>c.<>9__16_2;
			if (obj3 == null)
			{
				hook_OnEnter val3 = delegate(orig_OnEnter orig, Phase3 self)
				{
					orig.Invoke(self);
					OnPhaseEnter("Phase3");
				};
				<>c.<>9__16_2 = val3;
				obj3 = (object)val3;
			}
			Phase3.OnEnter += (hook_OnEnter)obj3;
			object obj4 = <>c.<>9__16_3;
			if (obj4 == null)
			{
				hook_OnEnter val4 = delegate(orig_OnEnter orig, Phase4 self)
				{
					orig.Invoke(self);
					OnPhaseEnter("Phase4");
				};
				<>c.<>9__16_3 = val4;
				obj4 = (object)val4;
			}
			Phase4.OnEnter += (hook_OnEnter)obj4;
			object obj5 = <>c.<>9__16_4;
			if (obj5 == null)
			{
				hook_BeginGameOver val5 = delegate(orig_BeginGameOver orig, Run self, GameEndingDef ending)
				{
					StopSong();
					currentPhase = null;
					orig.Invoke(self, ending);
				};
				<>c.<>9__16_4 = val5;
				obj5 = (object)val5;
			}
			Run.BeginGameOver += (hook_BeginGameOver)obj5;
			object obj6 = <>c.<>9__16_5;
			if (obj6 == null)
			{
				hook_BeginStage val6 = delegate(orig_BeginStage orig, Run self)
				{
					orig.Invoke(self);
					if (debugPlayOnStageStart.Value)
					{
						Log.LogInfo((object)"[Debug] Stage started, triggering song");
						StartSong();
					}
				};
				<>c.<>9__16_5 = val6;
				obj6 = (object)val6;
			}
			Run.BeginStage += (hook_BeginStage)obj6;
		}

		private static void OnPhaseEnter(string name)
		{
			Log.LogInfo((object)("[Mithrix] Entered phase: " + name));
			if (currentPhase == phaseChoice.Value && name != phaseChoice.Value)
			{
				StopSong();
			}
			currentPhase = name;
			if (name == phaseChoice.Value)
			{
				StartSong();
			}
		}

		private IEnumerator LoadCoroutine()
		{
			yield return null;
			Assembly executingAssembly = Assembly.GetExecutingAssembly();
			Stream manifestResourceStream = executingAssembly.GetManifestResourceStream("MithrixChimeraMusic.intermission.wav");
			if (manifestResourceStream == null)
			{
				Log.LogError((object)("Embedded resource 'MithrixChimeraMusic.intermission.wav' not found. Available: " + string.Join(", ", executingAssembly.GetManifestResourceNames())));
				yield break;
			}
			byte[] array;
			using (MemoryStream memoryStream = new MemoryStream())
			{
				manifestResourceStream.CopyTo(memoryStream);
				array = memoryStream.ToArray();
			}
			manifestResourceStream.Dispose();
			Log.LogInfo((object)$"WAV resource loaded: {array.Length} bytes");
			ApplyVolumeToPCM16(array, volume.Value);
			string text = Path.Combine(Paths.CachePath, "MithrixChimeraMusic");
			Directory.CreateDirectory(text);
			string text2 = Path.Combine(text, "intermission.wav");
			File.WriteAllBytes(text2, array);
			Log.LogInfo((object)("Wrote WAV to " + text2));
			try
			{
				player = new SoundPlayer(text2);
				player.LoadAsync();
				Log.LogInfo((object)"SoundPlayer.LoadAsync called");
			}
			catch (Exception arg)
			{
				Log.LogError((object)$"SoundPlayer init failed: {arg}");
				yield break;
			}
			Log.LogInfo((object)"Mithrix Chimera Music loaded");
		}

		private static void ApplyVolumeToPCM16(byte[] data, float v)
		{
			if (Math.Abs(v - 1f) < 0.001f)
			{
				return;
			}
			if (v < 0f)
			{
				v = 0f;
			}
			if (data.Length < 44)
			{
				return;
			}
			int num = 0;
			int num2 = 0;
			int num3 = -1;
			int num4 = 0;
			int num5;
			for (int i = 12; i <= data.Length - 8; i += 8 + num5 + (num5 & 1))
			{
				string @string = Encoding.ASCII.GetString(data, i, 4);
				num5 = BitConverter.ToInt32(data, i + 4);
				if (@string == "fmt ")
				{
					num = BitConverter.ToInt16(data, i + 8);
					num2 = BitConverter.ToInt16(data, i + 22);
				}
				else if (@string == "data")
				{
					num3 = i + 8;
					num4 = num5;
					break;
				}
			}
			if (num != 1 || num2 != 16 || num3 < 0)
			{
				Log.LogWarning((object)$"Skipping volume scale (format={num}, bits={num2}); file plays at native volume");
				return;
			}
			int num6 = Math.Min(num3 + num4, data.Length - 1);
			for (int j = num3; j + 1 < num6; j += 2)
			{
				int num7 = (int)((float)BitConverter.ToInt16(data, j) * v);
				if (num7 > 32767)
				{
					num7 = 32767;
				}
				if (num7 < -32768)
				{
					num7 = -32768;
				}
				data[j] = (byte)((uint)num7 & 0xFFu);
				data[j + 1] = (byte)((uint)(num7 >> 8) & 0xFFu);
			}
			Log.LogInfo((object)$"Applied volume scale {v:F2}");
		}

		private static void StartSong()
		{
			if (player == null)
			{
				Log.LogWarning((object)"Phase entered before SoundPlayer ready");
				return;
			}
			if (muteGameMusic.Value)
			{
				PostWwiseEvent(pauseEventName.Value);
			}
			try
			{
				player.PlayLooping();
				Log.LogInfo((object)"Custom song started (SoundPlayer.PlayLooping)");
			}
			catch (Exception ex)
			{
				Log.LogError((object)("PlayLooping failed: " + ex.Message));
			}
		}

		private static void StopSong()
		{
			if (player != null)
			{
				try
				{
					player.Stop();
				}
				catch (Exception ex)
				{
					Log.LogWarning((object)("Stop failed: " + ex.Message));
				}
				if (muteGameMusic.Value)
				{
					PostWwiseEvent(resumeEventName.Value);
				}
				Log.LogInfo((object)"Custom song stopped");
			}
		}

		private static void PostWwiseEvent(string eventName)
		{
			//IL_00a6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b0: Expected O, but got Unknown
			try
			{
				if (!akLookupAttempted)
				{
					akLookupAttempted = true;
					Type type = null;
					Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
					foreach (Assembly assembly in assemblies)
					{
						try
						{
							type = assembly.GetTypes().FirstOrDefault((Type t) => t.Name == "AkSoundEngine");
						}
						catch
						{
							continue;
						}
						if (type != null)
						{
							break;
						}
					}
					if (type != null)
					{
						akPostEvent = type.GetMethod("PostEvent", new Type[2]
						{
							typeof(string),
							typeof(GameObject)
						});
						akTarget = new GameObject("MithrixChimeraMusicWwiseTarget");
						Object.DontDestroyOnLoad((Object)(object)akTarget);
					}
					else
					{
						Log.LogWarning((object)"AkSoundEngine type not found, game music won't be muted");
					}
				}
				if (akPostEvent != null && (Object)(object)akTarget != (Object)null)
				{
					akPostEvent.Invoke(null, new object[2] { eventName, akTarget });
					Log.LogInfo((object)("Wwise event posted: " + eventName));
				}
			}
			catch (Exception ex)
			{
				Log.LogWarning((object)("Wwise event post failed: " + ex.Message));
			}
		}
	}
}