using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Security.Cryptography;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using BepInEx.Unity.IL2CPP;
using BepInEx.Unity.IL2CPP.Hook;
using Microsoft.CodeAnalysis;
[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(/*Could not decode attribute arguments.*/)]
[assembly: AssemblyVersion("0.0.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
[CompilerGenerated]
[Embedded]
internal sealed class EmbeddedAttribute : System.Attribute
{
}
}
namespace System.Runtime.CompilerServices
{
[CompilerGenerated]
[Embedded]
[AttributeUsage(/*Could not decode attribute arguments.*/)]
internal sealed class NullableAttribute : System.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(/*Could not decode attribute arguments.*/)]
internal sealed class NullableContextAttribute : System.Attribute
{
public readonly byte Flag;
public NullableContextAttribute(byte P_0)
{
Flag = P_0;
}
}
[CompilerGenerated]
[Embedded]
[AttributeUsage(/*Could not decode attribute arguments.*/)]
internal sealed class RefSafetyRulesAttribute : System.Attribute
{
public readonly int Version;
public RefSafetyRulesAttribute(int P_0)
{
Version = P_0;
}
}
}
namespace InFalsusChartAudio
{
public static class AudioSourceState
{
public static bool IsReady(int result, int state)
{
if (result == 0)
{
if (state != 0)
{
return state == 6;
}
return true;
}
return false;
}
}
public enum AudioRoute
{
Attack,
Hold,
MouseHold,
MouseTransition,
KeyboardHoldHit,
SideKeyHit
}
public struct AudioCue
{
public long Id;
public double TimeMs;
public double EndMs;
public AudioRoute Route;
public bool Loop;
}
public sealed class AudioTimeline
{
public readonly List<AudioCue> Events = new List<AudioCue>();
private int position;
private double previous = 0.0 / 0.0;
private bool armed;
private bool skipCurrent;
public int Position => position;
public void Sort()
{
Events.Sort((Comparison<AudioCue>)delegate(AudioCue a, AudioCue b)
{
int num = a.TimeMs.CompareTo(b.TimeMs);
return (num == 0) ? a.Id.CompareTo(b.Id) : num;
});
}
private int LowerBound(double time)
{
int num = 0;
int num2 = Events.Count;
while (num < num2)
{
int num3 = num + (num2 - num) / 2;
if (Events[num3].TimeMs < time)
{
num = num3 + 1;
}
else
{
num2 = num3;
}
}
return num;
}
public void Reset(bool skipAtCurrentTime = false)
{
armed = false;
previous = 0.0 / 0.0;
position = 0;
skipCurrent = skipAtCurrentTime;
}
public void Advance(double now, double ahead, Action<AudioCue, double> submit, Action cancel)
{
//IL_002c: 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_00d6: Unknown result type (might be due to invalid IL or missing references)
if (double.IsNaN(now) || double.IsInfinity(now) || ahead < 0.0 || double.IsNaN(ahead) || double.IsInfinity(ahead))
{
throw new ArgumentOutOfRangeException();
}
bool flag = armed && (now - previous < -20.0 || now - previous > 250.0);
if (flag)
{
cancel.Invoke();
armed = false;
}
if (!armed)
{
position = LowerBound(skipCurrent ? (now + 0.001) : ((!flag && now >= 0.0 && now <= 50.0) ? 0.0 : now));
Enumerator<AudioCue> enumerator = Events.GetEnumerator();
try
{
while (enumerator.MoveNext())
{
AudioCue current = enumerator.Current;
if (current.Loop && (current.TimeMs < now || (skipCurrent && current.TimeMs == now)) && current.EndMs > now)
{
submit.Invoke(current, now);
}
}
}
finally
{
((System.IDisposable)enumerator/*cast due to .constrained prefix*/).Dispose();
}
armed = true;
skipCurrent = false;
}
else if (now < previous)
{
return;
}
while (position < Events.Count && Events[position].TimeMs <= now + ahead)
{
AudioCue audioCue = Events[position];
submit.Invoke(audioCue, now);
position++;
}
previous = now;
}
public static List<AudioCue> CoalesceAttacks(List<AudioCue> source, double windowMs, out int merged)
{
//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)
source.Sort((Comparison<AudioCue>)delegate(AudioCue a, AudioCue b)
{
int num2 = a.TimeMs.CompareTo(b.TimeMs);
return (num2 == 0) ? a.Id.CompareTo(b.Id) : num2;
});
List<AudioCue> val = new List<AudioCue>();
Dictionary<AudioRoute, double> val2 = new Dictionary<AudioRoute, double>();
merged = 0;
Enumerator<AudioCue> enumerator = source.GetEnumerator();
try
{
double num = default(double);
while (enumerator.MoveNext())
{
AudioCue current = enumerator.Current;
if (!current.Loop)
{
if (val2.TryGetValue(current.Route, ref num) && current.TimeMs - num <= windowMs)
{
merged++;
continue;
}
val2[current.Route] = current.TimeMs;
}
val.Add(current);
}
return val;
}
finally
{
((System.IDisposable)enumerator/*cast due to .constrained prefix*/).Dispose();
}
}
public static List<AudioCue> MergeLoops(System.Collections.Generic.IEnumerable<AudioCue> source, AudioRoute route)
{
//IL_007c: Unknown result type (might be due to invalid IL or missing references)
//IL_0081: Unknown result type (might be due to invalid IL or missing references)
List<AudioCue> val = new List<AudioCue>();
System.Collections.Generic.IEnumerator<AudioCue> enumerator = source.GetEnumerator();
try
{
while (((System.Collections.IEnumerator)enumerator).MoveNext())
{
AudioCue current = enumerator.Current;
if (current.Route == route && current.Loop && current.EndMs > current.TimeMs)
{
val.Add(current);
}
}
}
finally
{
((System.IDisposable)enumerator)?.Dispose();
}
val.Sort((Comparison<AudioCue>)((AudioCue a, AudioCue b) => a.TimeMs.CompareTo(b.TimeMs)));
List<AudioCue> val2 = new List<AudioCue>();
Enumerator<AudioCue> enumerator2 = val.GetEnumerator();
try
{
while (enumerator2.MoveNext())
{
AudioCue current2 = enumerator2.Current;
if (val2.Count == 0 || current2.TimeMs > val2[val2.Count - 1].EndMs)
{
val2.Add(current2);
continue;
}
AudioCue audioCue = val2[val2.Count - 1];
audioCue.EndMs = Math.Max(audioCue.EndMs, current2.EndMs);
val2[val2.Count - 1] = audioCue;
}
return val2;
}
finally
{
((System.IDisposable)enumerator2/*cast due to .constrained prefix*/).Dispose();
}
}
}
public static class DspTiming
{
public static ulong Target(ulong parentClock, double deltaMs, int sampleRate, double rate)
{
//IL_0020: Unknown result type (might be due to invalid IL or missing references)
//IL_004e: Unknown result type (might be due to invalid IL or missing references)
if (sampleRate <= 0 || !Finite(deltaMs) || !Finite(rate) || rate <= 0.0)
{
throw new ArgumentOutOfRangeException();
}
double num = Math.Max(0.0, deltaMs) * (double)sampleRate / (1000.0 * rate);
if (num > (double)(18446744073709551615uL - parentClock))
{
throw new ArgumentOutOfRangeException();
}
return checked(parentClock + (ulong)Math.Round(num));
}
private static bool Finite(double value)
{
if (!double.IsNaN(value))
{
return !double.IsInfinity(value);
}
return false;
}
}
internal sealed class FmodScheduler : System.IDisposable
{
private const string Dll = "fmodstudio";
private static System.IntPtr nativeModule;
private static readonly object moduleGate;
private readonly System.IntPtr system;
private readonly System.IntPtr[] sounds;
private readonly System.IntPtr[] groups;
private readonly int sampleRate;
private readonly List<System.IntPtr> owned = new List<System.IntPtr>();
public int SampleRate => sampleRate;
public int OwnedCount => owned.Count;
private static System.IntPtr ResolveModule()
{
//IL_0058: Unknown result type (might be due to invalid IL or missing references)
//IL_005f: Expected O, but got Unknown
//IL_00aa: Unknown result type (might be due to invalid IL or missing references)
if (nativeModule != System.IntPtr.Zero)
{
return nativeModule;
}
lock (moduleGate)
{
if (nativeModule != System.IntPtr.Zero)
{
return nativeModule;
}
foreach (ProcessModule item in (ReadOnlyCollectionBase)Process.GetCurrentProcess().Modules)
{
ProcessModule val = item;
if (val.ModuleName.Equals("fmodstudio.dll", (StringComparison)5))
{
nativeModule = val.BaseAddress;
return nativeModule;
}
}
throw new DllNotFoundException("The game's fmodstudio.dll has not loaded yet.");
}
}
static FmodScheduler()
{
//IL_0024: Unknown result type (might be due to invalid IL or missing references)
//IL_002e: Expected O, but got Unknown
moduleGate = new object();
NativeLibrary.SetDllImportResolver(typeof(FmodScheduler).Assembly, (DllImportResolver)((string name, Assembly assembly, DllImportSearchPath? path) => (!(name == "fmodstudio")) ? System.IntPtr.Zero : ResolveModule()));
}
[DllImport("fmodstudio", CallingConvention = (CallingConvention)2)]
private static extern int FMOD5_System_GetSoftwareFormat(System.IntPtr system, out int rate, out int mode, out int speakers);
[DllImport("fmodstudio", CallingConvention = (CallingConvention)2)]
private static extern int FMOD5_System_PlaySound(System.IntPtr system, System.IntPtr sound, System.IntPtr group, int paused, out System.IntPtr channel);
[DllImport("fmodstudio", CallingConvention = (CallingConvention)2)]
private static extern int FMOD5_Channel_GetDSPClock(System.IntPtr channel, out ulong own, out ulong parent);
[DllImport("fmodstudio", CallingConvention = (CallingConvention)2)]
private static extern int FMOD5_Channel_SetDelay(System.IntPtr channel, ulong start, ulong end, int stop);
[DllImport("fmodstudio", CallingConvention = (CallingConvention)2)]
private static extern int FMOD5_Channel_SetPaused(System.IntPtr channel, int paused);
[DllImport("fmodstudio", CallingConvention = (CallingConvention)2)]
private static extern int FMOD5_Channel_SetMode(System.IntPtr channel, uint mode);
[DllImport("fmodstudio", CallingConvention = (CallingConvention)2)]
private static extern int FMOD5_Channel_SetPitch(System.IntPtr channel, float pitch);
[DllImport("fmodstudio", CallingConvention = (CallingConvention)2)]
private static extern int FMOD5_Channel_SetVolume(System.IntPtr channel, float volume);
[DllImport("fmodstudio", CallingConvention = (CallingConvention)2)]
private static extern int FMOD5_Channel_SetPosition(System.IntPtr channel, uint position, uint unit);
[DllImport("fmodstudio", CallingConvention = (CallingConvention)2)]
private static extern int FMOD5_Channel_Stop(System.IntPtr channel);
[DllImport("fmodstudio", CallingConvention = (CallingConvention)2)]
private static extern int FMOD5_Sound_GetMode(System.IntPtr sound, out uint mode);
[DllImport("fmodstudio", CallingConvention = (CallingConvention)2)]
private static extern int FMOD5_Sound_GetLength(System.IntPtr sound, out uint length, uint unit);
[DllImport("fmodstudio", CallingConvention = (CallingConvention)2)]
private static extern int FMOD5_Sound_GetOpenState(System.IntPtr sound, out int state, out uint percent, out int starving, out int busy);
public static bool IsReady(System.IntPtr sound)
{
int result;
int state;
return Probe(sound, out result, out state);
}
public static bool Probe(System.IntPtr sound, out int result, out int state)
{
state = -1;
result = -1;
if (sound == System.IntPtr.Zero)
{
return false;
}
result = FMOD5_Sound_GetOpenState(sound, out state, out var _, out var _, out var _);
return AudioSourceState.IsReady(result, state);
}
private static void Check(int result, string operation)
{
//IL_0015: Unknown result type (might be due to invalid IL or missing references)
if (result != 0)
{
throw new InvalidOperationException(operation + " failed, FMOD result=" + result);
}
}
public FmodScheduler(System.IntPtr system, System.IntPtr[] sounds, System.IntPtr[] groups, bool[] used)
{
//IL_005f: Unknown result type (might be due to invalid IL or missing references)
//IL_0099: Unknown result type (might be due to invalid IL or missing references)
//IL_00cd: Unknown result type (might be due to invalid IL or missing references)
this.system = system;
this.sounds = sounds;
this.groups = groups;
Check(FMOD5_System_GetSoftwareFormat(system, out sampleRate, out var _, out var _), "GetSoftwareFormat");
if (sampleRate < 8000 || sampleRate > 384000)
{
throw new InvalidOperationException("Unexpected FMOD sample rate");
}
for (int i = 0; i < sounds.Length; i++)
{
if (used[i])
{
if (groups[i] == System.IntPtr.Zero || !IsReady(sounds[i]))
{
throw new InvalidOperationException("Sound/group is not ready for route " + i);
}
Check(FMOD5_Sound_GetMode(sounds[i], out var mode2), "GetMode");
if ((mode2 & 0x80) != 0)
{
throw new InvalidOperationException("Stream source cannot be safely shared; route " + i);
}
}
}
}
public void WarmUp(bool[] used)
{
for (int i = 0; i < used.Length; i++)
{
if (used[i])
{
Check(FMOD5_System_PlaySound(system, sounds[i], groups[i], 1, out var channel), "WarmUp.PlaySound(paused)");
try
{
Check(FMOD5_Channel_SetMode(channel, 1u), "WarmUp.SetMode");
Check(FMOD5_Channel_SetPitch(channel, 1f), "WarmUp.SetPitch");
Check(FMOD5_Channel_SetVolume(channel, 0f), "WarmUp.SetVolume");
Check(FMOD5_Sound_GetLength(sounds[i], out var _, 1u), "WarmUp.GetLength");
Check(FMOD5_Channel_SetPosition(channel, 0u, 1u), "WarmUp.SetPosition");
Check(FMOD5_Channel_GetDSPClock(channel, out var _, out var _), "WarmUp.GetDSPClock");
Check(FMOD5_Channel_SetDelay(channel, 0uL, 0uL, 1), "WarmUp.SetDelay");
Check(FMOD5_Channel_SetPaused(channel, 1), "WarmUp.KeepPaused");
}
finally
{
Check(FMOD5_Channel_Stop(channel), "WarmUp.Stop");
}
}
}
}
public void Submit(AudioCue cue, double now, double offset, double rate)
{
int route = (int)cue.Route;
double num = cue.TimeMs + offset;
double num2 = cue.EndMs + offset;
if (cue.Loop && num2 <= now)
{
return;
}
Check(FMOD5_System_PlaySound(system, sounds[route], groups[route], 1, out var channel), "PlaySound(paused)");
try
{
Check(FMOD5_Channel_SetMode(channel, (!cue.Loop) ? 1u : 2u), "SetMode");
Check(FMOD5_Channel_SetPitch(channel, (float)rate), "SetPitch");
Check(FMOD5_Channel_SetVolume(channel, 1f), "SetVolume");
if (cue.Loop && now > num)
{
Check(FMOD5_Sound_GetLength(sounds[route], out var length, 1u), "GetLength");
if (length != 0)
{
Check(FMOD5_Channel_SetPosition(channel, (uint)((now - num) % (double)length), 1u), "SetPosition");
}
}
Check(FMOD5_Channel_GetDSPClock(channel, out var _, out var parent), "GetDSPClock");
ulong num3 = DspTiming.Target(parent, num - now, sampleRate, rate);
ulong num4 = (cue.Loop ? DspTiming.Target(parent, num2 - now, sampleRate, rate) : 0);
if (cue.Loop && num4 <= num3)
{
num4 = num3 + 1;
}
Check(FMOD5_Channel_SetDelay(channel, num3, num4, 1), "SetDelay");
Check(FMOD5_Channel_SetPaused(channel, 0), "Unpause");
owned.Add(channel);
}
catch
{
FMOD5_Channel_Stop(channel);
throw;
}
}
public void StopAll()
{
//IL_0006: Unknown result type (might be due to invalid IL or missing references)
//IL_000b: Unknown result type (might be due to invalid IL or missing references)
Enumerator<System.IntPtr> enumerator = owned.GetEnumerator();
try
{
while (enumerator.MoveNext())
{
FMOD5_Channel_Stop(enumerator.Current);
}
}
finally
{
((System.IDisposable)enumerator/*cast due to .constrained prefix*/).Dispose();
}
owned.Clear();
}
public void Dispose()
{
StopAll();
}
}
public sealed class HoldHeadScope
{
private readonly HashSet<long> ids = new HashSet<long>();
private System.IntPtr queue;
public int Count => ids.Count;
public void Register(long id)
{
ids.Add(id);
}
public System.IntPtr Enter(bool active, long id, System.IntPtr target)
{
System.IntPtr result = queue;
queue = ((active && ids.Contains(id)) ? target : System.IntPtr.Zero);
return result;
}
public void Restore(System.IntPtr previous)
{
queue = previous;
}
public bool Owns(System.IntPtr target, byte eventType, int keyIndex)
{
if (queue != System.IntPtr.Zero && queue == target && eventType == 1 && keyIndex >= 0)
{
return keyIndex < 6;
}
return false;
}
}
public static class NoteAudioPolicy
{
public static bool IsSide(int lane)
{
if (lane != 2)
{
return lane == 3;
}
return true;
}
public static bool OwnsKeyboardFeedback(int type, int lane)
{
if (lane >= 1 && lane <= 3)
{
if (type != 1)
{
return type == 2;
}
return true;
}
return false;
}
public static AudioRoute FeedbackRoute(int firstKey, int lastKey)
{
//IL_0011: Unknown result type (might be due to invalid IL or missing references)
if (firstKey < 0 || lastKey < firstKey || lastKey >= 6)
{
throw new InvalidOperationException("Invalid keyboard key range");
}
if (firstKey != 0 && firstKey != 5)
{
return AudioRoute.KeyboardHoldHit;
}
return AudioRoute.SideKeyHit;
}
}
public static class NoteJudgements
{
[DllImport("GameAssembly", CallingConvention = (CallingConvention)2)]
private static extern System.IntPtr il2cpp_object_get_class(System.IntPtr obj);
[DllImport("GameAssembly", CallingConvention = (CallingConvention)2)]
private static extern System.IntPtr il2cpp_class_get_element_class(System.IntPtr klass);
[DllImport("GameAssembly", CallingConvention = (CallingConvention)2)]
private static extern System.IntPtr il2cpp_class_get_field_from_name(System.IntPtr klass, string name);
[DllImport("GameAssembly", CallingConvention = (CallingConvention)2)]
private static extern int il2cpp_field_get_offset(System.IntPtr field);
[DllImport("GameAssembly", CallingConvention = (CallingConvention)2)]
private static extern int il2cpp_class_value_size(System.IntPtr klass, out uint alignment);
private static int Offset(System.IntPtr klass, string field)
{
//IL_001f: Unknown result type (might be due to invalid IL or missing references)
System.IntPtr intPtr = il2cpp_class_get_field_from_name(klass, field);
if (intPtr == System.IntPtr.Zero)
{
throw new InvalidOperationException("Missing dictionary field " + field);
}
return il2cpp_field_get_offset(intPtr);
}
public static Dictionary<long, List<AudioCue>> Snapshot(System.IntPtr dictionary, int total)
{
//IL_0012: Unknown result type (might be due to invalid IL or missing references)
//IL_0055: Unknown result type (might be due to invalid IL or missing references)
//IL_008b: Unknown result type (might be due to invalid IL or missing references)
if (dictionary == System.IntPtr.Zero)
{
throw new InvalidOperationException("Missing per-note judgement dictionary");
}
System.IntPtr klass = il2cpp_object_get_class(dictionary);
int num = Marshal.ReadInt32(dictionary, Offset(klass, "_count"));
if (num < 0 || num > 200000 || Marshal.ReadInt32(dictionary, Offset(klass, "_freeCount")) != 0)
{
throw new InvalidOperationException("Unexpected chart dictionary state");
}
System.IntPtr intPtr = Marshal.ReadIntPtr(dictionary, Offset(klass, "_entries"));
if (intPtr == System.IntPtr.Zero || Marshal.ReadInt64(intPtr, 24) < num)
{
throw new InvalidOperationException("Invalid per-note entries");
}
System.IntPtr klass2 = il2cpp_class_get_element_class(il2cpp_object_get_class(intPtr));
uint alignment;
int size = il2cpp_class_value_size(klass2, out alignment);
int num2 = Offset(klass2, "hashCode");
int key = Offset(klass2, "key") - num2;
int value = Offset(klass2, "value") - num2;
return ReadEntries(intPtr, num, size, key, value, total);
}
public static Dictionary<long, List<AudioCue>> ReadEntries(System.IntPtr entries, int count, int size, int key, int value, int total)
{
//IL_0039: Unknown result type (might be due to invalid IL or missing references)
//IL_00e2: 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_0191: Unknown result type (might be due to invalid IL or missing references)
if (size != 32 || key != 8 || value != 16 || count < 0 || count > 200000 || entries == System.IntPtr.Zero || Marshal.ReadInt64(entries, 24) < count)
{
throw new InvalidOperationException("Unsupported per-note entry layout");
}
Dictionary<long, List<AudioCue>> val = new Dictionary<long, List<AudioCue>>();
HashSet<long> val2 = new HashSet<long>();
for (int i = 0; i < count; i++)
{
System.IntPtr intPtr = System.IntPtr.Add(entries, checked(32 + i * size));
long num = Marshal.ReadInt64(intPtr, key);
System.IntPtr intPtr2 = Marshal.ReadIntPtr(intPtr, value);
int num2 = Marshal.ReadInt32(intPtr, value + 8) & 0x7FFFFFFF;
int num3 = Marshal.ReadInt32(intPtr, value + 12) & 0x7FFFFFFF;
if (num3 == 0)
{
val.Add(num, new List<AudioCue>());
continue;
}
if (intPtr2 == System.IntPtr.Zero || num2 > Marshal.ReadInt64(intPtr2, 24) || num3 > total || (long)num2 + (long)num3 > Marshal.ReadInt64(intPtr2, 24))
{
throw new InvalidOperationException("Invalid per-note judgement memory");
}
List<AudioCue> val3 = new List<AudioCue>();
for (int j = 0; j < num3; j++)
{
System.IntPtr intPtr3 = System.IntPtr.Add(intPtr2, checked(32 + (num2 + j) * 40));
long num4 = Marshal.ReadInt64(intPtr3);
int num5 = Marshal.ReadInt32(intPtr3, 24);
if (num4 >= 0 && num4 < total && val2.Add(num4))
{
switch (num5)
{
case 1:
{
double num6 = BitConverter.Int64BitsToDouble(Marshal.ReadInt64(intPtr3, 16));
if (double.IsNaN(num6) || double.IsInfinity(num6) || num6 < -60000.0 || num6 > 86400000.0)
{
throw new InvalidOperationException("Invalid per-note judgement time");
}
val3.Add(new AudioCue
{
Id = num4,
TimeMs = num6,
Route = AudioRoute.KeyboardHoldHit
});
continue;
}
case 0:
case 2:
case 3:
case 4:
case 5:
continue;
}
}
throw new InvalidOperationException("Invalid per-note judgement identity");
}
val.Add(num, val3);
}
return val;
}
}
[BepInPlugin("local.infalsus.chartaudio", "In Falsus Chart Audio", "0.4.1")]
public sealed class Plugin : BasePlugin
{
[UnmanagedFunctionPointer(/*Could not decode attribute arguments.*/)]
private delegate void VoidMethod(System.IntPtr self, System.IntPtr mi);
[UnmanagedFunctionPointer(/*Could not decode attribute arguments.*/)]
private delegate System.IntPtr LoadSourceMethod(System.IntPtr self, byte kind, int group, System.IntPtr callback, [MarshalAs((UnmanagedType)3)] bool range, double from, double to, [MarshalAs((UnmanagedType)3)] bool flag, int extra, System.IntPtr mi);
[UnmanagedFunctionPointer(/*Could not decode attribute arguments.*/)]
private delegate double DoubleMethod(System.IntPtr self, System.IntPtr mi);
[UnmanagedFunctionPointer(/*Could not decode attribute arguments.*/)]
private delegate System.IntPtr GroupMethod(System.IntPtr groups, int group, System.IntPtr mi);
[UnmanagedFunctionPointer(/*Could not decode attribute arguments.*/)]
private delegate int NoteOffsetMethod(System.IntPtr note, System.IntPtr mi);
[UnmanagedFunctionPointer(/*Could not decode attribute arguments.*/)]
[return: MarshalAs((UnmanagedType)3)]
private delegate bool BoolMethod(System.IntPtr self, System.IntPtr mi);
[UnmanagedFunctionPointer(/*Could not decode attribute arguments.*/)]
private delegate void HitMethod(System.IntPtr self, byte kind, double offset, System.IntPtr mi);
[UnmanagedFunctionPointer(/*Could not decode attribute arguments.*/)]
private delegate void DispatchMethod(System.IntPtr self, System.IntPtr results, System.IntPtr evt, System.IntPtr mi);
[UnmanagedFunctionPointer(/*Could not decode attribute arguments.*/)]
private delegate void ProcessNoteMethod(System.IntPtr self, System.IntPtr records, System.IntPtr note, System.IntPtr queue, System.IntPtr mi);
[UnmanagedFunctionPointer(/*Could not decode attribute arguments.*/)]
private delegate void EnqueueMethod(System.IntPtr self, System.IntPtr evt, System.IntPtr mi);
private const string SupportedHash = "375F3A8DB0E5DFF69B78A74E0518B98D83214EB7E5BCDC428386615DDBCBC04F";
private readonly List<INativeDetour> hooks = new List<INativeDetour>();
private readonly List<System.Delegate> roots = new List<System.Delegate>();
private ConfigEntry<bool> enabled;
private ConfigEntry<bool> removeMuteLimit;
private ConfigEntry<bool> debug;
private ConfigEntry<double> soundOffset;
private ConfigEntry<double> scheduleAhead;
private System.IntPtr module;
private System.IntPtr logicalField;
private System.IntPtr controllerField;
private System.IntPtr systemField;
private System.IntPtr groupsField;
private System.IntPtr soundManagerField;
private System.IntPtr scene;
private System.IntPtr chartIdentity;
private RuntimePatch mutePatch;
private AudioTimeline timeline;
private FmodScheduler scheduler;
private SourceLeases sources;
private AudioTimeline preparedTimeline;
private HoldHeadScope preparedHeads;
private System.IntPtr preparedChart;
private bool[] preparedUsed;
private string preparedDescription;
private long nextPrepareAttempt;
private HoldHeadScope holdHeads = new HoldHeadScope();
private bool ready;
private bool faulted;
private bool wasPlaying;
private double appliedOffset;
private double lastRate = 1.0;
private long lastStatus;
private int scheduled;
private int suppressed;
private int feedbackSuppressed;
private readonly int[] scheduledByRoute = new int[System.Enum.GetValues(typeof(AudioRoute)).Length];
private VoidMethod originalUpdate;
private VoidMethod originalStart;
private VoidMethod originalDisable;
private VoidMethod originalDestroy;
private VoidMethod originalAudioInit;
private VoidMethod loopStart;
private VoidMethod loopStop;
private VoidMethod disposeSource;
private LoadSourceMethod loadSource;
private DispatchMethod originalDispatch;
private HitMethod originalHit;
private ProcessNoteMethod originalProcessNote;
private EnqueueMethod originalEnqueue;
private DoubleMethod musicPosition;
private GroupMethod groupSelector;
private NoteOffsetMethod noteOffset;
private BoolMethod mouseHoldActive;
[DllImport("GameAssembly", CallingConvention = (CallingConvention)2)]
private static extern System.IntPtr il2cpp_domain_get();
[DllImport("GameAssembly", CallingConvention = (CallingConvention)2)]
private static extern System.IntPtr il2cpp_domain_assembly_open(System.IntPtr domain, string name);
[DllImport("GameAssembly", CallingConvention = (CallingConvention)2)]
private static extern System.IntPtr il2cpp_assembly_get_image(System.IntPtr assembly);
[DllImport("GameAssembly", CallingConvention = (CallingConvention)2)]
private static extern System.IntPtr il2cpp_class_from_name(System.IntPtr image, string ns, string name);
[DllImport("GameAssembly", CallingConvention = (CallingConvention)2)]
private static extern System.IntPtr il2cpp_class_get_field_from_name(System.IntPtr klass, string name);
[DllImport("GameAssembly", CallingConvention = (CallingConvention)2)]
private static extern void il2cpp_field_static_get_value(System.IntPtr field, out System.IntPtr value);
[DllImport("GameAssembly", CallingConvention = (CallingConvention)2, EntryPoint = "il2cpp_field_static_get_value")]
private static extern void ReadStaticValue(System.IntPtr field, System.IntPtr buffer);
private static System.IntPtr Ptr(System.IntPtr p, int off)
{
return Marshal.ReadIntPtr(p, off);
}
private static int I32(System.IntPtr p, int off)
{
return Marshal.ReadInt32(p, off);
}
private static double F64(System.IntPtr p, int off)
{
return BitConverter.Int64BitsToDouble(Marshal.ReadInt64(p, off));
}
private static float F32(System.IntPtr p, int off)
{
return BitConverter.Int32BitsToSingle(I32(p, off));
}
private static bool Finite(double t)
{
if (!double.IsNaN(t))
{
return !double.IsInfinity(t);
}
return false;
}
private System.IntPtr At(int rva)
{
return System.IntPtr.Add(module, rva);
}
private T Function<T>(int rva) where T : System.Delegate
{
return Marshal.GetDelegateForFunctionPointer<T>(At(rva));
}
private void Hook<T>(int rva, T callback, out T original) where T : System.Delegate
{
RuntimeHelpers.PrepareDelegate((System.Delegate)callback);
roots.Add((System.Delegate)callback);
hooks.Add(INativeDetour.CreateAndApply<T>(At(rva), callback, ref original));
}
private void WarmManagedPaths()
{
//IL_00cf: Unknown result type (might be due to invalid IL or missing references)
//IL_00d4: Unknown result type (might be due to invalid IL or missing references)
//IL_005b: Unknown result type (might be due to invalid IL or missing references)
Stopwatch val = Stopwatch.StartNew();
int num = 0;
int num2 = 0;
System.Type[] types = typeof(Plugin).Assembly.GetTypes();
for (int i = 0; i < types.Length; i++)
{
MethodInfo[] methods = types[i].GetMethods((BindingFlags)62);
foreach (MethodInfo val2 in methods)
{
if (!((MethodBase)val2).ContainsGenericParameters && !((MethodBase)val2).IsAbstract && ((MethodBase)val2).GetMethodBody() != null)
{
try
{
RuntimeHelpers.PrepareMethod(((MethodBase)val2).MethodHandle);
num++;
}
catch (NotSupportedException)
{
num2++;
}
catch (ArgumentException)
{
num2++;
}
}
}
}
ManualLogSource log = ((BasePlugin)this).Log;
string[] obj = new string[6]
{
"Managed audio paths prepared: methods=",
num.ToString(),
" skipped=",
num2.ToString(),
" elapsedMs=",
null
};
TimeSpan elapsed = val.Elapsed;
obj[5] = ((TimeSpan)(ref elapsed)).TotalMilliseconds.ToString();
log.LogInfo((object)string.Concat(obj));
}
private System.IntPtr Field(string asm, string ns, string type, string name)
{
//IL_0023: Unknown result type (might be due to invalid IL or missing references)
//IL_004d: Unknown result type (might be due to invalid IL or missing references)
//IL_0079: Unknown result type (might be due to invalid IL or missing references)
System.IntPtr intPtr = il2cpp_domain_assembly_open(il2cpp_domain_get(), asm);
if (intPtr == System.IntPtr.Zero)
{
throw new InvalidOperationException("Missing assembly " + asm);
}
System.IntPtr intPtr2 = il2cpp_class_from_name(il2cpp_assembly_get_image(intPtr), ns, type);
if (intPtr2 == System.IntPtr.Zero)
{
throw new InvalidOperationException("Missing type " + type);
}
System.IntPtr intPtr3 = il2cpp_class_get_field_from_name(intPtr2, name);
if (intPtr3 == System.IntPtr.Zero)
{
throw new InvalidOperationException("Missing field " + type + "." + name);
}
return intPtr3;
}
private System.IntPtr StaticObject(System.IntPtr field)
{
il2cpp_field_static_get_value(field, out var value);
return value;
}
public override void Load()
{
//IL_043f: Unknown result type (might be due to invalid IL or missing references)
//IL_0444: Unknown result type (might be due to invalid IL or missing references)
//IL_012e: Unknown result type (might be due to invalid IL or missing references)
//IL_0134: Expected O, but got Unknown
//IL_0185: Unknown result type (might be due to invalid IL or missing references)
enabled = ((BasePlugin)this).Config.Bind<bool>("General", "Enabled", true, "Chart-driven audio. Restart after changing configuration.");
removeMuteLimit = ((BasePlugin)this).Config.Bind<bool>("General", "RemoveOffsetMuteLimit", true, "Remove abs(offset)>100ms automatic hitsound mute; keep user offsets and volume settings.");
soundOffset = ((BasePlugin)this).Config.Bind<double>("Audio", "SoundOffsetMs", 0.0, "Extra offset relative to music: positive delays, negative advances. Applied on next scene/retry.");
scheduleAhead = ((BasePlugin)this).Config.Bind<double>("Audio", "ScheduleAheadMs", 250.0, "Submit known chart sounds ahead of time to FMOD DSP scheduling (50..1000ms). Pending sounds are cancelled on pause/seek/exit.");
debug = ((BasePlugin)this).Config.Bind<bool>("Debug", "DebugLogging", true, "Record the first 16 scheduled sounds and periodic counters. Slow submissions and preparation timings are always logged.");
SHA256 val = SHA256.Create();
try
{
if (BitConverter.ToString(((HashAlgorithm)val).ComputeHash(File.ReadAllBytes(Path.Combine(Paths.GameRootPath, "GameAssembly.dll")))).Replace("-", "") != "375F3A8DB0E5DFF69B78A74E0518B98D83214EB7E5BCDC428386615DDBCBC04F")
{
((BasePlugin)this).Log.LogError((object)"Unsupported build; no patches applied.");
return;
}
}
finally
{
((System.IDisposable)val)?.Dispose();
}
foreach (ProcessModule item in (ReadOnlyCollectionBase)Process.GetCurrentProcess().Modules)
{
ProcessModule val2 = item;
if (val2.ModuleName.Equals("GameAssembly.dll", (StringComparison)5))
{
module = val2.BaseAddress;
}
}
if (module == System.IntPtr.Zero)
{
throw new InvalidOperationException("Missing GameAssembly");
}
try
{
logicalField = Field("Game", "ifapp.Game", "LogicalNotePlayer", "_Qe");
controllerField = Field("Game", "ifapp.Game.Scenes", "GameScene", "_DN");
systemField = Field("Game.Audio", "_J", "_IF", "_ZSA");
groupsField = Field("Game.Audio", "_J", "_IF", "_DtA");
soundManagerField = Field("Game.Audio", "_J", "_zf", "_psA");
loadSource = Function<LoadSourceMethod>(4498624);
disposeSource = Function<VoidMethod>(4439936);
musicPosition = Function<DoubleMethod>(4444576);
groupSelector = Function<GroupMethod>(4489632);
noteOffset = Function<NoteOffsetMethod>(5397200);
loopStart = Function<VoidMethod>(4482080);
loopStop = Function<VoidMethod>(4481616);
mouseHoldActive = Function<BoolMethod>(6282944);
if (removeMuteLimit.Value)
{
mutePatch = new RuntimePatch(At(7372871), new byte[2] { 127, 11 }, new byte[2] { 144, 144 });
((BasePlugin)this).Log.LogInfo((object)"Offset mute limit removed; user offset/volume settings preserved.");
}
Hook<VoidMethod>(7354544, Update, out originalUpdate);
Hook<VoidMethod>(7349536, Start, out originalStart);
Hook<VoidMethod>(7349328, Disable, out originalDisable);
Hook<VoidMethod>(7348624, Destroy, out originalDestroy);
Hook<VoidMethod>(7371024, AudioInit, out originalAudioInit);
Hook<DispatchMethod>(7356464, Dispatch, out originalDispatch);
Hook<HitMethod>(7368224, Hit, out originalHit);
Hook<ProcessNoteMethod>(6258240, ProcessNote, out originalProcessNote);
Hook<EnqueueMethod>(13700784, Enqueue, out originalEnqueue);
WarmManagedPaths();
((BasePlugin)this).Log.LogInfo((object)"Plugin initialized; chart takeover remains inactive until DSP queue ready is logged.");
}
catch (System.Exception ex)
{
((BasePlugin)this).Log.LogError((object)ex);
Enumerator<INativeDetour> enumerator2 = hooks.GetEnumerator();
try
{
while (enumerator2.MoveNext())
{
((System.IDisposable)enumerator2.Current).Dispose();
}
}
finally
{
((System.IDisposable)enumerator2/*cast due to .constrained prefix*/).Dispose();
}
hooks.Clear();
mutePatch?.Dispose();
}
}
private void ClearAudio()
{
scheduler?.Dispose();
scheduler = null;
sources?.Dispose();
sources = null;
}
private void Reset(System.IntPtr self)
{
ClearAudio();
ready = false;
timeline = null;
preparedTimeline = null;
preparedChart = System.IntPtr.Zero;
preparedUsed = null;
preparedHeads = null;
nextPrepareAttempt = 0L;
holdHeads = new HoldHeadScope();
scene = self;
chartIdentity = System.IntPtr.Zero;
faulted = false;
wasPlaying = false;
scheduled = 0;
suppressed = 0;
feedbackSuppressed = 0;
System.Array.Clear((System.Array)scheduledByRoute, 0, scheduledByRoute.Length);
}
private void SetOriginalLoop(int offset, bool active)
{
System.IntPtr intPtr = Ptr(scene, offset);
if (!(intPtr == System.IntPtr.Zero) && !(Ptr(intPtr, 24) == System.IntPtr.Zero))
{
if (active)
{
loopStart(intPtr, System.IntPtr.Zero);
}
else
{
loopStop(intPtr, System.IntPtr.Zero);
}
}
}
private void RestoreOriginalLoops()
{
if (!(scene == System.IntPtr.Zero))
{
System.IntPtr intPtr = StaticObject(controllerField);
if (!(intPtr == System.IntPtr.Zero))
{
System.IntPtr intPtr2 = Ptr(intPtr, 160);
System.IntPtr intPtr3 = Ptr(intPtr, 168);
SetOriginalLoop(368, intPtr2 != System.IntPtr.Zero && I32(intPtr2, 32) > 0);
SetOriginalLoop(376, intPtr3 != System.IntPtr.Zero && I32(intPtr3, 32) > 0);
SetOriginalLoop(384, mouseHoldActive(System.IntPtr.Add(intPtr, 24), System.IntPtr.Zero));
}
}
}
private void Fail(System.Exception e)
{
ClearAudio();
if (ready)
{
try
{
RestoreOriginalLoops();
}
catch (System.Exception ex)
{
((BasePlugin)this).Log.LogError((object)("Original loop restore: " + (object)ex));
}
}
ready = false;
timeline = null;
if (!faulted)
{
((BasePlugin)this).Log.LogError((object)("Chart audio disabled for this scene; original audio restored. " + (object)e));
}
faulted = true;
}
private static System.IntPtr MemoryData(System.IntPtr owner, int off, int size, out int count)
{
//IL_0076: Unknown result type (might be due to invalid IL or missing references)
//IL_0046: Unknown result type (might be due to invalid IL or missing references)
System.IntPtr intPtr = Ptr(owner, off);
int num = I32(owner, off + 8) & 0x7FFFFFFF;
count = I32(owner, off + 12) & 0x7FFFFFFF;
if (intPtr == System.IntPtr.Zero)
{
if (count == 0)
{
return System.IntPtr.Zero;
}
throw new InvalidOperationException("Missing chart array");
}
long num2 = Marshal.ReadInt64(intPtr, 24);
if (num2 < 0 || num2 > 2000000 || num > num2 || count > num2 - num)
{
throw new InvalidOperationException("Invalid chart memory range");
}
return System.IntPtr.Add(intPtr, checked(32 + num * size));
}
private static System.IntPtr SoundOf(System.IntPtr instance)
{
if (!(instance == System.IntPtr.Zero))
{
return Ptr(instance, 32);
}
return System.IntPtr.Zero;
}
private static System.IntPtr LoopSound(System.IntPtr self, int offset)
{
System.IntPtr intPtr = Ptr(self, offset);
if (!(intPtr == System.IntPtr.Zero))
{
return SoundOf(Ptr(intPtr, 24));
}
return System.IntPtr.Zero;
}
private System.IntPtr AcquireSource(int route)
{
System.IntPtr intPtr = StaticObject(soundManagerField);
System.IntPtr intPtr2 = Ptr(scene, 448);
if (intPtr == System.IntPtr.Zero || intPtr2 == System.IntPtr.Zero)
{
return System.IntPtr.Zero;
}
System.IntPtr intPtr3 = Ptr(intPtr2, 64);
if (intPtr3 == System.IntPtr.Zero)
{
return System.IntPtr.Zero;
}
byte kind = route switch
{
4 => Marshal.ReadByte(scene, 408),
3 => Marshal.ReadByte(scene, 424),
2 => 57,
1 => 63,
0 => Marshal.ReadByte(scene, 440),
_ => Marshal.ReadByte(scene, 392),
};
System.IntPtr result = loadSource(intPtr, kind, (route >= 4) ? 1 : ((route == 1) ? 2 : 3), intPtr3, range: false, -1.0, -1.0, flag: false, 0, System.IntPtr.Zero);
ManualLogSource log = ((BasePlugin)this).Log;
string[] obj = new string[6] { "Retained paused source: route=", null, null, null, null, null };
AudioRoute audioRoute = (AudioRoute)route;
obj[1] = ((object)audioRoute/*cast due to .constrained prefix*/).ToString();
obj[2] = " kind=";
obj[3] = kind.ToString();
obj[4] = " wrapper=";
obj[5] = result.ToString("X");
log.LogInfo((object)string.Concat(obj));
return result;
}
private bool BuildTimeline()
{
//IL_0139: Unknown result type (might be due to invalid IL or missing references)
//IL_09d4: Unknown result type (might be due to invalid IL or missing references)
//IL_015b: Unknown result type (might be due to invalid IL or missing references)
//IL_020c: Unknown result type (might be due to invalid IL or missing references)
//IL_02f1: Unknown result type (might be due to invalid IL or missing references)
//IL_0bb7: Unknown result type (might be due to invalid IL or missing references)
//IL_0bbc: Unknown result type (might be due to invalid IL or missing references)
//IL_0bdc: Unknown result type (might be due to invalid IL or missing references)
//IL_0be1: Unknown result type (might be due to invalid IL or missing references)
//IL_0505: Unknown result type (might be due to invalid IL or missing references)
//IL_050a: Unknown result type (might be due to invalid IL or missing references)
//IL_0342: Unknown result type (might be due to invalid IL or missing references)
//IL_054a: Unknown result type (might be due to invalid IL or missing references)
//IL_054f: Unknown result type (might be due to invalid IL or missing references)
//IL_03bc: Unknown result type (might be due to invalid IL or missing references)
//IL_03c1: Unknown result type (might be due to invalid IL or missing references)
//IL_0392: Unknown result type (might be due to invalid IL or missing references)
//IL_0497: Unknown result type (might be due to invalid IL or missing references)
//IL_064f: Unknown result type (might be due to invalid IL or missing references)
//IL_0654: Unknown result type (might be due to invalid IL or missing references)
//IL_07e9: Unknown result type (might be due to invalid IL or missing references)
//IL_07ee: Unknown result type (might be due to invalid IL or missing references)
long timestamp = Stopwatch.GetTimestamp();
if (timestamp < nextPrepareAttempt)
{
return false;
}
nextPrepareAttempt = timestamp + Stopwatch.Frequency / 10;
Stopwatch val = Stopwatch.StartNew();
System.IntPtr intPtr = StaticObject(logicalField);
if (intPtr == System.IntPtr.Zero)
{
return false;
}
System.IntPtr intPtr2 = Ptr(intPtr, 64);
if (intPtr2 == System.IntPtr.Zero)
{
return false;
}
System.IntPtr[] array = new System.IntPtr[6]
{
SoundOf(Ptr(scene, 448)),
LoopSound(scene, 368),
LoopSound(scene, 384),
SoundOf(Ptr(scene, 432)),
SoundOf(Ptr(scene, 416)),
SoundOf(Ptr(scene, 400))
};
TimeSpan elapsed;
if (preparedChart != intPtr2)
{
if (preparedChart != System.IntPtr.Zero)
{
ClearAudio();
}
int num = I32(intPtr2, 216);
if (num == 0)
{
return false;
}
if (num < 0 || num > 200000)
{
throw new InvalidOperationException("Invalid judgement count");
}
int count;
System.IntPtr intPtr3 = MemoryData(intPtr2, 176, 40, out count);
if (num > count)
{
throw new InvalidOperationException("Incomplete judgement array");
}
AudioTimeline audioTimeline = new AudioTimeline();
HashSet<long> val2 = new HashSet<long>();
int[] array2 = new int[6];
int merged = 0;
Dictionary<long, List<AudioCue>> val3 = NoteJudgements.Snapshot(Ptr(intPtr2, 56), num);
HoldHeadScope holdHeadScope = new HoldHeadScope();
for (int i = 0; i < num; i++)
{
System.IntPtr intPtr4 = System.IntPtr.Add(intPtr3, i * 40);
long num2 = Marshal.ReadInt64(intPtr4, 0);
int num3 = I32(intPtr4, 24);
double num4 = F64(intPtr4, 16);
if (num2 < 0 || num2 >= num || num3 < 0 || num3 >= 6 || !val2.Add(num2) || !Finite(num4) || num4 < -60000.0 || num4 > 86400000.0)
{
throw new InvalidOperationException("Unexpected judgement record");
}
array2[num3]++;
if (num3 == 1 || num3 == 2 || num3 == 5)
{
audioTimeline.Events.Add(new AudioCue
{
Id = num2,
TimeMs = num4,
Route = AudioRoute.Attack
});
}
}
List<AudioCue> val4 = new List<AudioCue>();
System.IntPtr intPtr5 = Ptr(intPtr, 48);
int num5 = 0;
int num6 = 0;
int num7 = 0;
int num8 = 0;
int[] array3 = new int[5];
int[] array4 = new int[30];
if (intPtr5 == System.IntPtr.Zero)
{
return false;
}
System.IntPtr intPtr6 = Ptr(intPtr5, 16);
int num9 = I32(intPtr5, 24);
if (intPtr6 == System.IntPtr.Zero || num9 < 0 || num9 > 200000 || num9 > Marshal.ReadInt64(intPtr6, 24))
{
throw new InvalidOperationException("Invalid note list");
}
List<AudioCue> val5 = default(List<AudioCue>);
Enumerator<AudioCue> enumerator;
for (int j = 0; j < num9; j++)
{
System.IntPtr intPtr7 = System.IntPtr.Add(intPtr6, checked(32 + j * 128));
int num10 = I32(intPtr7, 20);
int num11 = I32(intPtr7, 16);
if (num11 < 0 || num11 >= 5 || num10 < 0 || num10 >= 6)
{
throw new InvalidOperationException("Unrecognised chart note category/type; refusing partial takeover");
}
array3[num11]++;
array4[num11 * 6 + num10]++;
if (NoteAudioPolicy.OwnsKeyboardFeedback(num10, num11))
{
long num12 = Marshal.ReadInt64(intPtr7);
if (!val3.TryGetValue(num12, ref val5))
{
throw new InvalidOperationException("Missing keyboard note judgements");
}
holdHeadScope.Register(num12);
AudioRoute audioRoute = NoteAudioPolicy.FeedbackRoute(I32(intPtr7, 120), I32(intPtr7, 124));
enumerator = val5.GetEnumerator();
try
{
while (enumerator.MoveNext())
{
AudioCue current = enumerator.Current;
current.Route = audioRoute;
audioTimeline.Events.Add(current);
if (audioRoute == AudioRoute.SideKeyHit)
{
num8++;
}
}
}
finally
{
((System.IDisposable)enumerator/*cast due to .constrained prefix*/).Dispose();
}
if (NoteAudioPolicy.IsSide(num11))
{
num5++;
}
if (num10 == 2)
{
num6++;
if (NoteAudioPolicy.IsSide(num11))
{
num7++;
}
}
}
if (num10 == 2 || num10 == 5)
{
double num13 = noteOffset(intPtr7, System.IntPtr.Zero);
double num14 = (double)I32(intPtr7, 24) + num13;
double num15 = (double)I32(intPtr7, 28) + num13;
if (num14 < -60000.0 || num15 > 86400000.0 || num15 < num14)
{
throw new InvalidOperationException("Invalid hold interval");
}
if (num15 > num14)
{
val4.Add(new AudioCue
{
Id = -1 - (long)j * 2L,
TimeMs = num14,
EndMs = num15,
Loop = true,
Route = ((num10 == 2) ? AudioRoute.Hold : AudioRoute.MouseHold)
});
}
}
}
enumerator = AudioTimeline.MergeLoops((System.Collections.Generic.IEnumerable<AudioCue>)val4, AudioRoute.Hold).GetEnumerator();
try
{
while (enumerator.MoveNext())
{
AudioCue current2 = enumerator.Current;
audioTimeline.Events.Add(current2);
}
}
finally
{
((System.IDisposable)enumerator/*cast due to .constrained prefix*/).Dispose();
}
List<AudioCue> val6 = AudioTimeline.MergeLoops((System.Collections.Generic.IEnumerable<AudioCue>)val4, AudioRoute.MouseHold);
enumerator = val6.GetEnumerator();
try
{
while (enumerator.MoveNext())
{
AudioCue current3 = enumerator.Current;
audioTimeline.Events.Add(current3);
audioTimeline.Events.Add(new AudioCue
{
Id = current3.Id - 1,
TimeMs = current3.TimeMs,
Route = AudioRoute.MouseTransition
});
audioTimeline.Events.Add(new AudioCue
{
Id = current3.Id - 2,
TimeMs = current3.EndMs,
Route = AudioRoute.MouseTransition
});
}
}
finally
{
((System.IDisposable)enumerator/*cast due to .constrained prefix*/).Dispose();
}
AudioTimeline audioTimeline2 = new AudioTimeline();
audioTimeline2.Events.AddRange((System.Collections.Generic.IEnumerable<AudioCue>)AudioTimeline.CoalesceAttacks(audioTimeline.Events, 10.0, out merged));
if (audioTimeline2.Events.Count == 0)
{
return false;
}
bool[] array5 = new bool[array.Length];
enumerator = audioTimeline2.Events.GetEnumerator();
try
{
while (enumerator.MoveNext())
{
array5[(int)enumerator.Current.Route] = true;
}
}
finally
{
((System.IDisposable)enumerator/*cast due to .constrained prefix*/).Dispose();
}
preparedTimeline = audioTimeline2;
preparedHeads = holdHeadScope;
preparedUsed = array5;
preparedChart = intPtr2;
preparedDescription = string.Concat(new string[24]
{
"events=",
audioTimeline2.Events.Count.ToString(),
" judgementKinds=",
string.Join<int>(",", (System.Collections.Generic.IEnumerable<int>)array2),
" mouseAttacks=",
array2[5].ToString(),
" holdIntervals=",
val4.Count.ToString(),
" mouseLoopUnions=",
val6.Count.ToString(),
" mergedAttacks=",
merged.ToString(),
" keyboardHolds=",
num6.ToString(),
" sideNotes=",
num5.ToString(),
" sideHolds=",
num7.ToString(),
" sideHeads=",
num8.ToString(),
" laneCounts=",
string.Join<int>(",", (System.Collections.Generic.IEnumerable<int>)array3),
" laneTypeCounts=",
string.Join<int>(",", (System.Collections.Generic.IEnumerable<int>)array4)
});
ManualLogSource log = ((BasePlugin)this).Log;
string text = preparedDescription;
elapsed = val.Elapsed;
log.LogInfo((object)("Chart snapshot prepared: " + text + " elapsedMs=" + ((TimeSpan)(ref elapsed)).TotalMilliseconds));
}
if (sources == null)
{
sources = new SourceLeases(AcquireSource, SoundOf, [CompilerGenerated] (System.IntPtr p) =>
{
disposeSource(p, System.IntPtr.Zero);
}, FmodScheduler.IsReady);
}
for (int num16 = 0; num16 < preparedUsed.Length; num16++)
{
if (!preparedUsed[num16])
{
continue;
}
array[num16] = sources.Resolve(num16, array[num16]);
if (!FmodScheduler.Probe(array[num16], out var result, out var state))
{
long timestamp2 = Stopwatch.GetTimestamp();
if (timestamp2 - lastStatus > Stopwatch.Frequency * 5)
{
lastStatus = timestamp2;
ManualLogSource log2 = ((BasePlugin)this).Log;
string[] obj = new string[9] { "Waiting for game sound source: ", null, null, null, null, null, null, null, null };
AudioRoute audioRoute2 = (AudioRoute)num16;
obj[1] = ((object)audioRoute2/*cast due to .constrained prefix*/).ToString();
obj[2] = " sound=";
obj[3] = array[num16].ToString("X");
obj[4] = " result=";
obj[5] = result.ToString();
obj[6] = " openState=";
obj[7] = state.ToString();
obj[8] = "; original audio remains active.";
log2.LogInfo((object)string.Concat(obj));
}
return false;
}
}
if (!Finite(soundOffset.Value) || Math.Abs(soundOffset.Value) > 5000.0 || !Finite(scheduleAhead.Value) || scheduleAhead.Value < 50.0 || scheduleAhead.Value > 1000.0)
{
throw new InvalidOperationException("Invalid SoundOffsetMs/ScheduleAheadMs");
}
System.IntPtr intPtr8 = StaticObject(systemField);
if (intPtr8 == System.IntPtr.Zero)
{
return false;
}
System.IntPtr intPtr9 = Marshal.AllocHGlobal(80);
System.IntPtr[] array6 = new System.IntPtr[array.Length];
try
{
ReadStaticValue(groupsField, intPtr9);
array6[0] = groupSelector(intPtr9, 3, System.IntPtr.Zero);
array6[1] = groupSelector(intPtr9, 2, System.IntPtr.Zero);
array6[2] = array6[0];
array6[3] = array6[0];
array6[4] = groupSelector(intPtr9, 1, System.IntPtr.Zero);
array6[5] = array6[4];
}
finally
{
Marshal.FreeHGlobal(intPtr9);
}
FmodScheduler fmodScheduler = new FmodScheduler(intPtr8, array, array6, preparedUsed);
Stopwatch val7 = Stopwatch.StartNew();
try
{
fmodScheduler.WarmUp(preparedUsed);
SetOriginalLoop(368, active: false);
SetOriginalLoop(376, active: false);
SetOriginalLoop(384, active: false);
}
catch
{
fmodScheduler.Dispose();
throw;
}
scheduler = fmodScheduler;
timeline = preparedTimeline;
holdHeads = preparedHeads;
chartIdentity = intPtr2;
appliedOffset = soundOffset.Value;
ready = true;
ManualLogSource log3 = ((BasePlugin)this).Log;
string[] obj3 = new string[14]
{
"DSP queue ready: ",
preparedDescription,
" sampleRate=",
fmodScheduler.SampleRate.ToString(),
" aheadMs=",
scheduleAhead.Value.ToString(),
" offsetMs=",
appliedOffset.ToString(),
" gameOffsetMs=",
I32(scene, 556).ToString(),
" warmUpMs=",
null,
null,
null
};
elapsed = val7.Elapsed;
obj3[11] = ((TimeSpan)(ref elapsed)).TotalMilliseconds.ToString();
obj3[12] = " attemptMs=";
elapsed = val.Elapsed;
obj3[13] = ((TimeSpan)(ref elapsed)).TotalMilliseconds.ToString();
log3.LogInfo((object)string.Concat(obj3));
return true;
}
private void Start(System.IntPtr self, System.IntPtr mi)
{
Reset(self);
originalStart(self, mi);
}
private void AudioInit(System.IntPtr self, System.IntPtr mi)
{
Reset(self);
originalAudioInit(self, mi);
}
private void Disable(System.IntPtr self, System.IntPtr mi)
{
if (scene == self)
{
Reset(System.IntPtr.Zero);
}
originalDisable(self, mi);
}
private void Destroy(System.IntPtr self, System.IntPtr mi)
{
if (scene == self)
{
Reset(System.IntPtr.Zero);
}
originalDestroy(self, mi);
}
private void Update(System.IntPtr self, System.IntPtr mi)
{
//IL_0197: Unknown result type (might be due to invalid IL or missing references)
//IL_021f: Unknown result type (might be due to invalid IL or missing references)
//IL_0229: Expected O, but got Unknown
try
{
if (scene != self)
{
Reset(self);
}
if (enabled.Value && !faulted && !ready)
{
BuildTimeline();
}
}
catch (System.Exception e)
{
Fail(e);
}
originalUpdate(self, mi);
if (!ready)
{
return;
}
try
{
if (!enabled.Value)
{
RestoreOriginalLoops();
Reset(self);
return;
}
System.IntPtr intPtr = StaticObject(logicalField);
if (intPtr == System.IntPtr.Zero || Ptr(intPtr, 64) != chartIdentity)
{
Reset(self);
return;
}
System.IntPtr audio = Ptr(self, 488);
if (!(audio != System.IntPtr.Zero) || I32(audio, 48) != 2)
{
if (wasPlaying)
{
scheduler.StopAll();
timeline.Reset(skipAtCurrentTime: true);
wasPlaying = false;
}
return;
}
double num = musicPosition(audio, System.IntPtr.Zero) * 1000.0;
double rate = F32(audio, 72);
if (!Finite(num) || !Finite(rate) || rate <= 0.0 || rate > 10.0)
{
throw new InvalidOperationException("Invalid music clock/rate");
}
if (wasPlaying && Math.Abs(rate - lastRate) > 0.0001)
{
scheduler.StopAll();
timeline.Reset(skipAtCurrentTime: true);
}
wasPlaying = true;
lastRate = rate;
timeline.Advance(num - appliedOffset, scheduleAhead.Value * rate, delegate(AudioCue cue, double _)
{
double num3 = musicPosition(audio, System.IntPtr.Zero) * 1000.0;
long timestamp2 = Stopwatch.GetTimestamp();
scheduler.Submit(cue, num3, appliedOffset, rate);
scheduled++;
scheduledByRoute[(int)cue.Route]++;
double num4 = (double)(Stopwatch.GetTimestamp() - timestamp2) * 1000.0 / (double)Stopwatch.Frequency;
if (num4 > 20.0)
{
((BasePlugin)this).Log.LogWarning((object)string.Concat(new string[6]
{
"Slow chart submit: route=",
((object)cue.Route/*cast due to .constrained prefix*/).ToString(),
" elapsedMs=",
num4.ToString(),
" musicMs=",
num3.ToString()
}));
}
if (debug.Value && scheduled <= 16)
{
((BasePlugin)this).Log.LogInfo((object)string.Concat(new string[14]
{
"SCHEDULE id=",
cue.Id.ToString(),
" route=",
((object)cue.Route/*cast due to .constrained prefix*/).ToString(),
" startMs=",
cue.TimeMs.ToString(),
" endMs=",
(cue.Loop ? cue.EndMs : 0.0).ToString(),
" submittedMs=",
num3.ToString(),
" leadMs=",
(cue.TimeMs + appliedOffset - num3).ToString(),
" submitCostMs=",
num4.ToString()
}));
}
}, (Action)([CompilerGenerated] () =>
{
scheduler.StopAll();
}));
long timestamp = Stopwatch.GetTimestamp();
if (debug.Value && timestamp - lastStatus > Stopwatch.Frequency * 5)
{
lastStatus = timestamp;
System.IntPtr intPtr2 = StaticObject(controllerField);
double num2 = ((intPtr2 == System.IntPtr.Zero) ? (0.0 / 0.0) : (F64(intPtr2, 208) * 1000.0));
((BasePlugin)this).Log.LogInfo((object)string.Concat(new string[16]
{
"CLOCK musicMs=",
num.ToString(),
" calibratedMs=",
num2.ToString(),
" deltaMs=",
(num2 - num).ToString(),
" scheduled=",
scheduled.ToString(),
" byRoute=",
string.Join<int>(",", (System.Collections.Generic.IEnumerable<int>)scheduledByRoute),
" suppressed=",
suppressed.ToString(),
" keyboardFeedbackSuppressed=",
feedbackSuppressed.ToString(),
" rate=",
rate.ToString()
}));
}
}
catch (System.Exception e2)
{
Fail(e2);
}
}
private static bool IsOwnedEvent(byte kind)
{
if (kind != 0 && kind != 2 && kind != 4 && kind != 5)
{
return kind == 6;
}
return true;
}
private void ProcessNote(System.IntPtr self, System.IntPtr records, System.IntPtr note, System.IntPtr queue, System.IntPtr mi)
{
HoldHeadScope holdHeadScope = holdHeads;
bool flag = ready && enabled.Value && note != System.IntPtr.Zero && NoteAudioPolicy.OwnsKeyboardFeedback(I32(note, 20), I32(note, 16));
System.IntPtr previous = holdHeadScope.Enter(flag, flag ? Marshal.ReadInt64(note) : 0, queue);
try
{
originalProcessNote(self, records, note, queue, mi);
}
finally
{
holdHeadScope.Restore(previous);
}
}
private void Enqueue(System.IntPtr self, System.IntPtr evt, System.IntPtr mi)
{
if (ready && enabled.Value && evt != System.IntPtr.Zero && holdHeads.Owns(self, Marshal.ReadByte(evt), I32(evt, 4)))
{
suppressed++;
feedbackSuppressed++;
}
else
{
originalEnqueue(self, evt, mi);
}
}
private void Dispatch(System.IntPtr self, System.IntPtr results, System.IntPtr evt, System.IntPtr mi)
{
if (ready && enabled.Value && scene == self && evt != System.IntPtr.Zero && IsOwnedEvent(Marshal.ReadByte(evt, 0)))
{
suppressed++;
}
else
{
originalDispatch(self, results, evt, mi);
}
}
private void Hit(System.IntPtr self, byte kind, double offset, System.IntPtr mi)
{
if (ready && enabled.Value && scene == self)
{
suppressed++;
}
else
{
originalHit(self, kind, offset, mi);
}
}
public override bool Unload()
{
//IL_0032: 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)
ClearAudio();
if (ready)
{
try
{
RestoreOriginalLoops();
}
catch (System.Exception ex)
{
((BasePlugin)this).Log.LogError((object)ex);
}
}
ready = false;
Enumerator<INativeDetour> enumerator = hooks.GetEnumerator();
try
{
while (enumerator.MoveNext())
{
((System.IDisposable)enumerator.Current).Dispose();
}
}
finally
{
((System.IDisposable)enumerator/*cast due to .constrained prefix*/).Dispose();
}
hooks.Clear();
mutePatch?.Dispose();
return true;
}
}
internal sealed class RuntimePatch : System.IDisposable
{
private readonly System.IntPtr address;
private readonly byte[] before;
private readonly byte[] after;
private bool applied;
[DllImport("kernel32", SetLastError = true)]
private static extern bool VirtualProtect(System.IntPtr address, System.UIntPtr size, uint protection, out uint old);
[DllImport("kernel32", SetLastError = true)]
private static extern bool FlushInstructionCache(System.IntPtr process, System.IntPtr address, System.UIntPtr size);
[DllImport("kernel32")]
private static extern System.IntPtr GetCurrentProcess();
private static bool Matches(System.IntPtr p, byte[] value)
{
for (int i = 0; i < value.Length; i++)
{
if (Marshal.ReadByte(p, i) != value[i])
{
return false;
}
}
return true;
}
public RuntimePatch(System.IntPtr address, byte[] before, byte[] after)
{
//IL_0013: Unknown result type (might be due to invalid IL or missing references)
//IL_003c: Unknown result type (might be due to invalid IL or missing references)
if (before.Length != after.Length)
{
throw new ArgumentException("Patch sizes differ");
}
this.address = address;
this.before = before;
this.after = after;
if (!Matches(address, before))
{
throw new InvalidOperationException("Runtime patch bytes differ; refusing patch.");
}
Write(after);
applied = true;
}
private void Write(byte[] value)
{
//IL_001a: Unknown result type (might be due to invalid IL or missing references)
//IL_006d: Unknown result type (might be due to invalid IL or missing references)
//IL_004c: Unknown result type (might be due to invalid IL or missing references)
if (!VirtualProtect(address, (System.UIntPtr)(ulong)value.Length, 64u, out var old))
{
throw new Win32Exception();
}
try
{
Marshal.Copy(value, 0, address, value.Length);
if (!FlushInstructionCache(GetCurrentProcess(), address, (System.UIntPtr)(ulong)value.Length))
{
throw new Win32Exception();
}
}
finally
{
if (!VirtualProtect(address, (System.UIntPtr)(ulong)value.Length, old, out var _))
{
throw new Win32Exception();
}
}
}
public void Dispose()
{
if (applied && Matches(address, after))
{
Write(before);
applied = false;
}
}
}
public sealed class SourceLeases : System.IDisposable
{
private readonly System.IntPtr[] leases = new System.IntPtr[System.Enum.GetValues(typeof(AudioRoute)).Length];
private readonly Func<int, System.IntPtr> acquire;
private readonly Func<System.IntPtr, System.IntPtr> soundOf;
private readonly Action<System.IntPtr> release;
private readonly Func<System.IntPtr, bool> ready;
public SourceLeases(Func<int, System.IntPtr> acquire, Func<System.IntPtr, System.IntPtr> soundOf, Action<System.IntPtr> release, Func<System.IntPtr, bool> ready)
{
this.acquire = acquire;
this.soundOf = soundOf;
this.release = release;
this.ready = ready;
}
public System.IntPtr Resolve(int route, System.IntPtr borrowed)
{
if (leases[route] != System.IntPtr.Zero)
{
return soundOf.Invoke(leases[route]);
}
if (ready.Invoke(borrowed))
{
return borrowed;
}
System.IntPtr intPtr = acquire.Invoke(route);
if (intPtr != System.IntPtr.Zero)
{
leases[route] = intPtr;
}
if (!(intPtr == System.IntPtr.Zero))
{
return soundOf.Invoke(intPtr);
}
return System.IntPtr.Zero;
}
public void Dispose()
{
for (int i = 0; i < leases.Length; i++)
{
System.IntPtr intPtr = leases[i];
leases[i] = System.IntPtr.Zero;
if (intPtr != System.IntPtr.Zero)
{
release.Invoke(intPtr);
}
}
}
}
}