diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs
index bbb6c975d0..e388f5b6d1 100644
--- a/osu.Game/Beatmaps/BeatmapManager.cs
+++ b/osu.Game/Beatmaps/BeatmapManager.cs
@@ -307,6 +307,11 @@ namespace osu.Game.Beatmaps
/// The imported beatmap, or an existing instance if it is already present.
private BeatmapSetInfo importToStorage(ArchiveReader reader)
{
+ // let's make sure there are actually .osu files to import.
+ string mapName = reader.Filenames.FirstOrDefault(f => f.EndsWith(".osu"));
+ if (string.IsNullOrEmpty(mapName))
+ throw new InvalidOperationException("No beatmap files found in the map folder.");
+
// for now, concatenate all .osu files in the set to create a unique hash.
MemoryStream hashable = new MemoryStream();
foreach (string file in reader.Filenames.Where(f => f.EndsWith(".osu")))
@@ -339,7 +344,7 @@ namespace osu.Game.Beatmaps
BeatmapMetadata metadata;
- using (var stream = new StreamReader(reader.GetStream(reader.Filenames.First(f => f.EndsWith(".osu")))))
+ using (var stream = new StreamReader(reader.GetStream(mapName)))
metadata = BeatmapDecoder.GetDecoder(stream).Decode(stream).Metadata;
beatmapSet = new BeatmapSetInfo
diff --git a/osu.Game/Beatmaps/Formats/BeatmapDecoder.cs b/osu.Game/Beatmaps/Formats/BeatmapDecoder.cs
index 1c3eadc91e..234d65eee4 100644
--- a/osu.Game/Beatmaps/Formats/BeatmapDecoder.cs
+++ b/osu.Game/Beatmaps/Formats/BeatmapDecoder.cs
@@ -19,7 +19,9 @@ namespace osu.Game.Beatmaps.Formats
public static BeatmapDecoder GetDecoder(StreamReader stream)
{
- string line = stream.ReadLine()?.Trim();
+ string line;
+ do { line = stream.ReadLine()?.Trim(); }
+ while (line != null && line.Length == 0);
if (line == null || !decoders.ContainsKey(line))
throw new IOException(@"Unknown file format");
diff --git a/osu.Game/Beatmaps/Formats/OsuLegacyDecoder.cs b/osu.Game/Beatmaps/Formats/OsuLegacyDecoder.cs
index 433c23284f..46cbad2487 100644
--- a/osu.Game/Beatmaps/Formats/OsuLegacyDecoder.cs
+++ b/osu.Game/Beatmaps/Formats/OsuLegacyDecoder.cs
@@ -27,7 +27,9 @@ namespace osu.Game.Beatmaps.Formats
AddDecoder(@"osu file format v7");
AddDecoder(@"osu file format v6");
AddDecoder(@"osu file format v5");
- // TODO: Not sure how far back to go, or differences between versions
+ AddDecoder(@"osu file format v4");
+ AddDecoder(@"osu file format v3");
+ // TODO: differences between versions
}
private ConvertHitObjectParser parser;
@@ -222,6 +224,7 @@ namespace osu.Game.Beatmaps.Formats
{
while (line.IndexOf('$') >= 0)
{
+ string origLine = line;
string[] split = line.Split(',');
for (int i = 0; i < split.Length; i++)
{
@@ -231,6 +234,7 @@ namespace osu.Game.Beatmaps.Formats
}
line = string.Join(",", split);
+ if (line == origLine) break;
}
}
diff --git a/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs b/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs
index a4c319291c..b5e3f837fc 100644
--- a/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs
+++ b/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs
@@ -19,147 +19,155 @@ namespace osu.Game.Rulesets.Objects.Legacy
{
public override HitObject Parse(string text)
{
- 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)
+ try
{
- result = CreateHit(new Vector2(int.Parse(split[0]), int.Parse(split[1])), combo);
+ string[] split = text.Split(',');
- 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 { new Vector2(int.Parse(split[0]), int.Parse(split[1])) };
+ ConvertHitObjectType type = (ConvertHitObjectType)int.Parse(split[3]) & ~ConvertHitObjectType.ColourHax;
+ bool combo = type.HasFlag(ConvertHitObjectType.NewCombo);
+ type &= ~ConvertHitObjectType.NewCombo;
- string[] pointsplit = split[5].Split('|');
- foreach (string t in pointsplit)
+ var soundType = (LegacySoundType)int.Parse(split[4]);
+ var bankInfo = new SampleBankInfo();
+
+ HitObject result = null;
+
+ if ((type & ConvertHitObjectType.Circle) > 0)
{
- if (t.Length == 1)
+ 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 { new Vector2(int.Parse(split[0]), int.Parse(split[1])) };
+
+ string[] pointsplit = split[5].Split('|');
+ foreach (string t in pointsplit)
{
- switch (t)
+ if (t.Length == 1)
{
- case @"C":
- curveType = CurveType.Catmull;
- break;
- case @"B":
- curveType = CurveType.Bezier;
- break;
- case @"L":
- curveType = CurveType.Linear;
- break;
- case @"P":
- curveType = CurveType.PerfectCurve;
- break;
+ 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;
}
- continue;
+
+ string[] temp = t.Split(':');
+ points.Add(new Vector2((int)Convert.ToDouble(temp[0], CultureInfo.InvariantCulture), (int)Convert.ToDouble(temp[1], CultureInfo.InvariantCulture)));
}
- 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);
- int repeatCount = Convert.ToInt32(split[6], CultureInfo.InvariantCulture);
+ if (repeatCount > 9000)
+ throw new ArgumentOutOfRangeException(nameof(repeatCount), @"Repeat count is way too high");
- 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 > 7)
- length = Convert.ToDouble(split[7], CultureInfo.InvariantCulture);
+ if (split.Length > 10)
+ readCustomSampleBanks(split[10], bankInfo);
- 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;
- // 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();
- 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('|');
+ // Populate node sample bank infos with the default hit object sample bank
+ var nodeBankInfos = new List();
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)
{
- if (i >= sets.Length)
- break;
+ 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);
+ SampleBankInfo info = nodeBankInfos[i];
+ readCustomSampleBanks(sets[i], info);
+ }
}
- }
- // Populate node sound types with the default hit object sound type
- var nodeSoundTypes = new List();
- 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('|');
+ // Populate node sound types with the default hit object sound type
+ var nodeSoundTypes = new List();
for (int i = 0; i < nodes; i++)
+ nodeSoundTypes.Add(soundType);
+
+ // Read any per-node sound types
+ if (split.Length > 8 && split[8].Length > 0)
{
- if (i >= adds.Length)
- break;
+ 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;
+ int sound;
+ int.TryParse(adds[i], out sound);
+ nodeSoundTypes[i] = (LegacySoundType)sound;
+ }
}
+
+ // Generate the final per-node samples
+ var nodeSamples = new List(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);
}
-
- // Generate the final per-node samples
- var nodeSamples = new List(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]))
+ else if ((type & ConvertHitObjectType.Spinner) > 0)
{
- string[] ss = split[5].Split(':');
- endTime = Convert.ToDouble(ss[0], CultureInfo.InvariantCulture);
- readCustomSampleBanks(string.Join(":", ss.Skip(1)), bankInfo);
+ 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);
}
- 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.");
}
-
- 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;
}
private void readCustomSampleBanks(string str, SampleBankInfo bankInfo)