1
0
mirror of https://github.com/ppy/osu.git synced 2025-01-31 23:52:55 +08:00

Throw exceptions and let LegacyDecoder handle them

This commit is contained in:
smoogipoo 2019-08-08 14:44:04 +09:00
parent 54ee0cabd8
commit ac2060f1cf
3 changed files with 223 additions and 259 deletions

View File

@ -5,7 +5,6 @@ using System;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using osu.Framework.IO.File; using osu.Framework.IO.File;
using osu.Framework.Logging;
using osu.Game.Beatmaps.Timing; using osu.Game.Beatmaps.Timing;
using osu.Game.Rulesets.Objects.Legacy; using osu.Game.Rulesets.Objects.Legacy;
using osu.Game.Beatmaps.ControlPoints; using osu.Game.Beatmaps.ControlPoints;
@ -292,10 +291,7 @@ namespace osu.Game.Beatmaps.Formats
EventType type; EventType type;
if (!Enum.TryParse(split[0], out type)) if (!Enum.TryParse(split[0], out type))
{ throw new InvalidDataException($@"Unknown event type: {split[0]}");
Logger.Log($"Unknown beatmap event of type {split[0]} could not be parsed and will be ignored.", LoggingTarget.Runtime, LogLevel.Important);
return;
}
switch (type) switch (type)
{ {
@ -323,90 +319,79 @@ namespace osu.Game.Beatmaps.Formats
private void handleTimingPoint(string line) private void handleTimingPoint(string line)
{ {
try string[] split = line.Split(',');
double time = getOffsetTime(Parsing.ParseDouble(split[0].Trim()));
double beatLength = Parsing.ParseDouble(split[1].Trim());
double speedMultiplier = beatLength < 0 ? 100.0 / -beatLength : 1;
TimeSignatures timeSignature = TimeSignatures.SimpleQuadruple;
if (split.Length >= 3)
timeSignature = split[2][0] == '0' ? TimeSignatures.SimpleQuadruple : (TimeSignatures)Parsing.ParseInt(split[2]);
LegacySampleBank sampleSet = defaultSampleBank;
if (split.Length >= 4)
sampleSet = (LegacySampleBank)Parsing.ParseInt(split[3]);
int customSampleBank = 0;
if (split.Length >= 5)
customSampleBank = Parsing.ParseInt(split[4]);
int sampleVolume = defaultSampleVolume;
if (split.Length >= 6)
sampleVolume = Parsing.ParseInt(split[5]);
bool timingChange = true;
if (split.Length >= 7)
timingChange = split[6][0] == '1';
bool kiaiMode = false;
bool omitFirstBarSignature = false;
if (split.Length >= 8)
{ {
string[] split = line.Split(','); EffectFlags effectFlags = (EffectFlags)Parsing.ParseInt(split[7]);
kiaiMode = effectFlags.HasFlag(EffectFlags.Kiai);
double time = getOffsetTime(Parsing.ParseDouble(split[0].Trim())); omitFirstBarSignature = effectFlags.HasFlag(EffectFlags.OmitFirstBarLine);
double beatLength = Parsing.ParseDouble(split[1].Trim());
double speedMultiplier = beatLength < 0 ? 100.0 / -beatLength : 1;
TimeSignatures timeSignature = TimeSignatures.SimpleQuadruple;
if (split.Length >= 3)
timeSignature = split[2][0] == '0' ? TimeSignatures.SimpleQuadruple : (TimeSignatures)Parsing.ParseInt(split[2]);
LegacySampleBank sampleSet = defaultSampleBank;
if (split.Length >= 4)
sampleSet = (LegacySampleBank)Parsing.ParseInt(split[3]);
int customSampleBank = 0;
if (split.Length >= 5)
customSampleBank = Parsing.ParseInt(split[4]);
int sampleVolume = defaultSampleVolume;
if (split.Length >= 6)
sampleVolume = Parsing.ParseInt(split[5]);
bool timingChange = true;
if (split.Length >= 7)
timingChange = split[6][0] == '1';
bool kiaiMode = false;
bool omitFirstBarSignature = false;
if (split.Length >= 8)
{
EffectFlags effectFlags = (EffectFlags)Parsing.ParseInt(split[7]);
kiaiMode = effectFlags.HasFlag(EffectFlags.Kiai);
omitFirstBarSignature = effectFlags.HasFlag(EffectFlags.OmitFirstBarLine);
}
string stringSampleSet = sampleSet.ToString().ToLowerInvariant();
if (stringSampleSet == @"none")
stringSampleSet = @"normal";
if (timingChange)
{
var controlPoint = CreateTimingControlPoint();
controlPoint.Time = time;
controlPoint.BeatLength = beatLength;
controlPoint.TimeSignature = timeSignature;
handleTimingControlPoint(controlPoint);
}
handleDifficultyControlPoint(new DifficultyControlPoint
{
Time = time,
SpeedMultiplier = speedMultiplier,
AutoGenerated = timingChange
});
handleEffectControlPoint(new EffectControlPoint
{
Time = time,
KiaiMode = kiaiMode,
OmitFirstBarLine = omitFirstBarSignature,
AutoGenerated = timingChange
});
handleSampleControlPoint(new LegacySampleControlPoint
{
Time = time,
SampleBank = stringSampleSet,
SampleVolume = sampleVolume,
CustomSampleBank = customSampleBank,
AutoGenerated = timingChange
});
} }
catch (FormatException)
string stringSampleSet = sampleSet.ToString().ToLowerInvariant();
if (stringSampleSet == @"none")
stringSampleSet = @"normal";
if (timingChange)
{ {
Logger.Log("A timing point could not be parsed correctly and will be ignored", LoggingTarget.Runtime, LogLevel.Important); var controlPoint = CreateTimingControlPoint();
controlPoint.Time = time;
controlPoint.BeatLength = beatLength;
controlPoint.TimeSignature = timeSignature;
handleTimingControlPoint(controlPoint);
} }
catch (OverflowException)
handleDifficultyControlPoint(new DifficultyControlPoint
{ {
Logger.Log("A timing point could not be parsed correctly and will be ignored", LoggingTarget.Runtime, LogLevel.Important); Time = time,
} SpeedMultiplier = speedMultiplier,
AutoGenerated = timingChange
});
handleEffectControlPoint(new EffectControlPoint
{
Time = time,
KiaiMode = kiaiMode,
OmitFirstBarLine = omitFirstBarSignature,
AutoGenerated = timingChange
});
handleSampleControlPoint(new LegacySampleControlPoint
{
Time = time,
SampleBank = stringSampleSet,
SampleVolume = sampleVolume,
CustomSampleBank = customSampleBank,
AutoGenerated = timingChange
});
} }
private void handleTimingControlPoint(TimingControlPoint newPoint) private void handleTimingControlPoint(TimingControlPoint newPoint)

View File

@ -10,7 +10,6 @@ using osuTK;
using osuTK.Graphics; using osuTK.Graphics;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.IO.File; using osu.Framework.IO.File;
using osu.Framework.Logging;
using osu.Game.Storyboards; using osu.Game.Storyboards;
namespace osu.Game.Beatmaps.Formats namespace osu.Game.Beatmaps.Formats
@ -85,10 +84,7 @@ namespace osu.Game.Beatmaps.Formats
EventType type; EventType type;
if (!Enum.TryParse(split[0], out type)) if (!Enum.TryParse(split[0], out type))
{ throw new InvalidDataException($@"Unknown event type: {split[0]}");
Logger.Log($"Unknown storyboard event of type {split[0]} could not be parsed and will be ignored.", LoggingTarget.Runtime, LogLevel.Important);
return;
}
switch (type) switch (type)
{ {

View File

@ -10,7 +10,6 @@ using osu.Game.Beatmaps.Formats;
using osu.Game.Audio; using osu.Game.Audio;
using System.Linq; using System.Linq;
using JetBrains.Annotations; using JetBrains.Annotations;
using osu.Framework.Logging;
using osu.Framework.MathUtils; using osu.Framework.MathUtils;
namespace osu.Game.Rulesets.Objects.Legacy namespace osu.Game.Rulesets.Objects.Legacy
@ -41,208 +40,192 @@ namespace osu.Game.Rulesets.Objects.Legacy
[CanBeNull] [CanBeNull]
public override HitObject Parse(string text) public override HitObject Parse(string text)
{ {
try 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))
{ {
string[] split = text.Split(','); result = CreateHit(pos, combo, comboOffset);
Vector2 pos = new Vector2((int)Parsing.ParseFloat(split[0], Parsing.MAX_COORDINATE_VALUE), (int)Parsing.ParseFloat(split[1], Parsing.MAX_COORDINATE_VALUE)); if (split.Length > 5)
readCustomSampleBanks(split[5], bankInfo);
}
else if (type.HasFlag(ConvertHitObjectType.Slider))
{
PathType pathType = PathType.Catmull;
double? length = null;
double startTime = Parsing.ParseDouble(split[2]) + Offset; string[] pointSplit = split[5].Split('|');
ConvertHitObjectType type = (ConvertHitObjectType)Parsing.ParseInt(split[3]); int pointCount = 1;
foreach (var t in pointSplit)
if (t.Length > 1)
pointCount++;
int comboOffset = (int)(type & ConvertHitObjectType.ComboOffset) >> 4; var points = new Vector2[pointCount];
type &= ~ConvertHitObjectType.ComboOffset;
bool combo = type.HasFlag(ConvertHitObjectType.NewCombo); int pointIndex = 1;
type &= ~ConvertHitObjectType.NewCombo;
var soundType = (LegacySoundType)Parsing.ParseInt(split[4]); foreach (string t in pointSplit)
var bankInfo = new SampleBankInfo();
HitObject result = null;
if (type.HasFlag(ConvertHitObjectType.Circle))
{ {
result = CreateHit(pos, combo, comboOffset); if (t.Length == 1)
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)
{ {
switch (t) case @"C":
{ pathType = PathType.Catmull;
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;
}
// 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 = 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; break;
SampleBankInfo info = nodeBankInfos[i]; case @"B":
readCustomSampleBanks(sets[i], info); pathType = PathType.Bezier;
}
}
// 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; break;
int sound; case @"L":
int.TryParse(adds[i], out sound); pathType = PathType.Linear;
nodeSoundTypes[i] = (LegacySoundType)sound; break;
case @"P":
pathType = PathType.PerfectCurve;
break;
} }
continue;
} }
// Generate the final per-node samples string[] temp = t.Split(':');
var nodeSamples = new List<List<HitSampleInfo>>(nodes); points[pointIndex++] = new Vector2((int)Parsing.ParseDouble(temp[0], Parsing.MAX_COORDINATE_VALUE), (int)Parsing.ParseDouble(temp[1], Parsing.MAX_COORDINATE_VALUE)) - 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 = 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++) 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))
{
double endTime = Math.Max(startTime, Parsing.ParseDouble(split[5]) + Offset);
result = CreateSpinner(new Vector2(512, 384) / 2, combo, comboOffset, endTime);
if (split.Length > 6)
readCustomSampleBanks(split[6], bankInfo);
}
else if (type.HasFlag(ConvertHitObjectType.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(':'); if (i >= sets.Length)
endTime = Math.Max(startTime, Parsing.ParseDouble(ss[0])); break;
readCustomSampleBanks(string.Join(":", ss.Skip(1)), bankInfo);
SampleBankInfo info = nodeBankInfos[i];
readCustomSampleBanks(sets[i], info);
} }
result = CreateHold(pos, combo, comboOffset, endTime + Offset);
} }
if (result == null) // 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)
{ {
Logger.Log($"Unknown hit object type: {type}. Skipped.", level: LogLevel.Error); string[] adds = split[8].Split('|');
return null;
for (int i = 0; i < nodes; i++)
{
if (i >= adds.Length)
break;
int sound;
int.TryParse(adds[i], out sound);
nodeSoundTypes[i] = (LegacySoundType)sound;
}
} }
result.StartTime = startTime; // Generate the final per-node samples
var nodeSamples = new List<List<HitSampleInfo>>(nodes);
for (int i = 0; i < nodes; i++)
nodeSamples.Add(convertSoundType(nodeSoundTypes[i], nodeBankInfos[i]));
if (result.Samples.Count == 0) result = CreateSlider(pos, combo, comboOffset, points, length, pathType, repeatCount, nodeSamples);
result.Samples = convertSoundType(soundType, bankInfo);
FirstObject = false; // The samples are played when the slider ends, which is the last node
result.Samples = nodeSamples[nodeSamples.Count - 1];
return result;
} }
catch (FormatException) else if (type.HasFlag(ConvertHitObjectType.Spinner))
{ {
Logger.Log("A hitobject could not be parsed correctly and will be ignored", LoggingTarget.Runtime, LogLevel.Important); double endTime = Math.Max(startTime, Parsing.ParseDouble(split[5]) + Offset);
result = CreateSpinner(new Vector2(512, 384) / 2, combo, comboOffset, endTime);
if (split.Length > 6)
readCustomSampleBanks(split[6], bankInfo);
} }
catch (OverflowException) else if (type.HasFlag(ConvertHitObjectType.Hold))
{ {
Logger.Log("A hitobject could not be parsed correctly and will be ignored", LoggingTarget.Runtime, LogLevel.Important); // 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);
} }
return null; if (result == null)
throw new InvalidDataException($"Unknown hit object type: {type}");
result.StartTime = startTime;
if (result.Samples.Count == 0)
result.Samples = convertSoundType(soundType, bankInfo);
FirstObject = false;
return result;
} }
private void readCustomSampleBanks(string str, SampleBankInfo bankInfo) private void readCustomSampleBanks(string str, SampleBankInfo bankInfo)