diff --git a/osu.Game/Beatmaps/Formats/LegacyDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyDecoder.cs
index e28e235788..bbc0aad467 100644
--- a/osu.Game/Beatmaps/Formats/LegacyDecoder.cs
+++ b/osu.Game/Beatmaps/Formats/LegacyDecoder.cs
@@ -41,6 +41,7 @@ namespace osu.Game.Beatmaps.Formats
section = Section.None;
}
+ OnBeginNewSection(section);
continue;
}
@@ -57,6 +58,14 @@ namespace osu.Game.Beatmaps.Formats
protected virtual bool ShouldSkipLine(string line) => string.IsNullOrWhiteSpace(line) || line.AsSpan().TrimStart().StartsWith("//".AsSpan(), StringComparison.Ordinal);
+ ///
+ /// Invoked when a new has been entered.
+ ///
+ /// The entered .
+ protected virtual void OnBeginNewSection(Section section)
+ {
+ }
+
protected virtual void ParseLine(T output, Section section, string line)
{
line = StripComments(line);
@@ -139,7 +148,8 @@ namespace osu.Game.Beatmaps.Formats
Colours,
HitObjects,
Variables,
- Fonts
+ Fonts,
+ Mania
}
internal class LegacyDifficultyControlPoint : DifficultyControlPoint
diff --git a/osu.Game/Skinning/LegacyManiaSkinConfiguration.cs b/osu.Game/Skinning/LegacyManiaSkinConfiguration.cs
new file mode 100644
index 0000000000..5dd185879b
--- /dev/null
+++ b/osu.Game/Skinning/LegacyManiaSkinConfiguration.cs
@@ -0,0 +1,30 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using System;
+
+namespace osu.Game.Skinning
+{
+ public class LegacyManiaSkinConfiguration
+ {
+ public readonly int Keys;
+
+ public readonly float[] ColumnLineWidth;
+ public readonly float[] ColumnSpacing;
+ public readonly float[] ColumnWidth;
+
+ public float HitPosition = 124.8f; // (480 - 402) * 1.6f
+
+ public LegacyManiaSkinConfiguration(int keys)
+ {
+ Keys = keys;
+
+ ColumnLineWidth = new float[keys + 1];
+ ColumnSpacing = new float[keys - 1];
+ ColumnWidth = new float[keys];
+
+ ColumnLineWidth.AsSpan().Fill(2);
+ ColumnWidth.AsSpan().Fill(48);
+ }
+ }
+}
diff --git a/osu.Game/Skinning/LegacyManiaSkinDecoder.cs b/osu.Game/Skinning/LegacyManiaSkinDecoder.cs
new file mode 100644
index 0000000000..153a2c9626
--- /dev/null
+++ b/osu.Game/Skinning/LegacyManiaSkinDecoder.cs
@@ -0,0 +1,106 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Globalization;
+using osu.Game.Beatmaps.Formats;
+
+namespace osu.Game.Skinning
+{
+ public class LegacyManiaSkinDecoder : LegacyDecoder>
+ {
+ private const float size_scale_factor = 1.6f;
+
+ public LegacyManiaSkinDecoder()
+ : base(1)
+ {
+ }
+
+ private readonly List pendingLines = new List();
+ private LegacyManiaSkinConfiguration currentConfig;
+
+ protected override void OnBeginNewSection(Section section)
+ {
+ base.OnBeginNewSection(section);
+
+ // If a new section is reached with pending lines remaining, they can all be discarded as there isn't a valid configuration to parse them into.
+ pendingLines.Clear();
+ currentConfig = null;
+ }
+
+ protected override void ParseLine(List output, Section section, string line)
+ {
+ line = StripComments(line);
+
+ switch (section)
+ {
+ case Section.Mania:
+ var pair = SplitKeyVal(line);
+
+ switch (pair.Key)
+ {
+ case "Keys":
+ currentConfig = new LegacyManiaSkinConfiguration(int.Parse(pair.Value, CultureInfo.InvariantCulture));
+ output.Add(currentConfig);
+
+ // All existing lines can be flushed now that we have a valid configuration.
+ flushPendingLines();
+ break;
+
+ default:
+ pendingLines.Add(line);
+
+ // Hold all lines until a "Keys" item is found.
+ if (currentConfig != null)
+ flushPendingLines();
+ break;
+ }
+
+ break;
+ }
+ }
+
+ private void flushPendingLines()
+ {
+ Debug.Assert(currentConfig != null);
+
+ foreach (var line in pendingLines)
+ {
+ var pair = SplitKeyVal(line);
+
+ switch (pair.Key)
+ {
+ case "ColumnLineWidth":
+ parseArrayValue(pair.Value, currentConfig.ColumnLineWidth);
+ break;
+
+ case "ColumnSpacing":
+ parseArrayValue(pair.Value, currentConfig.ColumnSpacing);
+ break;
+
+ case "ColumnWidth":
+ parseArrayValue(pair.Value, currentConfig.ColumnWidth);
+ break;
+
+ case "HitPosition":
+ currentConfig.HitPosition = (480 - float.Parse(pair.Value, CultureInfo.InvariantCulture)) * size_scale_factor;
+ break;
+ }
+ }
+ }
+
+ private void parseArrayValue(string value, float[] output)
+ {
+ string[] values = value.Split(',');
+
+ for (int i = 0; i < values.Length; i++)
+ {
+ if (i >= output.Length)
+ break;
+
+ output[i] = float.Parse(values[i], CultureInfo.InvariantCulture) * size_scale_factor;
+ }
+ }
+ }
+}