// ReSharper disable InconsistentNaming using HarmonyLib; using Manager; using Manager.UserDatas; using Monitor; using System; using System.Collections.Generic; using System.Reflection; using System.Reflection.Emit; namespace UnlockFrameRate; [HarmonyPatch] internal class PatchFrameTime { private const float OriginalFrameRate = 60f; private const float OriginalFrameTime = 1000f / OriginalFrameRate; private const float OriginalFramePerMilliseconds = OriginalFrameRate / 1000; private static IEnumerable TargetMethods() { // This shouldn't be patched, because they make the judgements // harder or easier depending on your frame rate. // var noteJudge = AccessTools.TypeByName("NoteJudge"); // var juggeTiming = AccessTools.Inner(noteJudge, "JuggeTiming"); // lol // yield return AccessTools.Constructor(juggeTiming); // This changes the effect of judgement timing based on the set FPS, // so +2.0 at 120Hz will only add 17ms instead of 33ms. if (FrameRatePlugin.Instance.PatchJudgementTiming) { yield return AccessTools.Method(typeof(NoteJudge), nameof(NoteJudge.GetJudgeTiming)); yield return AccessTools.Method(typeof(NoteJudge), nameof(NoteJudge.GetSlideJudgeTiming)); yield return AccessTools.Method(typeof(UserOption), nameof(UserOption.GetAdjustMSec)); } yield return AccessTools.Method(typeof(NoteBase), "IsNoteCheckTimeStart"); yield return AccessTools.Method(typeof(TouchNoteB), "GetNoteYPosition"); yield return AccessTools.Method(typeof(SlideRoot), "IsNoteCheckTimeStart"); yield return AccessTools.Method(typeof(SlideJudge), nameof(SlideJudge.Initialize)); yield return AccessTools.Method(typeof(JudgeGrade), nameof(JudgeGrade.Initialize)); yield return AccessTools.Method(typeof(NotesManager), nameof(NotesManager.getPlayFirstMsec)); yield return AccessTools.Method(typeof(NotesManager), nameof(NotesManager.getPlayFinalMsec)); yield return AccessTools.Method(typeof(NotesManager), nameof(NotesManager.getCurrentDrawFrame)); yield return AccessTools.Method(typeof(NotesReader), nameof(NotesReader.calcFrame)); yield return AccessTools.Method(typeof(NotesReader), nameof(NotesReader.GetBPM_Frame)); yield return AccessTools.Method(typeof(NotesReader), nameof(NotesReader.getMeter_Frame)); yield return AccessTools.Method(typeof(NoteData), nameof(NoteData.getLengthFrame)); yield return AccessTools.Method(typeof(GameManager), nameof(GameManager.UpdateGameTimer)); yield return AccessTools.PropertyGetter(typeof(NotesTime), nameof(NotesTime.frame)); } private static IEnumerable Transpiler(IEnumerable instructions, MethodBase __originalMethod) { var targetFrameTime = 1000f / FrameRatePlugin.Instance.TargetFrameRate; var targetFramePerMs = (float)FrameRatePlugin.Instance.TargetFrameRate / 1000; var i = 0; foreach (var instruction in instructions) { if (instruction.opcode != OpCodes.Ldc_R4 || instruction.operand is not float operand) { yield return instruction; i++; continue; } var overridden = false; if (Math.Abs(operand - OriginalFrameTime) < float.Epsilon) { instruction.operand = targetFrameTime; overridden = true; } else if (Math.Abs(operand - OriginalFramePerMilliseconds) < float.Epsilon) { instruction.operand = targetFramePerMs; overridden = true; } if (overridden) { FrameRatePlugin.Logger.LogDebug( "Overrode constant at opcode index {0} in {1}: {2} => {3}", i, __originalMethod.Name, operand, instruction.operand); } yield return instruction; i++; } } }