Some mods target the Mono version of the game, which is available by opting into the Steam beta branch "alternate"
Decompiled source of Audio Guard v1.0.0
Mods/AudioGuard.dll
Decompiled 2 days agousing System; using System.Collections.Generic; using System.Diagnostics; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Versioning; using HarmonyLib; using Il2CppInterop.Runtime; using Il2CppInterop.Runtime.InteropTypes.Arrays; using Il2CppScheduleOne.Audio; using Il2CppSystem.Collections.Generic; using MelonLoader; using MelonLoader.Preferences; using Microsoft.CodeAnalysis; using ScheduleIAudioGuard; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: MelonInfo(typeof(AudioGuard), "Schedule I Audio Guard", "1.0.0", "RebornReaper", null)] [assembly: MelonGame("TVGS", "Schedule I")] [assembly: MelonPlatformDomain(/*Could not decode attribute arguments.*/)] [assembly: HarmonyDontPatchAll] [assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = ".NET 6.0")] [assembly: AssemblyCompany("RebornReaper")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyDescription("Standalone audio capacity utility for Schedule I.")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0")] [assembly: AssemblyProduct("Audio Guard for Schedule I")] [assembly: AssemblyTitle("AudioGuard")] [assembly: AssemblyVersion("1.0.0.0")] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [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] [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; } } } namespace ScheduleIAudioGuard { public sealed class AudioGuard : MelonMod { private struct AudioConfig { public int SpeakerMode; public int DspBufferSize; public int SampleRate; public int RealVoices; public int VirtualVoices; } [UnmanagedFunctionPointer(CallingConvention.Cdecl)] private delegate void GetConfiguration(out AudioConfig config); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] [return: MarshalAs(UnmanagedType.I1)] private delegate bool SetConfiguration(ref AudioConfig config); private static AudioGuard self = null; private static MelonPreferences_Entry<int> poolSize = null; private static MelonPreferences_Entry<int> realVoices = null; private static MelonPreferences_Entry<int> virtualVoices = null; private static MelonPreferences_Entry<bool> enabled = null; private static MelonPreferences_Entry<bool> diagnostics = null; private static long requests; private static long misses; private static int poolHighWater; private static float nextReport; private static bool voicesAttempted; private static bool reportFailed; private static SFXManager? manager; private static string voiceStatus = "not checked"; public override void OnInitializeMelon() { self = this; MelonPreferences_Category obj = MelonPreferences.CreateCategory("AudioGuard"); enabled = obj.CreateEntry<bool>("Enabled", true, (string)null, "Enable changes. Restart after editing any setting.", false, false, (ValueValidator)null, (string)null); poolSize = obj.CreateEntry<int>("SoundPoolSize", 64, (string)null, "Minimum footstep/impact pool, clamped to 20-256. Does not shrink a larger existing pool.", false, false, (ValueValidator)null, (string)null); realVoices = obj.CreateEntry<int>("RealVoices", 128, (string)null, "Minimum simultaneous audible sounds, clamped to 32-256.", false, false, (ValueValidator)null, (string)null); virtualVoices = obj.CreateEntry<int>("VirtualVoices", 1024, (string)null, "Minimum tracked sounds, clamped to 128-4096.", false, false, (ValueValidator)null, (string)null); diagnostics = obj.CreateEntry<bool>("Diagnostics", true, (string)null, "Audio summary every 60 seconds in MelonLoader Latest.log.", false, false, (ValueValidator)null, (string)null); if (!enabled.Value) { ((MelonBase)this).LoggerInstance.Msg("Disabled by configuration."); return; } Patch("SetupSoundPool", "ExpandPool"); Patch("TryPullAudioSource", "RecordPoolRequest"); ((MelonBase)this).LoggerInstance.Msg("Loaded. Pool and voice capacity changes apply this session only. Save files are not edited."); } private void Patch(string method, string postfix) { //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Expected O, but got Unknown try { ((MelonBase)this).HarmonyInstance.Patch((MethodBase)AccessTools.Method(typeof(SFXManager), method, (Type[])null, (Type[])null), (HarmonyMethod)null, new HarmonyMethod(typeof(AudioGuard), postfix, (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); } catch (Exception ex) { ((MelonBase)this).LoggerInstance.Error("Could not patch " + method + ": " + ex.Message); } } private static void ExpandPool(SFXManager __instance) { try { manager = __instance; List<AudioSourceController> soundPool = __instance._soundPool; List<AudioSourceController> soundsInUse = __instance._soundsInUse; SFXConfiguration configuration = __instance._configuration; AudioSourceController val = ((configuration != null) ? configuration.ImpactSoundPrefab : null); if (soundPool == null || soundsInUse == null || (Object)(object)val == (Object)null) { ((MelonBase)self).LoggerInstance.Warning("Pool configuration unavailable; leaving original pool intact."); return; } int num = soundPool.Count + soundsInUse.Count; int num2 = Math.Max(num, Math.Clamp(poolSize.Value, 20, 256)); while (soundPool.Count + soundsInUse.Count < num2) { AudioSourceController val2 = Object.Instantiate<AudioSourceController>(val, ((Component)__instance).transform); val2.Stop(); soundPool.Add(val2); } ((MelonBase)self).LoggerInstance.Msg($"Footstep/impact pool: {num} -> {soundPool.Count + soundsInUse.Count} sources."); } catch (Exception ex) { ((MelonBase)self).LoggerInstance.Error("Pool expansion failed: " + ex.Message); } } private static void RecordPoolRequest(SFXManager __instance, bool __result) { if (!diagnostics.Value) { return; } requests++; if (!__result) { misses++; } try { manager = __instance; poolHighWater = Math.Max(poolHighWater, __instance._soundsInUse?.Count ?? 0); } catch { } } public override void OnSceneWasInitialized(int buildIndex, string sceneName) { if (enabled.Value) { if (!voicesAttempted && (sceneName.Equals("Menu", StringComparison.OrdinalIgnoreCase) || sceneName.Contains("MainMenu", StringComparison.OrdinalIgnoreCase))) { voicesAttempted = true; ConfigureVoices(); } requests = (misses = 0L); poolHighWater = 0; nextReport = Time.realtimeSinceStartup + 15f; if (diagnostics.Value) { ((MelonBase)this).LoggerInstance.Msg($"Scene initialized: {sceneName}; voices: {voiceStatus}."); } } } private void ConfigureVoices() { try { IntPtr intPtr = IL2CPP.il2cpp_resolve_icall("UnityEngine.AudioSettings::GetConfiguration_Injected"); IntPtr intPtr2 = IL2CPP.il2cpp_resolve_icall("UnityEngine.AudioSettings::SetConfiguration_Injected"); if (intPtr == IntPtr.Zero || intPtr2 == IntPtr.Zero) { throw new NotSupportedException("Unity audio configuration calls unavailable"); } GetConfiguration delegateForFunctionPointer = Marshal.GetDelegateForFunctionPointer<GetConfiguration>(intPtr); SetConfiguration delegateForFunctionPointer2 = Marshal.GetDelegateForFunctionPointer<SetConfiguration>(intPtr2); delegateForFunctionPointer(out var config); if (config.SpeakerMode < 1 || config.SpeakerMode > 7 || config.SampleRate < 8000 || config.SampleRate > 384000 || config.DspBufferSize <= 0 || config.RealVoices <= 0 || config.VirtualVoices < config.RealVoices) { throw new InvalidOperationException("Unexpected Unity audio configuration; refusing to change it"); } AudioConfig config2 = config; config2.RealVoices = Math.Max(config.RealVoices, Math.Clamp(realVoices.Value, 32, 256)); config2.VirtualVoices = Math.Max(config.VirtualVoices, Math.Max(config2.RealVoices, Math.Clamp(virtualVoices.Value, 128, 4096))); if ((config2.RealVoices != config.RealVoices || config2.VirtualVoices != config.VirtualVoices) && !delegateForFunctionPointer2(ref config2)) { delegateForFunctionPointer2(ref config); throw new InvalidOperationException("Unity rejected the requested voice limits; rollback requested"); } delegateForFunctionPointer(out var config3); voiceStatus = $"real {config.RealVoices}->{config3.RealVoices}, virtual {config.VirtualVoices}->{config3.VirtualVoices}"; ((MelonBase)this).LoggerInstance.Msg("Unity voice capacity: " + voiceStatus + ". Speaker mode, sample rate and buffer preserved."); } catch (Exception ex) { voiceStatus = "capacity change unavailable"; ((MelonBase)this).LoggerInstance.Warning("Voice adjustment skipped: " + ex.Message + ". Pool expansion remains active."); } } public override void OnUpdate() { if (!enabled.Value || !diagnostics.Value || reportFailed || Time.realtimeSinceStartup < nextReport) { return; } nextReport = Time.realtimeSinceStartup + 60f; try { Il2CppArrayBase<AudioSource> val = Object.FindObjectsOfType<AudioSource>(); int num = 0; int num2 = 0; int num3 = 0; List<string> list = new List<string>(); foreach (AudioSource item in val) { if ((Object)(object)item == (Object)null || !item.isPlaying) { continue; } num++; if (item.loop) { num3++; } if (item.isVirtual) { num2++; if (list.Count < 8) { DefaultInterpolatedStringHandler defaultInterpolatedStringHandler = new DefaultInterpolatedStringHandler(13, 3); defaultInterpolatedStringHandler.AppendFormatted(((Object)item).name); defaultInterpolatedStringHandler.AppendLiteral("/"); AudioClip clip = item.clip; defaultInterpolatedStringHandler.AppendFormatted(((clip != null) ? ((Object)clip).name : null) ?? "one-shot"); defaultInterpolatedStringHandler.AppendLiteral(" (priority "); defaultInterpolatedStringHandler.AppendFormatted(item.priority); defaultInterpolatedStringHandler.AppendLiteral(")"); list.Add(defaultInterpolatedStringHandler.ToStringAndClear()); } } } int value = -1; int value2 = -1; if ((Object)(object)manager != (Object)null) { value = manager._soundPool?.Count ?? (-1); value2 = manager._soundsInUse?.Count ?? (-1); } ((MelonBase)this).LoggerInstance.Msg($"[AudioSummary] activeSources={val.Length}, playingSources={num}, virtualizedSources={num2}, loopingSources={num3}; poolFree={value}, poolBusy={value2}, poolPeak={poolHighWater}; poolRequests={requests}, droppedPoolRequests={misses}; {voiceStatus}."); if (list.Count > 0) { ((MelonBase)this).LoggerInstance.Msg("[VirtualizedExamples] " + string.Join("; ", list)); } } catch (Exception ex) { reportFailed = true; ((MelonBase)this).LoggerInstance.Warning("Audio reporting disabled after error: " + ex.Message + ". Capacity patches remain active."); } } } }