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";
}
}