using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Versioning; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using UnityEngine; using UnityEngine.Networking; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)] [assembly: AssemblyTitle("LC_CruiserTunes")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("LC_CruiserTunes")] [assembly: AssemblyCopyright("Copyright © 2024")] [assembly: AssemblyTrademark("")] [assembly: ComVisible(false)] [assembly: Guid("98830a3d-1b1a-47c1-bd5a-429e781a4830")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")] [assembly: AssemblyVersion("1.0.0.0")] public class CarPatch { [CompilerGenerated] private sealed class d__17 : IEnumerator, IDisposable, IEnumerator { private int <>1__state; private object <>2__current; public AudioSource radio; public VehicleController instance; private AudioClip 5__1; object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public d__17(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { 5__1 = null; <>1__state = -2; } private bool MoveNext() { switch (<>1__state) { default: return false; case 0: <>1__state = -1; if ((Object)(object)instance == (Object)null) { return false; } break; case 1: <>1__state = -1; if (instance.vehicleID == 0 && (Object)(object)5__1 != (Object)(object)radio.clip) { if (!CruiserTunesMod.DoRandomTime.Value) { radio.time = 0f; } if (CruiserTunesMod.GoodQuality.Value) { radio.volume = 1f; instance.radioInterference.volume = 0f; } instance.radioAudio.loop = CruiserTunesMod.DoLoop.Value; ((MonoBehaviour)instance).StartCoroutine(PlayNotification(instance)); } break; } 5__1 = radio.clip; <>2__current = null; <>1__state = 1; return true; } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } } [CompilerGenerated] private sealed class d__18 : IEnumerator, IDisposable, IEnumerator { private int <>1__state; private object <>2__current; public VehicleController instance; private AudioClip 5__1; object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public d__18(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { 5__1 = null; <>1__state = -2; } private bool MoveNext() { //IL_0061: Unknown result type (might be due to invalid IL or missing references) //IL_006b: Expected O, but got Unknown //IL_00be: Unknown result type (might be due to invalid IL or missing references) //IL_00ce: Unknown result type (might be due to invalid IL or missing references) switch (<>1__state) { default: return false; case 0: <>1__state = -1; if (!CruiserTunesMod.DoMessage.Value) { return false; } if ((Object)(object)instance == (Object)null) { return false; } 5__1 = instance.radioAudio.clip; <>2__current = (object)new WaitForSeconds(2f); <>1__state = 1; return true; case 1: <>1__state = -1; if ((Object)(object)5__1 == (Object)(object)instance.radioAudio.clip && (Object)(object)5__1 != (Object)null && Vector3.Distance(((Component)StartOfRound.Instance.localPlayerController).transform.position, ((Component)instance).transform.position) < 10f) { HUDManager.Instance.DisplayTip("Now Playing:", ((Object)5__1).name.Replace("Radio_", "") + " - " + FormatLength(5__1.length), false, false, "LC_Tip1"); } return false; } } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } } public static AudioClip[] tunes; public static int[] playOrder; private static VehicleController _car; public static float checkTimer; public static Type type = typeof(VehicleController); public static FieldInfo radioOnField = type.GetField("radioOn", BindingFlags.Instance | BindingFlags.NonPublic); public static VehicleController GetCar { get { if ((Object)(object)_car == (Object)null) { _car = Object.FindObjectOfType(); } return _car; } } [HarmonyPatch(typeof(VehicleController), "Awake")] [HarmonyPostfix] public static void AwakePatch(VehicleController __instance) { if (CruiserTunesMod.HasSongs) { AudioClip[] source = ((!CruiserTunesMod.IncludeOriginal.Value) ? CruiserTunesMod.CustomSongs : __instance.radioClips.Concat(CruiserTunesMod.CustomSongs).ToArray()); source = source.OrderByDescending((AudioClip str) => GetAsciiSum(((Object)str).name)).ToArray(); tunes = source; __instance.radioClips = tunes; if (__instance.vehicleID == 0) { __instance.radioAudio.loop = CruiserTunesMod.DoLoop.Value; } ((MonoBehaviour)__instance).StartCoroutine(AudioSourceListener(__instance.radioAudio, __instance)); } static int GetAsciiSum(string str) { return str.Sum((char c) => c); } } public static void UpdateLooping(object sender, EventArgs e) { VehicleController getCar = GetCar; if ((Object)(object)getCar != (Object)null && getCar.vehicleID == 0) { getCar.radioAudio.loop = CruiserTunesMod.DoLoop.Value; } } public static void ChangeVolume(object sender, EventArgs e) { VehicleController getCar = GetCar; if ((Object)(object)getCar != (Object)null && getCar.vehicleID == 0) { getCar.radioAudio.volume = GetVolume(); } } private static float GetVolume() { return Mathf.Clamp(CruiserTunesMod.Volume.Value, 0f, 1.25f); } [HarmonyPatch(typeof(VehicleController), "ChangeRadioStation")] [HarmonyPostfix] public static void ChangeRadioStationPatch(VehicleController __instance) { if (!CruiserTunesMod.DoRandomTime.Value && !((Object)(object)__instance == (Object)null)) { __instance.radioAudio.time = 0f; } } [HarmonyPatch(typeof(VehicleController), "SetRadioOnLocalClient")] [HarmonyPrefix] public static void SetOnClientPatch(VehicleController __instance) { if (!((Object)(object)__instance == (Object)null)) { Type type = ((object)__instance).GetType(); FieldInfo field = type.GetField("currentRadioClip", BindingFlags.Instance | BindingFlags.NonPublic); field.SetValue(__instance, (int)field.GetValue(__instance) % __instance.radioClips.Length); if (!CruiserTunesMod.DoRandomTime.Value) { __instance.radioAudio.time = 0f; } } } [HarmonyPatch(typeof(VehicleController), "SetRadioValues")] [HarmonyPrefix] public static void SetRadioValuesPatch(VehicleController __instance) { if (!((Object)(object)__instance == (Object)null) && __instance.vehicleID == 0 && CruiserTunesMod.GoodQuality.Value) { __instance.radioAudio.volume = GetVolume(); __instance.radioInterference.volume = 0f; } } [HarmonyPatch(typeof(VehicleController), "SwitchRadio")] [HarmonyPrefix] public static void SwitchRadioPatch(VehicleController __instance) { if (!((Object)(object)__instance == (Object)null) && !CruiserTunesMod.DoRandomTime.Value) { __instance.radioAudio.time = 0f; } } [HarmonyPatch(typeof(VehicleController), "SetRadioStationClientRpc")] [HarmonyPostfix] public static void SetRadioStationPatch(ref int radioStation, VehicleController __instance) { if (!((Object)(object)__instance == (Object)null)) { __instance.radioAudio.clip = __instance.radioClips[radioStation]; __instance.radioAudio.Play(); } } [IteratorStateMachine(typeof(d__17))] public static IEnumerator AudioSourceListener(AudioSource radio, VehicleController instance) { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new d__17(0) { radio = radio, instance = instance }; } [IteratorStateMachine(typeof(d__18))] public static IEnumerator PlayNotification(VehicleController instance) { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new d__18(0) { instance = instance }; } public static string FormatLength(float lengthInSeconds) { int num = Mathf.FloorToInt(lengthInSeconds / 3600f); int num2 = Mathf.FloorToInt(lengthInSeconds % 3600f / 60f); int num3 = Mathf.FloorToInt(lengthInSeconds % 60f); return $"{num:00}:{num2:00}:{num3:00}"; } public static void ChangeRadioStationWithoutServer(VehicleController instance) { Type type = ((object)instance).GetType(); FieldInfo field = type.GetField("radioSignalDecreaseThreshold", BindingFlags.Instance | BindingFlags.NonPublic); FieldInfo field2 = type.GetField("radioSignalQuality", BindingFlags.Instance | BindingFlags.NonPublic); FieldInfo field3 = type.GetField("currentRadioClip", BindingFlags.Instance | BindingFlags.NonPublic); FieldInfo field4 = type.GetField("currentSongTime", BindingFlags.Instance | BindingFlags.NonPublic); int num = ((int)field3.GetValue(instance) + 1) % instance.radioClips.Length; field3.SetValue(instance, num); instance.radioAudio.clip = instance.radioClips[num]; instance.radioAudio.time = Mathf.Clamp((float)field4.GetValue(instance) % instance.radioAudio.clip.length, 0.01f, instance.radioAudio.clip.length - 0.1f); instance.radioAudio.Play(); float num2 = 10f; float num3 = (float)field2.GetValue(instance); switch ((int)Mathf.Round(num3)) { case 3: num3 = 1f; num2 = 10f; break; case 0: num3 = 3f; num2 = 90f; break; case 1: num3 = 2f; num2 = 70f; break; case 2: num3 = 1f; num2 = 30f; break; } field2.SetValue(instance, num3); field.SetValue(instance, num2); ChangeRadioStationPatch(instance); } [HarmonyPatch(typeof(StartOfRound), "StartGame")] [HarmonyPostfix] public static void EnterMoon() { VehicleController getCar = GetCar; if (!((Object)(object)getCar == (Object)null) && CruiserTunesMod.DoRandomSorting.Value) { playOrder = new int[tunes.Length]; for (int i = 0; i < playOrder.Length; i++) { playOrder[i] = i; } Random r = new Random(StartOfRound.Instance.randomMapSeed); playOrder = playOrder.OrderBy((int x) => r.Next()).ToArray(); SortMusic(); } } public static void SortMusic() { VehicleController getCar = GetCar; if ((Object)(object)getCar == (Object)null || playOrder == null || tunes == null || tunes.Length == 0) { return; } if (playOrder.Length != tunes.Length) { CruiserTunesMod.mls.LogError((object)"Play order length does not match number of songs"); return; } AudioClip[] array = (AudioClip[])(object)new AudioClip[tunes.Length]; for (int i = 0; i < tunes.Length; i++) { array[i] = tunes[playOrder[i]]; } getCar.radioClips = array; CruiserTunesMod.mls.LogInfo((object)"printing order"); for (int j = 0; j < playOrder.Length; j++) { CruiserTunesMod.mls.LogInfo((object)playOrder[j]); } } } [BepInPlugin("Mellowdy.CruiserTunes", "CruiserTunes", "1.3.0")] public class CruiserTunesMod : BaseUnityPlugin { [CompilerGenerated] private sealed class d__21 : IEnumerator, IDisposable, IEnumerator { private int <>1__state; private object <>2__current; private bool 5__1; private bool[] <>s__2; private int <>s__3; private bool 5__4; private string 5__5; private string 5__6; object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public d__21(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { <>s__2 = null; 5__5 = null; 5__6 = null; <>1__state = -2; } private bool MoveNext() { switch (<>1__state) { default: return false; case 0: <>1__state = -1; mls.LogInfo((object)("Loading " + instance.loadingList.Length + " songs")); 5__1 = false; break; case 1: <>1__state = -1; break; } while (!5__1) { 5__1 = true; <>s__2 = instance.loadingList; for (<>s__3 = 0; <>s__3 < <>s__2.Length; <>s__3++) { 5__4 = <>s__2[<>s__3]; if (!5__4) { 5__1 = false; break; } } <>s__2 = null; if (!5__1) { <>2__current = null; <>1__state = 1; return true; } } instance.loadingList = null; mls.LogInfo((object)("Finished loading songs, final song count at:" + instance.SongList.Count)); if (instance.SongList.Count == 0) { 5__5 = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); 5__6 = Path.Combine(5__5, instance.folderName).Replace("\\", "/"); mls.LogError((object)"No songs found in folder"); mls.LogError((object)("Make sure to add any .mp3 or .wav or .ogg files you want as songs to: '" + 5__6 + "'")); 5__5 = null; 5__6 = null; } CustomSongs = instance.SongList.ToArray(); instance.SongList.Clear(); HasSongs = CustomSongs.Length != 0 || IncludeOriginal.Value; mls.LogInfo((object)"CruiserTunes has been loaded"); return false; } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } } [CompilerGenerated] private sealed class d__22 : IEnumerator, IDisposable, IEnumerator { private int <>1__state; private object <>2__current; public string filePath; public int index; public string fileType; private float 5__1; private float 5__2; private string 5__3; private AudioType 5__4; private UnityWebRequest 5__5; private AudioClip 5__6; object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public d__22(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { int num = <>1__state; if (num == -3 || num == 1) { try { } finally { <>m__Finally1(); } } 5__3 = null; 5__5 = null; 5__6 = null; <>1__state = -2; } private bool MoveNext() { //IL_0071: 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_0083: Unknown result type (might be due to invalid IL or missing references) bool result; try { switch (<>1__state) { default: result = false; goto end_IL_0000; case 0: <>1__state = -1; 5__1 = Time.time; 5__2 = 5f; 5__3 = Path.GetFileName(filePath); mls.LogInfo((object)("Loading " + 5__3)); 5__4 = GetAudioType(fileType); 5__5 = UnityWebRequestMultimedia.GetAudioClip(filePath, 5__4); <>1__state = -3; 5__5.SendWebRequest(); break; case 1: <>1__state = -3; break; } if (!5__5.isDone) { if (5__1 + 5__2 < Time.time) { mls.LogError((object)("Error loading clip from path: " + 5__3)); instance.loadingList[index] = true; result = false; goto IL_01c7; } <>2__current = null; <>1__state = 1; result = true; } else { instance.loadingList[index] = true; if (5__5.error != null) { mls.LogError((object)("Error loading clip from path: " + 5__3 + "\n" + 5__5.error)); result = false; goto IL_01c7; } 5__6 = DownloadHandlerAudioClip.GetContent(5__5); ((Object)5__6).name = 5__3; instance.SongList.Add(5__6); 5__6 = null; <>m__Finally1(); 5__5 = null; result = false; } goto end_IL_0000; IL_01c7: <>m__Finally1(); end_IL_0000:; } catch { //try-fault ((IDisposable)this).Dispose(); throw; } return result; } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } private void <>m__Finally1() { <>1__state = -1; if (5__5 != null) { ((IDisposable)5__5).Dispose(); } } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } } private const string modGUID = "Mellowdy.CruiserTunes"; private const string modName = "CruiserTunes"; private const string modVersion = "1.3.0"; private readonly Harmony harmony = new Harmony("Mellowdy.CruiserTunes"); public static ManualLogSource mls; public static CruiserTunesMod instance; public static ConfigEntry IncludeOriginal; public static ConfigEntry DoMessage; public static ConfigEntry DoLoop; public static ConfigEntry DoRandomTime; public static ConfigEntry GoodQuality; public static ConfigEntry Volume; public static ConfigEntry DoRandomSorting; public static bool HasSongs; public static AudioClip[] CustomSongs; private const string supportedAudioFileTypes = ".mp3 or .wav or .ogg"; private string folderName = "CustomSongs"; private string altFolderName = "Custom Songs"; private List SongList = new List(); private bool[] loadingList; private void Awake() { if ((Object)(object)instance == (Object)null) { instance = this; } mls = Logger.CreateLogSource("Mellowdy.CruiserTunes"); mls.LogInfo((object)"Loading..."); Object.DontDestroyOnLoad((Object)(object)((Component)this).gameObject); ((Object)((Component)this).gameObject).hideFlags = (HideFlags)61; IncludeOriginal = ((BaseUnityPlugin)this).Config.Bind("Settings", "Include Original Songs", true, "The radio is able to play the orignal songs"); DoMessage = ((BaseUnityPlugin)this).Config.Bind("Settings", "Notify When New Song", true, "The radio notifies nearby players what song is now playing"); DoLoop = ((BaseUnityPlugin)this).Config.Bind("Settings", "Loop", false, "The radio loops songs upon completion"); DoLoop.SettingChanged += CarPatch.UpdateLooping; DoRandomTime = ((BaseUnityPlugin)this).Config.Bind("Settings", "Random Start Time", false, "The randomly picks a point in the song to start playing when switching channels"); GoodQuality = ((BaseUnityPlugin)this).Config.Bind("Settings", "Always have good quality", true, "When switching between channels the quality will remain good"); Volume = ((BaseUnityPlugin)this).Config.Bind("Settings", "Volume", 0.75f, "Volume of the radio (does not require restart)"); Volume.SettingChanged += CarPatch.ChangeVolume; DoRandomSorting = ((BaseUnityPlugin)this).Config.Bind("Settings", "Do Random Sorting", false, "Should songs be randomly sorted when entering a moon?"); string directoryName = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); string text = Path.Combine(directoryName, folderName); List list = new List(); List list2 = new List(); string text2 = Path.Combine(Directory.GetParent(directoryName).FullName, folderName); string text3 = Path.Combine(Directory.GetParent(directoryName).FullName, altFolderName); if (Directory.Exists(text2)) { list.Add(text2); } else if (Directory.Exists(text3)) { list.Add(text3); } string text4 = Path.Combine(Directory.GetParent(directoryName).Parent.FullName, altFolderName); if (Directory.Exists(text4)) { list.Add(text4); } string[] directories = Directory.GetDirectories(Directory.GetParent(directoryName).FullName); foreach (string path in directories) { string text5 = Path.Combine(path, folderName); if (Directory.Exists(text5)) { list.Add(text5); } } if (list.Count == 0) { Directory.CreateDirectory(text); mls.LogWarning((object)("Folder not found. One has been created at: '" + text + "'")); return; } foreach (string item in list) { list2.AddRange(Directory.GetFiles(item)); } list2 = RemoveDuplicateFiles(list2); bool flag = false; loadingList = new bool[list2.Count]; for (int j = 0; j < list2.Count; j++) { string text6 = list2[j]; loadingList[j] = false; string fileName = Path.GetFileName(text6); string text7 = Path.GetExtension(text6).ToLower(); if (text7 == ".mp3" || text7 == ".wav" || text7 == ".ogg") { ((MonoBehaviour)this).StartCoroutine(LoadAudioClip(text6, j, text7)); continue; } loadingList[j] = true; if (text7 != ".old") { mls.LogWarning((object)(fileName + " is of invalid extention. Must be a .mp3 or .wav or .ogg file or .old file")); if (!flag) { mls.LogWarning((object)".old files ignore this message being printed."); } flag = true; } } if (loadingList.Length == 0) { mls.LogError((object)"No songs found"); } harmony.PatchAll(); harmony.PatchAll(typeof(CarPatch)); ((MonoBehaviour)this).StartCoroutine(EndOfPatch()); } [IteratorStateMachine(typeof(d__21))] private static IEnumerator EndOfPatch() { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new d__21(0); } [IteratorStateMachine(typeof(d__22))] private static IEnumerator LoadAudioClip(string filePath, int index, string fileType) { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new d__22(0) { filePath = filePath, index = index, fileType = fileType }; } public static AudioType GetAudioType(string fileType) { //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Unknown result type (might be due to invalid IL or missing references) return (AudioType)(fileType switch { ".mp3" => 13, ".wav" => 20, ".ogg" => 14, _ => 13, }); } public static List RemoveDuplicateFiles(List filePaths) { HashSet hashSet = new HashSet(); List list = new List(); foreach (string filePath in filePaths) { string fileName = Path.GetFileName(filePath); if (!hashSet.Contains(fileName)) { hashSet.Add(fileName); list.Add(filePath); } } return list; } }