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("TootTallyDiffCalcLibs")]
[assembly: AssemblyConfiguration("Debug")]
[assembly: AssemblyDescription("TootTally Module Template")]
[assembly: AssemblyFileVersion("1.0.7.0")]
[assembly: AssemblyInformationalVersion("1.0.7")]
[assembly: AssemblyProduct("TootTallyDiffCalcLibs")]
[assembly: AssemblyTitle("TootTallyDiffCalcLibs")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.7.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
[CompilerGenerated]
[Microsoft.CodeAnalysis.Embedded]
internal sealed class EmbeddedAttribute : Attribute
{
}
}
namespace System.Runtime.CompilerServices
{
[CompilerGenerated]
[Microsoft.CodeAnalysis.Embedded]
[AttributeUsage(AttributeTargets.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 Dictionary<float, 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 Dictionary<float, List<Note>>();
CreateNotes(0, 1f);
songLengthMult = GetSongLengthMult(notesDict[0f]);
sliderCount = GetNoteCount();
performances = new ChartPerformances(notesDict[0f].Count, sliderCount);
performances.Calculate(0, notesDict[0f], songLengthMult);
}
public void Process()
{
notesDict = new Dictionary<float, List<Note>>();
for (int i = 0; i < Utils.GAME_SPEED.Length; i++)
{
CreateNotes(i, Utils.GAME_SPEED[i]);
}
songLengthMult = GetSongLengthMult(notesDict[2f]);
sliderCount = GetNoteCount();
performances = new ChartPerformances(notesDict[0f].Count, sliderCount);
Stopwatch stopwatch = new Stopwatch();
stopwatch.Start();
for (int j = 0; j < Utils.GAME_SPEED.Length; j++)
{
performances.Calculate(j, notesDict[j], songLengthMult);
}
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 j = 0; j < array.Length; j++)
{
float num2 = array[j][1];
if (num2 <= 0f)
{
num2 = 0.015f;
}
bool isSlider = ((i <= 0) ? (j + 1 < array.Length && IsSlider(array[j], array[j + 1])) : notesDict[0f][j + 1].isSlider);
notesDict[i].Add(new Note(num, BeatToSeconds2(array[j][0], bpm), BeatToSeconds2(num2, bpm), array[j][2], array[j][3], array[j][4], isSlider));
num++;
}
}
private float GetSongLengthMult(List<Note> notes)
{
if (notes.Count > 2)
{
songLength = notes.Last().position - notes[1].position;
}
if (songLength < 1f)
{
songLength = 1f;
}
return Mathf.Pow(songLength / 15f, (float)Math.E * -4f / 25f) + 0.475f;
}
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 GetBaseTT(float speed)
{
return Utils.CalculateBaseTT(GetDiffRating(Mathf.Clamp(speed, 0.5f, 2f)));
}
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 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?.Clear();
performances.Dispose();
indexToMaxScoreDict?.Clear();
indexToNoteCountDict?.Clear();
}
}
public struct ChartPerformances : IDisposable
{
public struct DataVector
{
public float performance;
public float endurance;
public float time;
public float weight;
public DataVector(float time, float performance, float endurance, float weight)
{
this.time = time;
this.endurance = endurance;
this.performance = performance;
this.weight = weight;
}
}
public struct DataVectorAnalytics
{
public float perfMax;
public float perfWeightedAverage;
public float weightSum;
public DataVectorAnalytics(List<DataVector> dataVectorList, float songLengthMult)
{
perfMax = (perfWeightedAverage = 0f);
weightSum = 1f;
if (dataVectorList.Count > 0)
{
CalculateWeightSum(dataVectorList, songLengthMult);
CalculateData(dataVectorList);
}
}
public void CalculateWeightSum(List<DataVector> dataVectorList, float songLengthMult)
{
for (int i = 0; i < dataVectorList.Count; i++)
{
weightSum += dataVectorList[i].weight;
}
weightSum *= songLengthMult;
}
public void CalculateData(List<DataVector> dataVectorList)
{
for (int i = 0; i < dataVectorList.Count; i++)
{
if (dataVectorList[i].performance > perfMax)
{
perfMax = dataVectorList[i].performance;
}
perfWeightedAverage += (dataVectorList[i].performance + dataVectorList[i].endurance) * (dataVectorList[i].weight / weightSum);
}
}
}
public static readonly float[] weights = new float[65]
{
1f, 0.92f, 0.8464f, 0.7787f, 0.7164f, 0.6591f, 0.6064f, 0.5578f, 0.5132f, 0.4722f,
0.4344f, 0.3996f, 0.3677f, 0.3383f, 0.3112f, 0.2863f, 0.2634f, 0.2423f, 0.2229f, 0.2051f,
0.1887f, 0.1736f, 0.1597f, 0.1469f, 0.1352f, 0.1244f, 0.1144f, 0.1053f, 0.0968f, 0.0891f,
0.082f, 0.0754f, 0.0694f, 0.0638f, 0.0587f, 0.054f, 0.0497f, 0.0457f, 0.0421f, 0.0387f,
0.0356f, 0.0328f, 0.0301f, 0.0277f, 0.0255f, 0.0235f, 0.0216f, 0.0199f, 0.0183f, 0.0168f,
0.0155f, 0.0142f, 0.0131f, 0.012f, 0.0111f, 0.0102f, 0.0094f, 0.0086f, 0.0079f, 0.0073f,
0.0067f, 0.0062f, 0.0057f, 0.0052f, 0.0048f
};
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 NOTE_COUNT;
public const float AIM_DIV = 375f;
public const float TAP_DIV = 200f;
public const float ACC_DIV = 375f;
public const float AIM_END = 750f;
public const float TAP_END = 15f;
public const float ACC_END = 900f;
public const float MUL_END = 50f;
public const float MAX_DIST = 8f;
private const float a = -40f;
public const float BIAS = 0.75f;
public const float MAP = 0.05f;
public const float MACC = 0.5f;
public const float AIM_WEIGHT = 1.25f;
public const float TAP_WEIGHT = 1.12f;
public static readonly float[] HDWeights = new float[2] { 0.34f, 0.02f };
public static readonly float[] FLWeights = new float[2] { 0.55f, 0.02f };
public static readonly float[] EZWeights = new float[2] { -0.4f, -0.02f };
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);
}
NOTE_COUNT = noteCount;
}
public void CalculatePerformances(int speedIndex, List<Note> noteList)
{
float endurance = 0f;
float endurance2 = 0f;
for (int i = 0; i < NOTE_COUNT; i++)
{
Note note = noteList[i];
int num = 0;
float num2 = 0f;
float num3 = 0f;
float num4 = 0f;
int num5 = i - 1;
while (num5 >= 0 && num < 64 && (Mathf.Abs(note.position - noteList[num5].position) <= 8f || i - num5 <= 2))
{
Note note2 = noteList[num5];
Note note3 = noteList[num5 + 1];
if (note2.position >= note3.position)
{
break;
}
float num6 = weights[num];
num++;
num2 += num6;
float num7 = note2.length;
float num8 = Mathf.Abs(note2.pitchDelta);
if (num8 <= 34.375f)
{
num8 *= 0.35f;
}
while (note2.isSlider && num5-- > 0)
{
note2 = noteList[num5];
note3 = noteList[num5 + 1];
if (note2.pitchDelta == 0f)
{
num7 += note2.length * 0.85f;
continue;
}
float num9 = Mathf.Abs(note2.pitchDelta);
num7 += note2.length;
if (num9 <= 34.375f)
{
num9 *= 0.25f;
}
num8 += num9;
}
float deltaTime = note3.position - note2.position;
if (num8 != 0f)
{
num3 += ComputeStrain(CalcAccStrain(num7, num8, num6)) / 375f;
endurance += CalcAccEndurance(num7, num8, num6);
}
float num10 = Mathf.Abs(note3.pitchStart - note2.pitchEnd);
if (num10 != 0f || num8 != 0f)
{
num3 += ComputeStrain(CalcAimStrain(num10, num6, deltaTime)) / 375f;
endurance += CalcAimEndurance(num10, num6, deltaTime);
}
float tapDelta = note3.position - note2.position;
num4 += ComputeStrain(CalcTapStrain(tapDelta, num6, num10)) / 200f;
endurance2 += CalcTapEndurance(tapDelta, num6, num10);
num5--;
}
if (i > 0)
{
float num11 = 61f - Mathf.Min(note.position - noteList[i - 1].position, 5f) * 12f;
float num12 = Mathf.Pow(num3, 1.08f) * 1.2f;
float num13 = Mathf.Pow(num4, 1.08f) * 1.2f;
if (endurance >= num12)
{
ComputeEnduranceDecay(ref endurance, (endurance - num12) / num11);
}
if (endurance2 >= num13)
{
ComputeEnduranceDecay(ref endurance2, (endurance2 - num13) / num11);
}
}
aimPerfDict[speedIndex].Add(new DataVector(note.position, num3, endurance, num2));
tapPerfDict[speedIndex].Add(new DataVector(note.position, num4, endurance2, num2));
}
sortedAimPerfDict[speedIndex] = aimPerfDict[speedIndex].OrderBy((DataVector x) => x.performance + x.endurance).ToList();
sortedTapPerfDict[speedIndex] = tapPerfDict[speedIndex].OrderBy((DataVector x) => x.performance + x.endurance).ToList();
}
public static float ComputeStrain(float strain)
{
return -40f * Mathf.Pow(strain + 1f, -0.04349251f) - -40f - 5f * strain / -40f;
}
public static void ComputeEnduranceDecay(ref float endurance, float distanceFromLastNote)
{
endurance /= 1f + 0.2f * distanceFromLastNote;
}
public static float CalcAimStrain(float distance, float weight, float deltaTime)
{
float num = distance * 0.85f / Mathf.Pow(deltaTime, 1.35f);
return num * weight;
}
public static float CalcAimEndurance(float distance, float weight, float deltaTime)
{
float num = distance * 0.25f / Mathf.Pow(deltaTime, 1.15f) / 37500f;
return num * weight;
}
public static float CalcTapStrain(float tapDelta, float weight, float aimDistance)
{
float num = Mathf.Min(Utils.Lerp(8f, 16f, aimDistance / 103.125f), 20f);
return num / Mathf.Pow(tapDelta, 1.35f) * weight;
}
public static float CalcTapEndurance(float tapDelta, float weight, float aimDistance)
{
float num = Mathf.Min(Utils.Lerp(0.15f, 0.35f, aimDistance / 103.125f), 0.5f);
return num / Mathf.Pow(tapDelta, 1.3f) / 750f * weight;
}
public static float CalcAccStrain(float lengthSum, float slideDelta, float weight)
{
float num = slideDelta / Mathf.Pow(lengthSum, 1.18f);
return num * weight;
}
public float CalcAccEndurance(float lengthSum, float slideDelta, float weight)
{
float num = slideDelta / Mathf.Pow(lengthSum, 1.08f) / 45000f;
return num * weight;
}
public void Calculate(int speedIndex, List<Note> noteList, float songLengthMult)
{
CalculatePerformances(speedIndex, noteList);
CalculateAnalytics(speedIndex, songLengthMult);
CalculateRatings(speedIndex);
}
public void CalculateAnalytics(int speedIndex, float songLengthMult = 1f)
{
tapAnalyticsDict[speedIndex] = new DataVectorAnalytics(tapPerfDict[speedIndex], songLengthMult);
aimAnalyticsDict[speedIndex] = new DataVectorAnalytics(aimPerfDict[speedIndex], songLengthMult);
}
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 + 0.75f) * 1.25f;
float num7 = (num5 + 0.75f) * 1.12f;
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)
{
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 by = (speed - num2) / (num3 - num2);
return Utils.Lerp(firstFloat, secondFloat, by);
}
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, DiffCalcGlobals.selectedChart.songLengthMult).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");
float num5 = (flag ? 0.5f : 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 (num3 <= 0f)
{
num3 = 0.01f;
}
if (num4 <= 0f)
{
num4 = 0.01f;
}
num *= num3;
num2 *= num4;
}
float num6 = num + num2;
float num7 = num / num6;
float num8 = num2 / num6;
float num9 = (num7 + 0.75f) * 1.25f;
float num10 = (num8 + 0.75f) * 1.12f;
float num11 = num9 + num10;
return (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 by = (speed - num2) / (num3 - num2);
return Utils.Lerp(starRatingDict[num], starRatingDict[num + 1], by);
}
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.0.7")]
[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), "advanceSongs")]
[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> accToMultDict = new Dictionary<float, float>
{
{ 1f, 40.2f },
{ 0.999f, 32.4f },
{ 0.996f, 27.2f },
{ 0.993f, 23.2f },
{ 0.99f, 20.5f },
{ 0.985f, 18.1f },
{ 0.98f, 16.1f },
{ 0.97f, 13.8f },
{ 0.96f, 11.8f },
{ 0.95f, 10.8f },
{ 0.925f, 9.2f },
{ 0.9f, 8.2f },
{ 0.875f, 7.5f },
{ 0.85f, 7f },
{ 0.8f, 6f },
{ 0.7f, 4f },
{ 0.6f, 2.2f },
{ 0.5f, 0.65f },
{ 0.25f, 0.2f },
{ 0f, 0f }
};
public static readonly Dictionary<float, float> ezAccToMultDict = new Dictionary<float, float>
{
{ 1f, 15.4f },
{ 0.999f, 12.6f },
{ 0.996f, 11.6f },
{ 0.993f, 11f },
{ 0.99f, 10.6f },
{ 0.985f, 10f },
{ 0.98f, 9.6f },
{ 0.97f, 9f },
{ 0.96f, 8.6f },
{ 0.95f, 8.3f },
{ 0.925f, 7.6f },
{ 0.9f, 6.8f },
{ 0.875f, 6.2f },
{ 0.85f, 5.6f },
{ 0.8f, 4.6f },
{ 0.7f, 2.5f },
{ 0.6f, 1.12f },
{ 0.5f, 0.22f },
{ 0.25f, 0.03f },
{ 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 CalculateBaseTT(float starRating)
{
return 0.5f * FastPow(starRating, 2) + 7f * starRating + 0.05f;
}
public static float CalculateScoreTT(Chart chart, float replaySpeed, int hitCount, int noteCount, float percent, string[] modifiers = null)
{
return CalculateBaseTT(chart.GetDynamicDiffRating(replaySpeed, (float)hitCount / (float)noteCount, modifiers)) * GetMultiplier(percent, modifiers);
}
public static float CalculateScoreTT(float[] diffRatings, float replaySpeed, float percent, string[] modifiers = null)
{
return CalculateBaseTT(LerpDiff(diffRatings, replaySpeed)) * GetMultiplier(percent, modifiers);
}
public static float GetMultiplier(float percent, string[] modifiers = null)
{
Dictionary<float, float> dictionary = ((modifiers != null && modifiers.Contains("EZ")) ? ezAccToMultDict : 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 by = (percent - num2) / (num - num2);
return Lerp(dictionary[num2], dictionary[num], by);
}
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 by = (speed - num2) / (num3 - num2);
return Lerp(diffRatings[num], diffRatings[num + 1], by);
}
}
public static class PluginInfo
{
public const string PLUGIN_GUID = "TootTallyDiffCalcLibs";
public const string PLUGIN_NAME = "TootTallyDiffCalcLibs";
public const string PLUGIN_VERSION = "1.0.7";
}
}