Decompiled source of TootTallyDiffCalcLibs v1.1.0

plugins/TootTallyDiffCalcLibs.dll

Decompiled a week ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Serialization.Formatters.Binary;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Cryptography;
using System.Security.Permissions;
using System.Threading;
using System.Threading.Tasks;
using BaboonAPI.Hooks.Initializer;
using BaboonAPI.Hooks.Tracks;
using BepInEx;
using BepInEx.Configuration;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using Newtonsoft.Json;
using TootTallyCore;
using TootTallyCore.Utils.TootTallyModules;
using TrombLoader.CustomTracks;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: AssemblyCompany("TootTally")]
[assembly: AssemblyConfiguration("Debug")]
[assembly: AssemblyDescription("Difficulty calculation algorithm library for TootTally")]
[assembly: AssemblyFileVersion("1.1.0.0")]
[assembly: AssemblyInformationalVersion("1.1.0+f1446ac49c85422b2296fe32b476946414fa6acf")]
[assembly: AssemblyProduct("TootTallyDiffCalcLibs")]
[assembly: AssemblyTitle("TootTallyDiffCalcLibs")]
[assembly: AssemblyMetadata("RepositoryUrl", "https://github.com/TootTally/TootTallyDiffCalcLibs/")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.1.0.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace TootTallyDiffCalcLibs
{
	public struct Chart : IDisposable
	{
		public class Lyrics
		{
			public string bar;

			public string text;
		}

		public class LengthAccPair
		{
			public float length;

			public float acc;

			public LengthAccPair(float length, float acc)
			{
				this.length = length;
				this.acc = acc;
			}
		}

		public float[][] notes;

		public string[][] bgdata;

		public List<Note>[] notesDict;

		public List<string> note_color_start;

		public List<string> note_color_end;

		public float endpoint;

		public float savednotespacing;

		public float tempo;

		public string timesig;

		public string trackRef;

		public string name;

		public string shortName;

		public string author;

		public string genre;

		public string description;

		public string difficulty;

		public string year;

		public int maxScore;

		public int gameMaxScore;

		public Dictionary<int, int> indexToMaxScoreDict;

		public Dictionary<int, int> indexToNoteCountDict;

		public ChartPerformances performances;

		public TimeSpan calculationTime;

		public int sliderCount;

		public float songLength;

		public float songLengthMult;

		public void ProcessLite()
		{
			notesDict = new List<Note>[Utils.GAME_SPEED.Length];
			CreateNotes(0, 1f);
			sliderCount = GetNoteCount();
			performances = new ChartPerformances(notesDict[0].Count, sliderCount);
			performances.Calculate(0, notesDict[0]);
		}

		public void Process()
		{
			notesDict = new List<Note>[Utils.GAME_SPEED.Length];
			for (int i = 0; i < Utils.GAME_SPEED.Length; i++)
			{
				CreateNotes(i, Utils.GAME_SPEED[i]);
			}
			sliderCount = GetNoteCount();
			performances = new ChartPerformances(notesDict[0].Count, sliderCount);
			Stopwatch stopwatch = new Stopwatch();
			stopwatch.Start();
			for (int j = 0; j < Utils.GAME_SPEED.Length; j++)
			{
				try
				{
					performances.Calculate(j, notesDict[j]);
				}
				catch (Exception ex)
				{
					Plugin.LogError($"Something went wrong when calcing diff for {shortName} at {Utils.GAME_SPEED[j]}");
					Plugin.LogError("ERROR: " + ex.Message + "\n" + ex.StackTrace);
				}
			}
			stopwatch.Stop();
			calculationTime = stopwatch.Elapsed;
			CalcScores();
		}

		private void CreateNotes(int i, float gamespeed)
		{
			float bpm = tempo * gamespeed;
			int num = 1;
			notesDict[i] = new List<Note>(notes.Length)
			{
				new Note(0, 0f, 0.015f, 0f, 0f, 0f, isSlider: false)
			};
			float[][] array = notes.OrderBy((float[] x) => x[0]).ToArray();
			for (int num2 = 0; num2 < array.Length; num2++)
			{
				float num3 = array[num2][1];
				if (num3 <= 0f)
				{
					num3 = 0.015f;
				}
				bool isSlider = ((i <= 0) ? (num2 + 1 < array.Length && IsSlider(array[num2], array[num2 + 1])) : notesDict[0][num2 + 1].isSlider);
				notesDict[i].Add(new Note(num, BeatToSeconds2(array[num2][0], bpm), BeatToSeconds2(num3, bpm), array[num2][2], array[num2][3], array[num2][4], isSlider));
				num++;
			}
		}

		public static float GetLength(float length)
		{
			return Mathf.Clamp(length, 0.2f, 5f) * 8f + 10f;
		}

		public int GetNoteCount()
		{
			int num = 0;
			for (int i = 0; i < notes.Length; i++)
			{
				for (; i + 1 < notes.Length && IsSlider(notes[i], notes[i + 1]); i++)
				{
				}
				num++;
			}
			return num;
		}

		public void CalcScores()
		{
			maxScore = 0;
			gameMaxScore = 0;
			indexToMaxScoreDict = new Dictionary<int, int>();
			indexToNoteCountDict = new Dictionary<int, int>();
			int num = 0;
			for (int i = 0; i < notes.Length; i++)
			{
				float num2 = notes[i][1];
				for (; i + 1 < notes.Length && notes[i][0] + notes[i][1] + 0.025f >= notes[i + 1][0]; i++)
				{
					num2 += notes[i + 1][1];
				}
				double num3 = ((num > 23) ? 1.5 : 0.0);
				double num4 = ((double)Math.Min(num, 10) + num3) * 0.1 + 1.0;
				float length = GetLength(num2);
				int num5 = (int)(Math.Floor((float)((double)length * 100.0 * num4)) * 10.0);
				maxScore += num5;
				gameMaxScore += (int)Math.Floor(Math.Floor(length * 100f * 1.315f) * 10.0);
				indexToMaxScoreDict.Add(i, maxScore);
				num++;
				indexToNoteCountDict.Add(i, num);
			}
		}

		public float GetDiffRating(float speed)
		{
			return performances.GetDiffRating(Mathf.Clamp(speed, 0.5f, 2f));
		}

		public float GetDynamicDiffRating(float speed, float percent, string[] modifiers = null)
		{
			return performances.GetDynamicDiffRating(percent, speed, modifiers);
		}

		public float GetDynamicTTRating(float speed, float percent, float multiplier, string[] modifiers = null)
		{
			return performances.GetDynamicTTRating(percent, speed, multiplier, modifiers);
		}

		public float GetLerpedStarRating(float speed)
		{
			return performances.GetDiffRating(Mathf.Clamp(speed, 0.5f, 2f));
		}

		public float GetAimPerformance(float speed)
		{
			return performances.aimAnalyticsDict[SpeedToIndex(speed)].perfWeightedAverage;
		}

		public float GetTapPerformance(float speed)
		{
			return performances.tapAnalyticsDict[SpeedToIndex(speed)].perfWeightedAverage;
		}

		public float GetStarRating(float speed)
		{
			return performances.starRatingDict[SpeedToIndex(speed)];
		}

		public int SpeedToIndex(float speed)
		{
			return (int)((Mathf.Clamp(speed, 0.5f, 2f) - 0.5f) / 0.25f);
		}

		public static float BeatToSeconds2(float beat, float bpm)
		{
			return 60f / bpm * beat;
		}

		public static bool IsSlider(float[] currNote, float[] nextNote)
		{
			return currNote[0] + currNote[1] + 0.025f >= nextNote[0];
		}

		public static float GetHealthDiff(float acc)
		{
			return Mathf.Clamp((acc - 79f) * 0.2193f, -15f, 4.34f);
		}

		public static int GetScore(float acc, float totalLength, float mult, bool champ)
		{
			float num = Mathf.Clamp(totalLength, 0.2f, 5f) * 8f + 10f;
			return (int)Math.Floor(num * acc * ((mult + (champ ? 1.5f : 0f)) * 0.1f + 1f)) * 10;
		}

		public void Dispose()
		{
			notes = null;
			bgdata = null;
			notesDict = null;
			performances.Dispose();
			indexToMaxScoreDict?.Clear();
			indexToNoteCountDict?.Clear();
		}
	}
	public struct ChartPerformances : IDisposable
	{
		public struct DataVector
		{
			public float time;

			public float stamina;

			public float endurance;

			public float strain;

			public float weight;

			public DataVector(float time, float strain, float stamina, float endurance, float weight)
			{
				this.time = time;
				this.stamina = stamina;
				this.endurance = endurance;
				this.strain = strain;
				this.weight = weight;
			}
		}

		public struct DataVectorAnalytics
		{
			public float perfMax;

			public float perfSum;

			public float perfWeightedAverage;

			public float weightSum;

			public float sumTT;

			public const float STAR_MULT = 4f;

			public DataVectorAnalytics(List<DataVector> dataVectorList)
			{
				perfSum = 0f;
				perfMax = (perfWeightedAverage = 0f);
				weightSum = 200f;
				sumTT = 0f;
				if (dataVectorList.Count > 0)
				{
					CalculateWeightSum(dataVectorList);
					CalculateData(dataVectorList);
				}
			}

			public void CalculateWeightSum(List<DataVector> dataVectorList)
			{
				for (int i = 0; i < dataVectorList.Count; i++)
				{
					weightSum += dataVectorList[i].weight;
				}
			}

			public void CalculateData(List<DataVector> dataVectorList)
			{
				for (int i = 0; i < dataVectorList.Count; i++)
				{
					float num = dataVectorList[i].weight / weightSum;
					float num2 = dataVectorList[i].strain + dataVectorList[i].stamina + dataVectorList[i].endurance;
					if (perfMax < num2)
					{
						perfMax = num2;
					}
					perfSum += num2 * num * 4f;
					sumTT += CalcStrainTT(dataVectorList[i].strain * num) + CalcStamTT(dataVectorList[i].stamina * num) + CalcEnduTT(dataVectorList[i].endurance * num);
				}
				perfWeightedAverage = perfSum;
			}

			public static float CalcStrainTT(float performance)
			{
				return performance * 850f;
			}

			public static float CalcStamTT(float stamina)
			{
				return stamina * 375f;
			}

			public static float CalcEnduTT(float endurance)
			{
				return endurance * 375f;
			}
		}

		public static readonly float[] weights = new float[64]
		{
			1f, 0.9f, 0.81f, 0.729f, 0.6561f, 0.5905f, 0.5314f, 0.4783f, 0.4305f, 0.3874f,
			0.3487f, 0.3138f, 0.2824f, 0.2542f, 0.2288f, 0.2059f, 0.1853f, 0.1668f, 0.1501f, 0.1351f,
			0.1216f, 0.1094f, 0.0985f, 0.0887f, 0.0798f, 0.0718f, 0.0646f, 0.0582f, 0.0524f, 0.0472f,
			0.0425f, 0.0383f, 0.0345f, 0.0311f, 0.028f, 0.0252f, 0.0227f, 0.0204f, 0.0184f, 0.0166f,
			0.0149f, 0.0134f, 0.0121f, 0.0109f, 0.0098f, 0.0088f, 0.0079f, 0.0071f, 0.0064f, 0.0057f,
			0.0051f, 0.0046f, 0.0041f, 0.0037f, 0.0033f, 0.003f, 0.0027f, 0.0024f, 0.0022f, 0.002f,
			0.0018f, 0.0016f, 0.0015f, 0.0013f
		};

		public const float CHEESABLE_THRESHOLD = 34.375f;

		public List<DataVector>[] aimPerfDict;

		public List<DataVector>[] sortedAimPerfDict;

		public DataVectorAnalytics[] aimAnalyticsDict;

		public List<DataVector>[] tapPerfDict;

		public List<DataVector>[] sortedTapPerfDict;

		public DataVectorAnalytics[] tapAnalyticsDict;

		public float[] aimRatingDict;

		public float[] tapRatingDict;

		public float[] starRatingDict;

		private readonly int ALL_NOTE_COUNT;

		private readonly int NOTE_COUNT;

		public const float AIM_DIV = 8f;

		public const float TAP_DIV = 14f;

		public const float ACC_DIV = 12f;

		public const float MAX_DIST = 5f;

		public const int MAX_NOTE_COUNT = 16;

		private const float PLAY_AREA_RANGE = 360f;

		private const float STA_RISE_RATE = 1.45f;

		private const float STA_DECAY_RATE = 0.25f;

		private const float STA_DIV = 5f;

		private const float END_RISE_RATE = 0.15f;

		private const float END_DECAY_RATE = 0.15f;

		private const float END_DIV = 25f;

		public const float MAP = 0.05f;

		public const float MACC = 0.5f;

		public const float AIM_WEIGHT = 1.25f;

		public const float TAP_WEIGHT = 1f;

		public const float BIAS = 1f;

		public static readonly float[] HDWeights = new float[2] { 0.11f, 0.09f };

		public static readonly float[] FLWeights = new float[2] { 0.12f, 0.1f };

		public static readonly float[] EZWeights = new float[2] { -0.48f, -0.25f };

		public ChartPerformances(int noteCount, int sliderCount)
		{
			aimPerfDict = new List<DataVector>[7];
			sortedAimPerfDict = new List<DataVector>[7];
			tapPerfDict = new List<DataVector>[7];
			sortedTapPerfDict = new List<DataVector>[7];
			aimRatingDict = new float[7];
			tapRatingDict = new float[7];
			starRatingDict = new float[7];
			aimAnalyticsDict = new DataVectorAnalytics[7];
			tapAnalyticsDict = new DataVectorAnalytics[7];
			for (int i = 0; i < Utils.GAME_SPEED.Length; i++)
			{
				aimPerfDict[i] = new List<DataVector>(sliderCount);
				tapPerfDict[i] = new List<DataVector>(sliderCount);
			}
			ALL_NOTE_COUNT = noteCount;
			NOTE_COUNT = sliderCount;
		}

		public void CalculatePerformances(int speedIndex, List<Note> noteList)
		{
			float endurance = 0f;
			float num = 0f;
			float endurance2 = 0f;
			float num2 = 0f;
			for (int i = 1; i < ALL_NOTE_COUNT; i++)
			{
				int num3 = 0;
				float num4 = 0f;
				float num5 = 0f;
				float num6 = 1f;
				Note note = noteList[i];
				Note note2 = noteList[i - 1];
				Note note3 = new Note
				{
					count = -1
				};
				int num7 = i - 1;
				while (num7 >= 0 && num3 < 16 && (Mathf.Abs(note.position - note2.position) <= 5f || i - num7 <= 2))
				{
					note2 = noteList[num7];
					Note note4 = noteList[num7 + 1];
					num3++;
					float num8 = weights[num3 * 2];
					if (note2.position >= note4.position)
					{
						break;
					}
					float num9 = note2.length;
					float num10 = 0f;
					float num11 = 0f;
					float num12 = 0f;
					if (Mathf.Abs(note2.pitchDelta) >= 3.4375f)
					{
						num10 += 1f;
						float num13 = Mathf.Abs(note2.pitchDelta);
						float num14 = Mathf.Sqrt(NormalizePitch(num13)) * ((num13 >= 34.375f) ? 0.45f : 0.1f);
						num11 += num14 / Mathf.Pow(note2.length, 1.38f);
					}
					else
					{
						num12 += note2.length * 0.2f;
					}
					while (note2.isSlider && num7-- > 0)
					{
						note2 = noteList[num7];
						note4 = noteList[num7 + 1];
						num9 += note2.length;
						if (Mathf.Abs(note2.pitchDelta) >= 3.4375f)
						{
							num10 += 1f;
							float num15 = Mathf.Abs(note2.pitchDelta);
							float num16 = Mathf.Sqrt(NormalizePitch(num15)) * ((num15 >= 34.375f) ? 0.75f : 0.1f);
							num11 += num16 / Mathf.Pow(note2.length, 1.38f);
						}
						else
						{
							num12 += note2.length * 0.2f;
						}
					}
					if (note3.count == -1)
					{
						note3 = note2;
					}
					if (num10 != 0f)
					{
						num11 /= num10;
						num4 += num11 * num8 / 12f;
						num4 *= (num9 - num12) / num9;
					}
					float num17 = note4.position - note2.position;
					float num18 = Mathf.Abs(NormalizePitch(note4.pitchStart - note2.pitchEnd));
					if (num18 != 0f)
					{
						float num19 = Mathf.Sqrt(num18) * 0.45f / Mathf.Pow(num17, 1.32f);
						num4 += num19 * num8 / 8f;
					}
					float num20 = Mathf.Sqrt(num18) / 15f + 0.075f;
					num5 += num20 / Mathf.Pow(num17, 1.39f) * num8 / 14f;
					num6 += num8;
					num7--;
				}
				float tapDelta = Mathf.Sqrt(note.position - note3.position);
				num2 = ComputeStamina(num5 * 1.85f, num2, tapDelta);
				endurance2 = ComputeEndurance(num2 * 1.55f, endurance2, tapDelta);
				num = ComputeStamina(num4 * 0.55f, num, tapDelta);
				endurance = ComputeEndurance(num * 1.55f, endurance, tapDelta);
				aimPerfDict[speedIndex].Add(new DataVector(note.position, num4, num, endurance, num6));
				tapPerfDict[speedIndex].Add(new DataVector(note.position, num5, num2, endurance2, num6));
			}
			sortedAimPerfDict[speedIndex] = aimPerfDict[speedIndex].OrderBy((DataVector x) => x.strain + x.stamina + x.endurance).ToList();
			sortedTapPerfDict[speedIndex] = tapPerfDict[speedIndex].OrderBy((DataVector x) => x.strain + x.stamina + x.endurance).ToList();
		}

		public static float NormalizePitch(float pitch)
		{
			return pitch / 360f;
		}

		public static float ComputeVelocityDebuff(float lastVelocity, float currentVelocity)
		{
			return Mathf.Min(Mathf.Abs(currentVelocity - lastVelocity) * 0.03f + 0.45f, 1f);
		}

		public static float ComputeStamina(float strain, float stamina, float tapDelta)
		{
			return stamina + (strain - stamina) / 5f * ((strain > stamina) ? (1f - Mathf.Pow((float)Math.E, -1.45f * tapDelta)) : (1f - Mathf.Pow((float)Math.E, -0.25f * tapDelta)));
		}

		public static float ComputeEndurance(float stamina, float endurance, float tapDelta)
		{
			return endurance + (stamina - endurance) / 25f * ((stamina > endurance) ? (1f - Mathf.Pow((float)Math.E, -0.15f * tapDelta)) : (1f - Mathf.Pow((float)Math.E, -0.15f * tapDelta)));
		}

		public void Calculate(int speedIndex, List<Note> noteList)
		{
			CalculatePerformances(speedIndex, noteList);
			CalculateAnalytics(speedIndex);
			CalculateRatings(speedIndex);
		}

		public void CalculateAnalytics(int speedIndex)
		{
			aimAnalyticsDict[speedIndex] = new DataVectorAnalytics(aimPerfDict[speedIndex]);
			tapAnalyticsDict[speedIndex] = new DataVectorAnalytics(tapPerfDict[speedIndex]);
		}

		public void CalculateRatings(int speedIndex)
		{
			float num = (aimRatingDict[speedIndex] = aimAnalyticsDict[speedIndex].perfWeightedAverage + 0.01f);
			float num2 = (tapRatingDict[speedIndex] = tapAnalyticsDict[speedIndex].perfWeightedAverage + 0.01f);
			if (num != 0f && num2 != 0f)
			{
				float num3 = num + num2;
				float num4 = num / num3;
				float num5 = num2 / num3;
				float num6 = (num4 + 1f) * 1.25f;
				float num7 = (num5 + 1f) * 1f;
				float num8 = num6 + num7;
				starRatingDict[speedIndex] = (num * num6 + num2 * num7) / num8;
			}
			else
			{
				starRatingDict[speedIndex] = 0f;
			}
		}

		public float GetDynamicAimRating(float percent, float speed)
		{
			return GetDynamicSkillRating(percent, speed, sortedAimPerfDict);
		}

		public float GetDynamicTapRating(float percent, float speed)
		{
			return GetDynamicSkillRating(percent, speed, sortedTapPerfDict);
		}

		private float GetDynamicSkillRating(float percent, float speed, List<DataVector>[] skillRatingMatrix)
		{
			if (speed == 0f)
			{
				speed = 1f;
			}
			int num = (int)((speed - 0.5f) / 0.25f);
			if (skillRatingMatrix[num].Count <= 1 || percent <= 0f)
			{
				return 0f;
			}
			if (speed % 0.25f == 0f)
			{
				return CalcSkillRating(percent, skillRatingMatrix[num]);
			}
			float firstFloat = CalcSkillRating(percent, skillRatingMatrix[num]);
			float secondFloat = CalcSkillRating(percent, skillRatingMatrix[num + 1]);
			float num2 = Utils.GAME_SPEED[num];
			float num3 = Utils.GAME_SPEED[num + 1];
			float num4 = (speed - num2) / (num3 - num2);
			return Utils.Lerp(firstFloat, secondFloat, num4);
		}

		private float CalcSkillRating(float percent, List<DataVector> skillRatingArray)
		{
			int count = ((!(percent <= 0.5f)) ? ((int)Mathf.Clamp((float)skillRatingArray.Count * ((percent - 0.5f) * 1.9f + 0.05f), 1f, (float)skillRatingArray.Count)) : ((int)Mathf.Clamp((float)skillRatingArray.Count * (percent * 0.1f), 1f, (float)skillRatingArray.Count)));
			List<DataVector> range = skillRatingArray.GetRange(0, count);
			return new DataVectorAnalytics(range).perfWeightedAverage + 0.01f;
		}

		public float GetDynamicDiffRating(float percent, float gamespeed, string[] modifiers = null)
		{
			float num = GetDynamicAimRating(percent, gamespeed);
			float num2 = GetDynamicTapRating(percent, gamespeed);
			if (num == 0f && num2 == 0f)
			{
				return 0f;
			}
			if (modifiers != null)
			{
				float num3 = 1f;
				float num4 = 1f;
				bool flag = modifiers.Contains("EZ") || modifiers.Contains("AP");
				float num5 = (flag ? 0.25f : 1f);
				if (modifiers.Contains("HD"))
				{
					num3 += HDWeights[0] * num5;
					num4 += HDWeights[1] * num5;
				}
				if (modifiers.Contains("FL"))
				{
					num3 += FLWeights[0] * num5;
					num4 += FLWeights[1] * num5;
				}
				if (flag)
				{
					num3 += EZWeights[0];
					num4 += EZWeights[1];
				}
				if (modifiers.Contains("AP"))
				{
					num3 = 0f;
					num2 *= 0.55f;
				}
				if (modifiers.Contains("RX"))
				{
					num4 = 0f;
					num *= 0.55f;
				}
				if (modifiers.Contains("RK"))
				{
					num2 *= 0.1f;
				}
				if (num3 < 0f)
				{
					num3 = 0.01f;
				}
				if (num4 < 0f)
				{
					num4 = 0.01f;
				}
				num *= num3;
				num2 *= num4;
			}
			float num6 = num + num2;
			if (num6 <= 0f)
			{
				return 0f;
			}
			float num7 = num / num6;
			float num8 = num2 / num6;
			float num9 = (num7 + 1f) * 1.25f;
			float num10 = (num8 + 1f) * 1f;
			float num11 = num9 + num10;
			return (num * num9 + num2 * num10) / num11;
		}

		public float GetDynamicAimTT(float percent, float speed)
		{
			return GetDynamicTTRating(percent, speed, sortedAimPerfDict);
		}

		public float GetDynamicTapTT(float percent, float speed)
		{
			return GetDynamicTTRating(percent, speed, sortedTapPerfDict);
		}

		private float GetDynamicTTRating(float percent, float speed, List<DataVector>[] skillRatingMatrix)
		{
			if (speed == 0f)
			{
				speed = 1f;
			}
			int num = (int)((speed - 0.5f) / 0.25f);
			if (skillRatingMatrix[num].Count <= 1 || percent <= 0f)
			{
				return 0f;
			}
			if (speed % 0.5f == 0f)
			{
				return CalcTTRating(percent, skillRatingMatrix[num]);
			}
			float firstFloat = CalcTTRating(percent, skillRatingMatrix[num]);
			float secondFloat = CalcTTRating(percent, skillRatingMatrix[num + 1]);
			float num2 = Utils.GAME_SPEED[num];
			float num3 = Utils.GAME_SPEED[num + 1];
			float num4 = (speed - num2) / (num3 - num2);
			return Utils.Lerp(firstFloat, secondFloat, num4);
		}

		private float CalcTTRating(float percent, List<DataVector> skillRatingArray)
		{
			int count = ((!(percent <= 0.5f)) ? ((int)Mathf.Clamp((float)skillRatingArray.Count * ((percent - 0.5f) * 1.9f + 0.05f), 1f, (float)skillRatingArray.Count)) : ((int)Mathf.Clamp((float)skillRatingArray.Count * (percent * 0.1f), 1f, (float)skillRatingArray.Count)));
			List<DataVector> range = skillRatingArray.GetRange(0, count);
			return new DataVectorAnalytics(range).sumTT + 0.01f;
		}

		public float GetDynamicTTRating(float percent, float gamespeed, float multiplier, string[] modifiers = null)
		{
			float num = GetDynamicAimTT(percent, gamespeed);
			float num2 = GetDynamicTapTT(percent, gamespeed);
			if (num == 0f && num2 == 0f)
			{
				return 0f;
			}
			if (modifiers != null)
			{
				float num3 = 1f;
				float num4 = 1f;
				bool flag = modifiers.Contains("EZ") || modifiers.Contains("AP");
				float num5 = (flag ? 0.25f : 1f);
				if (modifiers.Contains("HD"))
				{
					num3 += HDWeights[0] * num5;
					num4 += HDWeights[1] * num5;
				}
				if (modifiers.Contains("FL"))
				{
					num3 += FLWeights[0] * num5;
					num4 += FLWeights[1] * num5;
				}
				if (flag)
				{
					num3 += EZWeights[0];
					num4 += EZWeights[1];
				}
				if (modifiers.Contains("AP"))
				{
					num3 = 0f;
					num2 *= 0.55f;
				}
				if (modifiers.Contains("RX"))
				{
					num4 = 0f;
					num *= 0.55f;
				}
				if (modifiers.Contains("RK"))
				{
					num2 *= 0.1f;
				}
				if (num3 < 0f)
				{
					num3 = 0.01f;
				}
				if (num4 < 0f)
				{
					num4 = 0.01f;
				}
				num *= num3;
				num2 *= num4;
			}
			float num6 = num + num2;
			if (num6 <= 0f)
			{
				return 0f;
			}
			float num7 = num / num6;
			float num8 = num2 / num6;
			float num9 = (num7 + 1f) * 1.25f;
			float num10 = (num8 + 1f) * 1f;
			float num11 = num9 + num10;
			return multiplier * (num * num9 + num2 * num10) / num11;
		}

		public void Dispose()
		{
			aimPerfDict = null;
			sortedAimPerfDict = null;
			aimAnalyticsDict = null;
			aimRatingDict = null;
			tapPerfDict = null;
			sortedTapPerfDict = null;
			tapAnalyticsDict = null;
			tapRatingDict = null;
			starRatingDict = null;
		}

		public float GetDiffRating(float speed)
		{
			int num = (int)((speed - 0.5f) / 0.25f);
			if (speed % 0.25f == 0f)
			{
				return starRatingDict[num];
			}
			float num2 = Utils.GAME_SPEED[num];
			float num3 = Utils.GAME_SPEED[num + 1];
			float num4 = (speed - num2) / (num3 - num2);
			return Utils.Lerp(starRatingDict[num], starRatingDict[num + 1], num4);
		}

		public static float BeatToSeconds2(float beat, float bpm)
		{
			return 60f / bpm * beat;
		}
	}
	public static class ChartReader
	{
		private static List<Chart> _allChartList = new List<Chart>();

		private static readonly string TrackassetDir = Application.streamingAssetsPath + "/trackassets";

		public static void AddChartToList(string path)
		{
			_allChartList.Add(LoadChart(path));
		}

		public static Chart ReadBaseGame(string trackRef)
		{
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			//IL_003c: Expected O, but got Unknown
			//IL_00e0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e7: Expected O, but got Unknown
			BinaryFormatter binaryFormatter = new BinaryFormatter();
			Chart result = default(Chart);
			string path = TrackassetDir + "/" + trackRef + "/metadata_en.tmb";
			using (FileStream serializationStream = File.Open(path, FileMode.Open))
			{
				SavedLevelMetadata val = (SavedLevelMetadata)binaryFormatter.Deserialize(serializationStream);
				result.name = val.trackname_long;
				result.shortName = val.trackname_short;
				result.trackRef = trackRef;
				result.author = val.artist;
				result.genre = val.genre;
				result.description = val.description;
				result.difficulty = val.difficulty.ToString();
				result.year = val.year;
			}
			string path2 = TrackassetDir + "/" + trackRef + "/trackdata.tmb";
			using FileStream serializationStream2 = File.Open(path2, FileMode.Open);
			SavedLevel val2 = (SavedLevel)binaryFormatter.Deserialize(serializationStream2);
			result.savednotespacing = val2.savednotespacing;
			result.endpoint = val2.endpoint;
			result.timesig = val2.timesig.ToString();
			result.tempo = val2.tempo;
			result.notes = val2.savedleveldata.ToArray();
			return result;
		}

		public static Chart LoadBaseGame(string trackRef)
		{
			Chart result = ReadBaseGame(trackRef);
			result.Process();
			return result;
		}

		public static Chart ReadCustomChart(string path)
		{
			using StreamReader streamReader = new StreamReader(path);
			string text = streamReader.ReadToEnd();
			return JsonConvert.DeserializeObject<Chart>(text);
		}

		public static Chart LoadChart(string path)
		{
			Chart result = ReadCustomChart(path);
			result.Process();
			return result;
		}

		public static Chart LoadChartFromJson(string json)
		{
			Chart result = JsonConvert.DeserializeObject<Chart>(json);
			result.Process();
			return result;
		}

		public static string CalcSHA256Hash(byte[] data)
		{
			using SHA256 sHA = SHA256.Create();
			string text = "";
			byte[] array = sHA.ComputeHash(data);
			byte[] array2 = array;
			foreach (byte b in array2)
			{
				text += $"{b:x2}";
			}
			return text;
		}

		public static void SaveChartData(string path, string json)
		{
			StreamWriter streamWriter = new StreamWriter(path);
			streamWriter.WriteLine(json);
			streamWriter.Close();
		}
	}
	public static class DiffCalcGlobals
	{
		public static Chart selectedChart;

		public static Action<Chart> OnSelectedChartSetEvent;
	}
	public struct Note
	{
		public int count;

		public float pitchStart;

		public float pitchDelta;

		public float pitchEnd;

		public float position;

		public float length;

		public bool isSlider;

		public Note(int count, float position, float length, float pitchStart, float pitchDelta, float pitchEnd, bool isSlider)
		{
			this.count = count;
			this.position = position;
			this.length = length;
			this.pitchStart = pitchStart;
			this.pitchDelta = pitchDelta;
			this.pitchEnd = pitchEnd;
			this.isSlider = isSlider;
		}
	}
	[BepInPlugin("TootTallyDiffCalcLibs", "TootTallyDiffCalcLibs", "1.1.0")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	public class Plugin : BaseUnityPlugin, ITootTallyModule
	{
		public static class DiffCalcPatches
		{
			private static CancellationTokenSource _cancellationToken;

			private static string _lastTrackref;

			[HarmonyPatch(typeof(LoadController), "Start")]
			[HarmonyPostfix]
			public static void ProcessChartBackup()
			{
				if (!(DiffCalcGlobals.selectedChart.trackRef == GlobalVariables.chosen_track_data.trackref))
				{
					string path = GetSongTMBPath(GlobalVariables.chosen_track_data.trackref);
					_cancellationToken?.Cancel();
					_cancellationToken = new CancellationTokenSource();
					bool isBaseGame = path == GlobalVariables.chosen_track_data.trackref;
					Task.Run(delegate
					{
						ProcessChart(path, isBaseGame, _cancellationToken);
					}, _cancellationToken.Token);
				}
			}

			[HarmonyPatch(typeof(LevelSelectController), "populateSongNames")]
			[HarmonyPostfix]
			public static void OnSongChangeProcessChartAsync(List<SingleTrackData> ___alltrackslist, int ___songindex)
			{
				string trackref = ___alltrackslist[___songindex].trackref;
				if (DiffCalcGlobals.selectedChart.trackRef == trackref || _lastTrackref == trackref)
				{
					LogInfo(DiffCalcGlobals.selectedChart.trackRef + " - " + trackref + " - trackref was the same.");
					return;
				}
				string path = GetSongTMBPath(trackref);
				_lastTrackref = trackref;
				_cancellationToken?.Cancel();
				_cancellationToken = new CancellationTokenSource();
				bool isBaseGame = path == trackref;
				Task.Run(delegate
				{
					ProcessChart(path, isBaseGame, _cancellationToken);
				}, _cancellationToken.Token);
			}

			[HarmonyPatch(typeof(LevelSelectController), "Start")]
			[HarmonyPostfix]
			public static void ProcessFirstChart(List<SingleTrackData> ___alltrackslist, int ___songindex)
			{
				OnSongChangeProcessChartAsync(___alltrackslist, ___songindex);
			}

			private static async void ProcessChart(string path, bool isBaseGame, CancellationTokenSource source)
			{
				if (isBaseGame)
				{
					LogInfo("Trying to get base game chart: " + path);
				}
				Chart chart = (isBaseGame ? ChartReader.LoadBaseGame(path) : ChartReader.LoadChart(path));
				if (source.IsCancellationRequested)
				{
					LogInfo("Disposing of " + chart.shortName);
					chart.Dispose();
					return;
				}
				LogInfo($"Song {chart.shortName} processed in {chart.calculationTime.TotalSeconds}s");
				DiffCalcGlobals.selectedChart.Dispose();
				DiffCalcGlobals.selectedChart = chart;
				DiffCalcGlobals.OnSelectedChartSetEvent?.Invoke(chart);
				_cancellationToken = null;
				await Task.Yield();
			}

			public static string GetSongTMBPath(string trackref)
			{
				TromboneTrack val = TrackLookup.lookup(trackref);
				CustomTrack val2 = (CustomTrack)(object)((val is CustomTrack) ? val : null);
				if (val2 != null)
				{
					string text = val2.folderPath + "/song.tmb";
					if (File.Exists(text))
					{
						return text;
					}
				}
				return trackref;
			}
		}

		public static Plugin Instance;

		private const string CONFIG_NAME = "TootTallyDiffCalcLibs.cfg";

		private Harmony _harmony;

		public ConfigEntry<bool> ModuleConfigEnabled { get; set; }

		public bool IsConfigInitialized { get; set; }

		public string Name
		{
			get
			{
				return "TootTallyDiffCalcLibs";
			}
			set
			{
				Name = value;
			}
		}

		public static void LogInfo(string msg)
		{
			((BaseUnityPlugin)Instance).Logger.LogInfo((object)msg);
		}

		public static void LogError(string msg)
		{
			((BaseUnityPlugin)Instance).Logger.LogError((object)msg);
		}

		private void Awake()
		{
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_002f: Expected O, but got Unknown
			if (!((Object)(object)Instance != (Object)null))
			{
				Instance = this;
				_harmony = new Harmony(((BaseUnityPlugin)this).Info.Metadata.GUID);
				GameInitializationEvent.Register(((BaseUnityPlugin)this).Info, (Action)TryInitialize);
			}
		}

		private void TryInitialize()
		{
			ModuleConfigEnabled = ((BaseUnityPlugin)Plugin.Instance).Config.Bind<bool>("Modules", "DiffCalcLibs", true, "Library to locally calculate the difficulty of charts.");
			TootTallyModuleManager.AddModule((ITootTallyModule)(object)this);
		}

		public void LoadModule()
		{
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: Expected O, but got Unknown
			string text = Path.Combine(Paths.BepInExRootPath, "config/");
			ConfigFile val = new ConfigFile(text + "TootTallyDiffCalcLibs.cfg", true)
			{
				SaveOnConfigSet = true
			};
			_harmony.PatchAll(typeof(DiffCalcPatches));
			LogInfo("Module loaded!");
		}

		public void UnloadModule()
		{
			_harmony.UnpatchSelf();
			LogInfo("Module unloaded!");
		}
	}
	public static class Utils
	{
		public static readonly float[] GAME_SPEED = new float[7] { 0.5f, 0.75f, 1f, 1.25f, 1.5f, 1.75f, 2f };

		public static readonly Dictionary<float, float> accToEZMultDict = new Dictionary<float, float>
		{
			{ 1f, 1f },
			{ 0.999f, 0.999f },
			{ 0.996f, 0.985f },
			{ 0.993f, 0.98f },
			{ 0.99f, 0.975f },
			{ 0.985f, 0.96f },
			{ 0.98f, 0.94f },
			{ 0.97f, 0.9f },
			{ 0.96f, 0.86f },
			{ 0.95f, 0.82f },
			{ 0.925f, 0.75f },
			{ 0.9f, 0.69f },
			{ 0.875f, 0.64f },
			{ 0.85f, 0.6f },
			{ 0.8f, 0.52f },
			{ 0.7f, 0.39f },
			{ 0.6f, 0.29f },
			{ 0.5f, 0.22f },
			{ 0.25f, 0.1f },
			{ 0f, 0f }
		};

		public static readonly Dictionary<float, float> accToMultDict = new Dictionary<float, float>
		{
			{ 1f, 1.9f },
			{ 0.999f, 1.8f },
			{ 0.996f, 1.65f },
			{ 0.993f, 1.5f },
			{ 0.99f, 1.35f },
			{ 0.985f, 1.25f },
			{ 0.98f, 1.15f },
			{ 0.97f, 1f },
			{ 0.96f, 0.9f },
			{ 0.95f, 0.8f },
			{ 0.925f, 0.7f },
			{ 0.9f, 0.625f },
			{ 0.875f, 0.565f },
			{ 0.85f, 0.52f },
			{ 0.8f, 0.45f },
			{ 0.7f, 0.33f },
			{ 0.6f, 0.25f },
			{ 0.5f, 0.2f },
			{ 0.25f, 0.125f },
			{ 0f, 0f }
		};

		public static float Lerp(float firstFloat, float secondFloat, float by)
		{
			return firstFloat + (secondFloat - firstFloat) * by;
		}

		public static float FastPow(double num, int exp)
		{
			double num2 = 1.0;
			while (exp > 0)
			{
				if (exp % 2 == 1)
				{
					num2 *= num;
				}
				exp >>= 1;
				num *= num;
			}
			return (float)num2;
		}

		public static float CalculateScoreTT(Chart chart, float replaySpeed, int hitCount, int noteCount, float percent, string[] modifiers = null)
		{
			return chart.GetDynamicTTRating(replaySpeed, (float)hitCount / (float)noteCount, GetMultiplier(percent, modifiers), modifiers);
		}

		public static float GetMultiplier(float percent, string[] modifiers = null)
		{
			Dictionary<float, float> dictionary = ((modifiers != null && (modifiers.Contains("EZ") || modifiers.Contains("AP"))) ? accToEZMultDict : accToMultDict);
			int i;
			for (i = 1; i < dictionary.Count && dictionary.Keys.ElementAt(i) > percent; i++)
			{
			}
			float num = dictionary.Keys.ElementAt(i);
			float num2 = dictionary.Keys.ElementAt(i - 1);
			float num3 = (percent - num2) / (num - num2);
			float num4 = Lerp(dictionary[num2], dictionary[num], num3);
			float num5 = ((modifiers != null && modifiers.Contains("AP") && !modifiers.Contains("EZ")) ? 1.2f : 1f);
			return num4 * num5;
		}

		public static float LerpDiff(float[] diffRatings, float speed)
		{
			int num = (int)((speed - 0.5f) / 0.25f);
			if (speed % 0.25f == 0f)
			{
				return diffRatings[num];
			}
			float num2 = GAME_SPEED[num];
			float num3 = GAME_SPEED[num + 1];
			float num4 = (speed - num2) / (num3 - num2);
			return Lerp(diffRatings[num], diffRatings[num + 1], num4);
		}
	}
	public static class PluginInfo
	{
		public const string PLUGIN_GUID = "TootTallyDiffCalcLibs";

		public const string PLUGIN_NAME = "TootTallyDiffCalcLibs";

		public const string PLUGIN_VERSION = "1.1.0";
	}
}