public InputFrame Clone()
        {
            InputFrame clone = new InputFrame {
                Frames  = Frames,
                Actions = Actions,
                Angle   = Angle,
                Line    = Line,
            };

            return(clone);
        }
        public static bool TryParse(string line, int studioLine, out InputFrame inputFrame)
        {
            int    index = line.IndexOf(",", StringComparison.Ordinal);
            string framesStr;

            if (index == -1)
            {
                framesStr = line;
                index     = 0;
            }
            else
            {
                framesStr = line.Substring(0, index);
            }

            if (!int.TryParse(framesStr, out int frames))
            {
                inputFrame = null;
                return(false);
            }

            frames     = Math.Min(frames, 9999);
            inputFrame = new InputFrame {
                Line = studioLine, Frames = frames
            };
            while (index < line.Length)
            {
                char c = line[index];

                switch (char.ToUpper(c))
                {
                case 'L':
                    inputFrame.Actions ^= Actions.Left;
                    break;

                case 'R':
                    inputFrame.Actions ^= Actions.Right;
                    break;

                case 'U':
                    inputFrame.Actions ^= Actions.Up;
                    break;

                case 'D':
                    inputFrame.Actions ^= Actions.Down;
                    break;

                case 'J':
                    inputFrame.Actions ^= Actions.Jump;
                    break;

                case 'X':
                    inputFrame.Actions ^= Actions.Dash;
                    break;

                case 'G':
                    inputFrame.Actions ^= Actions.Grab;
                    break;

                case 'S':
                    inputFrame.Actions ^= Actions.Start;
                    break;

                case 'Q':
                    inputFrame.Actions ^= Actions.Restart;
                    break;

                case 'N':
                    inputFrame.Actions ^= Actions.Journal;
                    break;

                case 'K':
                    inputFrame.Actions ^= Actions.Jump2;
                    break;

                case 'C':
                    inputFrame.Actions ^= Actions.Dash2;
                    break;

                case 'O':
                    inputFrame.Actions ^= Actions.Confirm;
                    break;

                case 'Z':
                    inputFrame.Actions ^= Actions.DemoDash;
                    break;

                case 'F':
                    inputFrame.Actions ^= Actions.Feather;
                    index++;
                    string angleAndUpperLimit = line.Substring(index + 1).Trim();
                    if (angleAndUpperLimit.IsNotNullOrEmpty())
                    {
                        string[] args  = angleAndUpperLimit.Split(',');
                        string   angle = args[0];
                        if (float.TryParse(angle, out float angleFloat))
                        {
                            inputFrame.Angle = angleFloat;
                        }

                        if (args.Length >= 2 && float.TryParse(args[1], out float upperLimitFloat))
                        {
                            inputFrame.UpperLimit = upperLimitFloat;
                        }
                    }

                    inputFrame.AngleVector2      = AnalogHelper.ComputeAngleVector2(inputFrame, out Vector2Short angleVector2Short);
                    inputFrame.AngleVector2Short = angleVector2Short;
                    continue;
                }

                index++;
            }

            LibTasHelper.WriteLibTasFrame(inputFrame);

            return(true);
        }
        public static bool TryParse(string line, int studioLine, out InputFrame inputFrame)
        {
            int    index = line.IndexOf(",", StringComparison.Ordinal);
            string framesStr;

            if (index == -1)
            {
                framesStr = line;
                index     = 0;
            }
            else
            {
                framesStr = line.Substring(0, index);
            }

            if (!int.TryParse(framesStr, out int frames))
            {
                inputFrame = null;
                return(false);
            }

            frames     = Math.Min(frames, 9999);
            inputFrame = new InputFrame {
                Line = studioLine, Frames = frames
            };
            while (index < line.Length)
            {
                char c = line[index];

                switch (char.ToUpper(c))
                {
                case 'L':
                    inputFrame.Actions ^= Actions.Left;
                    break;

                case 'R':
                    inputFrame.Actions ^= Actions.Right;
                    break;

                case 'U':
                    inputFrame.Actions ^= Actions.Up;
                    break;

                case 'D':
                    inputFrame.Actions ^= Actions.Down;
                    break;

                case 'J':
                    inputFrame.Actions ^= Actions.Jump;
                    break;

                case 'X':
                    inputFrame.Actions ^= Actions.Dash;
                    break;

                case 'G':
                    inputFrame.Actions ^= Actions.Grab;
                    break;

                case 'S':
                    inputFrame.Actions ^= Actions.Start;
                    break;

                case 'Q':
                    inputFrame.Actions ^= Actions.Restart;
                    break;

                case 'N':
                    inputFrame.Actions ^= Actions.Journal;
                    break;

                case 'K':
                    inputFrame.Actions ^= Actions.Jump2;
                    break;

                case 'C':
                    inputFrame.Actions ^= Actions.Dash2;
                    break;

                case 'O':
                    inputFrame.Actions ^= Actions.Confirm;
                    break;

                case 'Z':
                    inputFrame.Actions ^= Actions.DemoDash;
                    break;

                case 'F':
                    inputFrame.Actions ^= Actions.Feather;
                    index++;
                    string angle = line.Substring(index + 1);
                    if (angle == "")
                    {
                        inputFrame.Angle     = 0;
                        inputFrame.Precision = 1E-6f;
                    }
                    else
                    {
                        inputFrame.Angle = float.Parse(angle.Trim());
                        int             digits = 0;
                        MatchCollection match  = fractional.Matches(angle);
                        if (match.Count != 0)
                        {
                            Match mat = match[0];
                            digits = mat.Groups[0].Value.Length;
                        }
                        inputFrame.Precision = float.Parse($"0.5E-{digits+2}");
                    }
                    continue;
                }

                index++;
            }

            return(true);
        }