Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rework decoders to be more flexible #2191

Merged
merged 4 commits into from
Mar 12, 2018
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 8 additions & 8 deletions osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ public void TestDecodeBeatmapGeneral()
using (var resStream = Resource.OpenResource("Soleily - Renatus (Gamu) [Insane].osu"))
using (var stream = new StreamReader(resStream))
{
var beatmap = decoder.DecodeBeatmap(stream);
var beatmap = decoder.Decode(stream);
var beatmapInfo = beatmap.BeatmapInfo;
var metadata = beatmap.Metadata;

Expand All @@ -47,7 +47,7 @@ public void TestDecodeBeatmapEditor()
using (var resStream = Resource.OpenResource("Soleily - Renatus (Gamu) [Insane].osu"))
using (var stream = new StreamReader(resStream))
{
var beatmapInfo = decoder.DecodeBeatmap(stream).BeatmapInfo;
var beatmapInfo = decoder.Decode(stream).BeatmapInfo;

int[] expectedBookmarks =
{
Expand All @@ -72,7 +72,7 @@ public void TestDecodeBeatmapMetadata()
using (var resStream = Resource.OpenResource("Soleily - Renatus (Gamu) [Insane].osu"))
using (var stream = new StreamReader(resStream))
{
var beatmap = decoder.DecodeBeatmap(stream);
var beatmap = decoder.Decode(stream);
var beatmapInfo = beatmap.BeatmapInfo;
var metadata = beatmap.Metadata;

Expand All @@ -96,7 +96,7 @@ public void TestDecodeBeatmapDifficulty()
using (var resStream = Resource.OpenResource("Soleily - Renatus (Gamu) [Insane].osu"))
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(4, difficulty.CircleSize);
Expand All @@ -114,7 +114,7 @@ public void TestDecodeBeatmapEvents()
using (var resStream = Resource.OpenResource("Soleily - Renatus (Gamu) [Insane].osu"))
using (var stream = new StreamReader(resStream))
{
var beatmap = decoder.DecodeBeatmap(stream);
var beatmap = decoder.Decode(stream);
var metadata = beatmap.Metadata;
var breakPoint = beatmap.Breaks[0];

Expand All @@ -132,7 +132,7 @@ public void TestDecodeBeatmapTimingPoints()
using (var resStream = Resource.OpenResource("Soleily - Renatus (Gamu) [Insane].osu"))
using (var stream = new StreamReader(resStream))
{
var beatmap = decoder.DecodeBeatmap(stream);
var beatmap = decoder.Decode(stream);
var controlPoints = beatmap.ControlPointInfo;

Assert.AreEqual(4, controlPoints.TimingPoints.Count);
Expand Down Expand Up @@ -167,7 +167,7 @@ public void TestDecodeBeatmapColors()
using (var resStream = Resource.OpenResource("Soleily - Renatus (Gamu) [Insane].osu"))
using (var stream = new StreamReader(resStream))
{
var comboColors = decoder.DecodeBeatmap(stream).ComboColors;
var comboColors = decoder.Decode(stream).ComboColors;

Color4[] expectedColors =
{
Expand All @@ -191,7 +191,7 @@ public void TestDecodeBeatmapHitObjects()
using (var resStream = Resource.OpenResource("Soleily - Renatus (Gamu) [Insane].osu"))
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 positionData = hitObjects[0] as IHasPosition;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,11 @@ public class LegacyStoryboardDecoderTest
[Test]
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 stream = new StreamReader(resStream))
{
var storyboard = decoder.GetStoryboardDecoder().DecodeStoryboard(stream);
var storyboard = decoder.Decode(stream);

Assert.IsTrue(storyboard.HasDrawable);
Assert.AreEqual(4, storyboard.Layers.Count());
Expand Down
4 changes: 2 additions & 2 deletions osu.Game.Tests/Beatmaps/Formats/OsuJsonDecoderTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,7 @@ public void TestParity(string beatmap)
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 sw = new StreamWriter(ms))
using (var sr2 = new StreamReader(ms))
Expand All @@ -168,7 +168,7 @@ public void TestParity(string beatmap)
sw.Flush();

ms.Position = 0;
return (legacyDecoded, new JsonBeatmapDecoder().DecodeBeatmap(sr2));
return (legacyDecoded, new JsonBeatmapDecoder().Decode(sr2));
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion osu.Game.Tests/Beatmaps/IO/OszArchiveReaderTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ public void TestReadMetadata()

BeatmapMetadata meta;
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("Soleily", meta.Artist);
Expand Down
7 changes: 6 additions & 1 deletion osu.Game/Beatmaps/Beatmap.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ public class Beatmap<T> : IJsonSerializable
public BeatmapInfo BeatmapInfo = new BeatmapInfo();
public ControlPointInfo ControlPointInfo = new ControlPointInfo();
public List<BreakPeriod> Breaks = new List<BreakPeriod>();

public List<Color4> ComboColors = new List<Color4>
{
new Color4(17, 136, 170, 255),
Expand Down Expand Up @@ -85,9 +86,13 @@ public class Beatmap : Beatmap<HitObject>
/// Constructs a new beatmap.
/// </summary>
/// <param name="original">The original beatmap to use the parameters of.</param>
public Beatmap(Beatmap original = null)
public Beatmap(Beatmap original)
: base(original)
{
}

public Beatmap()
{
}
}
}
6 changes: 3 additions & 3 deletions osu.Game/Beatmaps/BeatmapManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -301,7 +301,7 @@ protected override BeatmapSetInfo CreateModel(ArchiveReader reader)

BeatmapMetadata metadata;
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
{
Expand All @@ -328,8 +328,8 @@ private List<BeatmapInfo> createBeatmapDifficulties(ArchiveReader reader)
raw.CopyTo(ms);
ms.Position = 0;

var decoder = Decoder.GetDecoder(sr);
Beatmap beatmap = decoder.DecodeBeatmap(sr);
var decoder = Decoder.GetDecoder<Beatmap>(sr);
Beatmap beatmap = decoder.Decode(sr);

beatmap.BeatmapInfo.Path = name;
beatmap.BeatmapInfo.Hash = ms.ComputeSHA2Hash();
Expand Down
20 changes: 9 additions & 11 deletions osu.Game/Beatmaps/BeatmapManager_WorkingBeatmap.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
using osu.Framework.Audio.Track;
using osu.Framework.Graphics.Textures;
using osu.Framework.IO.Stores;
using osu.Framework.Logging;
using osu.Game.Beatmaps.Formats;
using osu.Game.Graphics.Textures;
using osu.Game.Storyboards;
Expand All @@ -30,10 +31,7 @@ protected override Beatmap GetBeatmap()
try
{
using (var stream = new StreamReader(store.GetStream(getPathForFile(BeatmapInfo.Path))))
{
Decoder decoder = Decoder.GetDecoder(stream);
return decoder.DecodeBeatmap(stream);
}
return Decoder.GetDecoder<Beatmap>(stream).Decode(stream);
}
catch
{
Expand Down Expand Up @@ -78,23 +76,23 @@ protected override Storyboard GetStoryboard()
Storyboard storyboard;
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.

if (BeatmapSetInfo?.StoryboardFile == null)
storyboard = decoder.GetStoryboardDecoder().DecodeStoryboard(beatmap);
storyboard = decoder.Decode(stream);
else
{
using (var reader = new StreamReader(store.GetStream(getPathForFile(BeatmapSetInfo.StoryboardFile))))
storyboard = decoder.GetStoryboardDecoder().DecodeStoryboard(beatmap, reader);
using (var secondaryStream = new StreamReader(store.GetStream(getPathForFile(BeatmapSetInfo.StoryboardFile))))
storyboard = decoder.Decode(stream, secondaryStream);
}
}
}
catch
catch (Exception e)
{
Logger.Error(e, "Storyboard failed to load");
storyboard = new Storyboard();
}

Expand Down
79 changes: 38 additions & 41 deletions osu.Game/Beatmaps/Formats/Decoder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,80 +4,77 @@
using System;
using System.Collections.Generic;
using System.IO;
using osu.Game.Storyboards;
using System.Linq;

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
{
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()
{
LegacyDecoder.Register();
LegacyBeatmapDecoder.Register();
JsonBeatmapDecoder.Register();
LegacyStoryboardDecoder.Register();
}

/// <summary>
/// Retrieves a <see cref="Decoder"/> to parse a <see cref="Beatmap"/>.
/// </summary>
/// <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)
throw new ArgumentNullException(nameof(stream));

if (!decoders.TryGetValue(typeof(T), out var typedDecoders))
throw new IOException(@"Unknown decoder type");

string line;
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");

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>
/// Registers an instantiation function for a <see cref="Decoder"/>.
/// </summary>
/// <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>
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>>());

/// <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;
typedDecoders[magic] = constructor;
}

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);
}
}
14 changes: 3 additions & 11 deletions osu.Game/Beatmaps/Formats/JsonBeatmapDecoder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,17 @@

using System.IO;
using osu.Game.IO.Serialization;
using osu.Game.Storyboards;

namespace osu.Game.Beatmaps.Formats
{
public class JsonBeatmapDecoder : Decoder
public class JsonBeatmapDecoder : Decoder<Beatmap>
{
public static void Register()
{
AddDecoder("{", m => new JsonBeatmapDecoder());
AddDecoder<Beatmap>("{", m => new JsonBeatmapDecoder());
}

public override Decoder GetStoryboardDecoder() => this;

protected override void ParseBeatmap(StreamReader stream, Beatmap beatmap)
protected override void ParseStreamInto(StreamReader stream, Beatmap beatmap)
{
stream.BaseStream.Position = 0;
stream.DiscardBufferedData();
Expand All @@ -26,10 +23,5 @@ protected override void ParseBeatmap(StreamReader stream, Beatmap beatmap)
foreach (var hitObject in beatmap.HitObjects)
hitObject.ApplyDefaults(beatmap.ControlPointInfo, beatmap.BeatmapInfo.BaseDifficulty);
}

protected override void ParseStoryboard(StreamReader stream, Storyboard storyboard)
{
// throw new System.NotImplementedException();
}
}
}
Loading