private static void Save()
        {
            InputController temp = Manager.controller.Clone();

            //+1 speedrun tool, -5 buffered inputs
            temp.ReverseFrames(4);
            Engine.Scene.OnEndOfFrame += () => {
                if (StateManager.Instance.SaveState())
                {
                    savedController    = temp;
                    Manager.controller = savedController.Clone();

                    /*
                     * List<VirtualInput> inputs = (List<VirtualInput>)
                     *      typeof(MInput).GetField("VirtualInputs", BindingFlags.NonPublic | BindingFlags.Static).GetValue(null);
                     * foreach (VirtualInput input in inputs) {
                     *      if (input is VirtualButton)
                     *              savedBuffers.Add((float)input.GetPrivateField("bufferCounter"));
                     * }
                     */
                    routine = new Coroutine(LoadStateRoutine());
                }
            };
        }
        public static bool TryExecuteCommand(InputController state, string line, int studioLine)
        {
            try {
                if (char.IsLetter(line[0]))
                {
                    string[]   args        = Split(line);
                    string     commandType = args[0] + "Command";
                    MethodInfo method      = typeof(InputCommands).GetMethod(commandType, BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.IgnoreCase);
                    if (method == null)
                    {
                        return(false);
                    }

                    string[] commandArgs = new string[args.Length - 1];
                    for (int i = 1; i < args.Length; i++)
                    {
                        commandArgs[i - 1] = args[i];
                    }
                    TASCommandAttribute attribute = (TASCommandAttribute)method.GetCustomAttribute(typeof(TASCommandAttribute));
                    if (!(Manager.enforceLegal && attribute.illegalInMaingame))
                    {
                        if (attribute.executeAtStart)
                        {
                            return((bool)method.Invoke(null, new object[] { state, commandArgs, studioLine }));
                        }
                        else
                        {
                            Action commandCall = () => method.Invoke(null, new object[] { commandArgs });
                            state.inputs.Add(new InputRecord(commandCall));
                        }
                    }
                }
                return(false);
            }
            catch { return(false); }
        }
        private static void ExportInfo(InputFrame inputFrame)
        {
            InputController controller = Manager.Controller;
            string          output;

            if (Engine.Scene is Level level)
            {
                Player player = level.Tracker.GetEntity <Player>();
                if (player == null)
                {
                    return;
                }

                string time  = GameInfo.GetChapterTime(level);
                string pos   = player.ToSimplePositionString(CelesteTasModuleSettings.MaxDecimals);
                string speed = player.Speed.ToSimpleString(Settings.SpeedDecimals);

                int    dashCooldown = (int)GameInfo.GetDashCooldownTimer(player);
                string statuses     = GameInfo.GetStatuses(level, player, dashCooldown);

                output = string.Join("\t",
                                     inputFrame.Line + 1, $"{controller.CurrentFrameInInput}/{inputFrame}", controller.CurrentFrameInTas, time, pos, speed,
                                     PlayerStates.GetStateName(player.StateMachine.State),
                                     statuses);

                foreach (string typeName in trackedEntities.Keys)
                {
                    List <Entity> entities = trackedEntities[typeName].Invoke();
                    if (entities == null)
                    {
                        continue;
                    }

                    foreach (Entity entity in entities)
                    {
                        output += $"\t{typeName}: {entity.ToSimplePositionString(Settings.CustomInfoDecimals)}";
                    }
                }

                if (InfoCustom.Parse() is { } customInfo&& customInfo.IsNotEmpty())
                {
                    output += $"\t{customInfo.ReplaceLineBreak(" ")}";
                }

                if (InfoWatchEntity.GetWatchingEntitiesInfo("\t", true) is { } watchInfo&& watchInfo.IsNotEmpty())
                {
                    output += $"\t{watchInfo}";
                }
            }
            else
            {
                string sceneName;
                if (Engine.Scene is Overworld overworld)
                {
                    sceneName = $"Overworld {(overworld.Current ?? overworld.Next).GetType().Name}";
                }
                else
                {
                    sceneName = Engine.Scene.GetType().Name;
                }

                output = string.Join("\t", inputFrame.Line + 1, $"{controller.CurrentFrameInInput}/{inputFrame}", controller.CurrentFrameInTas,
                                     sceneName);
            }

            streamWriter.WriteLine(output);
            streamWriter.Flush();
        }
 private static void EnforceLegalCommand(InputController state, string[] args, int studioLine)
 {
     Manager.enforceLegal = true;
 }
 private static void UnsafeCommand(InputController state, string[] args, int studioLine)
 {
     Manager.allowUnsafeInput = true;
 }