Decompiled source of SimpleMusicReplacer v1.1.0

plugins\SimpleMusicReplacer.dll

Decompiled a week ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using BepInEx;
using BepInEx.Configuration;
using HarmonyLib;
using UnityEngine;
using UnityEngine.Networking;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: AssemblyVersion("0.0.0.0")]
namespace SimpleMusicReplacer;

[BepInPlugin("sakelig.simplemusicreplacer", "SimpleMusicReplacer", "1.1.0")]
public class SimpleMusicReplacerPlugin : BaseUnityPlugin
{
	private class Playlist
	{
		public string Folder;

		public List<AudioClip> Clips = new List<AudioClip>();

		public NamedMusic Music;

		public int Index;
	}

	public const string PluginGuid = "sakelig.simplemusicreplacer";

	public const string PluginName = "SimpleMusicReplacer";

	public const string PluginVersion = "1.1.0";

	private const string CustomPrefix = "smr_";

	private static readonly string[] AudioExtensions = new string[3] { ".ogg", ".wav", ".mp3" };

	private static SimpleMusicReplacerPlugin _instance;

	private ConfigEntry<float> _volume;

	private ConfigEntry<bool> _shuffle;

	private ConfigEntry<bool> _oceanReplacesSailing;

	private string _musicDir;

	private int _pendingLoads;

	private readonly Dictionary<string, Playlist> _playlists = new Dictionary<string, Playlist>();

	private MusicMan _appliedMusicMan;

	private EnvMan _appliedEnvMan;

	private AudioClip _lastClip;

	private void Awake()
	{
		//IL_0036: Unknown result type (might be due to invalid IL or missing references)
		//IL_0040: Expected O, but got Unknown
		//IL_00bd: Unknown result type (might be due to invalid IL or missing references)
		_instance = this;
		_volume = ((BaseUnityPlugin)this).Config.Bind<float>("General", "Volume", 0.7f, new ConfigDescription("Volume of custom biome music (also scaled by the in-game music volume).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), new object[0]));
		_shuffle = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Shuffle", false, "Shuffle playlists instead of playing them in alphabetical order.");
		_oceanReplacesSailing = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "OceanReplacesSailingMusic", true, "While on the Ocean biome, play the 'ocean' music instead of the vanilla sailing music.");
		_musicDir = Path.Combine(Paths.ConfigPath, "SimpleMusicReplacer");
		Directory.CreateDirectory(Path.Combine(_musicDir, "ocean"));
		LoadAllMusic();
		new Harmony("sakelig.simplemusicreplacer").PatchAll(typeof(SimpleMusicReplacerPlugin));
	}

	private void LoadAllMusic()
	{
		string[] array = new string[2]
		{
			_musicDir,
			Path.Combine(Path.GetDirectoryName(((BaseUnityPlugin)this).Info.Location), "music")
		};
		string[] array2 = array;
		foreach (string path in array2)
		{
			if (!Directory.Exists(path))
			{
				continue;
			}
			string[] directories = Directory.GetDirectories(path);
			foreach (string path2 in directories)
			{
				string[] array3 = (from f in Directory.GetFiles(path2)
					where AudioExtensions.Contains(Path.GetExtension(f).ToLowerInvariant())
					select f).OrderBy<string, string>((string f) => f, StringComparer.OrdinalIgnoreCase).ToArray();
				if (array3.Length != 0)
				{
					string text = Path.GetFileName(path2).ToLowerInvariant();
					if (!_playlists.TryGetValue(text, out var value))
					{
						Playlist playlist = new Playlist();
						playlist.Folder = text;
						value = playlist;
						_playlists[text] = value;
					}
					string[] array4 = array3;
					foreach (string path3 in array4)
					{
						((MonoBehaviour)this).StartCoroutine(LoadClip(path3, value));
					}
				}
			}
		}
		if (_playlists.Count == 0)
		{
			((BaseUnityPlugin)this).Logger.LogWarning((object)("No music found. Put .ogg/.wav/.mp3 files in " + Path.Combine(_musicDir, "ocean")));
		}
	}

	private IEnumerator LoadClip(string path, Playlist playlist)
	{
		_pendingLoads++;
		string url = "file:///" + path.Replace('\\', '/');
		UnityWebRequest req = UnityWebRequestMultimedia.GetAudioClip(url, GetAudioType(path));
		try
		{
			((DownloadHandlerAudioClip)req.downloadHandler).compressed = true;
			yield return req.SendWebRequest();
			if ((int)req.result == 1)
			{
				AudioClip content = DownloadHandlerAudioClip.GetContent(req);
				((Object)content).name = Path.GetFileNameWithoutExtension(path);
				playlist.Clips.Add(content);
				((BaseUnityPlugin)this).Logger.LogInfo((object)("Loaded " + playlist.Folder + "/" + Path.GetFileName(path)));
			}
			else
			{
				((BaseUnityPlugin)this).Logger.LogError((object)("Failed to load " + path + ": " + req.error));
			}
		}
		finally
		{
			((IDisposable)req)?.Dispose();
		}
		_pendingLoads--;
		if (_pendingLoads != 0)
		{
			yield break;
		}
		foreach (Playlist value in _playlists.Values)
		{
			value.Clips.Sort((AudioClip a, AudioClip b) => string.Compare(((Object)a).name, ((Object)b).name, StringComparison.OrdinalIgnoreCase));
			if (_shuffle.Value)
			{
				Shuffle(value.Clips);
			}
		}
	}

	private void Update()
	{
		if (_pendingLoads <= 0 && _playlists.Count != 0)
		{
			if ((Object)(object)MusicMan.instance != (Object)null && (Object)(object)MusicMan.instance != (Object)(object)_appliedMusicMan)
			{
				_appliedMusicMan = MusicMan.instance;
				_appliedEnvMan = null;
				ApplyToMusicMan(_appliedMusicMan);
			}
			if ((Object)(object)EnvMan.instance != (Object)null && (Object)(object)EnvMan.instance != (Object)(object)_appliedEnvMan && (Object)(object)_appliedMusicMan != (Object)null)
			{
				_appliedEnvMan = EnvMan.instance;
				ApplyToEnvMan(_appliedEnvMan);
			}
			if ((Object)(object)_appliedMusicMan != (Object)null)
			{
				AdvancePlaylists();
			}
		}
	}

	private void ApplyToMusicMan(MusicMan musicMan)
	{
		//IL_0091: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c3: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c9: Expected O, but got Unknown
		WriteAvailableMusic(musicMan);
		Dictionary<int, NamedMusic> value = Traverse.Create((object)musicMan).Field("m_musicHashes").GetValue<Dictionary<int, NamedMusic>>();
		foreach (Playlist p in _playlists.Values)
		{
			if (p.Clips.Count == 0)
			{
				continue;
			}
			NamedMusic val = ((IEnumerable<NamedMusic>)musicMan.m_music).FirstOrDefault((Func<NamedMusic, bool>)((NamedMusic m) => string.Equals(m.m_name, p.Folder, StringComparison.OrdinalIgnoreCase)));
			if (val == null)
			{
				if ((int)ParseBiome(p.Folder) == 0)
				{
					((BaseUnityPlugin)this).Logger.LogWarning((object)("'" + p.Folder + "' is not a biome or music name. See available_music.txt"));
					continue;
				}
				NamedMusic val2 = new NamedMusic();
				val2.m_name = "smr_" + p.Folder;
				val2.m_volume = _volume.Value;
				val2.m_fadeInTime = 3f;
				val2.m_alwaysFadeout = true;
				val2.m_loop = true;
				val2.m_ambientMusic = true;
				val = val2;
				musicMan.m_music.Add(val);
				value[StringExtensionMethods.GetStableHashCode(val.m_name)] = val;
			}
			val.m_enabled = true;
			val.m_resume = p.Clips.Count == 1;
			val.m_clips = (AudioClip[])(object)new AudioClip[1] { p.Clips[0] };
			p.Music = val;
			value[StringExtensionMethods.GetStableHashCode(val.m_name)] = val;
			((BaseUnityPlugin)this).Logger.LogInfo((object)("Replaced music '" + val.m_name + "' with " + p.Clips.Count + " track(s)"));
		}
	}

	private void ApplyToEnvMan(EnvMan envMan)
	{
		//IL_002b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0030: Unknown result type (might be due to invalid IL or missing references)
		//IL_0036: Unknown result type (might be due to invalid IL or missing references)
		foreach (Playlist value in _playlists.Values)
		{
			Biome biome = ParseBiome(value.Folder);
			if ((int)biome == 0 || value.Music == null || !value.Music.m_name.StartsWith("smr_"))
			{
				continue;
			}
			foreach (BiomeEnvSetup item in envMan.m_biomes.Where((BiomeEnvSetup b) => b.m_biome == biome))
			{
				item.m_musicMorning = value.Music.m_name;
				item.m_musicDay = value.Music.m_name;
				item.m_musicEvening = value.Music.m_name;
				item.m_musicNight = value.Music.m_name;
			}
		}
	}

	private void AdvancePlaylists()
	{
		AudioSource value = Traverse.Create((object)_appliedMusicMan).Field("m_musicSource").GetValue<AudioSource>();
		if ((Object)(object)value == (Object)null || (Object)(object)value.clip == (Object)(object)_lastClip)
		{
			return;
		}
		_lastClip = value.clip;
		foreach (Playlist value2 in _playlists.Values)
		{
			if (value2.Music != null && value2.Clips.Count >= 2 && value2.Clips.Contains(value.clip))
			{
				value.loop = false;
				value2.Index = (value2.Clips.IndexOf(value.clip) + 1) % value2.Clips.Count;
				if (value2.Index == 0 && _shuffle.Value)
				{
					Shuffle(value2.Clips);
				}
				value2.Music.m_clips = (AudioClip[])(object)new AudioClip[1] { value2.Clips[value2.Index] };
			}
		}
	}

	[HarmonyPatch(typeof(MusicMan), "GetSailingMusic")]
	[HarmonyPostfix]
	private static void GetSailingMusicPostfix(ref string __result)
	{
		//IL_0023: Unknown result type (might be due to invalid IL or missing references)
		//IL_002d: Invalid comparison between Unknown and I4
		if (_instance._oceanReplacesSailing.Value && !((Object)(object)Player.m_localPlayer == (Object)null) && (int)Player.m_localPlayer.GetCurrentBiome() == 256 && _instance._playlists.TryGetValue("ocean", out var value) && value.Music != null)
		{
			__result = value.Music.m_name;
		}
	}

	private void WriteAvailableMusic(MusicMan musicMan)
	{
		List<string> list = new List<string>();
		list.Add("Make a folder in BepInEx/config/SimpleMusicReplacer/ named after a biome or a music name");
		list.Add("and put .ogg/.wav/.mp3 files in it. Several files = playlist.");
		list.Add("");
		list.Add("Biomes (custom music plays everywhere in that biome):");
		List<string> list2 = list;
		list2.AddRange(from n in Enum.GetNames(typeof(Biome))
			where n != "None" && n != "All" && n != "Land"
			select "  " + n.ToLowerInvariant());
		list2.Add("");
		list2.Add("Valheim music names (replaces that music):");
		list2.AddRange(from n in (from m in musicMan.m_music
				where !m.m_name.StartsWith("smr_")
				select "  " + m.m_name).Distinct()
			orderby n
			select n);
		File.WriteAllLines(Path.Combine(_musicDir, "available_music.txt"), list2.ToArray());
	}

	private static Biome ParseBiome(string name)
	{
		//IL_001d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0022: Unknown result type (might be due to invalid IL or missing references)
		//IL_0023: Unknown result type (might be due to invalid IL or missing references)
		//IL_0029: Invalid comparison between Unknown and I4
		//IL_002b: Unknown result type (might be due to invalid IL or missing references)
		//IL_003f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0040: Unknown result type (might be due to invalid IL or missing references)
		//IL_0060: Unknown result type (might be due to invalid IL or missing references)
		foreach (Biome value in Enum.GetValues(typeof(Biome)))
		{
			if ((int)value != 895 && string.Equals(((object)value).ToString(), name, StringComparison.OrdinalIgnoreCase))
			{
				return value;
			}
		}
		return (Biome)0;
	}

	private static void Shuffle(List<AudioClip> clips)
	{
		for (int num = clips.Count - 1; num > 0; num--)
		{
			int index = Random.Range(0, num + 1);
			AudioClip value = clips[num];
			clips[num] = clips[index];
			clips[index] = value;
		}
	}

	private static AudioType GetAudioType(string path)
	{
		return (AudioType)(Path.GetExtension(path).ToLowerInvariant() switch
		{
			".wav" => 20, 
			".mp3" => 13, 
			_ => 14, 
		});
	}
}