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; LegacyHitObjectType type = (LegacyHitObjectType)Parsing.ParseInt(split[3]); int comboOffset = (int)(type & LegacyHitObjectType.ComboOffset) >> 4; type &= ~LegacyHitObjectType.ComboOffset; bool combo = type.HasFlag(LegacyHitObjectType.NewCombo); type &= ~LegacyHitObjectType.NewCombo; var soundType = (LegacyHitSoundType)Parsing.ParseInt(split[4]); var bankInfo = new SampleBankInfo(); HitObject result = null; if (type.HasFlag(LegacyHitObjectType.Circle)) { result = CreateHit(pos, combo, comboOffset); if (split.Length > 5) { readCustomSampleBanks(split[5], bankInfo); } } else if (type.HasFlag(LegacyHitObjectType.Slider)) { double?length = null; int repeatCount = Parsing.ParseInt(split[6]); if (repeatCount > 9000) { throw new FormatException(@"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], Parsing.MAX_COORDINATE_VALUE)); 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 <LegacyHitSoundType>(); 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] = (LegacyHitSoundType)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, convertPathString(split[5], pos), length, repeatCount, nodeSamples); } else if (type.HasFlag(LegacyHitObjectType.Spinner)) { double duration = Math.Max(0, Parsing.ParseDouble(split[5]) + Offset - startTime); result = CreateSpinner(new Vector2(512, 384) / 2, combo, comboOffset, duration); if (split.Length > 6) { readCustomSampleBanks(split[6], bankInfo); } } else if (type.HasFlag(LegacyHitObjectType.Hold)) { // Note: Hold is generated by BMS converts double endTime = Math.Max(startTime, Parsing.ParseDouble(split[2])); if (split.Length > 5 && !string.IsNullOrEmpty(split[5])) { string[] ss = split[5].Split(':'); endTime = Math.Max(startTime, Parsing.ParseDouble(ss[0])); readCustomSampleBanks(string.Join(':', ss.Skip(1)), bankInfo); } result = CreateHold(pos, combo, comboOffset, endTime + Offset - startTime); } if (result == null) { throw new InvalidDataException($"Unknown hit object type: {split[3]}"); } result.StartTime = startTime; if (result.Samples.Count == 0) { result.Samples = convertSoundType(soundType, bankInfo); } FirstObject = false; return(result); }
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; LegacyHitObjectType type = (LegacyHitObjectType)Parsing.ParseInt(split[3]); int comboOffset = (int)(type & LegacyHitObjectType.ComboOffset) >> 4; type &= ~LegacyHitObjectType.ComboOffset; bool combo = type.HasFlag(LegacyHitObjectType.NewCombo); type &= ~LegacyHitObjectType.NewCombo; var soundType = (LegacyHitSoundType)Parsing.ParseInt(split[4]); var bankInfo = new SampleBankInfo(); HitObject result = null; if (type.HasFlag(LegacyHitObjectType.Circle)) { result = CreateHit(pos, combo, comboOffset); if (split.Length > 5) { readCustomSampleBanks(split[5], bankInfo); } } else if (type.HasFlag(LegacyHitObjectType.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 FormatException(@"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], Parsing.MAX_COORDINATE_VALUE)); 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 <LegacyHitSoundType>(); 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] = (LegacyHitSoundType)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];