using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Text;
using System.Text.RegularExpressions;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using ComputerysModdingUtilities;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using UnityEngine;
using UnityEngine.Networking;
[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: StraftatMod(true)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("STRAFTATCustomMusic")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("STRAFTATCustomMusic")]
[assembly: AssemblyTitle("STRAFTATCustomMusic")]
[assembly: AssemblyVersion("1.0.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
[CompilerGenerated]
[Microsoft.CodeAnalysis.Embedded]
internal sealed class EmbeddedAttribute : Attribute
{
}
}
namespace System.Runtime.CompilerServices
{
[CompilerGenerated]
[Microsoft.CodeAnalysis.Embedded]
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
internal sealed class NullableAttribute : Attribute
{
public readonly byte[] NullableFlags;
public NullableAttribute(byte P_0)
{
NullableFlags = new byte[1] { P_0 };
}
public NullableAttribute(byte[] P_0)
{
NullableFlags = P_0;
}
}
[CompilerGenerated]
[Microsoft.CodeAnalysis.Embedded]
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)]
internal sealed class NullableContextAttribute : Attribute
{
public readonly byte Flag;
public NullableContextAttribute(byte P_0)
{
Flag = P_0;
}
}
[CompilerGenerated]
[Microsoft.CodeAnalysis.Embedded]
[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
internal sealed class RefSafetyRulesAttribute : Attribute
{
public readonly int Version;
public RefSafetyRulesAttribute(int P_0)
{
Version = P_0;
}
}
}
[BepInPlugin("com.garnetsunset.straftat.custommusic", "STRAFTAT Custom Music", "1.0.1")]
public class CustomMusicPlugin : BaseUnityPlugin
{
[HarmonyPatch]
private static class AwakePatch
{
private static MethodBase TargetMethod()
{
return typeof(SetMenuMusicVolume).GetMethod("Awake", BindingFlags.Instance | BindingFlags.NonPublic);
}
private static void Prefix(SetMenuMusicVolume __instance)
{
if (PendingMusicFiles.Count != 0)
{
Logger.LogInfo((object)$"Extending arrays for {PendingMusicFiles.Count} custom tracks...");
ExtendMusicArraysSync(__instance);
}
}
private static void Postfix(SetMenuMusicVolume __instance)
{
if (PendingMusicFiles.Count > 0 && (Object)(object)__instance.audio != (Object)null)
{
__instance.audio.Stop();
}
}
}
[HarmonyPatch]
private static class StartPatch
{
private static MethodBase TargetMethod()
{
return typeof(SetMenuMusicVolume).GetMethod("Start", BindingFlags.Instance | BindingFlags.NonPublic);
}
private static void Prefix(SetMenuMusicVolume __instance)
{
if (PendingMusicFiles.Count != 0)
{
Logger.LogInfo((object)$"Loading {PendingMusicFiles.Count} custom music files...");
((MonoBehaviour)__instance).StartCoroutine(LoadCustomMusicCoroutine(__instance));
}
}
}
[HarmonyPatch]
private static class UpdatePatch
{
private static MethodBase TargetMethod()
{
return typeof(SetMenuMusicVolume).GetMethod("Update", BindingFlags.Instance | BindingFlags.NonPublic);
}
private static void Prefix()
{
}
}
[CompilerGenerated]
private sealed class <LoadAudioFileCoroutineToIndex>d__18 : IEnumerator<object>, IEnumerator, IDisposable
{
private int <>1__state;
private object <>2__current;
public CustomMusicInfo musicInfo;
public SetMenuMusicVolume musicManager;
public int targetIndex;
private UnityWebRequest <www>5__2;
object IEnumerator<object>.Current
{
[DebuggerHidden]
get
{
return <>2__current;
}
}
object IEnumerator.Current
{
[DebuggerHidden]
get
{
return <>2__current;
}
}
[DebuggerHidden]
public <LoadAudioFileCoroutineToIndex>d__18(int <>1__state)
{
this.<>1__state = <>1__state;
}
[DebuggerHidden]
void IDisposable.Dispose()
{
int num = <>1__state;
if (num == -3 || num == 1)
{
try
{
}
finally
{
<>m__Finally1();
}
}
<www>5__2 = null;
<>1__state = -2;
}
private bool MoveNext()
{
//IL_00fb: Unknown result type (might be due to invalid IL or missing references)
//IL_0101: Invalid comparison between Unknown and I4
//IL_00a0: Unknown result type (might be due to invalid IL or missing references)
//IL_00b4: Unknown result type (might be due to invalid IL or missing references)
//IL_00b6: Unknown result type (might be due to invalid IL or missing references)
//IL_00ba: Unknown result type (might be due to invalid IL or missing references)
//IL_00a6: Unknown result type (might be due to invalid IL or missing references)
//IL_00ac: Unknown result type (might be due to invalid IL or missing references)
//IL_00b2: Unknown result type (might be due to invalid IL or missing references)
try
{
switch (<>1__state)
{
default:
return false;
case 0:
{
<>1__state = -1;
string filePath = musicInfo.FilePath;
if (!File.Exists(filePath))
{
Logger.LogError((object)("File not found: " + filePath));
return false;
}
string absoluteUri = new Uri(filePath).AbsoluteUri;
<www>5__2 = UnityWebRequestMultimedia.GetAudioClip(absoluteUri, (AudioType)(Path.GetExtension(musicInfo.FilePath).ToLowerInvariant() switch
{
".mp3" => 13,
".ogg" => 14,
".wav" => 20,
_ => 13,
}));
<>1__state = -3;
<>2__current = <www>5__2.SendWebRequest();
<>1__state = 1;
return true;
}
case 1:
<>1__state = -3;
if ((int)<www>5__2.result == 1)
{
try
{
AudioClip content = DownloadHandlerAudioClip.GetContent(<www>5__2);
if ((Object)(object)content != (Object)null && content.length > 0f)
{
((Object)content).name = musicInfo.FileName;
musicManager.audioClips[targetIndex] = content;
Logger.LogInfo((object)("Loaded: " + musicInfo.FileName + " - " + musicInfo.ArtistName));
}
else
{
Logger.LogError((object)("Failed to load " + musicInfo.FileName + ": AudioClip is null or zero length"));
}
}
catch (Exception ex)
{
Logger.LogError((object)("Exception loading " + musicInfo.FileName + ": " + ex.Message));
}
}
else
{
Logger.LogError((object)("Failed to load " + musicInfo.FilePath + ": " + <www>5__2.error));
}
<>m__Finally1();
<www>5__2 = null;
return false;
}
}
catch
{
//try-fault
((IDisposable)this).Dispose();
throw;
}
}
bool IEnumerator.MoveNext()
{
//ILSpy generated this explicit interface implementation from .override directive in MoveNext
return this.MoveNext();
}
private void <>m__Finally1()
{
<>1__state = -1;
if (<www>5__2 != null)
{
((IDisposable)<www>5__2).Dispose();
}
}
[DebuggerHidden]
void IEnumerator.Reset()
{
throw new NotSupportedException();
}
}
[CompilerGenerated]
private sealed class <LoadCustomMusicCoroutine>d__17 : IEnumerator<object>, IEnumerator, IDisposable
{
private int <>1__state;
private object <>2__current;
public SetMenuMusicVolume musicManager;
private bool <useCustomOnlyMode>5__2;
private int <startIndex>5__3;
private int <successCount>5__4;
private int <i>5__5;
object IEnumerator<object>.Current
{
[DebuggerHidden]
get
{
return <>2__current;
}
}
object IEnumerator.Current
{
[DebuggerHidden]
get
{
return <>2__current;
}
}
[DebuggerHidden]
public <LoadCustomMusicCoroutine>d__17(int <>1__state)
{
this.<>1__state = <>1__state;
}
[DebuggerHidden]
void IDisposable.Dispose()
{
<>1__state = -2;
}
private bool MoveNext()
{
//IL_0157: Unknown result type (might be due to invalid IL or missing references)
//IL_015c: Unknown result type (might be due to invalid IL or missing references)
//IL_0169: Unknown result type (might be due to invalid IL or missing references)
switch (<>1__state)
{
default:
return false;
case 0:
<>1__state = -1;
<useCustomOnlyMode>5__2 = CustomOnlyMode?.Value ?? CustomOnlyModeValue;
<startIndex>5__3 = ((!<useCustomOnlyMode>5__2) ? (musicManager.audioClips.Length - PendingMusicFiles.Count) : 0);
<successCount>5__4 = 0;
<i>5__5 = 0;
break;
case 1:
<>1__state = -1;
<successCount>5__4++;
<i>5__5++;
break;
}
if (<i>5__5 < PendingMusicFiles.Count)
{
CustomMusicInfo musicInfo = PendingMusicFiles[<i>5__5];
int targetIndex = <startIndex>5__3 + <i>5__5;
<>2__current = LoadAudioFileCoroutineToIndex(musicInfo, musicManager, targetIndex);
<>1__state = 1;
return true;
}
if (<successCount>5__4 > 0)
{
RecreateMusimcTracks(musicManager);
musicManager.Shuffle();
if (!musicManager.audio.isPlaying)
{
musicManager.currentTrackId = Mathf.Clamp(musicManager.currentTrackId, 0, musicManager.MusicTracks.Length - 1);
MusicTrack val = musicManager.MusicTracks[musicManager.currentTrackId];
musicManager.audio.clip = val.AudioClip;
musicManager.audio.Play();
RefreshMusicUI(musicManager);
}
string arg = (<useCustomOnlyMode>5__2 ? "custom-only" : "mixed");
Logger.LogInfo((object)$"Loaded {<successCount>5__4}/{PendingMusicFiles.Count} custom tracks in {arg} mode");
}
else
{
Logger.LogWarning((object)"No custom music files loaded successfully");
}
return false;
}
bool IEnumerator.MoveNext()
{
//ILSpy generated this explicit interface implementation from .override directive in MoveNext
return this.MoveNext();
}
[DebuggerHidden]
void IEnumerator.Reset()
{
throw new NotSupportedException();
}
}
[CompilerGenerated]
private sealed class <MonitorForMusicManager>d__8 : IEnumerator<object>, IEnumerator, IDisposable
{
private int <>1__state;
private object <>2__current;
public CustomMusicPlugin <>4__this;
object IEnumerator<object>.Current
{
[DebuggerHidden]
get
{
return <>2__current;
}
}
object IEnumerator.Current
{
[DebuggerHidden]
get
{
return <>2__current;
}
}
[DebuggerHidden]
public <MonitorForMusicManager>d__8(int <>1__state)
{
this.<>1__state = <>1__state;
}
[DebuggerHidden]
void IDisposable.Dispose()
{
<>1__state = -2;
}
private bool MoveNext()
{
//IL_0033: Unknown result type (might be due to invalid IL or missing references)
//IL_003d: Expected O, but got Unknown
int num = <>1__state;
CustomMusicPlugin customMusicPlugin = <>4__this;
if (num != 0)
{
if (num != 1)
{
return false;
}
<>1__state = -1;
SetMenuMusicVolume val = Object.FindObjectOfType<SetMenuMusicVolume>();
if ((Object)(object)val != (Object)null)
{
Logger.LogInfo((object)"Found SetMenuMusicVolume!");
if (PendingMusicFiles.Count > 0)
{
Logger.LogInfo((object)"Loading music directly...");
((MonoBehaviour)customMusicPlugin).StartCoroutine(LoadCustomMusicCoroutine(val));
}
return false;
}
}
else
{
<>1__state = -1;
Logger.LogInfo((object)"Looking for music manager...");
}
<>2__current = (object)new WaitForSeconds(1f);
<>1__state = 1;
return true;
}
bool IEnumerator.MoveNext()
{
//ILSpy generated this explicit interface implementation from .override directive in MoveNext
return this.MoveNext();
}
[DebuggerHidden]
void IEnumerator.Reset()
{
throw new NotSupportedException();
}
}
internal static ManualLogSource Logger = null;
private static string CustomMusicFolder = null;
private static readonly List<CustomMusicInfo> PendingMusicFiles = new List<CustomMusicInfo>();
private static ConfigEntry<bool> CustomOnlyMode = null;
private static bool CustomOnlyModeValue = false;
private void Awake()
{
//IL_00ef: Unknown result type (might be due to invalid IL or missing references)
//IL_00f4: Unknown result type (might be due to invalid IL or missing references)
Logger = ((BaseUnityPlugin)this).Logger;
CustomMusicFolder = Path.Combine(Paths.ConfigPath, "CustomMusic");
if (!Directory.Exists(CustomMusicFolder))
{
Directory.CreateDirectory(CustomMusicFolder);
CreateSampleFiles();
Logger.LogInfo((object)("Created custom music folder at: " + CustomMusicFolder));
}
Logger.LogInfo((object)"Setting up config...");
try
{
CustomOnlyMode = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "CustomOnlyMode", false, "Set to true to replace all original music with only custom tracks. Set to false to mix custom tracks with original soundtrack.");
CustomOnlyModeValue = CustomOnlyMode.Value;
Logger.LogInfo((object)$"Config loaded: CustomOnlyMode = {CustomOnlyModeValue}");
}
catch (Exception ex)
{
Logger.LogError((object)("Config failed: " + ex.Message));
Logger.LogInfo((object)"Making config file manually...");
CreateManualConfigFile();
}
LoadCustomMusicList();
try
{
Harmony val = new Harmony("com.garnetsunset.straftat.custommusic");
val.PatchAll();
Logger.LogInfo((object)"Harmony patches applied.");
List<MethodBase> list = val.GetPatchedMethods().ToList();
Logger.LogInfo((object)$"Got {list.Count} patched methods.");
if (list.Count == 0)
{
Logger.LogWarning((object)"No patches worked! Checking what went wrong...");
InvestigateTargetMethods();
}
}
catch (Exception ex2)
{
Logger.LogError((object)("Harmony failed: " + ex2.Message));
Logger.LogError((object)("Stack trace: " + ex2.StackTrace));
}
Logger.LogInfo((object)"Custom Music Plugin loaded!");
((MonoBehaviour)this).StartCoroutine(MonitorForMusicManager());
}
private void CreateManualConfigFile()
{
try
{
string text = Path.Combine(Paths.ConfigPath);
if (!Directory.Exists(text))
{
Directory.CreateDirectory(text);
Logger.LogInfo((object)("Made config directory: " + text));
}
string text2 = Path.Combine(text, "com.garnetsunset.straftat.custommusic.cfg");
if (!File.Exists(text2))
{
string contents = "## Settings file was created by plugin STRAFTAT Custom Music v1.0.0\r\n## Plugin GUID: com.garnetsunset.straftat.custommusic\r\n\r\n[General]\r\n\r\n## Set to true to replace all original music with only custom tracks. Set to false to mix custom tracks with original soundtrack.\r\n# Setting type: Boolean\r\n# Default value: false\r\nCustomOnlyMode = false\r\n";
File.WriteAllText(text2, contents);
Logger.LogInfo((object)("Created config file at: " + text2));
Logger.LogInfo((object)"Edit this file to change CustomOnlyMode setting.");
}
else
{
Logger.LogInfo((object)("Config file already exists: " + text2));
}
}
catch (Exception ex)
{
Logger.LogError((object)("Couldn't create config file: " + ex.Message));
}
}
private static void RefreshMusicUI(SetMenuMusicVolume musicManager)
{
//IL_000c: Unknown result type (might be due to invalid IL or missing references)
//IL_0011: Unknown result type (might be due to invalid IL or missing references)
//IL_0037: Unknown result type (might be due to invalid IL or missing references)
//IL_0047: Unknown result type (might be due to invalid IL or missing references)
try
{
MusicTrack val = musicManager.MusicTracks[musicManager.currentTrackId];
try
{
Type typeFromHandle = typeof(SetMenuMusicVolume);
FieldInfo field = typeFromHandle.GetField("trackTextInPlayer", BindingFlags.Instance | BindingFlags.NonPublic);
FieldInfo field2 = typeFromHandle.GetField("trackText", BindingFlags.Instance | BindingFlags.NonPublic);
string text = ((Object)val.AudioClip).name + " - " + val.ArtistName;
if (field != null)
{
object value = field.GetValue(musicManager);
value?.GetType().GetProperty("text")?.SetValue(value, text);
}
if (field2 != null)
{
object value2 = field2.GetValue(musicManager);
value2?.GetType().GetProperty("text")?.SetValue(value2, "music playing : " + text);
}
}
catch (Exception ex)
{
Logger.LogWarning((object)("Couldn't update UI text: " + ex.Message));
}
try
{
FieldInfo field3 = typeof(SetMenuMusicVolume).GetField("pauseButton", BindingFlags.Instance | BindingFlags.NonPublic);
FieldInfo field4 = typeof(SetMenuMusicVolume).GetField("playButton", BindingFlags.Instance | BindingFlags.NonPublic);
if (field3 != null && field4 != null)
{
object? value3 = field3.GetValue(musicManager);
GameObject val2 = (GameObject)((value3 is GameObject) ? value3 : null);
object? value4 = field4.GetValue(musicManager);
GameObject val3 = (GameObject)((value4 is GameObject) ? value4 : null);
if ((Object)(object)val2 != (Object)null && (Object)(object)val3 != (Object)null)
{
val2.SetActive(musicManager.audio.isPlaying);
val3.SetActive(!musicManager.audio.isPlaying);
}
}
}
catch (Exception ex2)
{
Logger.LogWarning((object)("Couldn't update buttons: " + ex2.Message));
}
}
catch (Exception ex3)
{
Logger.LogError((object)("Error checking track: " + ex3.Message));
}
}
[IteratorStateMachine(typeof(<MonitorForMusicManager>d__8))]
private IEnumerator MonitorForMusicManager()
{
//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
return new <MonitorForMusicManager>d__8(0)
{
<>4__this = this
};
}
private void DiagnoseAvailableTypes()
{
try
{
Logger.LogInfo((object)"=== Looking for SetMenuMusicVolume class ===");
Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
foreach (Assembly assembly in assemblies)
{
try
{
Type[] array = (from t in assembly.GetTypes()
where t.Name.Contains("Music") || t.Name.Contains("Volume") || t.Name.Contains("SetMenu")
select t).ToArray();
if (array.Length != 0)
{
Logger.LogInfo((object)("Assembly " + assembly.GetName().Name + " has music stuff:"));
Type[] array2 = array;
foreach (Type type in array2)
{
Logger.LogInfo((object)(" - " + type.FullName));
}
}
}
catch (Exception ex)
{
Logger.LogWarning((object)("Couldn't check assembly " + assembly.GetName().Name + ": " + ex.Message));
}
}
}
catch (Exception ex2)
{
Logger.LogError((object)("Error diagnosing: " + ex2.Message));
}
}
private void InvestigateTargetMethods()
{
try
{
Logger.LogInfo((object)"=== Checking target methods ===");
Type type = Type.GetType("SetMenuMusicVolume");
if (type == null)
{
Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
foreach (Assembly assembly in assemblies)
{
try
{
type = assembly.GetType("SetMenuMusicVolume");
if (type != null)
{
Logger.LogInfo((object)("Found SetMenuMusicVolume in: " + assembly.GetName().Name));
break;
}
}
catch
{
}
}
}
if (type != null)
{
Logger.LogInfo((object)("Found type: " + type.FullName));
MethodInfo[] methods = type.GetMethods(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
Logger.LogInfo((object)("Methods in " + type.Name + ":"));
MethodInfo[] array = methods;
foreach (MethodInfo methodInfo in array)
{
string text = string.Join(", ", from p in methodInfo.GetParameters()
select p.ParameterType.Name + " " + p.Name);
Logger.LogInfo((object)(" - " + methodInfo.ReturnType.Name + " " + methodInfo.Name + "(" + text + ")"));
}
}
else
{
Logger.LogError((object)"Can't find SetMenuMusicVolume anywhere!");
}
}
catch (Exception ex)
{
Logger.LogError((object)("Error checking methods: " + ex.Message));
}
}
private void CreateSampleFiles()
{
string contents = "STRAFTAT Custom Music Folder\r\n\r\nAdd your MP3 files here and they'll be loaded into the game!\r\n\r\nSupported Formats: MP3, OGG, WAV\r\n\r\nMetadata Priority:\r\n1. ID3 Tags (recommended) - Use any music tagger like Mp3tag\r\n2. JSON files - Create \"SongName.json\" with: {\"artist\": \"Name\", \"title\": \"Song\"}\r\n3. Text files - Create \"SongName.txt\" with just the artist name\r\n4. Filename format - \"Song Title - Artist Name.mp3\"\r\n5. Folder structure - \"Artist Name/Song Title.mp3\"\r\n\r\nConfiguration:\r\nEdit the config file at BepInEx/config/com.garnetsunset.straftat.custommusic.cfg\r\nSet CustomOnlyMode = true to replace all original music with only your custom tracks\r\nSet CustomOnlyMode = false to mix your custom tracks with the original soundtrack\r\n\r\nExamples:\r\n CustomMusic/\r\n ├── Awesome Song - Cool Artist.mp3\r\n ├── My Track.mp3\r\n ├── My Track.json\r\n └── Daft Punk/\r\n └── One More Time.mp3\r\n\r\nThe mod will automatically detect new files when you restart the game.\r\n";
File.WriteAllText(Path.Combine(CustomMusicFolder, "README.txt"), contents);
}
private static void LoadCustomMusicList()
{
PendingMusicFiles.Clear();
if (!Directory.Exists(CustomMusicFolder))
{
return;
}
string[] array = new string[3] { "*.mp3", "*.ogg", "*.wav" }.SelectMany((string ext) => Directory.GetFiles(CustomMusicFolder, ext, SearchOption.AllDirectories)).ToArray();
foreach (string text in array)
{
try
{
string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(text);
string text2 = GetBestMetadata(text);
string fileName = fileNameWithoutExtension;
if (fileNameWithoutExtension.Contains(" - "))
{
string[] array2 = fileNameWithoutExtension.Split(new string[1] { " - " }, StringSplitOptions.RemoveEmptyEntries);
if (array2.Length >= 2)
{
fileName = array2[0];
string text3 = string.Join(" - ", array2.Skip(1));
if (text2 == "Unknown Artist" || text2 == text3)
{
text2 = text3;
}
}
}
CustomMusicInfo customMusicInfo = new CustomMusicInfo
{
FilePath = text,
FileName = fileName,
ArtistName = text2
};
PendingMusicFiles.Add(customMusicInfo);
Logger.LogInfo((object)("Found: " + customMusicInfo.FileName + " - " + customMusicInfo.ArtistName));
}
catch (Exception ex)
{
Logger.LogError((object)("Error processing " + text + ": " + ex.Message));
}
}
Logger.LogInfo((object)$"Found {PendingMusicFiles.Count} custom music files");
}
private static void ExtendMusicArraysSync(SetMenuMusicVolume musicManager)
{
try
{
if (CustomOnlyMode?.Value ?? CustomOnlyModeValue)
{
Logger.LogInfo((object)$"Custom-only mode: Replacing {musicManager.audioClips.Length} original tracks with {PendingMusicFiles.Count} custom");
List<AudioClip> list = new List<AudioClip>();
List<string> list2 = new List<string>();
foreach (CustomMusicInfo pendingMusicFile in PendingMusicFiles)
{
AudioClip item = AudioClip.Create(pendingMusicFile.FileName, 44100, 1, 44100, false);
list.Add(item);
list2.Add(pendingMusicFile.ArtistName);
}
musicManager.audioClips = list.ToArray();
musicManager.trackNames = list2.ToArray();
Logger.LogInfo((object)$"Custom-only mode: {musicManager.audioClips.Length} tracks");
return;
}
Logger.LogInfo((object)$"Mixed mode: Extending {musicManager.audioClips.Length} original + {PendingMusicFiles.Count} custom");
int num = musicManager.audioClips.Length;
List<AudioClip> list3 = new List<AudioClip>(musicManager.audioClips);
List<string> list4 = new List<string>(musicManager.trackNames);
foreach (CustomMusicInfo pendingMusicFile2 in PendingMusicFiles)
{
AudioClip item2 = AudioClip.Create(pendingMusicFile2.FileName, 44100, 1, 44100, false);
list3.Add(item2);
list4.Add(pendingMusicFile2.ArtistName);
}
musicManager.audioClips = list3.ToArray();
musicManager.trackNames = list4.ToArray();
Logger.LogInfo((object)$"Mixed mode: {num} original + {PendingMusicFiles.Count} custom = {musicManager.audioClips.Length} total");
}
catch (Exception ex)
{
Logger.LogError((object)("Error extending arrays: " + ex.Message));
}
}
[IteratorStateMachine(typeof(<LoadCustomMusicCoroutine>d__17))]
private static IEnumerator LoadCustomMusicCoroutine(SetMenuMusicVolume musicManager)
{
//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
return new <LoadCustomMusicCoroutine>d__17(0)
{
musicManager = musicManager
};
}
[IteratorStateMachine(typeof(<LoadAudioFileCoroutineToIndex>d__18))]
private static IEnumerator LoadAudioFileCoroutineToIndex(CustomMusicInfo musicInfo, SetMenuMusicVolume musicManager, int targetIndex)
{
//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
return new <LoadAudioFileCoroutineToIndex>d__18(0)
{
musicInfo = musicInfo,
musicManager = musicManager,
targetIndex = targetIndex
};
}
private static void RecreateMusimcTracks(SetMenuMusicVolume musicManager)
{
//IL_0069: Unknown result type (might be due to invalid IL or missing references)
//IL_006e: Unknown result type (might be due to invalid IL or missing references)
//IL_0071: Unknown result type (might be due to invalid IL or missing references)
//IL_0072: Unknown result type (might be due to invalid IL or missing references)
try
{
Type typeFromHandle = typeof(MusicTrack);
if (typeFromHandle.GetConstructors().Length == 0)
{
Logger.LogError((object)"No constructors found for MusicTrack");
return;
}
MusicTrack[] array = (MusicTrack[])(object)new MusicTrack[musicManager.audioClips.Length];
for (int i = 0; i < musicManager.audioClips.Length; i++)
{
try
{
MusicTrack val = (MusicTrack)Activator.CreateInstance(typeFromHandle, musicManager.audioClips[i], musicManager.trackNames[i], i);
array[i] = val;
}
catch (Exception ex)
{
Logger.LogError((object)$"Error creating MusicTrack at {i}: {ex.Message}");
ManualLogSource logger = Logger;
AudioClip obj = musicManager.audioClips[i];
logger.LogError((object)$"Params: AudioClip={((obj != null) ? ((Object)obj).name : null)}, TrackName={musicManager.trackNames[i]}, Index={i}");
return;
}
}
musicManager.MusicTracks = array;
}
catch (Exception ex2)
{
Logger.LogError((object)("Error recreating MusicTracks: " + ex2.Message));
}
}
private static string GetBestMetadata(string filePath)
{
string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(filePath);
string fileName = Path.GetFileName(Path.GetDirectoryName(filePath) ?? "");
string text = ReadMP3Tags(filePath);
if (!string.IsNullOrEmpty(text))
{
return text;
}
string path = Path.ChangeExtension(filePath, ".json");
if (File.Exists(path))
{
string text2 = ParseJsonMetadata(File.ReadAllText(path));
if (!string.IsNullOrEmpty(text2))
{
return text2;
}
}
string path2 = Path.ChangeExtension(filePath, ".txt");
if (File.Exists(path2))
{
string text3 = File.ReadAllText(path2).Trim();
if (!string.IsNullOrEmpty(text3))
{
return text3;
}
}
string[] array = fileNameWithoutExtension.Split(new string[1] { " - " }, StringSplitOptions.RemoveEmptyEntries);
if (array.Length >= 2)
{
return string.Join(" - ", array.Skip(1));
}
if (fileName != "CustomMusic" && !string.IsNullOrEmpty(fileName))
{
return fileName;
}
return "Unknown Artist";
}
private static string? ReadMP3Tags(string filePath)
{
if (!filePath.EndsWith(".mp3", StringComparison.OrdinalIgnoreCase))
{
return null;
}
try
{
using FileStream stream = new FileStream(filePath, FileMode.Open, FileAccess.Read);
string text = ReadID3v2Tags(stream);
if (!string.IsNullOrEmpty(text))
{
return text;
}
string text2 = ReadID3v1Tags(stream);
if (!string.IsNullOrEmpty(text2))
{
return text2;
}
}
catch (Exception ex)
{
Logger.LogWarning((object)("Failed to read ID3 tags from " + Path.GetFileName(filePath) + ": " + ex.Message));
}
return null;
}
private static string? ReadID3v2Tags(FileStream stream)
{
stream.Seek(0L, SeekOrigin.Begin);
byte[] array = new byte[10];
if (stream.Read(array, 0, 10) != 10)
{
return null;
}
if (array[0] != 73 || array[1] != 68 || array[2] != 51)
{
return null;
}
int num = (array[6] << 21) | (array[7] << 14) | (array[8] << 7) | array[9];
if (num <= 0 || num > stream.Length - 10)
{
return null;
}
byte[] array2 = new byte[num];
if (stream.Read(array2, 0, num) != num)
{
return null;
}
string text = ExtractID3v2Frame(array2, "TPE1") ?? ExtractID3v2Frame(array2, "TPE2");
if (!string.IsNullOrEmpty(text))
{
return text;
}
string text2 = ExtractID3v2Frame(array2, "TIT2");
if (!string.IsNullOrEmpty(text2))
{
return text2;
}
return null;
}
private static string? ExtractID3v2Frame(byte[] data, string frameId)
{
byte[] bytes = Encoding.ASCII.GetBytes(frameId);
for (int i = 0; i <= data.Length - 10; i++)
{
if (data[i] != bytes[0] || data[i + 1] != bytes[1] || data[i + 2] != bytes[2] || data[i + 3] != bytes[3])
{
continue;
}
int num = (data[i + 4] << 24) | (data[i + 5] << 16) | (data[i + 6] << 8) | data[i + 7];
if (num > 0 && num < 1000000 && i + 10 + num <= data.Length)
{
byte b = data[i + 10];
int sourceIndex = i + 11;
int num2 = num - 1;
if (num2 > 0)
{
byte[] array = new byte[num2];
Array.Copy(data, sourceIndex, array, 0, num2);
return (b switch
{
0 => Encoding.ASCII.GetString(array),
1 => Encoding.Unicode.GetString(array),
3 => Encoding.UTF8.GetString(array),
_ => Encoding.UTF8.GetString(array),
}).Trim('\0', ' ');
}
}
}
return null;
}
private static string? ReadID3v1Tags(FileStream stream)
{
if (stream.Length < 128)
{
return null;
}
stream.Seek(-128L, SeekOrigin.End);
byte[] array = new byte[128];
if (stream.Read(array, 0, 128) != 128)
{
return null;
}
if (array[0] != 84 || array[1] != 65 || array[2] != 71)
{
return null;
}
string text = Encoding.ASCII.GetString(array, 33, 30).Trim('\0', ' ');
if (!string.IsNullOrEmpty(text))
{
return text;
}
string text2 = Encoding.ASCII.GetString(array, 3, 30).Trim('\0', ' ');
if (!string.IsNullOrEmpty(text2))
{
return text2;
}
return null;
}
private static string? ParseJsonMetadata(string jsonContent)
{
try
{
Match match = Regex.Match(jsonContent, "\"artist\"\\s*:\\s*\"([^\"]+)\"");
if (match.Success)
{
return match.Groups[1].Value;
}
Match match2 = Regex.Match(jsonContent, "\"title\"\\s*:\\s*\"([^\"]+)\"");
if (match2.Success)
{
return match2.Groups[1].Value;
}
}
catch (Exception ex)
{
Logger.LogWarning((object)("Error parsing JSON: " + ex.Message));
}
return null;
}
}
public class CustomMusicInfo
{
public string FilePath = "";
public string FileName = "";
public string ArtistName = "";
}