mirror of
https://github.com/ppy/osu.git
synced 2025-01-30 07:32:55 +08:00
Initial push for better decoders
This commit is contained in:
parent
c11f6efab5
commit
217dd2ecdc
@ -24,7 +24,7 @@ namespace osu.Game.Tests.Beatmaps.Formats
|
|||||||
using (var resStream = Resource.OpenResource("Soleily - Renatus (Gamu) [Insane].osu"))
|
using (var resStream = Resource.OpenResource("Soleily - Renatus (Gamu) [Insane].osu"))
|
||||||
using (var stream = new StreamReader(resStream))
|
using (var stream = new StreamReader(resStream))
|
||||||
{
|
{
|
||||||
var beatmap = decoder.DecodeBeatmap(stream);
|
var beatmap = decoder.Decode(stream);
|
||||||
var beatmapInfo = beatmap.BeatmapInfo;
|
var beatmapInfo = beatmap.BeatmapInfo;
|
||||||
var metadata = beatmap.Metadata;
|
var metadata = beatmap.Metadata;
|
||||||
|
|
||||||
@ -47,7 +47,7 @@ namespace osu.Game.Tests.Beatmaps.Formats
|
|||||||
using (var resStream = Resource.OpenResource("Soleily - Renatus (Gamu) [Insane].osu"))
|
using (var resStream = Resource.OpenResource("Soleily - Renatus (Gamu) [Insane].osu"))
|
||||||
using (var stream = new StreamReader(resStream))
|
using (var stream = new StreamReader(resStream))
|
||||||
{
|
{
|
||||||
var beatmapInfo = decoder.DecodeBeatmap(stream).BeatmapInfo;
|
var beatmapInfo = decoder.Decode(stream).BeatmapInfo;
|
||||||
|
|
||||||
int[] expectedBookmarks =
|
int[] expectedBookmarks =
|
||||||
{
|
{
|
||||||
@ -72,7 +72,7 @@ namespace osu.Game.Tests.Beatmaps.Formats
|
|||||||
using (var resStream = Resource.OpenResource("Soleily - Renatus (Gamu) [Insane].osu"))
|
using (var resStream = Resource.OpenResource("Soleily - Renatus (Gamu) [Insane].osu"))
|
||||||
using (var stream = new StreamReader(resStream))
|
using (var stream = new StreamReader(resStream))
|
||||||
{
|
{
|
||||||
var beatmap = decoder.DecodeBeatmap(stream);
|
var beatmap = decoder.Decode(stream);
|
||||||
var beatmapInfo = beatmap.BeatmapInfo;
|
var beatmapInfo = beatmap.BeatmapInfo;
|
||||||
var metadata = beatmap.Metadata;
|
var metadata = beatmap.Metadata;
|
||||||
|
|
||||||
@ -96,7 +96,7 @@ namespace osu.Game.Tests.Beatmaps.Formats
|
|||||||
using (var resStream = Resource.OpenResource("Soleily - Renatus (Gamu) [Insane].osu"))
|
using (var resStream = Resource.OpenResource("Soleily - Renatus (Gamu) [Insane].osu"))
|
||||||
using (var stream = new StreamReader(resStream))
|
using (var stream = new StreamReader(resStream))
|
||||||
{
|
{
|
||||||
var difficulty = decoder.DecodeBeatmap(stream).BeatmapInfo.BaseDifficulty;
|
var difficulty = decoder.Decode(stream).BeatmapInfo.BaseDifficulty;
|
||||||
|
|
||||||
Assert.AreEqual(6.5f, difficulty.DrainRate);
|
Assert.AreEqual(6.5f, difficulty.DrainRate);
|
||||||
Assert.AreEqual(4, difficulty.CircleSize);
|
Assert.AreEqual(4, difficulty.CircleSize);
|
||||||
@ -114,7 +114,7 @@ namespace osu.Game.Tests.Beatmaps.Formats
|
|||||||
using (var resStream = Resource.OpenResource("Soleily - Renatus (Gamu) [Insane].osu"))
|
using (var resStream = Resource.OpenResource("Soleily - Renatus (Gamu) [Insane].osu"))
|
||||||
using (var stream = new StreamReader(resStream))
|
using (var stream = new StreamReader(resStream))
|
||||||
{
|
{
|
||||||
var beatmap = decoder.DecodeBeatmap(stream);
|
var beatmap = decoder.Decode(stream);
|
||||||
var metadata = beatmap.Metadata;
|
var metadata = beatmap.Metadata;
|
||||||
var breakPoint = beatmap.Breaks[0];
|
var breakPoint = beatmap.Breaks[0];
|
||||||
|
|
||||||
@ -132,7 +132,7 @@ namespace osu.Game.Tests.Beatmaps.Formats
|
|||||||
using (var resStream = Resource.OpenResource("Soleily - Renatus (Gamu) [Insane].osu"))
|
using (var resStream = Resource.OpenResource("Soleily - Renatus (Gamu) [Insane].osu"))
|
||||||
using (var stream = new StreamReader(resStream))
|
using (var stream = new StreamReader(resStream))
|
||||||
{
|
{
|
||||||
var beatmap = decoder.DecodeBeatmap(stream);
|
var beatmap = decoder.Decode(stream);
|
||||||
var controlPoints = beatmap.ControlPointInfo;
|
var controlPoints = beatmap.ControlPointInfo;
|
||||||
|
|
||||||
Assert.AreEqual(4, controlPoints.TimingPoints.Count);
|
Assert.AreEqual(4, controlPoints.TimingPoints.Count);
|
||||||
@ -167,7 +167,7 @@ namespace osu.Game.Tests.Beatmaps.Formats
|
|||||||
using (var resStream = Resource.OpenResource("Soleily - Renatus (Gamu) [Insane].osu"))
|
using (var resStream = Resource.OpenResource("Soleily - Renatus (Gamu) [Insane].osu"))
|
||||||
using (var stream = new StreamReader(resStream))
|
using (var stream = new StreamReader(resStream))
|
||||||
{
|
{
|
||||||
var comboColors = decoder.DecodeBeatmap(stream).ComboColors;
|
var comboColors = decoder.Decode(stream).ComboColors;
|
||||||
|
|
||||||
Color4[] expectedColors =
|
Color4[] expectedColors =
|
||||||
{
|
{
|
||||||
@ -191,7 +191,7 @@ namespace osu.Game.Tests.Beatmaps.Formats
|
|||||||
using (var resStream = Resource.OpenResource("Soleily - Renatus (Gamu) [Insane].osu"))
|
using (var resStream = Resource.OpenResource("Soleily - Renatus (Gamu) [Insane].osu"))
|
||||||
using (var stream = new StreamReader(resStream))
|
using (var stream = new StreamReader(resStream))
|
||||||
{
|
{
|
||||||
var hitObjects = decoder.DecodeBeatmap(stream).HitObjects;
|
var hitObjects = decoder.Decode(stream).HitObjects;
|
||||||
|
|
||||||
var curveData = hitObjects[0] as IHasCurve;
|
var curveData = hitObjects[0] as IHasCurve;
|
||||||
var positionData = hitObjects[0] as IHasPosition;
|
var positionData = hitObjects[0] as IHasPosition;
|
||||||
|
@ -18,11 +18,11 @@ namespace osu.Game.Tests.Beatmaps.Formats
|
|||||||
[Test]
|
[Test]
|
||||||
public void TestDecodeStoryboardEvents()
|
public void TestDecodeStoryboardEvents()
|
||||||
{
|
{
|
||||||
var decoder = new LegacyBeatmapDecoder();
|
var decoder = new LegacyStoryboardDecoder();
|
||||||
using (var resStream = Resource.OpenResource("Himeringo - Yotsuya-san ni Yoroshiku (RLC) [Winber1's Extreme].osu"))
|
using (var resStream = Resource.OpenResource("Himeringo - Yotsuya-san ni Yoroshiku (RLC) [Winber1's Extreme].osu"))
|
||||||
using (var stream = new StreamReader(resStream))
|
using (var stream = new StreamReader(resStream))
|
||||||
{
|
{
|
||||||
var storyboard = decoder.GetStoryboardDecoder().DecodeStoryboard(stream);
|
var storyboard = decoder.Decode(stream);
|
||||||
|
|
||||||
Assert.IsTrue(storyboard.HasDrawable);
|
Assert.IsTrue(storyboard.HasDrawable);
|
||||||
Assert.AreEqual(4, storyboard.Layers.Count());
|
Assert.AreEqual(4, storyboard.Layers.Count());
|
||||||
|
@ -159,7 +159,7 @@ namespace osu.Game.Tests.Beatmaps.Formats
|
|||||||
using (var sr = new StreamReader(stream))
|
using (var sr = new StreamReader(stream))
|
||||||
{
|
{
|
||||||
|
|
||||||
var legacyDecoded = new LegacyBeatmapDecoder { ApplyOffsets = false }.DecodeBeatmap(sr);
|
var legacyDecoded = new LegacyBeatmapDecoder { ApplyOffsets = false }.Decode(sr);
|
||||||
using (var ms = new MemoryStream())
|
using (var ms = new MemoryStream())
|
||||||
using (var sw = new StreamWriter(ms))
|
using (var sw = new StreamWriter(ms))
|
||||||
using (var sr2 = new StreamReader(ms))
|
using (var sr2 = new StreamReader(ms))
|
||||||
@ -168,7 +168,7 @@ namespace osu.Game.Tests.Beatmaps.Formats
|
|||||||
sw.Flush();
|
sw.Flush();
|
||||||
|
|
||||||
ms.Position = 0;
|
ms.Position = 0;
|
||||||
return (legacyDecoded, new JsonBeatmapDecoder().DecodeBeatmap(sr2));
|
return (legacyDecoded, new JsonBeatmapDecoder().Decode(sr2));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -50,7 +50,7 @@ namespace osu.Game.Tests.Beatmaps.IO
|
|||||||
|
|
||||||
BeatmapMetadata meta;
|
BeatmapMetadata meta;
|
||||||
using (var stream = new StreamReader(reader.GetStream("Soleily - Renatus (Deif) [Platter].osu")))
|
using (var stream = new StreamReader(reader.GetStream("Soleily - Renatus (Deif) [Platter].osu")))
|
||||||
meta = Decoder.GetDecoder(stream).DecodeBeatmap(stream).Metadata;
|
meta = Decoder.GetDecoder<Beatmap>(stream).Decode(stream).Metadata;
|
||||||
|
|
||||||
Assert.AreEqual(241526, meta.OnlineBeatmapSetID);
|
Assert.AreEqual(241526, meta.OnlineBeatmapSetID);
|
||||||
Assert.AreEqual("Soleily", meta.Artist);
|
Assert.AreEqual("Soleily", meta.Artist);
|
||||||
|
@ -22,6 +22,7 @@ namespace osu.Game.Beatmaps
|
|||||||
public BeatmapInfo BeatmapInfo = new BeatmapInfo();
|
public BeatmapInfo BeatmapInfo = new BeatmapInfo();
|
||||||
public ControlPointInfo ControlPointInfo = new ControlPointInfo();
|
public ControlPointInfo ControlPointInfo = new ControlPointInfo();
|
||||||
public List<BreakPeriod> Breaks = new List<BreakPeriod>();
|
public List<BreakPeriod> Breaks = new List<BreakPeriod>();
|
||||||
|
|
||||||
public List<Color4> ComboColors = new List<Color4>
|
public List<Color4> ComboColors = new List<Color4>
|
||||||
{
|
{
|
||||||
new Color4(17, 136, 170, 255),
|
new Color4(17, 136, 170, 255),
|
||||||
@ -85,9 +86,13 @@ namespace osu.Game.Beatmaps
|
|||||||
/// Constructs a new beatmap.
|
/// Constructs a new beatmap.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="original">The original beatmap to use the parameters of.</param>
|
/// <param name="original">The original beatmap to use the parameters of.</param>
|
||||||
public Beatmap(Beatmap original = null)
|
public Beatmap(Beatmap original)
|
||||||
: base(original)
|
: base(original)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Beatmap()
|
||||||
|
{
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -301,7 +301,7 @@ namespace osu.Game.Beatmaps
|
|||||||
|
|
||||||
BeatmapMetadata metadata;
|
BeatmapMetadata metadata;
|
||||||
using (var stream = new StreamReader(reader.GetStream(mapName)))
|
using (var stream = new StreamReader(reader.GetStream(mapName)))
|
||||||
metadata = Decoder.GetDecoder(stream).DecodeBeatmap(stream).Metadata;
|
metadata = Decoder.GetDecoder<Beatmap>(stream).Decode(stream).Metadata;
|
||||||
|
|
||||||
return new BeatmapSetInfo
|
return new BeatmapSetInfo
|
||||||
{
|
{
|
||||||
@ -328,8 +328,8 @@ namespace osu.Game.Beatmaps
|
|||||||
raw.CopyTo(ms);
|
raw.CopyTo(ms);
|
||||||
ms.Position = 0;
|
ms.Position = 0;
|
||||||
|
|
||||||
var decoder = Decoder.GetDecoder(sr);
|
var decoder = Decoder.GetDecoder<Beatmap>(sr);
|
||||||
Beatmap beatmap = decoder.DecodeBeatmap(sr);
|
Beatmap beatmap = decoder.Decode(sr);
|
||||||
|
|
||||||
beatmap.BeatmapInfo.Path = name;
|
beatmap.BeatmapInfo.Path = name;
|
||||||
beatmap.BeatmapInfo.Hash = ms.ComputeSHA2Hash();
|
beatmap.BeatmapInfo.Hash = ms.ComputeSHA2Hash();
|
||||||
|
@ -7,6 +7,7 @@ using System.Linq;
|
|||||||
using osu.Framework.Audio.Track;
|
using osu.Framework.Audio.Track;
|
||||||
using osu.Framework.Graphics.Textures;
|
using osu.Framework.Graphics.Textures;
|
||||||
using osu.Framework.IO.Stores;
|
using osu.Framework.IO.Stores;
|
||||||
|
using osu.Framework.Logging;
|
||||||
using osu.Game.Beatmaps.Formats;
|
using osu.Game.Beatmaps.Formats;
|
||||||
using osu.Game.Graphics.Textures;
|
using osu.Game.Graphics.Textures;
|
||||||
using osu.Game.Storyboards;
|
using osu.Game.Storyboards;
|
||||||
@ -30,10 +31,7 @@ namespace osu.Game.Beatmaps
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
using (var stream = new StreamReader(store.GetStream(getPathForFile(BeatmapInfo.Path))))
|
using (var stream = new StreamReader(store.GetStream(getPathForFile(BeatmapInfo.Path))))
|
||||||
{
|
return Decoder.GetDecoder<Beatmap>(stream).Decode(stream);
|
||||||
Decoder decoder = Decoder.GetDecoder(stream);
|
|
||||||
return decoder.DecodeBeatmap(stream);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
{
|
{
|
||||||
@ -78,23 +76,23 @@ namespace osu.Game.Beatmaps
|
|||||||
Storyboard storyboard;
|
Storyboard storyboard;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
using (var beatmap = new StreamReader(store.GetStream(getPathForFile(BeatmapInfo.Path))))
|
using (var stream = new StreamReader(store.GetStream(getPathForFile(BeatmapInfo.Path))))
|
||||||
{
|
{
|
||||||
Decoder decoder = Decoder.GetDecoder(beatmap);
|
var decoder = Decoder.GetDecoder<Storyboard>(stream);
|
||||||
|
|
||||||
// todo: support loading from both set-wide storyboard *and* beatmap specific.
|
// todo: support loading from both set-wide storyboard *and* beatmap specific.
|
||||||
|
|
||||||
if (BeatmapSetInfo?.StoryboardFile == null)
|
if (BeatmapSetInfo?.StoryboardFile == null)
|
||||||
storyboard = decoder.GetStoryboardDecoder().DecodeStoryboard(beatmap);
|
storyboard = decoder.Decode(stream);
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
using (var reader = new StreamReader(store.GetStream(getPathForFile(BeatmapSetInfo.StoryboardFile))))
|
using (var secondaryStream = new StreamReader(store.GetStream(getPathForFile(BeatmapSetInfo.StoryboardFile))))
|
||||||
storyboard = decoder.GetStoryboardDecoder().DecodeStoryboard(beatmap, reader);
|
storyboard = decoder.Decode(stream, secondaryStream);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
|
Logger.Error(e, "Storyboard failed to load");
|
||||||
storyboard = new Storyboard();
|
storyboard = new Storyboard();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4,38 +4,64 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using osu.Game.Storyboards;
|
using System.Linq;
|
||||||
|
|
||||||
namespace osu.Game.Beatmaps.Formats
|
namespace osu.Game.Beatmaps.Formats
|
||||||
{
|
{
|
||||||
|
public abstract class Decoder<TOutput> : Decoder
|
||||||
|
where TOutput : new()
|
||||||
|
{
|
||||||
|
protected virtual TOutput CreateTemplateObject() => new TOutput();
|
||||||
|
|
||||||
|
public TOutput Decode(StreamReader primaryStream, params StreamReader[] otherStreams)
|
||||||
|
{
|
||||||
|
var output = CreateTemplateObject();
|
||||||
|
foreach (StreamReader stream in new[] { primaryStream }.Concat(otherStreams))
|
||||||
|
ParseStreamInto(stream, output);
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract void ParseStreamInto(StreamReader stream, TOutput beatmap);
|
||||||
|
}
|
||||||
|
|
||||||
public abstract class Decoder
|
public abstract class Decoder
|
||||||
{
|
{
|
||||||
private static readonly Dictionary<string, Func<string, Decoder>> decoders = new Dictionary<string, Func<string, Decoder>>();
|
private static readonly Dictionary<Type, Dictionary<string, Func<string, Decoder>>> decoders = new Dictionary<Type, Dictionary<string, Func<string, Decoder>>>();
|
||||||
|
|
||||||
static Decoder()
|
static Decoder()
|
||||||
{
|
{
|
||||||
LegacyDecoder.Register();
|
LegacyBeatmapDecoder.Register();
|
||||||
JsonBeatmapDecoder.Register();
|
JsonBeatmapDecoder.Register();
|
||||||
|
LegacyStoryboardDecoder.Register();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Retrieves a <see cref="Decoder"/> to parse a <see cref="Beatmap"/>.
|
/// Retrieves a <see cref="Decoder"/> to parse a <see cref="Beatmap"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="stream">A stream pointing to the <see cref="Beatmap"/>.</param>
|
/// <param name="stream">A stream pointing to the <see cref="Beatmap"/>.</param>
|
||||||
public static Decoder GetDecoder(StreamReader stream)
|
public static Decoder<T> GetDecoder<T>(StreamReader stream)
|
||||||
|
where T : new()
|
||||||
{
|
{
|
||||||
if (stream == null)
|
if (stream == null)
|
||||||
throw new ArgumentNullException(nameof(stream));
|
throw new ArgumentNullException(nameof(stream));
|
||||||
|
|
||||||
|
if (!decoders.TryGetValue(typeof(T), out var typedDecoders))
|
||||||
|
throw new IOException(@"Unknown decoder type");
|
||||||
|
|
||||||
string line;
|
string line;
|
||||||
do
|
do
|
||||||
{ line = stream.ReadLine()?.Trim(); }
|
{
|
||||||
while (line != null && line.Length == 0);
|
line = stream.ReadLine()?.Trim();
|
||||||
|
} while (line != null && line.Length == 0);
|
||||||
|
|
||||||
if (line == null || !decoders.ContainsKey(line))
|
if (line == null)
|
||||||
throw new IOException(@"Unknown file format");
|
throw new IOException(@"Unknown file format");
|
||||||
|
|
||||||
return decoders[line](line);
|
var decoder = typedDecoders.Select(d => line.StartsWith(d.Key) ? d.Value : null).FirstOrDefault();
|
||||||
|
if (decoder == null)
|
||||||
|
throw new IOException(@"Unknown file format");
|
||||||
|
|
||||||
|
return (Decoder<T>)decoder.Invoke(line);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -43,41 +69,12 @@ namespace osu.Game.Beatmaps.Formats
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="magic">A string in the file which triggers this decoder to be used.</param>
|
/// <param name="magic">A string in the file which triggers this decoder to be used.</param>
|
||||||
/// <param name="constructor">A function which constructs the <see cref="Decoder"/> given <paramref name="magic"/>.</param>
|
/// <param name="constructor">A function which constructs the <see cref="Decoder"/> given <paramref name="magic"/>.</param>
|
||||||
protected static void AddDecoder(string magic, Func<string, Decoder> constructor)
|
protected static void AddDecoder<T>(string magic, Func<string, Decoder> constructor)
|
||||||
{
|
{
|
||||||
decoders[magic] = constructor;
|
if (!decoders.TryGetValue(typeof(T), out var typedDecoders))
|
||||||
|
decoders.Add(typeof(T), typedDecoders = new Dictionary<string, Func<string, Decoder>>());
|
||||||
|
|
||||||
|
typedDecoders[magic] = constructor;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Retrieves a <see cref="Decoder"/> to parse a <see cref="Storyboard"/>
|
|
||||||
/// </summary>
|
|
||||||
public abstract Decoder GetStoryboardDecoder();
|
|
||||||
|
|
||||||
public virtual Beatmap DecodeBeatmap(StreamReader stream)
|
|
||||||
{
|
|
||||||
var beatmap = new Beatmap
|
|
||||||
{
|
|
||||||
BeatmapInfo = new BeatmapInfo
|
|
||||||
{
|
|
||||||
Metadata = new BeatmapMetadata(),
|
|
||||||
BaseDifficulty = new BeatmapDifficulty(),
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
ParseBeatmap(stream, beatmap);
|
|
||||||
return beatmap;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected abstract void ParseBeatmap(StreamReader stream, Beatmap beatmap);
|
|
||||||
|
|
||||||
public virtual Storyboard DecodeStoryboard(params StreamReader[] streams)
|
|
||||||
{
|
|
||||||
var storyboard = new Storyboard();
|
|
||||||
foreach (StreamReader stream in streams)
|
|
||||||
ParseStoryboard(stream, storyboard);
|
|
||||||
return storyboard;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected abstract void ParseStoryboard(StreamReader stream, Storyboard storyboard);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,20 +3,17 @@
|
|||||||
|
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using osu.Game.IO.Serialization;
|
using osu.Game.IO.Serialization;
|
||||||
using osu.Game.Storyboards;
|
|
||||||
|
|
||||||
namespace osu.Game.Beatmaps.Formats
|
namespace osu.Game.Beatmaps.Formats
|
||||||
{
|
{
|
||||||
public class JsonBeatmapDecoder : Decoder
|
public class JsonBeatmapDecoder : Decoder<Beatmap>
|
||||||
{
|
{
|
||||||
public static void Register()
|
public static void Register()
|
||||||
{
|
{
|
||||||
AddDecoder("{", m => new JsonBeatmapDecoder());
|
AddDecoder<Beatmap>("{", m => new JsonBeatmapDecoder());
|
||||||
}
|
}
|
||||||
|
|
||||||
public override Decoder GetStoryboardDecoder() => this;
|
protected override void ParseStreamInto(StreamReader stream, Beatmap beatmap)
|
||||||
|
|
||||||
protected override void ParseBeatmap(StreamReader stream, Beatmap beatmap)
|
|
||||||
{
|
{
|
||||||
stream.BaseStream.Position = 0;
|
stream.BaseStream.Position = 0;
|
||||||
stream.DiscardBufferedData();
|
stream.DiscardBufferedData();
|
||||||
@ -26,10 +23,5 @@ namespace osu.Game.Beatmaps.Formats
|
|||||||
foreach (var hitObject in beatmap.HitObjects)
|
foreach (var hitObject in beatmap.HitObjects)
|
||||||
hitObject.ApplyDefaults(beatmap.ControlPointInfo, beatmap.BeatmapInfo.BaseDifficulty);
|
hitObject.ApplyDefaults(beatmap.ControlPointInfo, beatmap.BeatmapInfo.BaseDifficulty);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void ParseStoryboard(StreamReader stream, Storyboard storyboard)
|
|
||||||
{
|
|
||||||
// throw new System.NotImplementedException();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
using OpenTK.Graphics;
|
using OpenTK.Graphics;
|
||||||
using osu.Game.Beatmaps.Timing;
|
using osu.Game.Beatmaps.Timing;
|
||||||
using osu.Game.Rulesets.Objects.Legacy;
|
using osu.Game.Rulesets.Objects.Legacy;
|
||||||
@ -12,8 +13,10 @@ using osu.Framework;
|
|||||||
|
|
||||||
namespace osu.Game.Beatmaps.Formats
|
namespace osu.Game.Beatmaps.Formats
|
||||||
{
|
{
|
||||||
public class LegacyBeatmapDecoder : LegacyDecoder
|
public class LegacyBeatmapDecoder : LegacyDecoder<Beatmap>
|
||||||
{
|
{
|
||||||
|
public const int LATEST_VERSION = 14;
|
||||||
|
|
||||||
private Beatmap beatmap;
|
private Beatmap beatmap;
|
||||||
|
|
||||||
private bool hasCustomColours;
|
private bool hasCustomColours;
|
||||||
@ -22,6 +25,11 @@ namespace osu.Game.Beatmaps.Formats
|
|||||||
private LegacySampleBank defaultSampleBank;
|
private LegacySampleBank defaultSampleBank;
|
||||||
private int defaultSampleVolume = 100;
|
private int defaultSampleVolume = 100;
|
||||||
|
|
||||||
|
public static void Register()
|
||||||
|
{
|
||||||
|
AddDecoder<Beatmap>(@"osu file format v", m => new LegacyBeatmapDecoder(int.Parse(m.Split('v').Last())));
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// lazer's audio timings in general doesn't match stable. this is the result of user testing, albeit limited.
|
/// lazer's audio timings in general doesn't match stable. this is the result of user testing, albeit limited.
|
||||||
/// This only seems to be required on windows. We need to eventually figure out why, with a bit of luck.
|
/// This only seems to be required on windows. We need to eventually figure out why, with a bit of luck.
|
||||||
@ -35,29 +43,16 @@ namespace osu.Game.Beatmaps.Formats
|
|||||||
|
|
||||||
private readonly int offset = UniversalOffset;
|
private readonly int offset = UniversalOffset;
|
||||||
|
|
||||||
public LegacyBeatmapDecoder()
|
public LegacyBeatmapDecoder(int version = LATEST_VERSION) : base(version)
|
||||||
{
|
{
|
||||||
}
|
|
||||||
|
|
||||||
public LegacyBeatmapDecoder(string header)
|
|
||||||
{
|
|
||||||
BeatmapVersion = int.Parse(header.Substring(17));
|
|
||||||
|
|
||||||
// BeatmapVersion 4 and lower had an incorrect offset (stable has this set as 24ms off)
|
// BeatmapVersion 4 and lower had an incorrect offset (stable has this set as 24ms off)
|
||||||
offset += BeatmapVersion < 5 ? 24 : 0;
|
offset += FormatVersion < 5 ? 24 : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void ParseBeatmap(StreamReader stream, Beatmap beatmap)
|
protected override void ParseStreamInto(StreamReader stream, Beatmap beatmap)
|
||||||
{
|
{
|
||||||
if (stream == null)
|
|
||||||
throw new ArgumentNullException(nameof(stream));
|
|
||||||
if (beatmap == null)
|
|
||||||
throw new ArgumentNullException(nameof(beatmap));
|
|
||||||
|
|
||||||
this.beatmap = beatmap;
|
this.beatmap = beatmap;
|
||||||
this.beatmap.BeatmapInfo.BeatmapVersion = BeatmapVersion;
|
base.ParseStreamInto(stream, beatmap);
|
||||||
|
|
||||||
ParseContent(stream);
|
|
||||||
|
|
||||||
// objects may be out of order *only* if a user has manually edited an .osu file.
|
// objects may be out of order *only* if a user has manually edited an .osu file.
|
||||||
// unfortunately there are ranked maps in this state (example: https://osu.ppy.sh/s/594828).
|
// unfortunately there are ranked maps in this state (example: https://osu.ppy.sh/s/594828).
|
||||||
@ -67,14 +62,9 @@ namespace osu.Game.Beatmaps.Formats
|
|||||||
hitObject.ApplyDefaults(this.beatmap.ControlPointInfo, this.beatmap.BeatmapInfo.BaseDifficulty);
|
hitObject.ApplyDefaults(this.beatmap.ControlPointInfo, this.beatmap.BeatmapInfo.BaseDifficulty);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override bool ShouldSkipLine(string line)
|
protected override bool ShouldSkipLine(string line) => base.ShouldSkipLine(line) || line.StartsWith(" ") || line.StartsWith("_");
|
||||||
{
|
|
||||||
if (base.ShouldSkipLine(line) || line.StartsWith(" ") || line.StartsWith("_"))
|
|
||||||
return true;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void ProcessSection(Section section, string line)
|
protected override void ParseLine(Beatmap beatmap, Section section, string line)
|
||||||
{
|
{
|
||||||
switch (section)
|
switch (section)
|
||||||
{
|
{
|
||||||
|
@ -4,47 +4,20 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using osu.Game.Beatmaps.Legacy;
|
|
||||||
using osu.Game.Storyboards;
|
|
||||||
|
|
||||||
namespace osu.Game.Beatmaps.Formats
|
namespace osu.Game.Beatmaps.Formats
|
||||||
{
|
{
|
||||||
public abstract class LegacyDecoder : Decoder
|
public abstract class LegacyDecoder<T> : Decoder<T>
|
||||||
|
where T : new()
|
||||||
{
|
{
|
||||||
public static void Register()
|
protected readonly int FormatVersion;
|
||||||
|
|
||||||
|
protected LegacyDecoder(int version)
|
||||||
{
|
{
|
||||||
AddDecoder(@"osu file format v14", m => new LegacyBeatmapDecoder(m));
|
FormatVersion = version;
|
||||||
AddDecoder(@"osu file format v13", m => new LegacyBeatmapDecoder(m));
|
|
||||||
AddDecoder(@"osu file format v12", m => new LegacyBeatmapDecoder(m));
|
|
||||||
AddDecoder(@"osu file format v11", m => new LegacyBeatmapDecoder(m));
|
|
||||||
AddDecoder(@"osu file format v10", m => new LegacyBeatmapDecoder(m));
|
|
||||||
AddDecoder(@"osu file format v9", m => new LegacyBeatmapDecoder(m));
|
|
||||||
AddDecoder(@"osu file format v8", m => new LegacyBeatmapDecoder(m));
|
|
||||||
AddDecoder(@"osu file format v7", m => new LegacyBeatmapDecoder(m));
|
|
||||||
AddDecoder(@"osu file format v6", m => new LegacyBeatmapDecoder(m));
|
|
||||||
AddDecoder(@"osu file format v5", m => new LegacyBeatmapDecoder(m));
|
|
||||||
AddDecoder(@"osu file format v4", m => new LegacyBeatmapDecoder(m));
|
|
||||||
AddDecoder(@"osu file format v3", m => new LegacyBeatmapDecoder(m));
|
|
||||||
// TODO: differences between versions
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected int BeatmapVersion;
|
protected override void ParseStreamInto(StreamReader stream, T beatmap)
|
||||||
|
|
||||||
public override Decoder GetStoryboardDecoder() => new LegacyStoryboardDecoder(BeatmapVersion);
|
|
||||||
|
|
||||||
public override Beatmap DecodeBeatmap(StreamReader stream) => new LegacyBeatmap(base.DecodeBeatmap(stream));
|
|
||||||
|
|
||||||
protected override void ParseBeatmap(StreamReader stream, Beatmap beatmap)
|
|
||||||
{
|
|
||||||
throw new NotImplementedException();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void ParseStoryboard(StreamReader stream, Storyboard storyboard)
|
|
||||||
{
|
|
||||||
throw new NotImplementedException();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void ParseContent(StreamReader stream)
|
|
||||||
{
|
{
|
||||||
Section section = Section.None;
|
Section section = Section.None;
|
||||||
|
|
||||||
@ -54,13 +27,6 @@ namespace osu.Game.Beatmaps.Formats
|
|||||||
if (ShouldSkipLine(line))
|
if (ShouldSkipLine(line))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
// It's already set in ParseBeatmap... why do it again?
|
|
||||||
//if (line.StartsWith(@"osu file format v"))
|
|
||||||
//{
|
|
||||||
// Beatmap.BeatmapInfo.BeatmapVersion = int.Parse(line.Substring(17));
|
|
||||||
// continue;
|
|
||||||
//}
|
|
||||||
|
|
||||||
if (line.StartsWith(@"[") && line.EndsWith(@"]"))
|
if (line.StartsWith(@"[") && line.EndsWith(@"]"))
|
||||||
{
|
{
|
||||||
if (!Enum.TryParse(line.Substring(1, line.Length - 2), out section))
|
if (!Enum.TryParse(line.Substring(1, line.Length - 2), out section))
|
||||||
@ -68,18 +34,13 @@ namespace osu.Game.Beatmaps.Formats
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
ProcessSection(section, line);
|
ParseLine(beatmap, section, line);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected virtual bool ShouldSkipLine(string line)
|
protected virtual bool ShouldSkipLine(string line) => string.IsNullOrWhiteSpace(line) || line.StartsWith("//");
|
||||||
{
|
|
||||||
if (string.IsNullOrWhiteSpace(line) || line.StartsWith("//"))
|
|
||||||
return true;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected abstract void ProcessSection(Section section, string line);
|
protected abstract void ParseLine(T output, Section section, string line);
|
||||||
|
|
||||||
protected KeyValuePair<string, string> SplitKeyVal(string line, char separator)
|
protected KeyValuePair<string, string> SplitKeyVal(string line, char separator)
|
||||||
{
|
{
|
||||||
|
@ -13,37 +13,34 @@ using osu.Game.Storyboards;
|
|||||||
|
|
||||||
namespace osu.Game.Beatmaps.Formats
|
namespace osu.Game.Beatmaps.Formats
|
||||||
{
|
{
|
||||||
public class LegacyStoryboardDecoder : LegacyDecoder
|
public class LegacyStoryboardDecoder : LegacyDecoder<Storyboard>
|
||||||
{
|
{
|
||||||
private Storyboard storyboard;
|
|
||||||
|
|
||||||
private StoryboardSprite storyboardSprite;
|
private StoryboardSprite storyboardSprite;
|
||||||
private CommandTimelineGroup timelineGroup;
|
private CommandTimelineGroup timelineGroup;
|
||||||
|
|
||||||
|
private Storyboard storyboard;
|
||||||
|
|
||||||
private readonly Dictionary<string, string> variables = new Dictionary<string, string>();
|
private readonly Dictionary<string, string> variables = new Dictionary<string, string>();
|
||||||
|
|
||||||
public LegacyStoryboardDecoder()
|
public LegacyStoryboardDecoder()
|
||||||
|
: base(0)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
public LegacyStoryboardDecoder(int beatmapVersion)
|
public static void Register()
|
||||||
{
|
{
|
||||||
BeatmapVersion = beatmapVersion;
|
// note that this isn't completely correct
|
||||||
|
AddDecoder<Storyboard>(@"osu file format v", m => new LegacyStoryboardDecoder());
|
||||||
|
AddDecoder<Storyboard>(@"[Events]", m => new LegacyStoryboardDecoder());
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void ParseStoryboard(StreamReader stream, Storyboard storyboard)
|
protected override void ParseStreamInto(StreamReader stream, Storyboard storyboard)
|
||||||
{
|
{
|
||||||
if (stream == null)
|
|
||||||
throw new ArgumentNullException(nameof(stream));
|
|
||||||
if (storyboard == null)
|
|
||||||
throw new ArgumentNullException(nameof(storyboard));
|
|
||||||
|
|
||||||
this.storyboard = storyboard;
|
this.storyboard = storyboard;
|
||||||
|
base.ParseStreamInto(stream, storyboard);
|
||||||
ParseContent(stream);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void ProcessSection(Section section, string line)
|
protected override void ParseLine(Storyboard storyboard, Section section, string line)
|
||||||
{
|
{
|
||||||
switch (section)
|
switch (section)
|
||||||
{
|
{
|
||||||
@ -269,6 +266,7 @@ namespace osu.Game.Beatmaps.Formats
|
|||||||
case LegacyOrigins.BottomRight:
|
case LegacyOrigins.BottomRight:
|
||||||
return Anchor.BottomRight;
|
return Anchor.BottomRight;
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new InvalidDataException($@"Unknown origin: {value}");
|
throw new InvalidDataException($@"Unknown origin: {value}");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -188,8 +188,8 @@ namespace osu.Game.Rulesets.Objects.Legacy
|
|||||||
|
|
||||||
string[] split = str.Split(':');
|
string[] split = str.Split(':');
|
||||||
|
|
||||||
var bank = (LegacyDecoder.LegacySampleBank)Convert.ToInt32(split[0]);
|
var bank = (LegacyBeatmapDecoder.LegacySampleBank)Convert.ToInt32(split[0]);
|
||||||
var addbank = (LegacyDecoder.LegacySampleBank)Convert.ToInt32(split[1]);
|
var addbank = (LegacyBeatmapDecoder.LegacySampleBank)Convert.ToInt32(split[1]);
|
||||||
|
|
||||||
// Let's not implement this for now, because this doesn't fit nicely into the bank structure
|
// Let's not implement this for now, because this doesn't fit nicely into the bank structure
|
||||||
//string sampleFile = split2.Length > 4 ? split2[4] : string.Empty;
|
//string sampleFile = split2.Length > 4 ? split2[4] : string.Empty;
|
||||||
|
@ -112,9 +112,9 @@ namespace osu.Game.Tests.Beatmaps
|
|||||||
using (var resStream = openResource($"{resource_namespace}.{name}.osu"))
|
using (var resStream = openResource($"{resource_namespace}.{name}.osu"))
|
||||||
using (var stream = new StreamReader(resStream))
|
using (var stream = new StreamReader(resStream))
|
||||||
{
|
{
|
||||||
var decoder = Decoder.GetDecoder(stream);
|
var decoder = Decoder.GetDecoder<Beatmap>(stream);
|
||||||
((LegacyBeatmapDecoder)decoder).ApplyOffsets = false;
|
((LegacyBeatmapDecoder)decoder).ApplyOffsets = false;
|
||||||
return decoder.DecodeBeatmap(stream);
|
return decoder.Decode(stream);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -65,7 +65,7 @@ namespace osu.Game.Tests.Visual
|
|||||||
|
|
||||||
using (var stream = new MemoryStream(Encoding.UTF8.GetBytes(test_beatmap_data)))
|
using (var stream = new MemoryStream(Encoding.UTF8.GetBytes(test_beatmap_data)))
|
||||||
using (var reader = new StreamReader(stream))
|
using (var reader = new StreamReader(stream))
|
||||||
beatmap = Game.Beatmaps.Formats.Decoder.GetDecoder(reader).DecodeBeatmap(reader);
|
beatmap = Game.Beatmaps.Formats.Decoder.GetDecoder<Beatmap>(reader).Decode(reader);
|
||||||
|
|
||||||
return beatmap;
|
return beatmap;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user