Merge pull request #1098 from MillhioreF/MillhioreF/fix_stable_import_errors

Fix or clarify various errors when importing from stable
This commit is contained in:
Dan Balasescu 2017-08-14 12:51:39 +09:00 committed by GitHub
commit 371917838f
4 changed files with 135 additions and 116 deletions

View File

@ -307,6 +307,11 @@ namespace osu.Game.Beatmaps
/// <returns>The imported beatmap, or an existing instance if it is already present.</returns> /// <returns>The imported beatmap, or an existing instance if it is already present.</returns>
private BeatmapSetInfo importToStorage(ArchiveReader reader) 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. // for now, concatenate all .osu files in the set to create a unique hash.
MemoryStream hashable = new MemoryStream(); MemoryStream hashable = new MemoryStream();
foreach (string file in reader.Filenames.Where(f => f.EndsWith(".osu"))) foreach (string file in reader.Filenames.Where(f => f.EndsWith(".osu")))
@ -339,7 +344,7 @@ namespace osu.Game.Beatmaps
BeatmapMetadata metadata; 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; metadata = BeatmapDecoder.GetDecoder(stream).Decode(stream).Metadata;
beatmapSet = new BeatmapSetInfo beatmapSet = new BeatmapSetInfo

View File

@ -19,7 +19,9 @@ namespace osu.Game.Beatmaps.Formats
public static BeatmapDecoder GetDecoder(StreamReader stream) 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)) if (line == null || !decoders.ContainsKey(line))
throw new IOException(@"Unknown file format"); throw new IOException(@"Unknown file format");

View File

@ -27,7 +27,9 @@ namespace osu.Game.Beatmaps.Formats
AddDecoder<OsuLegacyDecoder>(@"osu file format v7"); AddDecoder<OsuLegacyDecoder>(@"osu file format v7");
AddDecoder<OsuLegacyDecoder>(@"osu file format v6"); AddDecoder<OsuLegacyDecoder>(@"osu file format v6");
AddDecoder<OsuLegacyDecoder>(@"osu file format v5"); AddDecoder<OsuLegacyDecoder>(@"osu file format v5");
// TODO: Not sure how far back to go, or differences between versions AddDecoder<OsuLegacyDecoder>(@"osu file format v4");
AddDecoder<OsuLegacyDecoder>(@"osu file format v3");
// TODO: differences between versions
} }
private ConvertHitObjectParser parser; private ConvertHitObjectParser parser;
@ -222,6 +224,7 @@ namespace osu.Game.Beatmaps.Formats
{ {
while (line.IndexOf('$') >= 0) while (line.IndexOf('$') >= 0)
{ {
string origLine = line;
string[] split = line.Split(','); string[] split = line.Split(',');
for (int i = 0; i < split.Length; i++) for (int i = 0; i < split.Length; i++)
{ {
@ -231,6 +234,7 @@ namespace osu.Game.Beatmaps.Formats
} }
line = string.Join(",", split); line = string.Join(",", split);
if (line == origLine) break;
} }
} }

View File

@ -19,147 +19,155 @@ namespace osu.Game.Rulesets.Objects.Legacy
{ {
public override HitObject Parse(string text) public override HitObject Parse(string text)
{ {
string[] split = text.Split(','); try
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); string[] split = text.Split(',');
if (split.Length > 5) ConvertHitObjectType type = (ConvertHitObjectType)int.Parse(split[3]) & ~ConvertHitObjectType.ColourHax;
readCustomSampleBanks(split[5], bankInfo); bool combo = type.HasFlag(ConvertHitObjectType.NewCombo);
} type &= ~ConvertHitObjectType.NewCombo;
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('|'); var soundType = (LegacySoundType)int.Parse(split[4]);
foreach (string t in pointsplit) 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<Vector2> { 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": switch (t)
curveType = CurveType.Catmull; {
break; case @"C":
case @"B": curveType = CurveType.Catmull;
curveType = CurveType.Bezier; break;
break; case @"B":
case @"L": curveType = CurveType.Bezier;
curveType = CurveType.Linear; break;
break; case @"L":
case @"P": curveType = CurveType.Linear;
curveType = CurveType.PerfectCurve; break;
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(':'); int repeatCount = Convert.ToInt32(split[6], CultureInfo.InvariantCulture);
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 (repeatCount > 9000) if (split.Length > 7)
throw new ArgumentOutOfRangeException(nameof(repeatCount), @"Repeat count is way too high"); length = Convert.ToDouble(split[7], CultureInfo.InvariantCulture);
if (split.Length > 7) if (split.Length > 10)
length = Convert.ToDouble(split[7], CultureInfo.InvariantCulture); readCustomSampleBanks(split[10], bankInfo);
if (split.Length > 10) // One node for each repeat + the start and end nodes
readCustomSampleBanks(split[10], bankInfo); // 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 // Populate node sample bank infos with the default hit object sample bank
// Note that the first length of the slider is considered a repeat, but there are no actual repeats happening var nodeBankInfos = new List<SampleBankInfo>();
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++) 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) string[] sets = split[9].Split('|');
break; for (int i = 0; i < nodes; i++)
{
if (i >= sets.Length)
break;
SampleBankInfo info = nodeBankInfos[i]; SampleBankInfo info = nodeBankInfos[i];
readCustomSampleBanks(sets[i], info); readCustomSampleBanks(sets[i], info);
}
} }
}
// Populate node sound types with the default hit object sound type // Populate node sound types with the default hit object sound type
var nodeSoundTypes = new List<LegacySoundType>(); 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++) 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) string[] adds = split[8].Split('|');
break; for (int i = 0; i < nodes; i++)
{
if (i >= adds.Length)
break;
int sound; int sound;
int.TryParse(adds[i], out sound); int.TryParse(adds[i], out sound);
nodeSoundTypes[i] = (LegacySoundType)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)
// 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(':'); result = CreateSpinner(new Vector2(512, 384) / 2, Convert.ToDouble(split[5], CultureInfo.InvariantCulture));
endTime = Convert.ToDouble(ss[0], CultureInfo.InvariantCulture);
readCustomSampleBanks(string.Join(":", ss.Skip(1)), bankInfo); 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) private void readCustomSampleBanks(string str, SampleBankInfo bankInfo)