Decompiled source of STRAFTAT Custom Music v1.0.1

plugins/STRAFTATCustomMusic.dll

Decompiled a week ago
using System;
using System.Collections;
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 System.Text;
using System.Text.RegularExpressions;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using ComputerysModdingUtilities;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using UnityEngine;
using UnityEngine.Networking;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: StraftatMod(true)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("STRAFTATCustomMusic")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("STRAFTATCustomMusic")]
[assembly: AssemblyTitle("STRAFTATCustomMusic")]
[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.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]
	[Microsoft.CodeAnalysis.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]
	[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;
		}
	}
}
[BepInPlugin("com.garnetsunset.straftat.custommusic", "STRAFTAT Custom Music", "1.0.1")]
public class CustomMusicPlugin : BaseUnityPlugin
{
	[HarmonyPatch]
	private static class AwakePatch
	{
		private static MethodBase TargetMethod()
		{
			return typeof(SetMenuMusicVolume).GetMethod("Awake", BindingFlags.Instance | BindingFlags.NonPublic);
		}

		private static void Prefix(SetMenuMusicVolume __instance)
		{
			if (PendingMusicFiles.Count != 0)
			{
				Logger.LogInfo((object)$"Extending arrays for {PendingMusicFiles.Count} custom tracks...");
				ExtendMusicArraysSync(__instance);
			}
		}

		private static void Postfix(SetMenuMusicVolume __instance)
		{
			if (PendingMusicFiles.Count > 0 && (Object)(object)__instance.audio != (Object)null)
			{
				__instance.audio.Stop();
			}
		}
	}

	[HarmonyPatch]
	private static class StartPatch
	{
		private static MethodBase TargetMethod()
		{
			return typeof(SetMenuMusicVolume).GetMethod("Start", BindingFlags.Instance | BindingFlags.NonPublic);
		}

		private static void Prefix(SetMenuMusicVolume __instance)
		{
			if (PendingMusicFiles.Count != 0)
			{
				Logger.LogInfo((object)$"Loading {PendingMusicFiles.Count} custom music files...");
				((MonoBehaviour)__instance).StartCoroutine(LoadCustomMusicCoroutine(__instance));
			}
		}
	}

	[HarmonyPatch]
	private static class UpdatePatch
	{
		private static MethodBase TargetMethod()
		{
			return typeof(SetMenuMusicVolume).GetMethod("Update", BindingFlags.Instance | BindingFlags.NonPublic);
		}

		private static void Prefix()
		{
		}
	}

	[CompilerGenerated]
	private sealed class <LoadAudioFileCoroutineToIndex>d__18 : IEnumerator<object>, IEnumerator, IDisposable
	{
		private int <>1__state;

		private object <>2__current;

		public CustomMusicInfo musicInfo;

		public SetMenuMusicVolume musicManager;

		public int targetIndex;

		private UnityWebRequest <www>5__2;

		object IEnumerator<object>.Current
		{
			[DebuggerHidden]
			get
			{
				return <>2__current;
			}
		}

		object IEnumerator.Current
		{
			[DebuggerHidden]
			get
			{
				return <>2__current;
			}
		}

		[DebuggerHidden]
		public <LoadAudioFileCoroutineToIndex>d__18(int <>1__state)
		{
			this.<>1__state = <>1__state;
		}

		[DebuggerHidden]
		void IDisposable.Dispose()
		{
			int num = <>1__state;
			if (num == -3 || num == 1)
			{
				try
				{
				}
				finally
				{
					<>m__Finally1();
				}
			}
			<www>5__2 = null;
			<>1__state = -2;
		}

		private bool MoveNext()
		{
			//IL_00fb: Unknown result type (might be due to invalid IL or missing references)
			//IL_0101: Invalid comparison between Unknown and I4
			//IL_00a0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ba: 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_00ac: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b2: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				switch (<>1__state)
				{
				default:
					return false;
				case 0:
				{
					<>1__state = -1;
					string filePath = musicInfo.FilePath;
					if (!File.Exists(filePath))
					{
						Logger.LogError((object)("File not found: " + filePath));
						return false;
					}
					string absoluteUri = new Uri(filePath).AbsoluteUri;
					<www>5__2 = UnityWebRequestMultimedia.GetAudioClip(absoluteUri, (AudioType)(Path.GetExtension(musicInfo.FilePath).ToLowerInvariant() switch
					{
						".mp3" => 13, 
						".ogg" => 14, 
						".wav" => 20, 
						_ => 13, 
					}));
					<>1__state = -3;
					<>2__current = <www>5__2.SendWebRequest();
					<>1__state = 1;
					return true;
				}
				case 1:
					<>1__state = -3;
					if ((int)<www>5__2.result == 1)
					{
						try
						{
							AudioClip content = DownloadHandlerAudioClip.GetContent(<www>5__2);
							if ((Object)(object)content != (Object)null && content.length > 0f)
							{
								((Object)content).name = musicInfo.FileName;
								musicManager.audioClips[targetIndex] = content;
								Logger.LogInfo((object)("Loaded: " + musicInfo.FileName + " - " + musicInfo.ArtistName));
							}
							else
							{
								Logger.LogError((object)("Failed to load " + musicInfo.FileName + ": AudioClip is null or zero length"));
							}
						}
						catch (Exception ex)
						{
							Logger.LogError((object)("Exception loading " + musicInfo.FileName + ": " + ex.Message));
						}
					}
					else
					{
						Logger.LogError((object)("Failed to load " + musicInfo.FilePath + ": " + <www>5__2.error));
					}
					<>m__Finally1();
					<www>5__2 = null;
					return false;
				}
			}
			catch
			{
				//try-fault
				((IDisposable)this).Dispose();
				throw;
			}
		}

		bool IEnumerator.MoveNext()
		{
			//ILSpy generated this explicit interface implementation from .override directive in MoveNext
			return this.MoveNext();
		}

		private void <>m__Finally1()
		{
			<>1__state = -1;
			if (<www>5__2 != null)
			{
				((IDisposable)<www>5__2).Dispose();
			}
		}

		[DebuggerHidden]
		void IEnumerator.Reset()
		{
			throw new NotSupportedException();
		}
	}

	[CompilerGenerated]
	private sealed class <LoadCustomMusicCoroutine>d__17 : IEnumerator<object>, IEnumerator, IDisposable
	{
		private int <>1__state;

		private object <>2__current;

		public SetMenuMusicVolume musicManager;

		private bool <useCustomOnlyMode>5__2;

		private int <startIndex>5__3;

		private int <successCount>5__4;

		private int <i>5__5;

		object IEnumerator<object>.Current
		{
			[DebuggerHidden]
			get
			{
				return <>2__current;
			}
		}

		object IEnumerator.Current
		{
			[DebuggerHidden]
			get
			{
				return <>2__current;
			}
		}

		[DebuggerHidden]
		public <LoadCustomMusicCoroutine>d__17(int <>1__state)
		{
			this.<>1__state = <>1__state;
		}

		[DebuggerHidden]
		void IDisposable.Dispose()
		{
			<>1__state = -2;
		}

		private bool MoveNext()
		{
			//IL_0157: Unknown result type (might be due to invalid IL or missing references)
			//IL_015c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0169: Unknown result type (might be due to invalid IL or missing references)
			switch (<>1__state)
			{
			default:
				return false;
			case 0:
				<>1__state = -1;
				<useCustomOnlyMode>5__2 = CustomOnlyMode?.Value ?? CustomOnlyModeValue;
				<startIndex>5__3 = ((!<useCustomOnlyMode>5__2) ? (musicManager.audioClips.Length - PendingMusicFiles.Count) : 0);
				<successCount>5__4 = 0;
				<i>5__5 = 0;
				break;
			case 1:
				<>1__state = -1;
				<successCount>5__4++;
				<i>5__5++;
				break;
			}
			if (<i>5__5 < PendingMusicFiles.Count)
			{
				CustomMusicInfo musicInfo = PendingMusicFiles[<i>5__5];
				int targetIndex = <startIndex>5__3 + <i>5__5;
				<>2__current = LoadAudioFileCoroutineToIndex(musicInfo, musicManager, targetIndex);
				<>1__state = 1;
				return true;
			}
			if (<successCount>5__4 > 0)
			{
				RecreateMusimcTracks(musicManager);
				musicManager.Shuffle();
				if (!musicManager.audio.isPlaying)
				{
					musicManager.currentTrackId = Mathf.Clamp(musicManager.currentTrackId, 0, musicManager.MusicTracks.Length - 1);
					MusicTrack val = musicManager.MusicTracks[musicManager.currentTrackId];
					musicManager.audio.clip = val.AudioClip;
					musicManager.audio.Play();
					RefreshMusicUI(musicManager);
				}
				string arg = (<useCustomOnlyMode>5__2 ? "custom-only" : "mixed");
				Logger.LogInfo((object)$"Loaded {<successCount>5__4}/{PendingMusicFiles.Count} custom tracks in {arg} mode");
			}
			else
			{
				Logger.LogWarning((object)"No custom music files loaded successfully");
			}
			return false;
		}

		bool IEnumerator.MoveNext()
		{
			//ILSpy generated this explicit interface implementation from .override directive in MoveNext
			return this.MoveNext();
		}

		[DebuggerHidden]
		void IEnumerator.Reset()
		{
			throw new NotSupportedException();
		}
	}

	[CompilerGenerated]
	private sealed class <MonitorForMusicManager>d__8 : IEnumerator<object>, IEnumerator, IDisposable
	{
		private int <>1__state;

		private object <>2__current;

		public CustomMusicPlugin <>4__this;

		object IEnumerator<object>.Current
		{
			[DebuggerHidden]
			get
			{
				return <>2__current;
			}
		}

		object IEnumerator.Current
		{
			[DebuggerHidden]
			get
			{
				return <>2__current;
			}
		}

		[DebuggerHidden]
		public <MonitorForMusicManager>d__8(int <>1__state)
		{
			this.<>1__state = <>1__state;
		}

		[DebuggerHidden]
		void IDisposable.Dispose()
		{
			<>1__state = -2;
		}

		private bool MoveNext()
		{
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			//IL_003d: Expected O, but got Unknown
			int num = <>1__state;
			CustomMusicPlugin customMusicPlugin = <>4__this;
			if (num != 0)
			{
				if (num != 1)
				{
					return false;
				}
				<>1__state = -1;
				SetMenuMusicVolume val = Object.FindObjectOfType<SetMenuMusicVolume>();
				if ((Object)(object)val != (Object)null)
				{
					Logger.LogInfo((object)"Found SetMenuMusicVolume!");
					if (PendingMusicFiles.Count > 0)
					{
						Logger.LogInfo((object)"Loading music directly...");
						((MonoBehaviour)customMusicPlugin).StartCoroutine(LoadCustomMusicCoroutine(val));
					}
					return false;
				}
			}
			else
			{
				<>1__state = -1;
				Logger.LogInfo((object)"Looking for music manager...");
			}
			<>2__current = (object)new WaitForSeconds(1f);
			<>1__state = 1;
			return true;
		}

		bool IEnumerator.MoveNext()
		{
			//ILSpy generated this explicit interface implementation from .override directive in MoveNext
			return this.MoveNext();
		}

		[DebuggerHidden]
		void IEnumerator.Reset()
		{
			throw new NotSupportedException();
		}
	}

	internal static ManualLogSource Logger = null;

	private static string CustomMusicFolder = null;

	private static readonly List<CustomMusicInfo> PendingMusicFiles = new List<CustomMusicInfo>();

	private static ConfigEntry<bool> CustomOnlyMode = null;

	private static bool CustomOnlyModeValue = false;

	private void Awake()
	{
		//IL_00ef: Unknown result type (might be due to invalid IL or missing references)
		//IL_00f4: Unknown result type (might be due to invalid IL or missing references)
		Logger = ((BaseUnityPlugin)this).Logger;
		CustomMusicFolder = Path.Combine(Paths.ConfigPath, "CustomMusic");
		if (!Directory.Exists(CustomMusicFolder))
		{
			Directory.CreateDirectory(CustomMusicFolder);
			CreateSampleFiles();
			Logger.LogInfo((object)("Created custom music folder at: " + CustomMusicFolder));
		}
		Logger.LogInfo((object)"Setting up config...");
		try
		{
			CustomOnlyMode = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "CustomOnlyMode", false, "Set to true to replace all original music with only custom tracks. Set to false to mix custom tracks with original soundtrack.");
			CustomOnlyModeValue = CustomOnlyMode.Value;
			Logger.LogInfo((object)$"Config loaded: CustomOnlyMode = {CustomOnlyModeValue}");
		}
		catch (Exception ex)
		{
			Logger.LogError((object)("Config failed: " + ex.Message));
			Logger.LogInfo((object)"Making config file manually...");
			CreateManualConfigFile();
		}
		LoadCustomMusicList();
		try
		{
			Harmony val = new Harmony("com.garnetsunset.straftat.custommusic");
			val.PatchAll();
			Logger.LogInfo((object)"Harmony patches applied.");
			List<MethodBase> list = val.GetPatchedMethods().ToList();
			Logger.LogInfo((object)$"Got {list.Count} patched methods.");
			if (list.Count == 0)
			{
				Logger.LogWarning((object)"No patches worked! Checking what went wrong...");
				InvestigateTargetMethods();
			}
		}
		catch (Exception ex2)
		{
			Logger.LogError((object)("Harmony failed: " + ex2.Message));
			Logger.LogError((object)("Stack trace: " + ex2.StackTrace));
		}
		Logger.LogInfo((object)"Custom Music Plugin loaded!");
		((MonoBehaviour)this).StartCoroutine(MonitorForMusicManager());
	}

	private void CreateManualConfigFile()
	{
		try
		{
			string text = Path.Combine(Paths.ConfigPath);
			if (!Directory.Exists(text))
			{
				Directory.CreateDirectory(text);
				Logger.LogInfo((object)("Made config directory: " + text));
			}
			string text2 = Path.Combine(text, "com.garnetsunset.straftat.custommusic.cfg");
			if (!File.Exists(text2))
			{
				string contents = "## Settings file was created by plugin STRAFTAT Custom Music v1.0.0\r\n## Plugin GUID: com.garnetsunset.straftat.custommusic\r\n\r\n[General]\r\n\r\n## Set to true to replace all original music with only custom tracks. Set to false to mix custom tracks with original soundtrack.\r\n# Setting type: Boolean\r\n# Default value: false\r\nCustomOnlyMode = false\r\n";
				File.WriteAllText(text2, contents);
				Logger.LogInfo((object)("Created config file at: " + text2));
				Logger.LogInfo((object)"Edit this file to change CustomOnlyMode setting.");
			}
			else
			{
				Logger.LogInfo((object)("Config file already exists: " + text2));
			}
		}
		catch (Exception ex)
		{
			Logger.LogError((object)("Couldn't create config file: " + ex.Message));
		}
	}

	private static void RefreshMusicUI(SetMenuMusicVolume musicManager)
	{
		//IL_000c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0011: Unknown result type (might be due to invalid IL or missing references)
		//IL_0037: Unknown result type (might be due to invalid IL or missing references)
		//IL_0047: Unknown result type (might be due to invalid IL or missing references)
		try
		{
			MusicTrack val = musicManager.MusicTracks[musicManager.currentTrackId];
			try
			{
				Type typeFromHandle = typeof(SetMenuMusicVolume);
				FieldInfo field = typeFromHandle.GetField("trackTextInPlayer", BindingFlags.Instance | BindingFlags.NonPublic);
				FieldInfo field2 = typeFromHandle.GetField("trackText", BindingFlags.Instance | BindingFlags.NonPublic);
				string text = ((Object)val.AudioClip).name + " - " + val.ArtistName;
				if (field != null)
				{
					object value = field.GetValue(musicManager);
					value?.GetType().GetProperty("text")?.SetValue(value, text);
				}
				if (field2 != null)
				{
					object value2 = field2.GetValue(musicManager);
					value2?.GetType().GetProperty("text")?.SetValue(value2, "music playing : " + text);
				}
			}
			catch (Exception ex)
			{
				Logger.LogWarning((object)("Couldn't update UI text: " + ex.Message));
			}
			try
			{
				FieldInfo field3 = typeof(SetMenuMusicVolume).GetField("pauseButton", BindingFlags.Instance | BindingFlags.NonPublic);
				FieldInfo field4 = typeof(SetMenuMusicVolume).GetField("playButton", BindingFlags.Instance | BindingFlags.NonPublic);
				if (field3 != null && field4 != null)
				{
					object? value3 = field3.GetValue(musicManager);
					GameObject val2 = (GameObject)((value3 is GameObject) ? value3 : null);
					object? value4 = field4.GetValue(musicManager);
					GameObject val3 = (GameObject)((value4 is GameObject) ? value4 : null);
					if ((Object)(object)val2 != (Object)null && (Object)(object)val3 != (Object)null)
					{
						val2.SetActive(musicManager.audio.isPlaying);
						val3.SetActive(!musicManager.audio.isPlaying);
					}
				}
			}
			catch (Exception ex2)
			{
				Logger.LogWarning((object)("Couldn't update buttons: " + ex2.Message));
			}
		}
		catch (Exception ex3)
		{
			Logger.LogError((object)("Error checking track: " + ex3.Message));
		}
	}

	[IteratorStateMachine(typeof(<MonitorForMusicManager>d__8))]
	private IEnumerator MonitorForMusicManager()
	{
		//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
		return new <MonitorForMusicManager>d__8(0)
		{
			<>4__this = this
		};
	}

	private void DiagnoseAvailableTypes()
	{
		try
		{
			Logger.LogInfo((object)"=== Looking for SetMenuMusicVolume class ===");
			Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
			foreach (Assembly assembly in assemblies)
			{
				try
				{
					Type[] array = (from t in assembly.GetTypes()
						where t.Name.Contains("Music") || t.Name.Contains("Volume") || t.Name.Contains("SetMenu")
						select t).ToArray();
					if (array.Length != 0)
					{
						Logger.LogInfo((object)("Assembly " + assembly.GetName().Name + " has music stuff:"));
						Type[] array2 = array;
						foreach (Type type in array2)
						{
							Logger.LogInfo((object)("  - " + type.FullName));
						}
					}
				}
				catch (Exception ex)
				{
					Logger.LogWarning((object)("Couldn't check assembly " + assembly.GetName().Name + ": " + ex.Message));
				}
			}
		}
		catch (Exception ex2)
		{
			Logger.LogError((object)("Error diagnosing: " + ex2.Message));
		}
	}

	private void InvestigateTargetMethods()
	{
		try
		{
			Logger.LogInfo((object)"=== Checking target methods ===");
			Type type = Type.GetType("SetMenuMusicVolume");
			if (type == null)
			{
				Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
				foreach (Assembly assembly in assemblies)
				{
					try
					{
						type = assembly.GetType("SetMenuMusicVolume");
						if (type != null)
						{
							Logger.LogInfo((object)("Found SetMenuMusicVolume in: " + assembly.GetName().Name));
							break;
						}
					}
					catch
					{
					}
				}
			}
			if (type != null)
			{
				Logger.LogInfo((object)("Found type: " + type.FullName));
				MethodInfo[] methods = type.GetMethods(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
				Logger.LogInfo((object)("Methods in " + type.Name + ":"));
				MethodInfo[] array = methods;
				foreach (MethodInfo methodInfo in array)
				{
					string text = string.Join(", ", from p in methodInfo.GetParameters()
						select p.ParameterType.Name + " " + p.Name);
					Logger.LogInfo((object)("  - " + methodInfo.ReturnType.Name + " " + methodInfo.Name + "(" + text + ")"));
				}
			}
			else
			{
				Logger.LogError((object)"Can't find SetMenuMusicVolume anywhere!");
			}
		}
		catch (Exception ex)
		{
			Logger.LogError((object)("Error checking methods: " + ex.Message));
		}
	}

	private void CreateSampleFiles()
	{
		string contents = "STRAFTAT Custom Music Folder\r\n\r\nAdd your MP3 files here and they'll be loaded into the game!\r\n\r\nSupported Formats: MP3, OGG, WAV\r\n\r\nMetadata Priority:\r\n1. ID3 Tags (recommended) - Use any music tagger like Mp3tag\r\n2. JSON files - Create \"SongName.json\" with: {\"artist\": \"Name\", \"title\": \"Song\"}\r\n3. Text files - Create \"SongName.txt\" with just the artist name\r\n4. Filename format - \"Song Title - Artist Name.mp3\"\r\n5. Folder structure - \"Artist Name/Song Title.mp3\"\r\n\r\nConfiguration:\r\nEdit the config file at BepInEx/config/com.garnetsunset.straftat.custommusic.cfg\r\nSet CustomOnlyMode = true to replace all original music with only your custom tracks\r\nSet CustomOnlyMode = false to mix your custom tracks with the original soundtrack\r\n\r\nExamples:\r\n  CustomMusic/\r\n  ├── Awesome Song - Cool Artist.mp3\r\n  ├── My Track.mp3\r\n  ├── My Track.json\r\n  └── Daft Punk/\r\n      └── One More Time.mp3\r\n\r\nThe mod will automatically detect new files when you restart the game.\r\n";
		File.WriteAllText(Path.Combine(CustomMusicFolder, "README.txt"), contents);
	}

	private static void LoadCustomMusicList()
	{
		PendingMusicFiles.Clear();
		if (!Directory.Exists(CustomMusicFolder))
		{
			return;
		}
		string[] array = new string[3] { "*.mp3", "*.ogg", "*.wav" }.SelectMany((string ext) => Directory.GetFiles(CustomMusicFolder, ext, SearchOption.AllDirectories)).ToArray();
		foreach (string text in array)
		{
			try
			{
				string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(text);
				string text2 = GetBestMetadata(text);
				string fileName = fileNameWithoutExtension;
				if (fileNameWithoutExtension.Contains(" - "))
				{
					string[] array2 = fileNameWithoutExtension.Split(new string[1] { " - " }, StringSplitOptions.RemoveEmptyEntries);
					if (array2.Length >= 2)
					{
						fileName = array2[0];
						string text3 = string.Join(" - ", array2.Skip(1));
						if (text2 == "Unknown Artist" || text2 == text3)
						{
							text2 = text3;
						}
					}
				}
				CustomMusicInfo customMusicInfo = new CustomMusicInfo
				{
					FilePath = text,
					FileName = fileName,
					ArtistName = text2
				};
				PendingMusicFiles.Add(customMusicInfo);
				Logger.LogInfo((object)("Found: " + customMusicInfo.FileName + " - " + customMusicInfo.ArtistName));
			}
			catch (Exception ex)
			{
				Logger.LogError((object)("Error processing " + text + ": " + ex.Message));
			}
		}
		Logger.LogInfo((object)$"Found {PendingMusicFiles.Count} custom music files");
	}

	private static void ExtendMusicArraysSync(SetMenuMusicVolume musicManager)
	{
		try
		{
			if (CustomOnlyMode?.Value ?? CustomOnlyModeValue)
			{
				Logger.LogInfo((object)$"Custom-only mode: Replacing {musicManager.audioClips.Length} original tracks with {PendingMusicFiles.Count} custom");
				List<AudioClip> list = new List<AudioClip>();
				List<string> list2 = new List<string>();
				foreach (CustomMusicInfo pendingMusicFile in PendingMusicFiles)
				{
					AudioClip item = AudioClip.Create(pendingMusicFile.FileName, 44100, 1, 44100, false);
					list.Add(item);
					list2.Add(pendingMusicFile.ArtistName);
				}
				musicManager.audioClips = list.ToArray();
				musicManager.trackNames = list2.ToArray();
				Logger.LogInfo((object)$"Custom-only mode: {musicManager.audioClips.Length} tracks");
				return;
			}
			Logger.LogInfo((object)$"Mixed mode: Extending {musicManager.audioClips.Length} original + {PendingMusicFiles.Count} custom");
			int num = musicManager.audioClips.Length;
			List<AudioClip> list3 = new List<AudioClip>(musicManager.audioClips);
			List<string> list4 = new List<string>(musicManager.trackNames);
			foreach (CustomMusicInfo pendingMusicFile2 in PendingMusicFiles)
			{
				AudioClip item2 = AudioClip.Create(pendingMusicFile2.FileName, 44100, 1, 44100, false);
				list3.Add(item2);
				list4.Add(pendingMusicFile2.ArtistName);
			}
			musicManager.audioClips = list3.ToArray();
			musicManager.trackNames = list4.ToArray();
			Logger.LogInfo((object)$"Mixed mode: {num} original + {PendingMusicFiles.Count} custom = {musicManager.audioClips.Length} total");
		}
		catch (Exception ex)
		{
			Logger.LogError((object)("Error extending arrays: " + ex.Message));
		}
	}

	[IteratorStateMachine(typeof(<LoadCustomMusicCoroutine>d__17))]
	private static IEnumerator LoadCustomMusicCoroutine(SetMenuMusicVolume musicManager)
	{
		//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
		return new <LoadCustomMusicCoroutine>d__17(0)
		{
			musicManager = musicManager
		};
	}

	[IteratorStateMachine(typeof(<LoadAudioFileCoroutineToIndex>d__18))]
	private static IEnumerator LoadAudioFileCoroutineToIndex(CustomMusicInfo musicInfo, SetMenuMusicVolume musicManager, int targetIndex)
	{
		//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
		return new <LoadAudioFileCoroutineToIndex>d__18(0)
		{
			musicInfo = musicInfo,
			musicManager = musicManager,
			targetIndex = targetIndex
		};
	}

	private static void RecreateMusimcTracks(SetMenuMusicVolume musicManager)
	{
		//IL_0069: Unknown result type (might be due to invalid IL or missing references)
		//IL_006e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0071: 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)
		try
		{
			Type typeFromHandle = typeof(MusicTrack);
			if (typeFromHandle.GetConstructors().Length == 0)
			{
				Logger.LogError((object)"No constructors found for MusicTrack");
				return;
			}
			MusicTrack[] array = (MusicTrack[])(object)new MusicTrack[musicManager.audioClips.Length];
			for (int i = 0; i < musicManager.audioClips.Length; i++)
			{
				try
				{
					MusicTrack val = (MusicTrack)Activator.CreateInstance(typeFromHandle, musicManager.audioClips[i], musicManager.trackNames[i], i);
					array[i] = val;
				}
				catch (Exception ex)
				{
					Logger.LogError((object)$"Error creating MusicTrack at {i}: {ex.Message}");
					ManualLogSource logger = Logger;
					AudioClip obj = musicManager.audioClips[i];
					logger.LogError((object)$"Params: AudioClip={((obj != null) ? ((Object)obj).name : null)}, TrackName={musicManager.trackNames[i]}, Index={i}");
					return;
				}
			}
			musicManager.MusicTracks = array;
		}
		catch (Exception ex2)
		{
			Logger.LogError((object)("Error recreating MusicTracks: " + ex2.Message));
		}
	}

	private static string GetBestMetadata(string filePath)
	{
		string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(filePath);
		string fileName = Path.GetFileName(Path.GetDirectoryName(filePath) ?? "");
		string text = ReadMP3Tags(filePath);
		if (!string.IsNullOrEmpty(text))
		{
			return text;
		}
		string path = Path.ChangeExtension(filePath, ".json");
		if (File.Exists(path))
		{
			string text2 = ParseJsonMetadata(File.ReadAllText(path));
			if (!string.IsNullOrEmpty(text2))
			{
				return text2;
			}
		}
		string path2 = Path.ChangeExtension(filePath, ".txt");
		if (File.Exists(path2))
		{
			string text3 = File.ReadAllText(path2).Trim();
			if (!string.IsNullOrEmpty(text3))
			{
				return text3;
			}
		}
		string[] array = fileNameWithoutExtension.Split(new string[1] { " - " }, StringSplitOptions.RemoveEmptyEntries);
		if (array.Length >= 2)
		{
			return string.Join(" - ", array.Skip(1));
		}
		if (fileName != "CustomMusic" && !string.IsNullOrEmpty(fileName))
		{
			return fileName;
		}
		return "Unknown Artist";
	}

	private static string? ReadMP3Tags(string filePath)
	{
		if (!filePath.EndsWith(".mp3", StringComparison.OrdinalIgnoreCase))
		{
			return null;
		}
		try
		{
			using FileStream stream = new FileStream(filePath, FileMode.Open, FileAccess.Read);
			string text = ReadID3v2Tags(stream);
			if (!string.IsNullOrEmpty(text))
			{
				return text;
			}
			string text2 = ReadID3v1Tags(stream);
			if (!string.IsNullOrEmpty(text2))
			{
				return text2;
			}
		}
		catch (Exception ex)
		{
			Logger.LogWarning((object)("Failed to read ID3 tags from " + Path.GetFileName(filePath) + ": " + ex.Message));
		}
		return null;
	}

	private static string? ReadID3v2Tags(FileStream stream)
	{
		stream.Seek(0L, SeekOrigin.Begin);
		byte[] array = new byte[10];
		if (stream.Read(array, 0, 10) != 10)
		{
			return null;
		}
		if (array[0] != 73 || array[1] != 68 || array[2] != 51)
		{
			return null;
		}
		int num = (array[6] << 21) | (array[7] << 14) | (array[8] << 7) | array[9];
		if (num <= 0 || num > stream.Length - 10)
		{
			return null;
		}
		byte[] array2 = new byte[num];
		if (stream.Read(array2, 0, num) != num)
		{
			return null;
		}
		string text = ExtractID3v2Frame(array2, "TPE1") ?? ExtractID3v2Frame(array2, "TPE2");
		if (!string.IsNullOrEmpty(text))
		{
			return text;
		}
		string text2 = ExtractID3v2Frame(array2, "TIT2");
		if (!string.IsNullOrEmpty(text2))
		{
			return text2;
		}
		return null;
	}

	private static string? ExtractID3v2Frame(byte[] data, string frameId)
	{
		byte[] bytes = Encoding.ASCII.GetBytes(frameId);
		for (int i = 0; i <= data.Length - 10; i++)
		{
			if (data[i] != bytes[0] || data[i + 1] != bytes[1] || data[i + 2] != bytes[2] || data[i + 3] != bytes[3])
			{
				continue;
			}
			int num = (data[i + 4] << 24) | (data[i + 5] << 16) | (data[i + 6] << 8) | data[i + 7];
			if (num > 0 && num < 1000000 && i + 10 + num <= data.Length)
			{
				byte b = data[i + 10];
				int sourceIndex = i + 11;
				int num2 = num - 1;
				if (num2 > 0)
				{
					byte[] array = new byte[num2];
					Array.Copy(data, sourceIndex, array, 0, num2);
					return (b switch
					{
						0 => Encoding.ASCII.GetString(array), 
						1 => Encoding.Unicode.GetString(array), 
						3 => Encoding.UTF8.GetString(array), 
						_ => Encoding.UTF8.GetString(array), 
					}).Trim('\0', ' ');
				}
			}
		}
		return null;
	}

	private static string? ReadID3v1Tags(FileStream stream)
	{
		if (stream.Length < 128)
		{
			return null;
		}
		stream.Seek(-128L, SeekOrigin.End);
		byte[] array = new byte[128];
		if (stream.Read(array, 0, 128) != 128)
		{
			return null;
		}
		if (array[0] != 84 || array[1] != 65 || array[2] != 71)
		{
			return null;
		}
		string text = Encoding.ASCII.GetString(array, 33, 30).Trim('\0', ' ');
		if (!string.IsNullOrEmpty(text))
		{
			return text;
		}
		string text2 = Encoding.ASCII.GetString(array, 3, 30).Trim('\0', ' ');
		if (!string.IsNullOrEmpty(text2))
		{
			return text2;
		}
		return null;
	}

	private static string? ParseJsonMetadata(string jsonContent)
	{
		try
		{
			Match match = Regex.Match(jsonContent, "\"artist\"\\s*:\\s*\"([^\"]+)\"");
			if (match.Success)
			{
				return match.Groups[1].Value;
			}
			Match match2 = Regex.Match(jsonContent, "\"title\"\\s*:\\s*\"([^\"]+)\"");
			if (match2.Success)
			{
				return match2.Groups[1].Value;
			}
		}
		catch (Exception ex)
		{
			Logger.LogWarning((object)("Error parsing JSON: " + ex.Message));
		}
		return null;
	}
}
public class CustomMusicInfo
{
	public string FilePath = "";

	public string FileName = "";

	public string ArtistName = "";
}