public override HitObject Parse(string text)
        {
            try
            {
                string[] split = text.Split(',');

                Vector2 pos = new Vector2((int)Convert.ToSingle(split[0], CultureInfo.InvariantCulture), (int)Convert.ToSingle(split[1], CultureInfo.InvariantCulture));

                ConvertHitObjectType type = (ConvertHitObjectType)int.Parse(split[3]);

                int comboOffset = (int)(type & ConvertHitObjectType.ComboOffset) >> 4;
                type &= ~ConvertHitObjectType.ComboOffset;

                bool combo = type.HasFlag(ConvertHitObjectType.NewCombo);
                type &= ~ConvertHitObjectType.NewCombo;

                var soundType = (LegacySoundType)int.Parse(split[4]);
                var bankInfo  = new SampleBankInfo();

                HitObject result = null;

                if (type.HasFlag(ConvertHitObjectType.Circle))
                {
                    result = CreateHit(pos, combo, comboOffset);

                    if (split.Length > 5)
                    {
                        readCustomSampleBanks(split[5], bankInfo);
                    }
                }
                else if (type.HasFlag(ConvertHitObjectType.Slider))
                {
                    PathType pathType = PathType.Catmull;
                    double   length   = 0;

                    string[] pointSplit = split[5].Split('|');

                    int pointCount = 1;
                    foreach (var t in pointSplit)
                    {
                        if (t.Length > 1)
                        {
                            pointCount++;
                        }
                    }

                    var points = new Vector2[pointCount];

                    int pointIndex = 1;
                    foreach (string t in pointSplit)
                    {
                        if (t.Length == 1)
                        {
                            switch (t)
                            {
                            case @"C":
                                pathType = PathType.Catmull;
                                break;

                            case @"B":
                                pathType = PathType.Bezier;
                                break;

                            case @"L":
                                pathType = PathType.Linear;
                                break;

                            case @"P":
                                pathType = PathType.PerfectCurve;
                                break;
                            }

                            continue;
                        }

                        string[] temp = t.Split(':');
                        points[pointIndex++] = new Vector2((int)Convert.ToDouble(temp[0], CultureInfo.InvariantCulture), (int)Convert.ToDouble(temp[1], CultureInfo.InvariantCulture)) - pos;
                    }

                    // osu-stable special-cased colinear perfect curves to a CurveType.Linear
                    bool isLinear(Vector2[] p) => Precision.AlmostEquals(0, (p[1].Y - p[0].Y) * (p[2].X - p[0].X) - (p[1].X - p[0].X) * (p[2].Y - p[0].Y));

                    if (points.Length == 3 && pathType == PathType.PerfectCurve && isLinear(points))
                    {
                        pathType = PathType.Linear;
                    }

                    int repeatCount = Convert.ToInt32(split[6], CultureInfo.InvariantCulture);

                    if (repeatCount > 9000)
                    {
                        throw new ArgumentOutOfRangeException(nameof(repeatCount), @"Repeat count is way too high");
                    }

                    // osu-stable treated the first span of the slider as a repeat, but no repeats are happening
                    repeatCount = Math.Max(0, repeatCount - 1);

                    if (split.Length > 7)
                    {
                        length = Convert.ToDouble(split[7], CultureInfo.InvariantCulture);
                    }

                    if (split.Length > 10)
                    {
                        readCustomSampleBanks(split[10], bankInfo);
                    }

                    // One node for each repeat + the start and end nodes
                    int nodes = repeatCount + 2;

                    // Populate node sample bank infos with the default hit object sample bank
                    var nodeBankInfos = new List <SampleBankInfo>();
                    for (int i = 0; i < nodes; i++)
                    {
                        nodeBankInfos.Add(bankInfo.Clone());
                    }

                    // Read any per-node sample banks
                    if (split.Length > 9 && split[9].Length > 0)
                    {
                        string[] sets = split[9].Split('|');
                        for (int i = 0; i < nodes; i++)
                        {
                            if (i >= sets.Length)
                            {
                                break;
                            }

                            SampleBankInfo info = nodeBankInfos[i];
                            readCustomSampleBanks(sets[i], info);
                        }
                    }

                    // Populate node sound types with the default hit object sound type
                    var nodeSoundTypes = new List <LegacySoundType>();
                    for (int i = 0; i < nodes; i++)
                    {
                        nodeSoundTypes.Add(soundType);
                    }

                    // Read any per-node sound types
                    if (split.Length > 8 && split[8].Length > 0)
                    {
                        string[] adds = split[8].Split('|');
                        for (int i = 0; i < nodes; i++)
                        {
                            if (i >= adds.Length)
                            {
                                break;
                            }

                            int sound;
                            int.TryParse(adds[i], out sound);
                            nodeSoundTypes[i] = (LegacySoundType)sound;
                        }
                    }

                    // Generate the final per-node samples
                    var nodeSamples = new List <List <SampleInfo> >(nodes);
                    for (int i = 0; i < nodes; i++)
                    {
                        nodeSamples.Add(convertSoundType(nodeSoundTypes[i], nodeBankInfos[i]));
                    }

                    result = CreateSlider(pos, combo, comboOffset, points, length, pathType, repeatCount, nodeSamples);

                    // The samples are played when the slider ends, which is the last node
                    result.Samples = nodeSamples[nodeSamples.Count - 1];
                }
                else if (type.HasFlag(ConvertHitObjectType.Spinner))
                {
                    result = CreateSpinner(new Vector2(512, 384) / 2, combo, comboOffset, Convert.ToDouble(split[5], CultureInfo.InvariantCulture) + Offset);

                    if (split.Length > 6)
                    {
                        readCustomSampleBanks(split[6], bankInfo);
                    }
                }
                else if (type.HasFlag(ConvertHitObjectType.Hold))
                {
                    // Note: Hold is generated by BMS converts

                    double endTime = Convert.ToDouble(split[2], CultureInfo.InvariantCulture);

                    if (split.Length > 5 && !string.IsNullOrEmpty(split[5]))
                    {
                        string[] ss = split[5].Split(':');
                        endTime = Convert.ToDouble(ss[0], CultureInfo.InvariantCulture);
                        readCustomSampleBanks(string.Join(":", ss.Skip(1)), bankInfo);
                    }

                    result = CreateHold(pos, combo, comboOffset, endTime + Offset);
                }

                if (result == null)
                {
                    Logger.Log($"Unknown hit object type: {type}. Skipped.", level: LogLevel.Error);
                    return(null);
                }

                result.StartTime = Convert.ToDouble(split[2], CultureInfo.InvariantCulture) + Offset;

                if (result.Samples.Count == 0)
                {
                    result.Samples = convertSoundType(soundType, bankInfo);
                }

                FirstObject = false;

                return(result);
            }
            catch (FormatException)
            {
                throw new FormatException("One or more hit objects were malformed.");
            }
        }
Exemple #2
0
        public override HitObject Parse(string text)
        {
            try
            {
                string[] split = text.Split(',');

                ConvertHitObjectType type = (ConvertHitObjectType)int.Parse(split[3]) & ~ConvertHitObjectType.ColourHax;
                bool combo = type.HasFlag(ConvertHitObjectType.NewCombo);
                type &= ~ConvertHitObjectType.NewCombo;

                var soundType = (LegacySoundType)int.Parse(split[4]);
                var bankInfo  = new SampleBankInfo();

                HitObject result = null;

                if ((type & ConvertHitObjectType.Circle) > 0)
                {
                    result = CreateHit(new Vector2(int.Parse(split[0]), int.Parse(split[1])), combo);

                    if (split.Length > 5)
                    {
                        readCustomSampleBanks(split[5], bankInfo);
                    }
                }
                else if ((type & ConvertHitObjectType.Slider) > 0)
                {
                    CurveType curveType = CurveType.Catmull;
                    double    length    = 0;
                    var       points    = new List <Vector2> {
                        new Vector2(int.Parse(split[0]), int.Parse(split[1]))
                    };

                    string[] pointsplit = split[5].Split('|');
                    foreach (string t in pointsplit)
                    {
                        if (t.Length == 1)
                        {
                            switch (t)
                            {
                            case @"C":
                                curveType = CurveType.Catmull;
                                break;

                            case @"B":
                                curveType = CurveType.Bezier;
                                break;

                            case @"L":
                                curveType = CurveType.Linear;
                                break;

                            case @"P":
                                curveType = CurveType.PerfectCurve;
                                break;
                            }
                            continue;
                        }

                        string[] temp = t.Split(':');
                        points.Add(new Vector2((int)Convert.ToDouble(temp[0], CultureInfo.InvariantCulture), (int)Convert.ToDouble(temp[1], CultureInfo.InvariantCulture)));
                    }

                    int repeatCount = Convert.ToInt32(split[6], CultureInfo.InvariantCulture);

                    if (repeatCount > 9000)
                    {
                        throw new ArgumentOutOfRangeException(nameof(repeatCount), @"Repeat count is way too high");
                    }

                    if (split.Length > 7)
                    {
                        length = Convert.ToDouble(split[7], CultureInfo.InvariantCulture);
                    }

                    if (split.Length > 10)
                    {
                        readCustomSampleBanks(split[10], bankInfo);
                    }

                    // One node for each repeat + the start and end nodes
                    // Note that the first length of the slider is considered a repeat, but there are no actual repeats happening
                    int nodes = Math.Max(0, repeatCount - 1) + 2;

                    // Populate node sample bank infos with the default hit object sample bank
                    var nodeBankInfos = new List <SampleBankInfo>();
                    for (int i = 0; i < nodes; i++)
                    {
                        nodeBankInfos.Add(bankInfo.Clone());
                    }

                    // Read any per-node sample banks
                    if (split.Length > 9 && split[9].Length > 0)
                    {
                        string[] sets = split[9].Split('|');
                        for (int i = 0; i < nodes; i++)
                        {
                            if (i >= sets.Length)
                            {
                                break;
                            }

                            SampleBankInfo info = nodeBankInfos[i];
                            readCustomSampleBanks(sets[i], info);
                        }
                    }

                    // Populate node sound types with the default hit object sound type
                    var nodeSoundTypes = new List <LegacySoundType>();
                    for (int i = 0; i < nodes; i++)
                    {
                        nodeSoundTypes.Add(soundType);
                    }

                    // Read any per-node sound types
                    if (split.Length > 8 && split[8].Length > 0)
                    {
                        string[] adds = split[8].Split('|');
                        for (int i = 0; i < nodes; i++)
                        {
                            if (i >= adds.Length)
                            {
                                break;
                            }

                            int sound;
                            int.TryParse(adds[i], out sound);
                            nodeSoundTypes[i] = (LegacySoundType)sound;
                        }
                    }

                    // Generate the final per-node samples
                    var nodeSamples = new List <SampleInfoList>(nodes);
                    for (int i = 0; i <= repeatCount; i++)
                    {
                        nodeSamples.Add(convertSoundType(nodeSoundTypes[i], nodeBankInfos[i]));
                    }

                    result = CreateSlider(new Vector2(int.Parse(split[0]), int.Parse(split[1])), combo, points, length, curveType, repeatCount, nodeSamples);
                }
                else if ((type & ConvertHitObjectType.Spinner) > 0)
                {
                    result = CreateSpinner(new Vector2(512, 384) / 2, Convert.ToDouble(split[5], CultureInfo.InvariantCulture));

                    if (split.Length > 6)
                    {
                        readCustomSampleBanks(split[6], bankInfo);
                    }
                }
                else if ((type & ConvertHitObjectType.Hold) > 0)
                {
                    // Note: Hold is generated by BMS converts

                    double endTime = Convert.ToDouble(split[2], CultureInfo.InvariantCulture);

                    if (split.Length > 5 && !string.IsNullOrEmpty(split[5]))
                    {
                        string[] ss = split[5].Split(':');
                        endTime = Convert.ToDouble(ss[0], CultureInfo.InvariantCulture);
                        readCustomSampleBanks(string.Join(":", ss.Skip(1)), bankInfo);
                    }

                    result = CreateHold(new Vector2(int.Parse(split[0]), int.Parse(split[1])), combo, endTime);
                }

                if (result == null)
                {
                    throw new InvalidOperationException($@"Unknown hit object type {type}.");
                }

                result.StartTime = Convert.ToDouble(split[2], CultureInfo.InvariantCulture);
                result.Samples   = convertSoundType(soundType, bankInfo);

                return(result);
            }
            catch (FormatException)
            {
                throw new FormatException("One or more hit objects were malformed.");
            }
        }
Exemple #3
0
        public override HitObject Parse(string text)
        {
            string[] split = text.Split(',');

            Vector2 pos = new Vector2((int)Parsing.ParseFloat(split[0], Parsing.MAX_COORDINATE_VALUE), (int)Parsing.ParseFloat(split[1], Parsing.MAX_COORDINATE_VALUE));

            double startTime = Parsing.ParseDouble(split[2]) + Offset;

            ConvertHitObjectType type = (ConvertHitObjectType)Parsing.ParseInt(split[3]);

            int comboOffset = (int)(type & ConvertHitObjectType.ComboOffset) >> 4;

            type &= ~ConvertHitObjectType.ComboOffset;

            bool combo = type.HasFlag(ConvertHitObjectType.NewCombo);

            type &= ~ConvertHitObjectType.NewCombo;

            var soundType = (LegacySoundType)Parsing.ParseInt(split[4]);
            var bankInfo  = new SampleBankInfo();

            HitObject result = null;

            if (type.HasFlag(ConvertHitObjectType.Circle))
            {
                result = CreateHit(pos, combo, comboOffset);

                if (split.Length > 5)
                {
                    readCustomSampleBanks(split[5], bankInfo);
                }
            }
            else if (type.HasFlag(ConvertHitObjectType.Slider))
            {
                PathType pathType = PathType.Catmull;
                double?  length   = null;

                string[] pointSplit = split[5].Split('|');

                int pointCount = 1;

                foreach (var t in pointSplit)
                {
                    if (t.Length > 1)
                    {
                        pointCount++;
                    }
                }

                var points = new Vector2[pointCount];

                int pointIndex = 1;

                foreach (string t in pointSplit)
                {
                    if (t.Length == 1)
                    {
                        switch (t)
                        {
                        case @"C":
                            pathType = PathType.Catmull;
                            break;

                        case @"B":
                            pathType = PathType.Bezier;
                            break;

                        case @"L":
                            pathType = PathType.Linear;
                            break;

                        case @"P":
                            pathType = PathType.PerfectCurve;
                            break;
                        }

                        continue;
                    }

                    string[] temp = t.Split(':');
                    points[pointIndex++] = new Vector2((int)Parsing.ParseDouble(temp[0], Parsing.MAX_COORDINATE_VALUE), (int)Parsing.ParseDouble(temp[1], Parsing.MAX_COORDINATE_VALUE)) - pos;
                }
Exemple #4
0
        public override HitObject Parse(string text)
        {
            string[] split = text.Split(',');

            Vector2 pos = new Vector2((int)Parsing.ParseFloat(split[0], Parsing.MAX_COORDINATE_VALUE), (int)Parsing.ParseFloat(split[1], Parsing.MAX_COORDINATE_VALUE));

            double startTime = Parsing.ParseDouble(split[2]) + Offset;

            ConvertHitObjectType type = (ConvertHitObjectType)Parsing.ParseInt(split[3]);

            int comboOffset = (int)(type & ConvertHitObjectType.ComboOffset) >> 4;

            type &= ~ConvertHitObjectType.ComboOffset;

            bool combo = type.HasFlag(ConvertHitObjectType.NewCombo);

            type &= ~ConvertHitObjectType.NewCombo;

            var soundType = (LegacySoundType)Parsing.ParseInt(split[4]);
            var bankInfo  = new SampleBankInfo();

            HitObject result = null;

            if (type.HasFlag(ConvertHitObjectType.Circle))
            {
                result = CreateHit(pos, combo, comboOffset);

                if (split.Length > 5)
                {
                    readCustomSampleBanks(split[5], bankInfo);
                }
            }
            else if (type.HasFlag(ConvertHitObjectType.Slider))
            {
                PathType pathType = PathType.Catmull;
                double?  length   = null;

                string[] pointSplit = split[5].Split('|');

                int pointCount = 1;

                foreach (var t in pointSplit)
                {
                    if (t.Length > 1)
                    {
                        pointCount++;
                    }
                }

                var points = new Vector2[pointCount];

                int pointIndex = 1;

                foreach (string t in pointSplit)
                {
                    if (t.Length == 1)
                    {
                        switch (t)
                        {
                        case @"C":
                            pathType = PathType.Catmull;
                            break;

                        case @"B":
                            pathType = PathType.Bezier;
                            break;

                        case @"L":
                            pathType = PathType.Linear;
                            break;

                        case @"P":
                            pathType = PathType.PerfectCurve;
                            break;
                        }

                        continue;
                    }

                    string[] temp = t.Split(':');
                    points[pointIndex++] = new Vector2((int)Parsing.ParseDouble(temp[0], Parsing.MAX_COORDINATE_VALUE), (int)Parsing.ParseDouble(temp[1], Parsing.MAX_COORDINATE_VALUE)) - pos;
                }

                int repeatCount = Parsing.ParseInt(split[6]);

                if (repeatCount > 9000)
                {
                    throw new ArgumentOutOfRangeException(nameof(repeatCount), @"Repeat count is way too high");
                }

                // osu-stable treated the first span of the slider as a repeat, but no repeats are happening
                repeatCount = Math.Max(0, repeatCount - 1);

                if (split.Length > 7)
                {
                    length = Math.Max(0, Parsing.ParseDouble(split[7]));
                    if (length == 0)
                    {
                        length = null;
                    }
                }

                if (split.Length > 10)
                {
                    readCustomSampleBanks(split[10], bankInfo);
                }

                // One node for each repeat + the start and end nodes
                int nodes = repeatCount + 2;

                // Populate node sample bank infos with the default hit object sample bank
                var nodeBankInfos = new List <SampleBankInfo>();
                for (int i = 0; i < nodes; i++)
                {
                    nodeBankInfos.Add(bankInfo.Clone());
                }

                // Read any per-node sample banks
                if (split.Length > 9 && split[9].Length > 0)
                {
                    string[] sets = split[9].Split('|');

                    for (int i = 0; i < nodes; i++)
                    {
                        if (i >= sets.Length)
                        {
                            break;
                        }

                        SampleBankInfo info = nodeBankInfos[i];
                        readCustomSampleBanks(sets[i], info);
                    }
                }

                // Populate node sound types with the default hit object sound type
                var nodeSoundTypes = new List <LegacySoundType>();
                for (int i = 0; i < nodes; i++)
                {
                    nodeSoundTypes.Add(soundType);
                }

                // Read any per-node sound types
                if (split.Length > 8 && split[8].Length > 0)
                {
                    string[] adds = split[8].Split('|');

                    for (int i = 0; i < nodes; i++)
                    {
                        if (i >= adds.Length)
                        {
                            break;
                        }

                        int.TryParse(adds[i], out var sound);
                        nodeSoundTypes[i] = (LegacySoundType)sound;
                    }
                }

                // Generate the final per-node samples
                var nodeSamples = new List <IList <HitSampleInfo> >(nodes);
                for (int i = 0; i < nodes; i++)
                {
                    nodeSamples.Add(convertSoundType(nodeSoundTypes[i], nodeBankInfos[i]));
                }

                result = CreateSlider(pos, combo, comboOffset, convertControlPoints(points, pathType), length, repeatCount, nodeSamples);

                // The samples are played when the slider ends, which is the last node
                result.Samples = nodeSamples[^ 1];