Decompiled source of VoidSpeaker v2.2.0
Mods/VoidSpeaker.dll
Decompiled a month agousing System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using AudioImportLib; using BoneLib; using BoneLib.BoneMenu; using BoneLib.Notifications; using FieldInjector; using Il2CppInterop.Runtime.InteropTypes.Arrays; using Il2CppUltEvents; using MelonLoader; using MelonLoader.Preferences; using MelonLoader.Utils; using Microsoft.CodeAnalysis; using TagLib; using UnityEngine; using WeatherElectric.VoidSpeaker; using WeatherElectric.VoidSpeaker.BoneMenu; using WeatherElectric.VoidSpeaker.Melon; using WeatherElectric.VoidSpeaker.Music; using WeatherElectric.VoidSpeaker.Music.Behaviours; using WeatherElectric.VoidSpeaker.Music.Files; using WeatherElectric.VoidSpeaker.Music.Helpers; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: AssemblyTitle("A music player for BONELAB")] [assembly: AssemblyDescription("A music player for BONELAB")] [assembly: AssemblyCompany("Weather Electric")] [assembly: AssemblyProduct("VoidSpeaker")] [assembly: AssemblyCopyright("Developed by FragileDeviations")] [assembly: AssemblyTrademark("Weather Electric")] [assembly: AssemblyFileVersion("2.2.0")] [assembly: MelonInfo(typeof(Main), "VoidSpeaker", "2.2.0", "FragileDeviations", "https://thunderstore.io/c/bonelab/p/SoulWithMae/VoidSpeaker/")] [assembly: MelonColor(255, 0, 139, 139)] [assembly: MelonGame("Stress Level Zero", "BONELAB")] [assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = ".NET 6.0")] [assembly: AssemblyVersion("2.2.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 WeatherElectric.VoidSpeaker { public class Main : MelonMod { internal const string Name = "VoidSpeaker"; internal const string Description = "A music player for BONELAB"; internal const string Author = "FragileDeviations"; internal const string Company = "Weather Electric"; internal const string Version = "2.2.0"; internal const string DownloadLink = "https://thunderstore.io/c/bonelab/p/SoulWithMae/VoidSpeaker/"; private static bool _hasRanSetup; internal static Assembly ModAsm => Assembly.GetExecutingAssembly(); public override void OnInitializeMelon() { ModConsole.Setup(((MelonBase)this).LoggerInstance); Preferences.Setup(); WeatherElectric.VoidSpeaker.BoneMenu.BoneMenu.Setup(); UserData.Setup(); MusicLoader.Load(); SerialisationHandler.Inject<MetadataListener>(0); Hooking.OnLevelLoaded += OnLevelLoad; } private static void OnLevelLoad(LevelInfo levelInfo) { //IL_0013: Unknown result type (might be due to invalid IL or missing references) if (!_hasRanSetup) { _hasRanSetup = true; new GameObject("MusicPlayer").AddComponent<MusicPlayer>(); if (Preferences.UseTagLib.Value) { MusicList.CacheValues(); } } } } } namespace WeatherElectric.VoidSpeaker.Music { internal static class MusicList { public static readonly List<MusicFile> Music = new List<MusicFile>(); public static void CacheValues() { foreach (MusicFile item in Music) { item.CacheValues(); } } } internal static class MusicLoader { public static void Load() { if (Directory.GetFiles(UserData.ModPath).Length == 0) { ModConsole.Error("No music files found in the mod folder!"); return; } string[] files = Directory.GetFiles(UserData.ModPath); foreach (string text in files) { string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(text); AudioClip val = API.LoadAudioClip(text, true); if ((Object)(object)val == (Object)null) { ModConsole.Error("Failed to load audio clip from file: " + text); continue; } MusicFile item = new MusicFile(text, fileNameWithoutExtension, val); MusicList.Music.Add(item); ModConsole.Msg("Loaded audio clip from file: " + text, 1); } } public static void LoadNewFiles() { string[] files = Directory.GetFiles(UserData.ModPath); List<MusicFile> list = new List<MusicFile>(); foreach (MusicFile item in MusicList.Music) { string[] array = files; foreach (string text in array) { if (!(text == item.Path)) { AudioClip audioClip = API.LoadAudioClip(text, true); MusicFile musicFile = new MusicFile(text, Path.GetFileNameWithoutExtension(text), audioClip); if (Preferences.UseTagLib.Value) { musicFile.CacheValues(); } MusicPlayer.Instance.Stop(); list.Add(musicFile); } } } if (list.Count == 0) { return; } foreach (MusicFile item2 in list) { MusicList.Music.Add(item2); ModConsole.Msg("Loaded audio clip from file: " + item2.Path, 1); } } public static void RemoveMissingFiles() { List<MusicFile> list = new List<MusicFile>(); foreach (MusicFile item in MusicList.Music) { if (!File.Exists(item.Path)) { list.Add(item); item.Dispose(); } } if (list.Count == 0) { return; } foreach (MusicFile item2 in list) { MusicList.Music.Remove(item2); } } } } namespace WeatherElectric.VoidSpeaker.Music.Helpers { internal static class ExtensionMethods { public static void Shuffle<T>(this List<T> list) { Random random = new Random(); int num = list.Count; while (num > 1) { num--; int num2 = random.Next(num + 1); int index = num2; int index2 = num; T value = list[num]; T value2 = list[num2]; list[index] = value; list[index2] = value2; } } public static void Setup(this AudioSource audioSource) { audioSource.spatialBlend = 0f; audioSource.playOnAwake = false; audioSource.loop = false; audioSource.volume = Preferences.Volume.Value; } public static void Send(this Notification notif) { Notifier.Send(notif); } public static Texture2D ProperResize(this Texture2D texture2D, int width, int height) { //IL_0004: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Expected O, but got Unknown //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_001e: 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) //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Expected O, but got Unknown RenderTexture val2 = (RenderTexture.active = new RenderTexture(width, height, 24)); Graphics.Blit((Texture)(object)texture2D, val2); Texture2D val3 = new Texture2D(width, height); val3.ReadPixels(new Rect(0f, 0f, (float)width, (float)height), 0, 0); val3.Apply(); Object.Destroy((Object)(object)val2); return val3; } } internal static class TagLibWrapper { public enum Tag { Title, Artist, Album, AlbumArtist, Genre, Year, Track, Disc, Composer, Conductor } private static string _tag; public static string GetTag(string filepath, Tag tag) { File val = File.Create(filepath); _tag = tag switch { Tag.Title => val.Tag.Title, Tag.Artist => val.Tag.FirstPerformer, Tag.Album => val.Tag.Album, Tag.AlbumArtist => val.Tag.FirstAlbumArtist, Tag.Genre => val.Tag.FirstGenre, Tag.Year => val.Tag.Year.ToString(), Tag.Track => val.Tag.Track.ToString(), Tag.Disc => val.Tag.Disc.ToString(), Tag.Composer => val.Tag.FirstComposer, Tag.Conductor => val.Tag.Conductor, _ => null, }; if (_tag != null) { return _tag; } ModConsole.Error(filepath + "'s " + tag.ToString() + " field is null!"); return null; } public static Texture2D GetCover(string filepath) { //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_0047: Expected O, but got Unknown //IL_0049: Expected O, but got Unknown IPicture val = File.Create(filepath).Tag.Pictures[0]; if (val == null) { ModConsole.Error(filepath + "'s album art field is null!"); return null; } Texture2D val2 = new Texture2D(2, 2); ImageConversion.LoadImage(val2, Il2CppStructArray<byte>.op_Implicit(val.Data.Data), false); return val2; } } } namespace WeatherElectric.VoidSpeaker.Music.Files { internal class MusicFile { public readonly string Path; public readonly string Name; public readonly AudioClip AudioClip; public Texture2D CachedArt; public string CachedTitle; public string CachedArtist; public MusicFile(string path, string name, AudioClip audioClip) { Path = path; Name = name; AudioClip = audioClip; base..ctor(); } public void CacheValues() { CachedTitle = TagLibWrapper.GetTag(Path, TagLibWrapper.Tag.Title); CachedArtist = TagLibWrapper.GetTag(Path, TagLibWrapper.Tag.Artist); Texture2D cover = TagLibWrapper.GetCover(Path); if (!((Object)(object)cover == (Object)null)) { Texture2D cachedArt = cover.ProperResize(336, 336); CachedArt = cachedArt; Object.Destroy((Object)(object)cover); } } public void Dispose() { Object.Destroy((Object)(object)AudioClip); Object.Destroy((Object)(object)CachedArt); CachedArt = null; CachedTitle = null; } } } namespace WeatherElectric.VoidSpeaker.Music.Behaviours { public class MetadataListener : MonoBehaviour { internal static readonly List<MetadataListener> Listeners = new List<MetadataListener>(); public RenderTexture renderTexture; public UltEvent<Texture2D> RecieveArt; public UltEvent<string> RecieveTitle; public UltEvent<string> RecieveArtist; public UltEvent<string> RecieveFilename; private void Awake() { Listeners.Add(this); } private void UpdateRenderTexture(Texture2D art) { if (((Behaviour)this).enabled) { Graphics.Blit((Texture)(object)art, renderTexture); } } public void SetArtOfMaterial(Material material, Texture2D art) { if (((Behaviour)this).enabled) { material.mainTexture = (Texture)(object)art; } } internal void InvokeTitleChanged(string title) { if (((Behaviour)this).enabled) { RecieveTitle.Invoke(title); } } internal void InvokeArtistChanged(string artist) { if (((Behaviour)this).enabled) { RecieveArtist.Invoke(artist); } } internal void InvokeArtChanged(Texture2D art) { if (((Behaviour)this).enabled) { UpdateRenderTexture(art); RecieveArt.Invoke(art); } } internal void InvokeFilenameChanged(string filename) { if (((Behaviour)this).enabled) { RecieveFilename.Invoke(filename); } } private void OnDestroy() { Listeners.Remove(this); } public MetadataListener(IntPtr ptr) : base(ptr) { } } [RegisterTypeInIl2Cpp] internal class MusicPlayer : MonoBehaviour { private AudioSource _audioSource; private int _currentMusicIndex; private bool _paused; private bool _playingAtAll; public static MusicPlayer Instance { get; private set; } private void Awake() { Instance = this; Object.DontDestroyOnLoad((Object)(object)((Component)this).gameObject); } private void Start() { _audioSource = ((Component)this).gameObject.AddComponent<AudioSource>(); _audioSource.Setup(); } private void Update() { if (!_paused && _playingAtAll && !_audioSource.isPlaying) { _currentMusicIndex++; if (_currentMusicIndex >= MusicList.Music.Count) { _currentMusicIndex = 0; } Play(); } } public void SetVolume(float volume) { _audioSource.volume = volume; } public void Play() { if (!_playingAtAll) { _playingAtAll = true; MusicFile musicFile = MusicList.Music[_currentMusicIndex]; _audioSource.clip = musicFile.AudioClip; _audioSource.Play(); if (Preferences.SendNotifications.Value) { SendNotification(musicFile); } UpdateListeners(musicFile); } } private static void UpdateListeners(MusicFile musicFile) { if (MetadataListener.Listeners.Count == 0) { return; } if (Preferences.UseTagLib.Value) { foreach (MetadataListener listener in MetadataListener.Listeners) { try { listener.InvokeTitleChanged(musicFile.CachedTitle); } catch (Exception value) { ModConsole.Error($"Failed to invoke title event for MetadataListener on GameObject {((Object)((Component)listener).gameObject).name}! Exception: {value}"); } try { listener.InvokeArtistChanged(musicFile.CachedArtist); } catch (Exception value2) { ModConsole.Error($"Failed to invoke title event for MetadataListener on GameObject {((Object)((Component)listener).gameObject).name}! Exception: {value2}"); } try { listener.InvokeArtChanged(musicFile.CachedArt); } catch (Exception value3) { ModConsole.Error($"Failed to invoke art event for MetadataListener on GameObject {((Object)((Component)listener).gameObject).name}! Exception: {value3}"); } } return; } foreach (MetadataListener listener2 in MetadataListener.Listeners) { listener2.InvokeFilenameChanged(musicFile.Name); } } private static void SendNotification(MusicFile musicFile) { //IL_0175: Unknown result type (might be due to invalid IL or missing references) //IL_017a: Unknown result type (might be due to invalid IL or missing references) //IL_0180: Unknown result type (might be due to invalid IL or missing references) //IL_0185: Unknown result type (might be due to invalid IL or missing references) //IL_018a: Unknown result type (might be due to invalid IL or missing references) //IL_019a: Unknown result type (might be due to invalid IL or missing references) //IL_019f: Unknown result type (might be due to invalid IL or missing references) //IL_01a4: 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) //IL_01ab: Unknown result type (might be due to invalid IL or missing references) //IL_01bb: Unknown result type (might be due to invalid IL or missing references) //IL_01c7: Expected O, but got Unknown //IL_00bc: Unknown result type (might be due to invalid IL or missing references) //IL_00c1: Unknown result type (might be due to invalid IL or missing references) //IL_00c7: Unknown result type (might be due to invalid IL or missing references) //IL_00cc: Unknown result type (might be due to invalid IL or missing references) //IL_00d1: Unknown result type (might be due to invalid IL or missing references) //IL_00e1: Unknown result type (might be due to invalid IL or missing references) //IL_00e6: Unknown result type (might be due to invalid IL or missing references) //IL_00eb: Unknown result type (might be due to invalid IL or missing references) //IL_00ed: Unknown result type (might be due to invalid IL or missing references) //IL_00f2: Unknown result type (might be due to invalid IL or missing references) //IL_0102: Unknown result type (might be due to invalid IL or missing references) //IL_010e: Expected O, but got Unknown //IL_010f: Unknown result type (might be due to invalid IL or missing references) //IL_0114: Unknown result type (might be due to invalid IL or missing references) //IL_011a: Unknown result type (might be due to invalid IL or missing references) //IL_011f: Unknown result type (might be due to invalid IL or missing references) //IL_0124: Unknown result type (might be due to invalid IL or missing references) //IL_013b: Unknown result type (might be due to invalid IL or missing references) //IL_0140: Unknown result type (might be due to invalid IL or missing references) //IL_0145: Unknown result type (might be due to invalid IL or missing references) //IL_0151: Unknown result type (might be due to invalid IL or missing references) //IL_0153: Unknown result type (might be due to invalid IL or missing references) //IL_0158: Unknown result type (might be due to invalid IL or missing references) //IL_0168: Unknown result type (might be due to invalid IL or missing references) //IL_0174: Expected O, but got Unknown if (Preferences.UseTagLib.Value) { bool num = musicFile.CachedTitle != null; bool flag = musicFile.CachedTitle != null; bool flag2 = Object.op_Implicit((Object)(object)musicFile.CachedArt); if (!num) { string tag = TagLibWrapper.GetTag(musicFile.Path, TagLibWrapper.Tag.Title); if (tag == null) { return; } musicFile.CachedTitle = tag; } if (!flag) { string tag2 = TagLibWrapper.GetTag(musicFile.Path, TagLibWrapper.Tag.Artist); if (tag2 == null) { return; } musicFile.CachedArtist = tag2; } if (!flag2) { Texture2D cover = TagLibWrapper.GetCover(musicFile.Path); if (!Object.op_Implicit((Object)(object)cover)) { return; } Texture2D cachedArt = cover.ProperResize(336, 336); musicFile.CachedArt = cachedArt; Object.Destroy((Object)(object)cover); } if (musicFile.CachedTitle == null || musicFile.CachedArtist == null || !Object.op_Implicit((Object)(object)musicFile.CachedArt)) { ExtensionMethods.Send(new Notification { Title = NotificationText.op_Implicit("Now Playing:"), Message = NotificationText.op_Implicit(musicFile.Name ?? ""), Type = (NotificationType)0, PopupLength = Preferences.NotificationDuration.Value, ShowTitleOnPopup = true }); } else { ExtensionMethods.Send(new Notification { Title = NotificationText.op_Implicit("Now Playing:"), Message = NotificationText.op_Implicit(musicFile.CachedTitle + " by " + musicFile.CachedArtist), CustomIcon = musicFile.CachedArt, Type = (NotificationType)4, PopupLength = Preferences.NotificationDuration.Value, ShowTitleOnPopup = true }); } } else { ExtensionMethods.Send(new Notification { Title = NotificationText.op_Implicit("Now Playing:"), Message = NotificationText.op_Implicit(musicFile.Name ?? ""), Type = (NotificationType)0, PopupLength = Preferences.NotificationDuration.Value, ShowTitleOnPopup = true }); } } public bool PauseUnpause() { if (!_playingAtAll) { return false; } _paused = !_paused; if (_paused) { _audioSource.Pause(); } else { _audioSource.UnPause(); } return _paused; } public void Pause() { if (_playingAtAll) { _paused = true; _audioSource.Pause(); } } public void Unpause() { if (_playingAtAll) { _paused = false; _audioSource.UnPause(); } } public void Shuffle() { if (_playingAtAll) { Stop(); MusicList.Music.Shuffle(); Play(); } else { MusicList.Music.Shuffle(); } } public void Skip() { if (_playingAtAll) { _currentMusicIndex++; if (_currentMusicIndex >= MusicList.Music.Count) { _currentMusicIndex = 0; } Stop(resetIndex: false); Play(); } } public void Stop(bool resetIndex = true) { if (_playingAtAll) { _playingAtAll = false; _audioSource.Stop(); if (resetIndex) { _currentMusicIndex = 0; } } } private void OnDestroy() { Instance = null; } public MusicPlayer(IntPtr ptr) : base(ptr) { } } } namespace WeatherElectric.VoidSpeaker.Melon { internal static class ModConsole { private static Instance _logger; public static void Setup(Instance loggerInstance) { _logger = loggerInstance; } public static void Msg(object obj, int loggingMode = 0) { string text = ((loggingMode == 1) ? $"[DEBUG] {obj}" : obj.ToString()); ConsoleColor consoleColor = ((loggingMode == 1) ? ConsoleColor.Yellow : ConsoleColor.Gray); if (Preferences.LoggingMode.Value >= loggingMode) { _logger.Msg(consoleColor, text); } } public static void Msg(string txt, int loggingMode = 0) { string text = ((loggingMode == 1) ? ("[DEBUG] " + txt) : txt); ConsoleColor consoleColor = ((loggingMode == 1) ? ConsoleColor.Yellow : ConsoleColor.Gray); if (Preferences.LoggingMode.Value >= loggingMode) { _logger.Msg(consoleColor, text); } } public static void Msg(ConsoleColor txtcolor, object obj, int loggingMode = 0) { string text = ((loggingMode == 1) ? $"[DEBUG] {obj}" : obj.ToString()); if (Preferences.LoggingMode.Value >= loggingMode) { _logger.Msg(txtcolor, text); } } public static void Msg(ConsoleColor txtcolor, string txt, int loggingMode = 0) { string text = ((loggingMode == 1) ? ("[DEBUG] " + txt) : txt); if (Preferences.LoggingMode.Value >= loggingMode) { _logger.Msg(txtcolor, text); } } public static void Msg(string txt, int loggingMode = 0, params object[] args) { string text = ((loggingMode == 1) ? ("[DEBUG] " + txt) : txt); ConsoleColor consoleColor = ((loggingMode == 1) ? ConsoleColor.Yellow : ConsoleColor.Gray); if (Preferences.LoggingMode.Value >= loggingMode) { _logger.Msg(consoleColor, text, args); } } public static void Msg(ConsoleColor txtcolor, string txt, int loggingMode = 0, params object[] args) { string text = ((loggingMode == 1) ? ("[DEBUG] " + txt) : txt); if (Preferences.LoggingMode.Value >= loggingMode) { _logger.Msg(txtcolor, text, args); } } public static void Error(object obj, int loggingMode = 0) { string text = ((loggingMode == 1) ? $"[DEBUG] {obj}" : obj.ToString()); if (Preferences.LoggingMode.Value >= loggingMode) { _logger.Error(text); } } public static void Error(string txt, int loggingMode = 0) { string text = ((loggingMode == 1) ? ("[DEBUG] " + txt) : txt); if (Preferences.LoggingMode.Value >= loggingMode) { _logger.Error(text); } } public static void Error(string txt, int loggingMode = 0, params object[] args) { string text = ((loggingMode == 1) ? ("[DEBUG] " + txt) : txt); if (Preferences.LoggingMode.Value >= loggingMode) { _logger.Error(text, args); } } public static void Warning(object obj, int loggingMode = 0) { string text = ((loggingMode == 1) ? $"[DEBUG] {obj}" : obj.ToString()); if (Preferences.LoggingMode.Value >= loggingMode) { _logger.Warning(text); } } public static void Warning(string txt, int loggingMode = 0) { string text = ((loggingMode == 1) ? ("[DEBUG] " + txt) : txt); if (Preferences.LoggingMode.Value >= loggingMode) { _logger.Warning(text); } } public static void Warning(string txt, int loggingMode = 0, params object[] args) { string text = ((loggingMode == 1) ? ("[DEBUG] " + txt) : txt); if (Preferences.LoggingMode.Value >= loggingMode) { _logger.Warning(text, args); } } } internal static class Preferences { public static readonly MelonPreferences_Category GlobalCategory = MelonPreferences.CreateCategory("Global"); public static readonly MelonPreferences_Category OwnCategory = MelonPreferences.CreateCategory("VoidSpeaker"); public static MelonPreferences_Entry<int> LoggingMode { get; set; } public static MelonPreferences_Entry<float> Volume { get; set; } public static MelonPreferences_Entry<bool> SendNotifications { get; set; } public static MelonPreferences_Entry<bool> UseTagLib { get; set; } public static MelonPreferences_Entry<float> NotificationDuration { get; set; } public static void Setup() { LoggingMode = GlobalCategory.GetEntry<int>("LoggingMode") ?? GlobalCategory.CreateEntry<int>("LoggingMode", 0, "Logging Mode", "The level of logging to use. 0 = Important Only, 1 = All", false, false, (ValueValidator)null, (string)null); GlobalCategory.SetFilePath(MelonEnvironment.UserDataDirectory + "/WeatherElectric.cfg"); GlobalCategory.SaveToFile(false); Volume = OwnCategory.CreateEntry<float>("Volume", 1f, "Volume", "The volume the music plays at.", false, false, (ValueValidator)null, (string)null); SendNotifications = OwnCategory.CreateEntry<bool>("SendNotifications", true, "Send Notifications", "Send notifications when a new song starts playing.", false, false, (ValueValidator)null, (string)null); UseTagLib = OwnCategory.CreateEntry<bool>("UseTagLib", !HelperMethods.IsAndroid(), "Use TagLib", "Use TagLib to get metadata from music files. If disabled, the mod will use the file name.", false, false, (ValueValidator)null, (string)null); NotificationDuration = OwnCategory.CreateEntry<float>("NotificationDuration", 2f, "Notification Duration", "The duration of the notification popup in seconds.", false, false, (ValueValidator)null, (string)null); OwnCategory.SetFilePath(MelonEnvironment.UserDataDirectory + "/WeatherElectric.cfg"); OwnCategory.SaveToFile(false); ModConsole.Msg("Finished preferences setup for VoidSpeaker", 1); } } internal static class UserData { private static readonly string WeatherElectricPath = Path.Combine(MelonEnvironment.UserDataDirectory, "Weather Electric"); public static readonly string ModPath = Path.Combine(MelonEnvironment.UserDataDirectory, "Weather Electric/Void Speaker"); public static void Setup() { if (!Directory.Exists(WeatherElectricPath)) { Directory.CreateDirectory(WeatherElectricPath); } if (!Directory.Exists(ModPath)) { Directory.CreateDirectory(ModPath); } if (Directory.GetFiles(ModPath).Length == 0) { byte[] resourceBytes = HelperMethods.GetResourceBytes(Main.ModAsm, "WeatherElectric.VoidSpeaker.Resources.Default.mp3"); File.WriteAllBytes(Path.Combine(ModPath, "Don't Fence Me In.mp3"), resourceBytes); } } } } namespace WeatherElectric.VoidSpeaker.BoneMenu { internal static class BoneMenu { private static FunctionElement _pauseElement; public static void Setup() { //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_001b: 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) //IL_0076: 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_00d6: Unknown result type (might be due to invalid IL or missing references) //IL_010a: Unknown result type (might be due to invalid IL or missing references) //IL_013a: 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) //IL_017b: Unknown result type (might be due to invalid IL or missing references) //IL_0197: Unknown result type (might be due to invalid IL or missing references) //IL_01b3: Unknown result type (might be due to invalid IL or missing references) //IL_01dd: Unknown result type (might be due to invalid IL or missing references) Page obj = Page.Root.CreatePage("<color=#6FBDFF>Weather Electric</color>", Color.white, 0, true).CreatePage("<color=#bdd9da>Void Speaker</color>", Color.white, 0, true); obj.CreateFloat("Volume", Color.white, Preferences.Volume.Value, 0.05f, 0f, 1f, (Action<float>)delegate(float f) { MusicPlayer.Instance.SetVolume(f); Preferences.Volume.Value = f; Preferences.OwnCategory.SaveToFile(false); }); obj.CreateFunction("Play", Color.green, (Action)delegate { MusicPlayer.Instance.Play(); }); obj.CreateFunction("Stop", Color.red, (Action)delegate { //IL_001f: Unknown result type (might be due to invalid IL or missing references) MusicPlayer.Instance.Stop(); ((Element)_pauseElement).ElementName = "Pause"; ((Element)_pauseElement).ElementColor = Color.yellow; MusicPlayer.Instance.Unpause(); }); _pauseElement = obj.CreateFunction("Pause", Color.yellow, (Action)delegate { //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Unknown result type (might be due to invalid IL or missing references) bool flag = MusicPlayer.Instance.PauseUnpause(); ((Element)_pauseElement).ElementName = (flag ? "Unpause" : "Pause"); ((Element)_pauseElement).ElementColor = (flag ? Color.green : Color.yellow); }); obj.CreateFunction("Skip", Color.white, (Action)delegate { MusicPlayer.Instance.Skip(); }); obj.CreateFunction("Shuffle", Color.white, (Action)delegate { MusicPlayer.Instance.Shuffle(); }); Page obj2 = obj.CreatePage("Settings", Color.gray, 0, true); obj2.CreateBoolPreference("Send Notifications", Color.white, Preferences.SendNotifications, Preferences.OwnCategory); obj2.CreateBoolPreference("Use TagLib", Color.white, Preferences.UseTagLib, Preferences.OwnCategory); obj2.CreateFloatPreference("Notification Duration", Color.white, 0.5f, 0.5f, 10f, Preferences.NotificationDuration, Preferences.OwnCategory); obj2.CreateFunction("Refresh Music", Color.white, (Action)delegate { Menu.DisplayDialog("Warning!", "This will freeze the game for a while, depending on how many songs you added!", (Texture2D)null, (Action)RefreshMusic, (Action)null); }); static void RefreshMusic() { MusicLoader.RemoveMissingFiles(); MusicLoader.LoadNewFiles(); } } } internal static class Extensions { public static BoolElement CreateBoolPreference(this Page category, string name, Color color, MelonPreferences_Entry<bool> pref, MelonPreferences_Category prefCategory, bool autoSave = true) { //IL_001f: Unknown result type (might be due to invalid IL or missing references) return category.CreateBool(name, color, pref.Value, (Action<bool>)delegate(bool v) { pref.Value = v; if (autoSave) { prefCategory.SaveToFile(false); } }); } public static FloatElement CreateFloatPreference(this Page category, string name, Color color, float increment, float min, float max, MelonPreferences_Entry<float> pref, MelonPreferences_Category prefCategory, bool autoSave = true) { //IL_0020: Unknown result type (might be due to invalid IL or missing references) return category.CreateFloat(name, color, pref.Value, increment, min, max, (Action<float>)delegate(float v) { pref.Value = v; if (autoSave) { prefCategory.SaveToFile(false); } }); } public static IntElement CreateIntPreference(this Page category, string name, Color color, int increment, int min, int max, MelonPreferences_Entry<int> pref, MelonPreferences_Category prefCategory, bool autoSave = true) { //IL_0020: Unknown result type (might be due to invalid IL or missing references) return category.CreateInt(name, color, pref.Value, increment, min, max, (Action<int>)delegate(int v) { pref.Value = v; if (autoSave) { prefCategory.SaveToFile(false); } }); } public static EnumElement CreateEnumPreference(this Page category, string name, Color color, MelonPreferences_Entry<Enum> pref, MelonPreferences_Category prefCategory, bool autoSave = true) { //IL_001f: Unknown result type (might be due to invalid IL or missing references) return category.CreateEnum(name, color, pref.Value, (Action<Enum>)delegate(Enum v) { pref.Value = v; if (autoSave) { prefCategory.SaveToFile(false); } }); } } }
UserLibs/TagLibSharp.dll
Decompiled a month ago
The result has been truncated due to the large size, download it to view full contents!
#define DEBUG using System; using System.Collections; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Diagnostics; using System.Globalization; using System.IO; using System.IO.Compression; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Serialization; using System.Runtime.Versioning; using System.Text; using System.Xml; using Microsoft.CodeAnalysis; using TagLib.Aac; using TagLib.Aiff; using TagLib.Ape; using TagLib.Asf; using TagLib.Audible; using TagLib.Dsf; using TagLib.Flac; using TagLib.Gif; using TagLib.IFD; using TagLib.IFD.Entries; using TagLib.IFD.Makernotes; using TagLib.IFD.Tags; using TagLib.IIM; using TagLib.Id3v1; using TagLib.Id3v2; using TagLib.Image; using TagLib.Image.NoMetadata; using TagLib.Jpeg; using TagLib.Matroska; using TagLib.Mpeg; using TagLib.Mpeg4; using TagLib.MusePack; using TagLib.NonContainer; using TagLib.Ogg; using TagLib.Ogg.Codecs; using TagLib.Png; using TagLib.Riff; using TagLib.Tiff; using TagLib.Tiff.Arw; using TagLib.Tiff.Cr2; using TagLib.Tiff.Dng; using TagLib.Tiff.Nef; using TagLib.Tiff.Pef; using TagLib.Tiff.Rw2; using TagLib.WavPack; using TagLib.Xmp; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)] [assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = ".NET 6.0")] [assembly: AssemblyCompany("Brian Nickel, Gabriel Burt, Stephen Shaw, etc")] [assembly: AssemblyConfiguration("Debug")] [assembly: AssemblyCopyright("Copyright (c) 2006-2007 Brian Nickel. Copyright (c) 2009-2022 Other contributors")] [assembly: AssemblyDescription("A library for for reading and writing metadata in media files, including video, audio, and photo formats.")] [assembly: AssemblyFileVersion("2.4.0.0")] [assembly: AssemblyInformationalVersion("2.4.0.0+b2b21107ba078f66d827571ce64541f5db000b10")] [assembly: AssemblyProduct("TagLib#")] [assembly: AssemblyTitle("TagLibSharp")] [assembly: AssemblyMetadata("RepositoryUrl", "https://github.com/mono/taglib-sharp")] [assembly: AssemblyVersion("2.4.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 TagLib { public enum StringType { Latin1, UTF16, UTF16BE, UTF8, UTF16LE } public class ByteVector : IList<byte>, ICollection<byte>, IEnumerable<byte>, IEnumerable, IComparable<ByteVector> { private static readonly uint[] crc_table = new uint[256] { 0u, 79764919u, 159529838u, 222504665u, 319059676u, 398814059u, 445009330u, 507990021u, 638119352u, 583659535u, 797628118u, 726387553u, 890018660u, 835552979u, 1015980042u, 944750013u, 1276238704u, 1221641927u, 1167319070u, 1095957929u, 1595256236u, 1540665371u, 1452775106u, 1381403509u, 1780037320u, 1859660671u, 1671105958u, 1733955601u, 2031960084u, 2111593891u, 1889500026u, 1952343757u, 2552477408u, 2632100695u, 2443283854u, 2506133561u, 2334638140u, 2414271883u, 2191915858u, 2254759653u, 3190512472u, 3135915759u, 3081330742u, 3009969537u, 2905550212u, 2850959411u, 2762807018u, 2691435357u, 3560074640u, 3505614887u, 3719321342u, 3648080713u, 3342211916u, 3287746299u, 3467911202u, 3396681109u, 4063920168u, 4143685023u, 4223187782u, 4286162673u, 3779000052u, 3858754371u, 3904687514u, 3967668269u, 881225847u, 809987520u, 1023691545u, 969234094u, 662832811u, 591600412u, 771767749u, 717299826u, 311336399u, 374308984u, 453813921u, 533576470u, 25881363u, 88864420u, 134795389u, 214552010u, 2023205639u, 2086057648u, 1897238633u, 1976864222u, 1804852699u, 1867694188u, 1645340341u, 1724971778u, 1587496639u, 1516133128u, 1461550545u, 1406951526u, 1302016099u, 1230646740u, 1142491917u, 1087903418u, 2896545431u, 2825181984u, 2770861561u, 2716262478u, 3215044683u, 3143675388u, 3055782693u, 3001194130u, 2326604591u, 2389456536u, 2200899649u, 2280525302u, 2578013683u, 2640855108u, 2418763421u, 2498394922u, 3769900519u, 3832873040u, 3912640137u, 3992402750u, 4088425275u, 4151408268u, 4197601365u, 4277358050u, 3334271071u, 3263032808u, 3476998961u, 3422541446u, 3585640067u, 3514407732u, 3694837229u, 3640369242u, 1762451694u, 1842216281u, 1619975040u, 1682949687u, 2047383090u, 2127137669u, 1938468188u, 2001449195u, 1325665622u, 1271206113u, 1183200824u, 1111960463u, 1543535498u, 1489069629u, 1434599652u, 1363369299u, 622672798u, 568075817u, 748617968u, 677256519u, 907627842u, 853037301u, 1067152940u, 995781531u, 51762726u, 131386257u, 177728840u, 240578815u, 269590778u, 349224269u, 429104020u, 491947555u, 4046411278u, 4126034873u, 4172115296u, 4234965207u, 3794477266u, 3874110821u, 3953728444u, 4016571915u, 3609705398u, 3555108353u, 3735388376u, 3664026991u, 3290680682u, 3236090077u, 3449943556u, 3378572211u, 3174993278u, 3120533705u, 3032266256u, 2961025959u, 2923101090u, 2868635157u, 2813903052u, 2742672763u, 2604032198u, 2683796849u, 2461293480u, 2524268063u, 2284983834u, 2364738477u, 2175806836u, 2238787779u, 1569362073u, 1498123566u, 1409854455u, 1355396672u, 1317987909u, 1246755826u, 1192025387u, 1137557660u, 2072149281u, 2135122070u, 1912620623u, 1992383480u, 1753615357u, 1816598090u, 1627664531u, 1707420964u, 295390185u, 358241886u, 404320391u, 483945776u, 43990325u, 106832002u, 186451547u, 266083308u, 932423249u, 861060070u, 1041341759u, 986742920u, 613929101u, 542559546u, 756411363u, 701822548u, 3316196985u, 3244833742u, 3425377559u, 3370778784u, 3601682597u, 3530312978u, 3744426955u, 3689838204u, 3819031489u, 3881883254u, 3928223919u, 4007849240u, 4037393693u, 4100235434u, 4180117107u, 4259748804u, 2310601993u, 2373574846u, 2151335527u, 2231098320u, 2596047829u, 2659030626u, 2470359227u, 2550115596u, 2947551409u, 2876312838u, 2788305887u, 2733848168u, 3165939309u, 3094707162u, 3040238851u, 2985771188u }; private static bool use_broken_latin1; private static readonly ReadOnlyByteVector td1 = new ReadOnlyByteVector(1); private static readonly ReadOnlyByteVector td2 = new ReadOnlyByteVector(2); private static Encoding last_utf16_encoding = Encoding.Unicode; private readonly List<byte> data = new List<byte>(); public byte[] Data => data.ToArray(); public bool IsEmpty => data.Count == 0; public uint Checksum { get { uint num = 0u; foreach (byte datum in data) { num = (num << 8) ^ crc_table[((num >> 24) & 0xFF) ^ datum]; } return num; } } public static bool UseBrokenLatin1Behavior { get { return use_broken_latin1; } set { use_broken_latin1 = value; } } public int Count => data.Count; public bool IsSynchronized => false; public object SyncRoot => this; public virtual bool IsReadOnly => false; public virtual bool IsFixedSize => false; public byte this[int index] { get { return data[index]; } set { if (IsReadOnly) { throw new NotSupportedException("Cannot edit readonly objects."); } data[index] = value; } } public ByteVector() { } public ByteVector(ByteVector vector) { if (vector != null) { data.AddRange(vector); } } public ByteVector(params byte[] data) { if (data != null) { this.data.AddRange(data); } } public ByteVector(byte[] data, int length) { if (length > data.Length) { throw new ArgumentOutOfRangeException("length", "Length exceeds size of data."); } if (length < 0) { throw new ArgumentOutOfRangeException("length", "Length is less than zero."); } if (length == data.Length) { this.data.AddRange(data); return; } byte[] array = new byte[length]; Array.Copy(data, 0, array, 0, length); this.data.AddRange(array); } public ByteVector(int size) : this(size, 0) { } public ByteVector(int size, byte value) { if (size < 0) { throw new ArgumentOutOfRangeException("size", "Size is less than zero."); } if (size != 0) { byte[] array = new byte[size]; for (int i = 0; i < size; i++) { array[i] = value; } data.AddRange(array); } } public ByteVector Mid(int startIndex, int length) { if (startIndex < 0 || startIndex > Count) { throw new ArgumentOutOfRangeException("startIndex"); } if (length < 0 || startIndex + length > Count) { throw new ArgumentOutOfRangeException("length"); } if (length == 0) { return new ByteVector(); } if (startIndex + length > data.Count) { length = data.Count - startIndex; } byte[] array = new byte[length]; data.CopyTo(startIndex, array, 0, length); return array; } public ByteVector Mid(int index) { return Mid(index, Count - index); } public int Find(ByteVector pattern, int offset, int byteAlign) { if (pattern == null) { throw new ArgumentNullException("pattern"); } if (offset < 0) { throw new ArgumentOutOfRangeException("offset", "offset must be at least 0."); } if (byteAlign < 1) { throw new ArgumentOutOfRangeException("byteAlign", "byteAlign must be at least 1."); } if (pattern.Count > Count - offset) { return -1; } if (pattern.Count == 1) { byte b = pattern[0]; for (int i = offset; i < data.Count; i += byteAlign) { if (data[i] == b) { return i; } } return -1; } int[] array = new int[256]; for (int j = 0; j < 256; j++) { array[j] = pattern.Count; } for (int k = 0; k < pattern.Count - 1; k++) { array[pattern[k]] = pattern.Count - k - 1; } for (int l = pattern.Count - 1 + offset; l < data.Count; l += array[data[l]]) { int num = l; int num2 = pattern.Count - 1; while (num2 >= 0 && data[num] == pattern[num2]) { num--; num2--; } if (-1 == num2 && (num + 1 - offset) % byteAlign == 0) { return num + 1; } } return -1; } public int Find(ByteVector pattern, int offset) { return Find(pattern, offset, 1); } public int Find(ByteVector pattern) { return Find(pattern, 0, 1); } public int RFind(ByteVector pattern, int offset, int byteAlign) { if (pattern == null) { throw new ArgumentNullException("pattern"); } if (offset < 0) { throw new ArgumentOutOfRangeException("offset"); } if (pattern.Count == 0 || pattern.Count > Count - offset) { return -1; } if (pattern.Count == 1) { byte b = pattern[0]; for (int num = Count - offset - 1; num >= 0; num -= byteAlign) { if (data[num] == b) { return num; } } return -1; } int[] array = new int[256]; for (int i = 0; i < 256; i++) { array[i] = pattern.Count; } for (int num2 = pattern.Count - 1; num2 > 0; num2--) { array[pattern[num2]] = num2; } for (int num3 = Count - offset - pattern.Count; num3 >= 0; num3 -= array[data[num3]]) { if ((offset - num3) % byteAlign == 0 && ContainsAt(pattern, num3)) { return num3; } } return -1; } public int RFind(ByteVector pattern, int offset) { return RFind(pattern, offset, 1); } public int RFind(ByteVector pattern) { return RFind(pattern, 0, 1); } public bool ContainsAt(ByteVector pattern, int offset, int patternOffset, int patternLength) { if (pattern == null) { throw new ArgumentNullException("pattern"); } if (pattern.Count < patternLength) { patternLength = pattern.Count; } if (patternLength > data.Count || offset >= data.Count || patternOffset >= pattern.Count || patternLength <= 0 || offset < 0) { return false; } for (int i = 0; i < patternLength - patternOffset; i++) { if (data[i + offset] != pattern[i + patternOffset]) { return false; } } return true; } public bool ContainsAt(ByteVector pattern, int offset, int patternOffset) { return ContainsAt(pattern, offset, patternOffset, int.MaxValue); } public bool ContainsAt(ByteVector pattern, int offset) { return ContainsAt(pattern, offset, 0); } public bool StartsWith(ByteVector pattern) { return ContainsAt(pattern, 0); } public bool EndsWith(ByteVector pattern) { if (pattern == null) { throw new ArgumentNullException("pattern"); } return ContainsAt(pattern, data.Count - pattern.Count); } public int EndsWithPartialMatch(ByteVector pattern) { if (pattern == null) { throw new ArgumentNullException("pattern"); } if (pattern.Count > data.Count) { return -1; } int num = data.Count - pattern.Count; for (int i = 1; i < pattern.Count; i++) { if (ContainsAt(pattern, num + i, 0, pattern.Count - i)) { return num + i; } } return -1; } public void Add(ByteVector data) { if (IsReadOnly) { throw new NotSupportedException("Cannot edit readonly objects."); } if (data != null) { this.data.AddRange(data); } } public void Add(byte[] data) { if (IsReadOnly) { throw new NotSupportedException("Cannot edit readonly objects."); } if (data != null) { this.data.AddRange(data); } } public void Insert(int index, ByteVector data) { if (IsReadOnly) { throw new NotSupportedException("Cannot edit readonly objects."); } if (data != null) { this.data.InsertRange(index, data); } } public void Insert(int index, byte[] data) { if (IsReadOnly) { throw new NotSupportedException("Cannot edit readonly objects."); } if (data != null) { this.data.InsertRange(index, data); } } public ByteVector Resize(int size, byte padding) { if (IsReadOnly) { throw new NotSupportedException("Cannot edit readonly objects."); } if (data.Count > size) { data.RemoveRange(size, data.Count - size); } while (data.Count < size) { data.Add(padding); } return this; } public ByteVector Resize(int size) { return Resize(size, 0); } public void RemoveRange(int index, int count) { if (IsReadOnly) { throw new NotSupportedException("Cannot edit readonly objects."); } data.RemoveRange(index, count); } public int ToInt(bool mostSignificantByteFirst) { int num = 0; int num2 = ((Count > 4) ? 3 : (Count - 1)); for (int i = 0; i <= num2; i++) { int num3 = (mostSignificantByteFirst ? (num2 - i) : i); num |= this[i] << num3 * 8; } return num; } public int ToInt() { return ToInt(mostSignificantByteFirst: true); } public uint ToUInt(bool mostSignificantByteFirst) { uint num = 0u; int num2 = ((Count > 4) ? 3 : (Count - 1)); for (int i = 0; i <= num2; i++) { int num3 = (mostSignificantByteFirst ? (num2 - i) : i); num |= (uint)(this[i] << num3 * 8); } return num; } public uint ToUInt() { return ToUInt(mostSignificantByteFirst: true); } public short ToShort(bool mostSignificantByteFirst) { short num = 0; int num2 = ((Count > 2) ? 1 : (Count - 1)); for (int i = 0; i <= num2; i++) { int num3 = (mostSignificantByteFirst ? (num2 - i) : i); num |= (short)(this[i] << num3 * 8); } return num; } public short ToShort() { return ToShort(mostSignificantByteFirst: true); } public ushort ToUShort(bool mostSignificantByteFirst) { ushort num = 0; int num2 = ((Count > 2) ? 1 : (Count - 1)); for (int i = 0; i <= num2; i++) { int num3 = (mostSignificantByteFirst ? (num2 - i) : i); num |= (ushort)(this[i] << num3 * 8); } return num; } public ushort ToUShort() { return ToUShort(mostSignificantByteFirst: true); } public long ToLong(bool mostSignificantByteFirst) { long num = 0L; int num2 = ((Count > 8) ? 7 : (Count - 1)); for (int i = 0; i <= num2; i++) { int num3 = (mostSignificantByteFirst ? (num2 - i) : i); num |= (long)((ulong)this[i] << num3 * 8); } return num; } public long ToLong() { return ToLong(mostSignificantByteFirst: true); } public ulong ToULong(bool mostSignificantByteFirst) { ulong num = 0uL; int num2 = ((Count > 8) ? 7 : (Count - 1)); for (int i = 0; i <= num2; i++) { int num3 = (mostSignificantByteFirst ? (num2 - i) : i); num |= (ulong)this[i] << num3 * 8; } return num; } public ulong ToULong() { return ToULong(mostSignificantByteFirst: true); } public float ToFloat(bool mostSignificantByteFirst) { byte[] array = (byte[])Data.Clone(); if (mostSignificantByteFirst) { Array.Reverse(array); } return BitConverter.ToSingle(array, 0); } public float ToFloat() { return ToFloat(mostSignificantByteFirst: true); } public double ToDouble(bool mostSignificantByteFirst) { byte[] array = (byte[])Data.Clone(); if (mostSignificantByteFirst) { Array.Reverse(array); } return BitConverter.ToDouble(array, 0); } public double ToDouble() { return ToDouble(mostSignificantByteFirst: true); } public double ToExtendedPrecision() { int num = ((this[0] & 0x7F) << 8) | this[1]; ulong num2 = ((ulong)this[2] << 24) | ((ulong)this[3] << 16) | ((ulong)this[4] << 8) | this[5]; ulong num3 = ((ulong)this[6] << 24) | ((ulong)this[7] << 16) | ((ulong)this[8] << 8) | this[9]; double num4; if (num == 0 && num2 == 0L && num3 == 0) { num4 = 0.0; } else if (num == 32767) { num4 = double.PositiveInfinity; } else { num -= 16383; num4 = (double)num2 * Math.Pow(2.0, num -= 31); num4 += (double)num3 * Math.Pow(2.0, num -= 32); } return ((this[0] & 0x80u) != 0) ? (0.0 - num4) : num4; } public string ToString(StringType type, int offset, int count) { if (offset < 0 || offset > Count) { throw new ArgumentOutOfRangeException("offset"); } if (count < 0 || count + offset > Count) { throw new ArgumentOutOfRangeException("count"); } ByteVector bom = ((type == StringType.UTF16 && data.Count - offset > 1) ? Mid(offset, 2) : null); string @string = StringTypeToEncoding(type, bom).GetString(Data, offset, count); if (@string.Length != 0 && (@string[0] == '\ufffe' || @string[0] == '\ufeff')) { return @string.Substring(1); } return @string; } [Obsolete("Use ToString(StringType,int,int)")] public string ToString(StringType type, int offset) { return ToString(type, offset, Count - offset); } public string ToString(StringType type) { return ToString(type, 0, Count); } public override string ToString() { return ToString(StringType.UTF8, 0, Count); } public string[] ToStrings(StringType type, int offset) { return ToStrings(type, offset, int.MaxValue); } public string[] ToStrings(StringType type, int offset, int count) { int num = 0; int num2 = offset; List<string> list = new List<string>(); ByteVector byteVector = TextDelimiter(type); int count2 = byteVector.Count; while (num < count && num2 < Count) { int num3 = num2; if (num + 1 == count) { num2 = offset + count; } else { num2 = Find(byteVector, num3, count2); if (num2 < 0) { num2 = Count; } } int num4 = num2 - num3; if (num4 == 0) { list.Add(string.Empty); } else { string text = ToString(type, num3, num4); if (text.Length != 0 && (text[0] == '\ufffe' || text[0] == '\ufeff')) { text = text.Substring(1); } list.Add(text); } num2 += count2; } return list.ToArray(); } public static bool operator ==(ByteVector first, ByteVector second) { bool flag = (object)first == null; bool flag2 = (object)second == null; if (flag && flag2) { return true; } if (flag || flag2) { return false; } return first.Equals(second); } public static bool operator !=(ByteVector first, ByteVector second) { return !(first == second); } public static bool operator <(ByteVector first, ByteVector second) { if (first == null) { throw new ArgumentNullException("first"); } if (second == null) { throw new ArgumentNullException("second"); } return first.CompareTo(second) < 0; } public static bool operator <=(ByteVector first, ByteVector second) { if (first == null) { throw new ArgumentNullException("first"); } if (second == null) { throw new ArgumentNullException("second"); } return first.CompareTo(second) <= 0; } public static bool operator >(ByteVector first, ByteVector second) { if (first == null) { throw new ArgumentNullException("first"); } if (second == null) { throw new ArgumentNullException("second"); } return first.CompareTo(second) > 0; } public static bool operator >=(ByteVector first, ByteVector second) { if (first == null) { throw new ArgumentNullException("first"); } if (second == null) { throw new ArgumentNullException("second"); } return first.CompareTo(second) >= 0; } public static ByteVector operator +(ByteVector first, ByteVector second) { return new ByteVector(first) { second }; } public static implicit operator ByteVector(byte value) { return new ByteVector(value); } public static implicit operator ByteVector(byte[] value) { return new ByteVector(value); } public static implicit operator ByteVector(string value) { return FromString(value, StringType.UTF8); } public static ByteVector FromInt(int value, bool mostSignificantByteFirst) { ByteVector byteVector = new ByteVector(); for (int i = 0; i < 4; i++) { int num = (mostSignificantByteFirst ? (3 - i) : i); byteVector.Add((byte)((uint)(value >> num * 8) & 0xFFu)); } return byteVector; } public static ByteVector FromInt(int value) { return FromInt(value, mostSignificantByteFirst: true); } public static ByteVector FromUInt(uint value, bool mostSignificantByteFirst) { ByteVector byteVector = new ByteVector(); for (int i = 0; i < 4; i++) { int num = (mostSignificantByteFirst ? (3 - i) : i); byteVector.Add((byte)((value >> num * 8) & 0xFFu)); } return byteVector; } public static ByteVector FromUInt(uint value) { return FromUInt(value, mostSignificantByteFirst: true); } public static ByteVector FromShort(short value, bool mostSignificantByteFirst) { ByteVector byteVector = new ByteVector(); for (int i = 0; i < 2; i++) { int num = (mostSignificantByteFirst ? (1 - i) : i); byteVector.Add((byte)((uint)(value >> num * 8) & 0xFFu)); } return byteVector; } public static ByteVector FromShort(short value) { return FromShort(value, mostSignificantByteFirst: true); } public static ByteVector FromUShort(ushort value, bool mostSignificantByteFirst) { ByteVector byteVector = new ByteVector(); for (int i = 0; i < 2; i++) { int num = (mostSignificantByteFirst ? (1 - i) : i); byteVector.Add((byte)((uint)(value >> num * 8) & 0xFFu)); } return byteVector; } public static ByteVector FromUShort(ushort value) { return FromUShort(value, mostSignificantByteFirst: true); } public static ByteVector FromLong(long value, bool mostSignificantByteFirst) { ByteVector byteVector = new ByteVector(); for (int i = 0; i < 8; i++) { int num = (mostSignificantByteFirst ? (7 - i) : i); byteVector.Add((byte)((value >> num * 8) & 0xFF)); } return byteVector; } public static ByteVector FromLong(long value) { return FromLong(value, mostSignificantByteFirst: true); } public static ByteVector FromULong(ulong value, bool mostSignificantByteFirst) { ByteVector byteVector = new ByteVector(); for (int i = 0; i < 8; i++) { int num = (mostSignificantByteFirst ? (7 - i) : i); byteVector.Add((byte)((value >> num * 8) & 0xFF)); } return byteVector; } public static ByteVector FromULong(ulong value) { return FromULong(value, mostSignificantByteFirst: true); } public static ByteVector FromString(string text, StringType type, int length) { ByteVector byteVector = new ByteVector(); if (type == StringType.UTF16) { byteVector.Add(new byte[2] { 255, 254 }); } if (text == null || text.Length == 0) { return byteVector; } if (text.Length > length) { text = text.Substring(0, length); } byteVector.Add(StringTypeToEncoding(type, byteVector).GetBytes(text)); return byteVector; } public static ByteVector FromString(string text, StringType type) { return FromString(text, type, int.MaxValue); } public static ByteVector FromString(string text, int length) { return FromString(text, StringType.UTF8, length); } [Obsolete("Use FromString(string,StringType)")] public static ByteVector FromString(string text) { return FromString(text, StringType.UTF8); } public static ByteVector FromPath(string path) { byte[] firstChunk; return FromPath(path, out firstChunk, copyFirstChunk: false); } internal static ByteVector FromPath(string path, out byte[] firstChunk, bool copyFirstChunk) { if (path == null) { throw new ArgumentNullException("path"); } return FromFile(new File.LocalFileAbstraction(path), out firstChunk, copyFirstChunk); } public static ByteVector FromFile(File.IFileAbstraction abstraction) { byte[] firstChunk; return FromFile(abstraction, out firstChunk, copyFirstChunk: false); } internal static ByteVector FromFile(File.IFileAbstraction abstraction, out byte[] firstChunk, bool copyFirstChunk) { if (abstraction == null) { throw new ArgumentNullException("abstraction"); } Stream readStream = abstraction.ReadStream; ByteVector result = FromStream(readStream, out firstChunk, copyFirstChunk); abstraction.CloseStream(readStream); return result; } public static ByteVector FromStream(Stream stream) { byte[] firstChunk; return FromStream(stream, out firstChunk, copyFirstChunk: false); } internal static ByteVector FromStream(Stream stream, out byte[] firstChunk, bool copyFirstChunk) { ByteVector byteVector = new ByteVector(); byte[] array = new byte[4096]; int num = array.Length; int num2 = 0; bool flag = false; firstChunk = null; int num3; do { Array.Clear(array, 0, array.Length); num3 = stream.Read(array, 0, num); byteVector.Add(array); num2 += num3; if (flag) { continue; } if (copyFirstChunk) { if (firstChunk == null || firstChunk.Length != num) { firstChunk = new byte[num]; } Array.Copy(array, 0, firstChunk, 0, num3); } flag = true; } while ((num2 != stream.Length || stream.Length <= 0) && (num3 >= num || stream.Length > 0)); if (stream.Length > 0 && byteVector.Count != stream.Length) { byteVector.Resize((int)stream.Length); } return byteVector; } public static ByteVector TextDelimiter(StringType type) { return (type == StringType.UTF16 || type == StringType.UTF16BE || type == StringType.UTF16LE) ? td2 : td1; } private static Encoding StringTypeToEncoding(StringType type, ByteVector bom) { switch (type) { case StringType.UTF16: if (bom == null) { return last_utf16_encoding; } if (bom[0] == byte.MaxValue && bom[1] == 254) { return last_utf16_encoding = Encoding.Unicode; } if (bom[1] == byte.MaxValue && bom[0] == 254) { return last_utf16_encoding = Encoding.BigEndianUnicode; } return last_utf16_encoding; case StringType.UTF16BE: return Encoding.BigEndianUnicode; case StringType.UTF8: return Encoding.UTF8; case StringType.UTF16LE: return Encoding.Unicode; default: if (use_broken_latin1) { return Encoding.Default; } try { return Encoding.GetEncoding("latin1"); } catch (ArgumentException) { return Encoding.UTF8; } } } public override bool Equals(object other) { if (!(other is ByteVector)) { return false; } return Equals((ByteVector)other); } public bool Equals(ByteVector other) { return CompareTo(other) == 0; } public override int GetHashCode() { return (int)Checksum; } public int CompareTo(ByteVector other) { if ((object)other == null) { throw new ArgumentNullException("other"); } int num = Count - other.Count; int num2 = 0; while (num == 0 && num2 < Count) { num = this[num2] - other[num2]; num2++; } return num; } public IEnumerator<byte> GetEnumerator() { return data.GetEnumerator(); } IEnumerator IEnumerable.GetEnumerator() { return data.GetEnumerator(); } public void Clear() { if (IsReadOnly) { throw new NotSupportedException("Cannot edit readonly objects."); } data.Clear(); } public void Add(byte item) { if (IsReadOnly) { throw new NotSupportedException("Cannot edit readonly objects."); } data.Add(item); } public bool Remove(byte item) { if (IsReadOnly) { throw new NotSupportedException("Cannot edit readonly objects."); } return data.Remove(item); } public void CopyTo(byte[] array, int arrayIndex) { data.CopyTo(array, arrayIndex); } public bool Contains(byte item) { return data.Contains(item); } public void RemoveAt(int index) { if (IsReadOnly) { throw new NotSupportedException("Cannot edit readonly objects."); } data.RemoveAt(index); } public void Insert(int index, byte item) { if (IsReadOnly) { throw new NotSupportedException("Cannot edit readonly objects."); } data.Insert(index, item); } public int IndexOf(byte item) { return data.IndexOf(item); } } [ComVisible(false)] public class ByteVectorCollection : ListBase<ByteVector> { public ByteVectorCollection() { } public ByteVectorCollection(IEnumerable<ByteVector> list) { if (list != null) { Add(list); } } public ByteVectorCollection(params ByteVector[] list) { if (list != null) { Add(list); } } public override void SortedInsert(ByteVector item, bool unique) { if (item == null) { throw new ArgumentNullException("item"); } int i; for (i = 0; i < base.Count; i++) { if (item == base[i] && unique) { return; } if (item >= base[i]) { break; } } Insert(i + 1, item); } public ByteVector ToByteVector(ByteVector separator) { if (separator == null) { throw new ArgumentNullException("separator"); } ByteVector byteVector = new ByteVector(); for (int i = 0; i < base.Count; i++) { if (i != 0 && separator.Count > 0) { byteVector.Add(separator); } byteVector.Add(base[i]); } return byteVector; } public static ByteVectorCollection Split(ByteVector vector, ByteVector pattern, int byteAlign, int max) { if (vector == null) { throw new ArgumentNullException("vector"); } if (pattern == null) { throw new ArgumentNullException("pattern"); } if (byteAlign < 1) { throw new ArgumentOutOfRangeException("byteAlign", "byteAlign must be at least 1."); } ByteVectorCollection byteVectorCollection = new ByteVectorCollection(); int num = 0; int num2 = vector.Find(pattern, 0, byteAlign); while (num2 != -1 && (max < 1 || max > byteVectorCollection.Count + 1)) { byteVectorCollection.Add(vector.Mid(num, num2 - num)); num = num2 + pattern.Count; num2 = vector.Find(pattern, num2 + pattern.Count, byteAlign); } if (num < vector.Count) { byteVectorCollection.Add(vector.Mid(num, vector.Count - num)); } return byteVectorCollection; } public static ByteVectorCollection Split(ByteVector vector, ByteVector pattern, int byteAlign) { return Split(vector, pattern, byteAlign, 0); } public static ByteVectorCollection Split(ByteVector vector, ByteVector pattern) { return Split(vector, pattern, 1); } } public class CombinedTag : Tag { private readonly List<Tag> tags; public virtual Tag[] Tags => tags.ToArray(); public override TagTypes TagTypes { get { TagTypes tagTypes = TagTypes.None; foreach (Tag tag in tags) { if (tag != null) { tagTypes |= tag.TagTypes; } } return tagTypes; } } public override string Title { get { foreach (Tag tag in tags) { if (tag != null) { string title = tag.Title; if (title != null) { return title; } } } return null; } set { foreach (Tag tag in tags) { if (tag != null) { tag.Title = value; } } } } public override string Subtitle { get { foreach (Tag tag in tags) { if (tag != null) { string subtitle = tag.Subtitle; if (subtitle != null) { return subtitle; } } } return null; } set { foreach (Tag tag in tags) { if (tag != null) { tag.Subtitle = value; } } } } public override string Description { get { foreach (Tag tag in tags) { if (tag != null) { string description = tag.Description; if (description != null) { return description; } } } return null; } set { foreach (Tag tag in tags) { if (tag != null) { tag.Description = value; } } } } public override string[] Performers { get { foreach (Tag tag in tags) { if (tag != null) { string[] performers = tag.Performers; if (performers != null && performers.Length != 0) { return performers; } } } return new string[0]; } set { foreach (Tag tag in tags) { if (tag != null) { tag.Performers = value; } } } } public override string[] PerformersSort { get { foreach (Tag tag in tags) { if (tag != null) { string[] performersSort = tag.PerformersSort; if (performersSort != null && performersSort.Length != 0) { return performersSort; } } } return new string[0]; } set { foreach (Tag tag in tags) { if (tag != null) { tag.PerformersSort = value; } } } } public override string[] PerformersRole { get { foreach (Tag tag in tags) { if (tag != null) { string[] performersRole = tag.PerformersRole; if (performersRole != null && performersRole.Length != 0) { return performersRole; } } } return new string[0]; } set { foreach (Tag tag in tags) { if (tag != null) { tag.PerformersRole = value; } } } } public override string[] AlbumArtistsSort { get { foreach (Tag tag in tags) { if (tag != null) { string[] albumArtistsSort = tag.AlbumArtistsSort; if (albumArtistsSort != null && albumArtistsSort.Length != 0) { return albumArtistsSort; } } } return new string[0]; } set { foreach (Tag tag in tags) { if (tag != null) { tag.AlbumArtistsSort = value; } } } } public override string[] AlbumArtists { get { foreach (Tag tag in tags) { if (tag != null) { string[] albumArtists = tag.AlbumArtists; if (albumArtists != null && albumArtists.Length != 0) { return albumArtists; } } } return new string[0]; } set { foreach (Tag tag in tags) { if (tag != null) { tag.AlbumArtists = value; } } } } public override string[] Composers { get { foreach (Tag tag in tags) { if (tag != null) { string[] composers = tag.Composers; if (composers != null && composers.Length != 0) { return composers; } } } return new string[0]; } set { foreach (Tag tag in tags) { if (tag != null) { tag.Composers = value; } } } } public override string[] ComposersSort { get { foreach (Tag tag in tags) { if (tag != null) { string[] composersSort = tag.ComposersSort; if (composersSort != null && composersSort.Length != 0) { return composersSort; } } } return new string[0]; } set { foreach (Tag tag in tags) { if (tag != null) { tag.ComposersSort = value; } } } } public override string TitleSort { get { foreach (Tag tag in tags) { if (tag != null) { string titleSort = tag.TitleSort; if (titleSort != null && titleSort.Length > 0) { return titleSort; } } } return null; } set { foreach (Tag tag in tags) { if (tag != null) { tag.TitleSort = value; } } } } public override string AlbumSort { get { foreach (Tag tag in tags) { if (tag != null) { string albumSort = tag.AlbumSort; if (albumSort != null && albumSort.Length > 0) { return albumSort; } } } return null; } set { foreach (Tag tag in tags) { if (tag != null) { tag.AlbumSort = value; } } } } public override string Album { get { foreach (Tag tag in tags) { if (tag != null) { string album = tag.Album; if (album != null) { return album; } } } return null; } set { foreach (Tag tag in tags) { if (tag != null) { tag.Album = value; } } } } public override string Comment { get { foreach (Tag tag in tags) { if (tag != null) { string comment = tag.Comment; if (comment != null) { return comment; } } } return null; } set { foreach (Tag tag in tags) { if (tag != null) { tag.Comment = value; } } } } public override string[] Genres { get { foreach (Tag tag in tags) { if (tag != null) { string[] genres = tag.Genres; if (genres != null && genres.Length != 0) { return genres; } } } return new string[0]; } set { foreach (Tag tag in tags) { if (tag != null) { tag.Genres = value; } } } } public override uint Year { get { foreach (Tag tag in tags) { if (tag != null) { uint year = tag.Year; if (year != 0) { return year; } } } return 0u; } set { foreach (Tag tag in tags) { if (tag != null) { tag.Year = value; } } } } public override uint Track { get { foreach (Tag tag in tags) { if (tag != null) { uint track = tag.Track; if (track != 0) { return track; } } } return 0u; } set { foreach (Tag tag in tags) { if (tag != null) { tag.Track = value; } } } } public override uint TrackCount { get { foreach (Tag tag in tags) { if (tag != null) { uint trackCount = tag.TrackCount; if (trackCount != 0) { return trackCount; } } } return 0u; } set { foreach (Tag tag in tags) { if (tag != null) { tag.TrackCount = value; } } } } public override uint Disc { get { foreach (Tag tag in tags) { if (tag != null) { uint disc = tag.Disc; if (disc != 0) { return disc; } } } return 0u; } set { foreach (Tag tag in tags) { if (tag != null) { tag.Disc = value; } } } } public override uint DiscCount { get { foreach (Tag tag in tags) { if (tag != null) { uint discCount = tag.DiscCount; if (discCount != 0) { return discCount; } } } return 0u; } set { foreach (Tag tag in tags) { if (tag != null) { tag.DiscCount = value; } } } } public override string Lyrics { get { foreach (Tag tag in tags) { if (tag != null) { string lyrics = tag.Lyrics; if (lyrics != null) { return lyrics; } } } return null; } set { foreach (Tag tag in tags) { if (tag != null) { tag.Lyrics = value; } } } } public override string Grouping { get { foreach (Tag tag in tags) { if (tag != null) { string grouping = tag.Grouping; if (grouping != null) { return grouping; } } } return null; } set { foreach (Tag tag in tags) { if (tag != null) { tag.Grouping = value; } } } } public override uint BeatsPerMinute { get { foreach (Tag tag in tags) { if (tag != null) { uint beatsPerMinute = tag.BeatsPerMinute; if (beatsPerMinute != 0) { return beatsPerMinute; } } } return 0u; } set { foreach (Tag tag in tags) { if (tag != null) { tag.BeatsPerMinute = value; } } } } public override string Conductor { get { foreach (Tag tag in tags) { if (tag != null) { string conductor = tag.Conductor; if (conductor != null) { return conductor; } } } return null; } set { foreach (Tag tag in tags) { if (tag != null) { tag.Conductor = value; } } } } public override string Copyright { get { foreach (Tag tag in tags) { if (tag != null) { string copyright = tag.Copyright; if (copyright != null) { return copyright; } } } return null; } set { foreach (Tag tag in tags) { if (tag != null) { tag.Copyright = value; } } } } public override DateTime? DateTagged { get { foreach (Tag tag in tags) { if (tag != null) { DateTime? dateTagged = tag.DateTagged; if (dateTagged.HasValue) { return dateTagged; } } } return null; } set { foreach (Tag tag in tags) { if (tag != null) { tag.DateTagged = value; } } } } public override string MusicBrainzArtistId { get { foreach (Tag tag in tags) { if (tag != null) { string musicBrainzArtistId = tag.MusicBrainzArtistId; if (musicBrainzArtistId != null) { return musicBrainzArtistId; } } } return null; } set { foreach (Tag tag in tags) { if (tag != null) { tag.MusicBrainzArtistId = value; } } } } public override string MusicBrainzReleaseGroupId { get { foreach (Tag tag in tags) { if (tag != null) { string musicBrainzReleaseGroupId = tag.MusicBrainzReleaseGroupId; if (musicBrainzReleaseGroupId != null) { return musicBrainzReleaseGroupId; } } } return null; } set { foreach (Tag tag in tags) { if (tag != null) { tag.MusicBrainzReleaseGroupId = value; } } } } public override string MusicBrainzReleaseId { get { foreach (Tag tag in tags) { if (tag != null) { string musicBrainzReleaseId = tag.MusicBrainzReleaseId; if (musicBrainzReleaseId != null) { return musicBrainzReleaseId; } } } return null; } set { foreach (Tag tag in tags) { if (tag != null) { tag.MusicBrainzReleaseId = value; } } } } public override string MusicBrainzReleaseArtistId { get { foreach (Tag tag in tags) { if (tag != null) { string musicBrainzReleaseArtistId = tag.MusicBrainzReleaseArtistId; if (musicBrainzReleaseArtistId != null) { return musicBrainzReleaseArtistId; } } } return null; } set { foreach (Tag tag in tags) { if (tag != null) { tag.MusicBrainzReleaseArtistId = value; } } } } public override string MusicBrainzTrackId { get { foreach (Tag tag in tags) { if (tag != null) { string musicBrainzTrackId = tag.MusicBrainzTrackId; if (musicBrainzTrackId != null) { return musicBrainzTrackId; } } } return null; } set { foreach (Tag tag in tags) { if (tag != null) { tag.MusicBrainzTrackId = value; } } } } public override string MusicBrainzDiscId { get { foreach (Tag tag in tags) { if (tag != null) { string musicBrainzDiscId = tag.MusicBrainzDiscId; if (musicBrainzDiscId != null) { return musicBrainzDiscId; } } } return null; } set { foreach (Tag tag in tags) { if (tag != null) { tag.MusicBrainzDiscId = value; } } } } public override string MusicIpId { get { foreach (Tag tag in tags) { if (tag != null) { string musicIpId = tag.MusicIpId; if (musicIpId != null) { return musicIpId; } } } return null; } set { foreach (Tag tag in tags) { if (tag != null) { tag.MusicIpId = value; } } } } public override string AmazonId { get { foreach (Tag tag in tags) { if (tag != null) { string amazonId = tag.AmazonId; if (amazonId != null) { return amazonId; } } } return null; } set { foreach (Tag tag in tags) { if (tag != null) { tag.AmazonId = value; } } } } public override string MusicBrainzReleaseStatus { get { foreach (Tag tag in tags) { if (tag != null) { string musicBrainzReleaseStatus = tag.MusicBrainzReleaseStatus; if (musicBrainzReleaseStatus != null) { return musicBrainzReleaseStatus; } } } return null; } set { foreach (Tag tag in tags) { if (tag != null) { tag.MusicBrainzReleaseStatus = value; } } } } public override string MusicBrainzReleaseType { get { foreach (Tag tag in tags) { if (tag != null) { string musicBrainzReleaseType = tag.MusicBrainzReleaseType; if (musicBrainzReleaseType != null) { return musicBrainzReleaseType; } } } return null; } set { foreach (Tag tag in tags) { if (tag != null) { tag.MusicBrainzReleaseType = value; } } } } public override string MusicBrainzReleaseCountry { get { foreach (Tag tag in tags) { if (tag != null) { string musicBrainzReleaseCountry = tag.MusicBrainzReleaseCountry; if (musicBrainzReleaseCountry != null) { return musicBrainzReleaseCountry; } } } return null; } set { foreach (Tag tag in tags) { if (tag != null) { tag.MusicBrainzReleaseCountry = value; } } } } public override IPicture[] Pictures { get { foreach (Tag tag in tags) { if (tag != null) { IPicture[] pictures = tag.Pictures; if (pictures != null && pictures.Length != 0) { return pictures; } } } return base.Pictures; } set { foreach (Tag tag in tags) { if (tag != null) { tag.Pictures = value; } } } } public override double ReplayGainTrackGain { get { foreach (Tag tag in tags) { if (tag != null) { double replayGainTrackGain = tag.ReplayGainTrackGain; if (!double.IsNaN(replayGainTrackGain)) { return replayGainTrackGain; } } } return double.NaN; } set { foreach (Tag tag in tags) { if (tag != null) { tag.ReplayGainTrackGain = value; } } } } public override double ReplayGainTrackPeak { get { foreach (Tag tag in tags) { if (tag != null) { double replayGainTrackPeak = tag.ReplayGainTrackPeak; if (!double.IsNaN(replayGainTrackPeak)) { return replayGainTrackPeak; } } } return double.NaN; } set { foreach (Tag tag in tags) { if (tag != null) { tag.ReplayGainTrackPeak = value; } } } } public override double ReplayGainAlbumGain { get { foreach (Tag tag in tags) { if (tag != null) { double replayGainAlbumGain = tag.ReplayGainAlbumGain; if (!double.IsNaN(replayGainAlbumGain)) { return replayGainAlbumGain; } } } return double.NaN; } set { foreach (Tag tag in tags) { if (tag != null) { tag.ReplayGainAlbumGain = value; } } } } public override double ReplayGainAlbumPeak { get { foreach (Tag tag in tags) { if (tag != null) { double replayGainAlbumPeak = tag.ReplayGainAlbumPeak; if (!double.IsNaN(replayGainAlbumPeak)) { return replayGainAlbumPeak; } } } return double.NaN; } set { foreach (Tag tag in tags) { if (tag != null) { tag.ReplayGainAlbumPeak = value; } } } } public override string InitialKey { get { foreach (Tag tag in tags) { if (tag != null) { string initialKey = tag.InitialKey; if (initialKey != null) { return initialKey; } } } return null; } set { foreach (Tag tag in tags) { if (tag != null) { tag.InitialKey = value; } } } } public override string RemixedBy { get { foreach (Tag tag in tags) { if (tag != null) { string remixedBy = tag.RemixedBy; if (remixedBy != null) { return remixedBy; } } } return null; } set { foreach (Tag tag in tags) { if (tag != null) { tag.RemixedBy = value; } } } } public override string Publisher { get { foreach (Tag tag in tags) { if (tag != null) { string publisher = tag.Publisher; if (publisher != null) { return publisher; } } } return null; } set { foreach (Tag tag in tags) { if (tag != null) { tag.Publisher = value; } } } } public override string ISRC { get { foreach (Tag tag in tags) { if (tag != null) { string iSRC = tag.ISRC; if (iSRC != null) { return iSRC; } } } return null; } set { foreach (Tag tag in tags) { if (tag != null) { tag.ISRC = value; } } } } public override string Length { get { foreach (Tag tag in tags) { if (tag != null) { string length = tag.Length; if (length != null) { return length; } } } return null; } set { foreach (Tag tag in tags) { if (tag != null) { tag.Length = value; } } } } public override bool IsEmpty { get { foreach (Tag tag in tags) { if (tag.IsEmpty) { return true; } } return false; } } public CombinedTag() { tags = new List<Tag>(); } public CombinedTag(params Tag[] tags) { this.tags = new List<Tag>(tags); } public void SetTags(params Tag[] tags) { this.tags.Clear(); this.tags.AddRange(tags); } protected void InsertTag(int index, Tag tag) { tags.Insert(index, tag); } protected void AddTag(Tag tag) { tags.Add(tag); } protected void RemoveTag(Tag tag) { tags.Remove(tag); } protected void ClearTags() { tags.Clear(); } public override void Clear() { foreach (Tag tag in tags) { tag.Clear(); } } } [Serializable] public class CorruptFileException : Exception { public CorruptFileException(string message) : base(message) { } public CorruptFileException() { } public CorruptFileException(string message, Exception innerException) : base(message, innerException) { } protected CorruptFileException(SerializationInfo info, StreamingContext context) : base(info, context) { } } internal static class Debugger { public delegate void DebugMessageSentHandler(string message); private struct DebugTimeData { public TimeSpan time; public long occurances; public DebugTimeData(TimeSpan time, int occurances) { this.time = time; this.occurances = occurances; } } private static readonly string allowed = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ`~!@#$%^&*()_+-={}[];:'\",.<>?/\\|"; private static readonly Dictionary<object, Dictionary<object, DebugTimeData>> debug_times = new Dictionary<object, Dictionary<object, DebugTimeData>>(); public static event DebugMessageSentHandler DebugMessageSent; public static void Debug(string message) { Debugger.DebugMessageSent?.Invoke(message); } public static void DumpHex(ByteVector data) { DumpHex(data.Data); } public static void DumpHex(byte[] data) { int num = 16; int num2 = data.Length / num + ((data.Length % num != 0) ? 1 : 0); for (int i = 0; i < num2; i++) { for (int j = 0; j < num; j++) { if (i == num2 - 1 && data.Length % num != 0 && j >= data.Length % num) { Console.Write(" "); } else { Console.Write(" {0:x2}", data[i * num + j]); } } Console.Write(" | "); for (int k = 0; k < num; k++) { if (i == num2 - 1 && data.Length % num != 0 && k >= data.Length % num) { Console.Write(" "); } else { WriteByte2(data[i * num + k]); } } Console.WriteLine(); } Console.WriteLine(); } private static void WriteByte2(byte data) { string text = allowed; foreach (char c in text) { if (c == data) { Console.Write(c); return; } } Console.Write("."); } public static void AddDebugTime(object o1, object o2, DateTime start) { DebugTimeData value = new DebugTimeData(DateTime.Now - start, 1); if (debug_times.ContainsKey(o1) && debug_times[o1].ContainsKey(o2)) { value.time += debug_times[o1][o2].time; value.occurances += debug_times[o1][o2].occurances; } if (!debug_times.ContainsKey(o1)) { debug_times.Add(o1, new Dictionary<object, DebugTimeData>()); } if (!debug_times[o1].ContainsKey(o2)) { debug_times[o1].Add(o2, value); } else { debug_times[o1][o2] = value; } } public static void DumpDebugTime(object o1) { Console.WriteLine(o1.ToString()); if (!debug_times.ContainsKey(o1)) { return; } foreach (KeyValuePair<object, DebugTimeData> item in debug_times[o1]) { Console.WriteLine(" {0}", item.Key); Console.WriteLine(" Objects: {0}", item.Value.time); Console.WriteLine(" Total: {0}", item.Value.occurances); Console.WriteLine(" Average: {0}", new TimeSpan(item.Value.time.Ticks / item.Value.occurances)); Console.WriteLine(); } debug_times.Remove(o1); } } [Flags] public enum ReadStyle { None = 0, Average = 2, PictureLazy = 4 } public abstract class File : IDisposable { public enum AccessMode { Read, Write, Closed } public delegate File FileTypeResolver(IFileAbstraction abstraction, string mimetype, ReadStyle style); public class LocalFileAbstraction : IFileAbstraction { private readonly string name; public string Name => name; public Stream ReadStream => System.IO.File.Open(Name, FileMode.Open, FileAccess.Read, FileShare.Read); public Stream WriteStream => System.IO.File.Open(Name, FileMode.Open, FileAccess.ReadWrite); public LocalFileAbstraction(string path) { if (path == null) { throw new ArgumentNullException("path"); } name = path; } public void CloseStream(Stream stream) { if (stream == null) { throw new ArgumentNullException("stream"); } stream.Close(); } } public interface IFileAbstraction { string Name { get; } Stream ReadStream { get; } Stream WriteStream { get; } void CloseStream(Stream stream); } private Stream file_stream; protected IFileAbstraction file_abstraction; private static readonly int buffer_size = 1024; private static readonly List<FileTypeResolver> file_type_resolvers = new List<FileTypeResolver>(); private List<string> corruption_reasons; public static uint BufferSize => (uint)buffer_size; public abstract Tag Tag { get; } public abstract Properties Properties { get; } public TagTypes TagTypesOnDisk { get; protected set; } = TagTypes.None; public TagTypes TagTypes => Tag?.TagTypes ?? TagTypes.None; public string Name => file_abstraction.Name; public string MimeType { get; internal set; } public long Tell => (Mode == AccessMode.Closed) ? 0 : file_stream.Position; public long Length => (Mode == AccessMode.Closed) ? 0 : file_stream.Length; public long InvariantStartPosition { get; protected set; } = -1L; public long InvariantEndPosition { get; protected set; } = -1L; public AccessMode Mode { get { if (file_stream == null) { return AccessMode.Closed; } if (file_stream.CanWrite) { return AccessMode.Write; } return AccessMode.Read; } set { if (Mode != value && (Mode != AccessMode.Write || value != 0)) { if (file_stream != null) { file_abstraction.CloseStream(file_stream); } file_stream = null; switch (value) { case AccessMode.Read: file_stream = file_abstraction.ReadStream; break; case AccessMode.Write: file_stream = file_abstraction.WriteStream; break; } Mode = value; } } } public IFileAbstraction FileAbstraction => file_abstraction; public virtual bool Writeable => !PossiblyCorrupt; public bool PossiblyCorrupt => corruption_reasons != null; public IEnumerable<string> CorruptionReasons => corruption_reasons; protected File(string path) { if (path == null) { throw new ArgumentNullException("path"); } file_abstraction = new LocalFileAbstraction(path); } protected File(IFileAbstraction abstraction) { if (abstraction == null) { throw new ArgumentNullException("abstraction"); } file_abstraction = abstraction; } internal void MarkAsCorrupt(string reason) { if (corruption_reasons == null) { corruption_reasons = new List<string>(); } corruption_reasons.Add(reason); } public void Dispose() { Mode = AccessMode.Closed; } public abstract void Save(); public abstract void RemoveTags(TagTypes types); public abstract Tag GetTag(TagTypes type, bool create); public Tag GetTag(TagTypes type) { return GetTag(type, create: false); } public ByteVector ReadBlock(int length) { if (length < 0) { throw new ArgumentException("Length must be non-negative", "length"); } if (length == 0) { return new ByteVector(); } Mode = AccessMode.Read; byte[] array = new byte[length]; int num = 0; int num2 = 0; int num3 = length; do { num = file_stream.Read(array, num2, num3); num2 += num; num3 -= num; } while (num3 > 0 && num != 0); return new ByteVector(array, num2); } public void WriteBlock(ByteVector data) { if (data == null) { throw new ArgumentNullException("data"); } Mode = AccessMode.Write; file_stream.Write(data.Data, 0, data.Count); } public long Find(ByteVector pattern, long startPosition, ByteVector before) { if (pattern == null) { throw new ArgumentNullException("pattern"); } Mode = AccessMode.Read; if (pattern.Count > buffer_size) { return -1L; } long num = startPosition; long position = file_stream.Position; try { file_stream.Position = startPosition; ByteVector byteVector = ReadBlock(buffer_size); while (byteVector.Count > 0) { int num2 = byteVector.Find(pattern); if (before != null) { int num3 = byteVector.Find(before); if (num3 < num2) { return -1L; } } if (num2 >= 0) { return num + num2; } num += buffer_size - pattern.Count; if (before != null && before.Count > pattern.Count) { num -= before.Count - pattern.Count; } file_stream.Position = num; byteVector = ReadBlock(buffer_size); } return -1L; } finally { file_stream.Position = position; } } public long Find(ByteVector pattern, long startPosition) { return Find(pattern, startPosition, null); } public long Find(ByteVector pattern) { return Find(pattern, 0L); } private long RFind(ByteVector pattern, long startPosition, ByteVector after) { if (pattern == null) { throw new ArgumentNullException("pattern"); } Mode = AccessMode.Read; if (pattern.Count > buffer_size) { return -1L; } long position = file_stream.Position; long num = Length - startPosition; int num2 = buffer_size; num2 = (int)Math.Min(num, buffer_size); num -= num2; file_stream.Position = num; ByteVector byteVector = ReadBlock(num2); while (byteVector.Count > 0) { long num3 = byteVector.RFind(pattern); if (num3 >= 0) { file_stream.Position = position; return num + num3; } if (after != null && byteVector.RFind(after) >= 0) { file_stream.Position = position; return -1L; } num2 = (int)Math.Min(num, buffer_size); num -= num2; if (num2 + pattern.Count > buffer_size) { num += pattern.Count; } file_stream.Position = num; byteVector = ReadBlock(num2); } file_stream.Position = position; return -1L; } public long RFind(ByteVector pattern, long startPosition) { return RFind(pattern, startPosition, null); } public long RFind(ByteVector pattern) { return RFind(pattern, 0L); } public void Insert(ByteVector data, long start, long replace) { if (data == null) { throw new ArgumentNullException("data"); } Insert(data, data.Count, start, replace); } public void Insert(ByteVector data, long start) { Insert(data, start, 0L); } public void Insert(long size, long start) { Insert(null, size, start, 0L); } public void RemoveBlock(long start, long length) { if (length > 0) { Mode = AccessMode.Write; int length2 = buffer_size; long num = start + length; long num2 = start; ByteVector byteVector = (byte)1; while (byteVector.Count != 0) { file_stream.Position = num; byteVector = ReadBlock(length2); num += byteVector.Count; file_stream.Position = num2; WriteBlock(byteVector); num2 += byteVector.Count; } Truncate(num2); } } public void Seek(long offset, SeekOrigin origin) { if (Mode != AccessMode.Closed) { file_stream.Seek(offset, origin); } } public void Seek(long offset) { Seek(offset, SeekOrigin.Begin); } public static File Create(string path) { return Create(path, null, ReadStyle.Average); } public static File Create(IFileAbstraction abstraction) { return Create(abstraction, null, ReadStyle.Average); } public static File Create(string path, ReadStyle propertiesStyle) { return Create(path, null, propertiesStyle); } public static File Create(IFileAbstraction abstraction, ReadStyle propertiesStyle) { return Create(abstraction, null, propertiesStyle); } public static File Create(string path, string mimetype, ReadStyle propertiesStyle) { return Create(new LocalFileAbstraction(path), mimetype, propertiesStyle); } public static File Create(IFileAbstraction abstraction, string mimetype, ReadStyle propertiesStyle) { if (mimetype == null) { string text = string.Empty; int num = abstraction.Name.LastIndexOf(".") + 1; if (num >= 1 && num < abstraction.Name.Length) { text = abstraction.Name.Substring(num, abstraction.Name.Length - num); } mimetype = "taglib/" + text.ToLower(CultureInfo.InvariantCulture); } foreach (FileTypeResolver file_type_resolver in file_type_resolvers) { File file = file_type_resolver(abstraction, mimetype, propertiesStyle); if (file != null) { return file; } } if (!FileTypes.AvailableTypes.ContainsKey(mimetype)) { throw new UnsupportedFormatException(string.Format(CultureInfo.InvariantCulture, "{0} ({1})", abstraction.Name, mimetype)); } Type type = FileTypes.AvailableTypes[mimetype]; try { File file2 = (File)Activator.CreateInstance(type, abstraction, propertiesStyle); file2.MimeType = mimetype; return file2; } catch (TargetInvocationException ex) { PrepareExceptionForRethrow(ex.InnerException); throw ex.InnerException; } } public static void AddFileTypeResolver(FileTypeResolver resolver) { if (resolver != null) { file_type_resolvers.Insert(0, resolver); } } protected void PreSave() { if (!Writeable) { throw new InvalidOperationException("File not writeable."); } if (PossiblyCorrupt) { throw new CorruptFileException("Corrupted file cannot be saved."); } if (Tag?.Pictures == null) { return; } IPicture[] pictures = Tag.Pictures; foreach (IPicture picture in pictures) { if (picture is ILazy lazy) { lazy.Load(); } } } private void Insert(ByteVector data, long size, long start, long replace) { Mode = AccessMode.Write; if (size == replace) { if (data != null) { file_stream.Position = start; WriteBlock(data); } return; } if (size < replace) { if (data != null) { file_stream.Position = start; WriteBlock(data); } RemoveBlock(start + size, replace - size); return; } int num = (int)(size - replace); int num2 = num % buffer_size; if (num2 != 0) { num += buffer_size - num2; } long num3 = start + replace; long num4 = start; file_stream.Position = num3; byte[] data2 = ReadBlock(num).Data; num3 += num; if (data != null) { file_stream.Position = num4; WriteBlock(data); } else if (start + size > Length) { file_stream.SetLength(start + size); } num4 += size; byte[] array = new byte[data2.Length]; Array.Copy(data2, 0, array, 0, data2.Length); while (num != 0) { file_stream.Position = num3; int num5 = file_stream.Read(data2, 0, (num < data2.Length) ? num : data2.Length); num3 += num; file_stream.Position = num4; file_stream.Write(array, 0, (num < array.Length) ? num : array.Length); num4 += num; Array.Copy(data2, 0, array, 0, num5); num = num5; } } protected void Truncate(long length) { AccessMode mode = Mode; Mode = AccessMode.Write; file_stream.SetLength(length); Mode = mode; } private static void PrepareExceptionForRethrow(Exception ex) { StreamingContext context = new StreamingContext(StreamingContextStates.CrossAppDomain); ObjectManager objectManager = new ObjectManager(null, context); SerializationInfo info = new SerializationInfo(ex.GetType(), new FormatterConverter()); ex.GetObjectData(info, context); objectManager.RegisterObject(ex, 1L, info); objectManager.DoFixups(); } } public static class FileTypes { private static Dictionary<string, Type> file_types; private static readonly Type[] static_file_types; public static IDictionary<string, Type> AvailableTypes => file_types; static FileTypes() { static_file_types = new Type[26] { typeof(TagLib.Aac.File), typeof(TagLib.Aiff.File), typeof(TagLib.Ape.File), typeof(TagLib.Asf.File), typeof(TagLib.Audible.File), typeof(TagLib.Dsf.File), typeof(TagLib.Flac.File), typeof(TagLib.Matroska.File), typeof(TagLib.Gif.File), typeof(TagLib.Image.NoMetadata.File), typeof(TagLib.Jpeg.File), typeof(TagLib.Mpeg4.File), typeof(AudioFile), typeof(TagLib.Mpeg.File), typeof(TagLib.MusePack.File), typeof(TagLib.Ogg.File), typeof(TagLib.Png.File), typeof(TagLib.Riff.File), typeof(TagLib.Tiff.Arw.File), typeof(TagLib.Tiff.Cr2.File), typeof(TagLib.Tiff.Dng.File), typeof(TagLib.Tiff.File), typeof(TagLib.Tiff.Nef.File), typeof(TagLib.Tiff.Pef.File), typeof(TagLib.Tiff.Rw2.File), typeof(TagLib.WavPack.File) }; Init(); } internal static void Init() { if (file_types == null) { file_types = new Dictionary<string, Type>(); Type[] array = static_file_types; foreach (Type type in array) { Register(type); } } } public static void Register(Type type) { Attribute[] customAttributes = Attribute.GetCustomAttributes(type, typeof(SupportedMimeType), inherit: false); if (customAttributes.Length != 0) { Attribute[] array = customAttributes; for (int i = 0; i < array.Length; i++) { SupportedMimeType supportedMimeType = (SupportedMimeType)array[i]; file_types.Add(supportedMimeType.MimeType, type); } } } } public static class Genres { private static readonly string[] audio = new string[148] { "Blues", "Classic Rock", "Country", "Dance", "Disco", "Funk", "Grunge", "Hip-Hop", "Jazz", "Metal", "New Age", "Oldies", "Other", "Pop", "R&B", "Rap", "Reggae", "Rock", "Techno", "Industrial", "Alternative", "Ska", "Death Metal", "Pranks", "Soundtrack", "Euro-Techno", "Ambient", "Trip-Hop", "Vocal", "Jazz+Funk", "Fusion", "Trance", "Classical", "Instrumental", "Acid", "House", "Game", "Sound Clip", "Gospel", "Noise", "Alternative Rock", "Bass", "Soul", "Punk", "Space", "Meditative", "Instrumental Pop", "Instrumental Rock", "Ethnic", "Gothic", "Darkwave", "Techno-Industrial", "Electronic", "Pop-Folk", "Eurodance", "Dream", "Southern Rock", "Comedy", "Cult", "Gangsta", "Top 40", "Christian Rap", "Pop/Funk", "Jungle", "Native American", "Cabaret", "New Wave", "Psychedelic", "Rave", "Showtunes", "Trailer", "Lo-Fi", "Tribal", "Acid Punk", "Acid Jazz", "Polka", "Retro", "Musical", "Rock & Roll", "Hard Rock", "Folk", "Folk/Rock", "National Folk", "Swing", "Fusion", "Bebob", "Latin", "Revival", "Celtic", "Bluegrass", "Avantgarde", "Gothic Rock", "Progressive Rock", "Psychedelic Rock", "Symphonic Rock", "Slow Rock", "Big Band", "Chorus", "Easy Listening", "Acoustic", "Humour", "Speech", "Chanson", "Opera", "Chamber Music", "Sonata", "Symphony", "Booty Bass", "Primus", "Porn Groove", "Satire", "Slow Jam", "Club", "Tango", "Samba", "Folklore", "Ballad", "Power Ballad", "Rhythmic Soul", "Freestyle", "Duet", "Punk Rock", "Drum Solo", "A Cappella", "Euro-House", "Dance Hall", "Goa", "Drum & Bass", "Club-House", "Hardcore", "Terror", "Indie", "BritPop", "Negerpunk", "Polsk Punk", "Beat", "Christian Gangsta Rap", "Heavy Metal", "Black Metal", "Crossover", "Contemporary Christian", "Christian Rock", "Merengue", "Salsa", "Thrash Metal", "Anime", "Jpop", "Synthpop" }; private static readonly string[] video = new string[40] { "Action", "Action/Adventure", "Adult", "Adventure", "Catastrophe", "Child's", "Claymation", "Comedy", "Concert", "Documentary", "Drama", "Eastern", "Entertaining", "Erotic", "Extremal Sport", "Fantasy", "Fashion", "Historical", "Horror", "Horror/Mystic", "Humor", "Indian", "Informercial", "Melodrama", "Military & War", "Music Video", "Musical", "Mystery", "Nature", "Political Satire", "Popular Science", "Psychological Thriller", "Religion", "Science Fiction", "Scifi Action", "Slapstick", "Splatter", "Sports", "Thriller", "Western" }; public static string[] Audio => (string[])audio.Clone(); public static string[] Video => (string[])video.Clone(); public static byte AudioToIndex(string name) { for (byte b = 0; b < audio.Length; b++) { if (name == audio[b]) { return b; } } return byte.MaxValue; } public static byte VideoToIndex(string name) { for (byte b = 0; b < video.Length; b++) { if (name == video[b]) { return b; } } return byte.MaxValue; } public static string IndexToAudio(byte index) { return (index < audio.Length) ? audio[index] : null; } public static string IndexToVideo(byte index) { return (index < video.Length) ? video[index] : null; } public static string IndexToAudio(string text) { return IndexToAudio(StringToByte(text)); } public static string IndexToVideo(string text) { return IndexToVideo(StringToByte(text)); } private static byte StringToByte(string text) { int num; if (text != null && text.Length > 2 && text[0] == '(' && (num = text.IndexOf(')')) != -1 && byte.TryParse(text.Substring(1, num - 1), out var result)) { return result; } if (text != null && byte.TryParse(text, out result)) { return result; } return byte.MaxValue; } } [Flags] public enum MediaTypes { None = 0, Audio = 1, Video = 2, Photo = 4, Text = 8 } public interface ICodec { TimeSpan Duration { get; } MediaTypes MediaTypes { get; } string Description { get; } } public interface IAudioCodec : ICodec { int AudioBitrate { get; } int AudioSampleRate { get; } int AudioChannels { get; } } public interface ILosslessAudioCodec { int BitsPerSample { get; } } public interface IVideoCodec : ICodec { int VideoWidth { get; } int VideoHeight { get; } } public interface IPhotoCodec : ICodec { int PhotoWidth { get; } int PhotoHeight { get; } int PhotoQuality { get; } } public interface ILazy { bool IsLoaded { get; } void Load(); } public class ListBase<T> : IList<T>, ICollection<T>, IEnumerable<T>, IEnumerable where T : IComparable<T> { private readonly List<T> data = new List<T>(); public bool IsEmpty => Count == 0; public bool IsReadOnly => false; public bool IsFixedSize => false; public T this[int index] { get { return data[index]; } set { data[index] = value; } } public int Count => data.Count; public bool IsSynchronized => false; public object SyncRoot => this; public ListBase() { } public ListBase(ListBase<T> list) { if (list != null) { Add(list); } } public ListBase(params T[] list) { if (list != null) { Add(list); } } public void Add(ListBase<T> list) { if (list != null) { data.AddRange(list); } } public void Add(IEnumerable<T> list) { if (list != null) { data.AddRange(list); } } public void Add(T[] list) { if (list != null) { data.AddRange(list); } } public virtual void SortedInsert(T item, bool unique) { if (item == null) { throw new ArgumentNullException("item"); } int i; for (i = 0; i < data.Count; i++) { ref T reference = ref item; T val = default(T); if (val == null) { val = reference; reference = ref val; } if (reference.CompareTo(data[i]) == 0 && unique) { return; } ref T reference2 = ref item; val = default(T); if (val == null) { val = reference2; reference2 = ref val; } if (reference2.CompareTo(data[i]) <= 0) { break; } } Insert(i, item); } public void SortedInsert(T item) { if (item == null) { throw new ArgumentNullException("item"); } SortedInsert(item, unique: false); } public T[] ToArray() { return data.ToArray(); } public void Add(T item) { data.Add(item); } public void Clear() { data.Clear(); } public bool Contains(T item) { return data.Contains(item); } public int IndexOf(T item) { return data.IndexOf(item); } public void Insert(int index, T item) { data.Insert(index, item); } public bool Remove(T item) { return data.Remove(item); } public void RemoveAt(int index) { data.RemoveAt(index); } public string ToString(string separator) { StringBuilder stringBuilder = new StringBuilder(); for (int i = 0; i < Count; i++) { if (i != 0) { stringBuilder.Append(separator); } stringBuilder.Append(this[i]); } return stringBuilder.ToString(); } public override string ToString() { return ToString(", "); } public void CopyTo(T[] array, int arrayIndex) { data.CopyTo(array, arrayIndex); } public IEnumerator<T> GetEnumerator() { return data.GetEnumerator(); } IEnumerator IEnumerable.GetEnumerator() { return data.GetEnumerator(); } } public class TextBox : Box { private ByteVector data; public override ByteVector Data { get { return data; } set { data = value; } } public TextBox(BoxHeader header, File file, IsoHandlerBox handler) : base(header, handler) { if (file == null) { throw new ArgumentNullException("file"); } data = LoadData(file); } } public class UrlBox : Box { private ByteVector data; public override ByteVector Data { get { return data; } set { data = value; } } public UrlBox(BoxHeader header, File file, IsoHandlerBox handler) : base(header, handler) { if (file == null) { throw new ArgumentNullException("file"); } data = LoadData(file); } } public enum PictureType { Other = 0, FileIcon = 1, OtherFileIcon = 2, FrontCover = 3, BackCover = 4, LeafletPage = 5, Media = 6, LeadArtist = 7, Artist = 8, Conductor = 9, Band = 10, Composer = 11, Lyricist = 12, RecordingLocation = 13, DuringRecording = 14, DuringPerformance = 15, MovieScreenCapture = 16, ColoredFish = 17, Illustration = 18, BandLogo = 19, PublisherLogo = 20, NotAPicture = 255 } public interface IPicture { string MimeType { get; set; } PictureType Type { get; set; } string Filename { get; set; } string Description { get; set; } ByteVector Data { get; set; } } public class Picture : IPicture { private static readonly string[] lutExtensionMime = new string[150] { "aac", "audio/aac", "abw", "application/x-abiword", "arc", "application/octet-stream", "avi", "video/x-msvideo", "azw", "application/vnd.amazon.ebook", "bin", "application/octet-stream", "bmp", "image/bmp", "bmp", "image/x-windows-bmp", "bm", "image/bmp", "bz", "application/x-bzip", "bz2", "application/x-bzip2", "csh", "application/x-csh", "css", "text/css", "csv", "text/csv", "doc", "application/msword", "eot", "application/vnd.ms-fontobject", "epub", "application/epub+zip", "gif", "image/gif", "htm", "text/html", "html", "text/html", "ico", "image/x-icon", "ics", "text/calendar", "jar", "application/java-archive", "jpg", "image/jpeg", "jpeg", "image/jpeg", "js", "application/javascript", "json", "application/json", "mid", "audio/midi", "midi", "audio/midi", "mp3", "audio/mpeg", "mp1", "audio/mpeg", "mp2", "audio/mpeg", "mpg", "video/mpeg", "mpeg", "video/mpeg", "m4a", "audio/mp4", "mp4", "video/mp4", "m4v", "video/mp4", "mpkg", "application/vnd.apple.installer+xml", "odp", "application/vnd.oasis.opendocument.presentation", "ods", "application/vnd.oasis.opendocument.spreadsheet", "odt", "application/vnd.oasis.opendocument.text", "oga", "audio/ogg", "ogg", "audio/ogg", "ogx", "application/ogg", "ogv", "video/ogg", "otf", "font/otf", "png", "image/png", "pdf", "application/pdf", "ppt", "application/vnd.ms-powerpoint", "rar", "application/x-rar-compressed", "rtf", "application/rtf", "sh", "application/x-sh", "svg", "image/svg+xml", "swf", "application/x-shockwave-flash", "tar", "application/x-tar", "tif", "image/tiff", "tiff", "image/tiff", "ts", "video/vnd.dlna.mpeg-tts", "ttf", "font/ttf", "vsd", "application/vnd.visio", "wav", "audio/x-wav", "weba", "audio/webm", "webm", "video/webm", "webp", "image/webp", "woff", "font/woff", "woff2", "font/woff2", "xhtml", "application/xhtml+xml", "xls", "application/vnd.ms", "xlsx", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", "xml", "application/xml", "xul", "application/vnd.mozilla.xul+xml", "zip", "application/zip", "3gp", "video/3gpp", "3g2", "video/3gpp2", "7z", "application/x-7z-compressed" }; public string MimeType { get; set; } public PictureType Type { get; set; } public string Filename { get; set; } public string Description { get; set; } public ByteVector Data { get; set; } public Picture() { } public Picture(string path) { if (path == null) { throw new ArgumentNullException("path"); } Data = ByteVector.FromPath(path); Filename = Path.GetFileName(path); Description = Filename; MimeType = GetMimeFromExtension(Filename); Type = (MimeType.StartsWith("image/") ? PictureType.FrontCover : PictureType.NotAPicture); } public Picture(File.IFileAbstraction abstraction) { if (abstraction == null) { throw new ArgumentNullException("abstraction"); } Data = ByteVector.FromFile(abstraction); Filename = abstraction.Name; Description = abstraction.Name; if (!string.IsNullOrEmpty(Filename) && Filename.Contains(".")) { MimeType = GetMimeFromExtension(Filename); Type = (MimeType.StartsWith("image/") ? PictureType.FrontCover : PictureType.NotAPicture); return; } string extensionFromData = GetExtensionFromData(Data); MimeType = GetMimeFromExtension(extensionFromData); if (extensionFromData != null) { Type = PictureType.FrontCover; Filename = (Description = "cover" + extensionFromData); } else { Type = PictureType.NotAPicture; Filename = "UnknownType"; } } public Picture(ByteVector data) { if (data == null) { throw new ArgumentNullException("data"); } Data = new ByteVector(data); string extensionFromData = GetExtensionFromData(data); MimeType = GetMimeFromExtension(extensionFromData); if (extensionFromData != null) { Type = PictureType.FrontCover; Filename = (Description = "cover" + extensionFromData); } else { Type = PictureType.NotAPicture; Filename = "UnknownType"; } } public Picture(IPicture picture) { MimeType = picture.MimeType; Type = picture.Type; Filename = picture.Filename; Description = picture.Description; Data = picture.Data; } [Obsolete("Use Picture(string filename) constructor instead.")] public static Picture CreateFromPath(string filename) { return new Picture(filename); } [Obsolete("Use Picture(File.IFileAbstraction abstraction) constructor instead.")] public static Picture CreateFromFile(File.IFileAbstraction abstraction) { return new Picture(abstraction); } public static string GetExtensionFromData(ByteVector data) { string result = null; if (data.Count >= 4) { if (data[1] == 80 && data[2] == 78 && data[3] == 71) { result = ".png"; } else if (data[0] == 71 && data[1] == 73 && data[2] == 70) { result = ".gif"; } else if (data[0] == 66 && data[1] == 77) { result = ".bmp"; } else if (data[0] == byte.MaxValue && data[1] == 216 && data[data.Count - 2] == byte.MaxValue && data[data.Count - 1] == 217) { result = ".jpg"; } } return result; } public static string GetExtensionFromMime(string mime) { string result = null; for (int i = 1; i < lutExtensionMime.Length; i += 2) { if (lutExtensionMime[i] == mime) { result = lutExtensionMime[i - 1]; break; } } return result; } public static string GetMimeFromExtension(string name) { string result = "application/octet-stream"; if (string.IsNullOrEmpty(name)) { return result; } string extension = Path.GetExtension(name); extension = ((!string.IsNullOrEmpty(extension)) ? extension.Substring(1) : name); extension = extension.ToLower(); for (int i = 0; i < lutExtensionMime.Length; i += 2) { if (lutExtensionMime[i] == extension) { result = lutExtensionMime[i + 1]; break; } } return result; } } public class PictureLazy : IPicture, ILazy { private string mime_type; private PictureType type; private string filename; private ByteVector data; private File.IFileAbstraction file; private readonly long stream_offset; private readonly long stream_size = -1L; public string MimeType { get { if (mime_type == null) { Load(); } return mime_type; } set { mime_type = value; } } public PictureType Type { get { if (type == PictureType.Other && mime_type == null) { Load(); } return type; } set { type = value; } } public string Filename { get { if (filename == null) { Load(); } return filename; } set { filename = value; } } public string Description { get; set; } public ByteVector Data { get { if (data == null) { Load(); } return data; } set { data = value; } } public bool IsLoaded => data != null; public PictureLazy() { } public PictureLazy(string path) { if (path == null) { throw new ArgumentNullException("path"); } file = new File.LocalFileAbstraction(path); filename = Path.GetFileName(path); Description = filename; mime_type = Picture.GetMimeFromExtension(filename); type = (mime_type.StartsWith("image/") ? PictureType.FrontCover : PictureType.NotAPicture); } public PictureLazy(File.IFileAbstraction abstraction, long offset = 0L, long size = -1L) { if (abstraction == null) { throw new ArgumentNullException("abstraction"); } file = abstraction; stream_offset = offset; stream_size = size; filename = abstraction.Name; Description = abstraction.Name; if (!string.IsNullOrEmpty(filename) && filename.Contains(".")) { mime_type = Picture.GetMimeFromExtension(filename); type = (mime_type.StartsWith("image/") ? PictureType.FrontCover : PictureType.NotAPicture); } } public PictureLazy(ByteVector data) { if (data == null) { throw new ArgumentNullException("data"); } Data = new ByteVector(data); string extensionFromData = Picture.GetExtensionFromData(data); MimeType = Picture.GetMimeFromExtension(extensionFromData); if (extensionFromData != null) { type = PictureType.FrontCover; filename = (Description = "cover" + extensionFromData); } else { type = PictureType.NotAPicture; filename = "UnknownType"; } } public PictureLazy(IPicture picture) { mime_type = picture.MimeType; type = picture.Type; filename = picture.Filename; Description = picture.Description; data = picture.Data; } public void Load() { if (data != null) { return; } Stream stream = null; try { if (stream_size == 0) { data = new ByteVector(); } else if (stream_size > 0) { stream = file.ReadStream; stream.Seek(stream_offset, SeekOrigin.Begin); int num = 0; int num2 = 0; int num3 = (int)stream_size; byte[] buffer = new byte[num3]; do { num = stream.Read(buffer, num2, num3); num2 += num; num3 -= num; } while (num3 > 0 && num != 0); data = new ByteVector(buffer, num2); } else { stream = file.ReadStream; stream.Seek(stream_offset, SeekOrigin.Begin); data = ByteVector.FromStream(stream); } } finally { if (stream != null && file != null) { file.CloseStream(stream); } file = null; } if (mime_type != null) { return; } string extensionFromData = Picture.GetExtensionFromData(data); MimeType = Picture.GetMimeFromExtension(extensionFromData); if (extensionFromData != null) { type = PictureType.FrontCover; if (filename == null) { string text2 = (Description = "cover" + extensionFromData); filename = text2; } } else { type = PictureType.NotAPicture; if (filename == null) { filename = "UnknownType"; } } } } public class Properties : IAudioCodec, ICodec, IVideoCodec, IPhotoCodec { private readonly ICodec[] codecs = new ICodec[0]; private TimeSpan duration = TimeSpan.Zero; public IEnumerable<ICodec> Codecs => codecs; public TimeSpan Duration { get { TimeSpan timeSpan = duration; if (timeSpan != TimeSpan.Zero) { return timeSpan; } ICodec[] array = codecs; foreach (ICodec codec in array) { if (codec != null && codec.Duration > timeSpan) { timeSpan = codec.Duration; } } return timeSpan; } } public MediaTypes MediaTypes { get { MediaTypes mediaTypes = MediaTypes.None; ICodec[] array = codecs; foreach (ICodec codec in array) { if (codec != null) { mediaTypes |= codec.MediaTypes; } } return mediaTypes; } } public string Description { get { StringBuilder stringBuilder = new StringBuilder(); ICodec[] array = codecs; foreach (ICodec codec in array) { if (codec != null) { if (stringBuilder.Length != 0) { stringBuilder.Append("; "); } stringBuilder.Append(codec.Description); } } return stringBuilder.ToString(); } } public int AudioBitrate { get { ICodec[] array = codecs; foreach (ICodec codec in array) { if (codec != null && (codec.MediaTypes & MediaTypes.Audio) != 0 && codec is IAudioCodec audioCodec && audioCodec.AudioBitrate != 0) { return audioCodec.AudioBitrate; } } return 0; } } public int AudioSampleRate { get { ICodec[] array = codecs; foreach (ICodec codec in array) { if (codec != null && (codec.MediaTypes & MediaTypes.Audio) != 0 && codec is IAudioCodec audioCodec && audioCodec.AudioSampleRate != 0) { return audioCodec.AudioSampleRate; } } return 0; } } public int BitsPerSample { get { ICodec[] array = codecs; foreach (ICodec codec in array) { if (codec != null && (codec.MediaTypes & MediaTypes.Audio) != 0 && codec is ILosslessAudioCodec losslessAudioCodec && losslessAudioCodec.BitsPerSample != 0) { return losslessAudioCodec.BitsPerSample; } } return 0; } } public int AudioChannels { get { ICodec[] array = codecs; foreach (ICodec codec in array) { if (codec != null && (codec.MediaTypes & MediaTypes.Audio) != 0 && codec is IAudioCodec audioCodec && audioCodec.AudioChannels != 0) { return audioCodec.AudioChannels; } } return 0; } } public int VideoWidth { get { ICodec[] array = codecs; foreach (ICodec codec in array) { if (codec != null && (codec.MediaTypes & MediaTypes.Video) != 0 && codec is IVideoCodec videoCodec && videoCodec.VideoWidth != 0) { return videoCodec.VideoWidth; } } return 0; } } public int VideoHeight { get { ICodec[] array = codecs; foreach (ICodec codec in array) { if (codec != null && (codec.MediaTypes & MediaTypes.Video) != 0 && codec is IVideoCodec videoCodec && videoCodec.VideoHeight != 0) { return videoCodec.VideoHeight; } } return 0; } } public int PhotoWidth { get { ICodec[] array = codecs; foreach (ICodec codec in array) { if (codec != null && (codec.MediaTypes & MediaTypes.Photo) != 0 && codec is IPhotoCodec photoCodec && photoCodec.PhotoWidth != 0) { return photoCodec.PhotoWidth; } } return 0; } } public int PhotoHeight { get { ICodec[] array = codecs; foreach (ICodec codec in array) { if (codec != null && (codec.MediaTypes & MediaTypes.Photo) != 0 && codec is IPhotoCodec photoCodec && photoCodec.PhotoHeight != 0) { return photoCodec.PhotoHeight; } } return 0; } } public int PhotoQuality { get { ICodec[] array = codecs; foreach (ICodec codec in array) { if (codec != null && (codec.MediaTypes & MediaTypes.Photo) != 0 && codec is IPhotoCodec photoCodec && photoCodec.PhotoQuality != 0) { return photoCodec.PhotoQuality; } } return 0; } } public Properties() { } public Properties(TimeSpan duration, params ICodec[] codecs) { this.duration = duration; if (codecs != null) { this.codecs = codecs; } } public Properties(TimeSpan duration, IEnumerable<ICodec> codecs) { this.duration = duration; if (codecs != null) { this.codecs = new List<ICodec>(codecs).ToArray(); } } } public sealed class ReadOnlyByteVector : ByteVector { public override bool IsReadOnly => true; public override bool IsFixedSize => true; public ReadOnlyByteVector() { } public ReadOnlyByteVector(int size, byte value) : base(size, value) { } public ReadOnlyByteVector(int size) : this(size, 0) { } public ReadOnlyByteVector(ByteVector vector) : base(vector) { } public ReadOnlyByteVector(byte[] data, int length) : base(data, length) { } public ReadOnlyByteVector(params byte[] data) : base(data) { } public static implicit operator ReadOnlyByteVector(byte value) { return new ReadOnlyByteVector(value); } public static implicit operator ReadOnlyByteVector(byte[] value) { return new ReadOnlyByteVector(value); } public static implicit operator ReadOnlyByteVector(string value) { return new ReadOnlyByteVector(ByteVector.FromString(value, StringType.UTF8)); } } [ComVisible(false)] public class StringCollection : ListBase<string> { public StringCollection() { } public StringCollection(StringCollection values) { Add(values); } public StringCollection(params string[] values) { Add(values); } public StringCollection(ByteVectorCollection vectorList, StringType type) { foreach (ByteVector vector in vectorList) { Add(vector.ToString(type)); } } public StringCollection(ByteVectorCollection vectorList) : this(vectorList, StringType.UTF8) { } public static StringCollection Split(string value, string pattern) { if (value == null) { throw new ArgumentNullException("value"); } if (pattern == null) { throw new ArgumentNullException("pattern"); } StringCollection stringCollection = new StringCollection(); int num = 0; int num2 = value.IndexOf(pattern, 0); int length = pattern.Length; while (num2 != -1) { stringCollection.Add(value.Substring(num, num2 - num)); num = num2 + length; num2 = value.IndexOf(pattern, num); } stringCollection.Add(value.Substring(num)); return stringCollection; } } [AttributeUsage(AttributeTargets.Class, AllowMultiple = true)] public sealed class SupportedMimeType : Attribute { private static readonly List<SupportedMimeType> mimetypes; public string MimeType { get; private set; } public string Extension { get; private set; } public static IEnumerable<string> AllMimeTypes { get { foreach (SupportedMimeType type in mimetypes) { yield return type.MimeType; } } } public static IEnumerable<string> AllExtensions { get { foreach (SupportedMimeType type in mimetypes) { if (type.Extension != null) { yield return type.Extension; } } } } static SupportedMimeType() { mimetypes = new List<SupportedMimeType>(); FileTypes.Init(); } public SupportedMimeType(string mimetype) { MimeType = mimetype; mimetypes.Add(this); } public SupportedMimeType(string mimetype, string extension) : this(mimetype) { Extension = extension; } } [Flags] public enum TagTypes : uint { None = 0u, Xiph = 1u, Id3v1 = 2u, Id3v2 = 4u, Ape = 8u, Apple = 0x10u, Asf = 0x20u, RiffInfo = 0x40u, MovieId = 0x80u, DivX = 0x100u, FlacMetadata = 0x200u, TiffIFD = 0x400u, XMP = 0x800u, JpegComment = 0x1000u, GifComment = 0x2000u, Png = 0x4000u, IPTCIIM = 0x8000u, AudibleMetadata = 0x10000u, Matroska = 0x20000u, AllTags = uint.MaxValue } public abstract class Tag { public abstract TagTypes TagTypes { get; } public virtual string Title { get { return null; } set { } } public virtual string TitleSort { get { return null; } set { } } public virtual string Subtitle { get { return null; } set { } } public virtual string Description { get { return null; } set { } } public virtual string[] Performers { get { return new string[0]; } set { } } public virtual string[] PerformersSort { get { return new string[0]; } set { } } public virtual string[] PerformersRole { get { return new string[0]; } set { } } public virtual string[] AlbumArtists { get { return new string[0]; } set { } } public virtual string[] AlbumArtistsSort { get { return new string[0]; } set { } } public virtual string[] Composers { get { return new string[0]; } set { } } public virtual string[] ComposersSort { get { return new string[0]; } set { } } public virtual string Album { get { return null; } set { } } public virtual string AlbumSort { get { return null; } set { } } public virtual string Comment { get { return null; } set { } } public virtual string[] Genres { get { return new string[0]; } set { } } public virtual uint Year { get { return 0u; } set { } } public virtual uint Track { get { return 0u; } set { } } public virtual uint TrackCount { get { return 0u; } set { } } public virtual uint Disc { get { return 0u; } set { } } public virtual uint DiscCount { get { return 0u; } set { } } public virtual string Lyrics { get { return null; } set { } } public virtual string Grouping { get { return null; } set { } } public virtual uint BeatsPerMinute { get { return 0u; } set { } } public virtual string Conductor { get { return null; } set { } } public virtual string Copyright { get { return null; } set { } } public virtual DateTime? DateTagged { get { return null; } set { } } public virtual string MusicBrainzArtistId { get { return null; } set { } } public virtual string MusicBrainzReleaseGroupId { get { return null; } set { } } public virtual string MusicBrainzReleaseId { get { return null; } set { } } public virtual string MusicBrainzReleaseArtistId { get { return null; } set { } } public virtual string MusicBrainzTrackId { get { return null; } set { } } public virtual string MusicBrainzDiscId { get { return null; } set { } } public virtual string MusicIpId { get { return null; } set { } } public virtual string AmazonId { get { return null; } set { } } public virtual string MusicBrainzReleaseStatus { get { return null; } set { } } public virtual string MusicBrainzReleaseType { get { return null; } set { } } public virtual string MusicBrainzReleaseCountry { get { return null; } set { } } public virtual double ReplayGainTrackGain { get { return double.NaN; } set { } } public virtual double ReplayGainTrackPeak { get { retu