2019-01-24 08:43:03 +00:00
|
|
|
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
|
|
|
|
// See the LICENCE file in the repository root for full licence text.
|
2018-04-13 09:19:50 +00:00
|
|
|
|
|
|
|
|
|
using System;
|
|
|
|
|
using System.Collections.Generic;
|
2020-01-23 15:23:53 +00:00
|
|
|
|
using osu.Framework.Extensions;
|
2018-04-13 09:19:50 +00:00
|
|
|
|
using osu.Framework.Logging;
|
2018-06-28 09:20:43 +00:00
|
|
|
|
using osu.Game.Audio;
|
|
|
|
|
using osu.Game.Beatmaps.ControlPoints;
|
2019-09-09 22:43:30 +00:00
|
|
|
|
using osu.Game.IO;
|
2018-11-20 07:51:59 +00:00
|
|
|
|
using osuTK.Graphics;
|
2018-04-13 09:19:50 +00:00
|
|
|
|
|
|
|
|
|
namespace osu.Game.Beatmaps.Formats
|
|
|
|
|
{
|
|
|
|
|
public abstract class LegacyDecoder<T> : Decoder<T>
|
|
|
|
|
where T : new()
|
|
|
|
|
{
|
|
|
|
|
protected readonly int FormatVersion;
|
|
|
|
|
|
|
|
|
|
protected LegacyDecoder(int version)
|
|
|
|
|
{
|
|
|
|
|
FormatVersion = version;
|
|
|
|
|
}
|
|
|
|
|
|
2019-09-09 22:43:30 +00:00
|
|
|
|
protected override void ParseStreamInto(LineBufferedReader stream, T output)
|
2018-04-13 09:19:50 +00:00
|
|
|
|
{
|
|
|
|
|
Section section = Section.None;
|
|
|
|
|
|
|
|
|
|
string line;
|
2019-04-01 03:16:05 +00:00
|
|
|
|
|
2018-04-13 09:19:50 +00:00
|
|
|
|
while ((line = stream.ReadLine()) != null)
|
|
|
|
|
{
|
|
|
|
|
if (ShouldSkipLine(line))
|
|
|
|
|
continue;
|
|
|
|
|
|
2020-02-08 17:05:27 +00:00
|
|
|
|
if (line.StartsWith('[') && line.EndsWith(']'))
|
2018-04-13 09:19:50 +00:00
|
|
|
|
{
|
2019-12-14 12:54:22 +00:00
|
|
|
|
if (!Enum.TryParse(line[1..^1], out section))
|
2018-04-13 09:19:50 +00:00
|
|
|
|
{
|
2019-08-11 16:42:05 +00:00
|
|
|
|
Logger.Log($"Unknown section \"{line}\" in \"{output}\"");
|
2018-04-13 09:19:50 +00:00
|
|
|
|
section = Section.None;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
2019-08-07 10:33:54 +00:00
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
ParseLine(output, section, line);
|
|
|
|
|
}
|
|
|
|
|
catch (Exception e)
|
|
|
|
|
{
|
2019-08-11 16:42:05 +00:00
|
|
|
|
Logger.Log($"Failed to process line \"{line}\" into \"{output}\": {e.Message}", LoggingTarget.Runtime, LogLevel.Important);
|
2019-08-07 10:33:54 +00:00
|
|
|
|
}
|
2018-04-13 09:19:50 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-05-14 07:16:55 +00:00
|
|
|
|
protected virtual bool ShouldSkipLine(string line) => string.IsNullOrWhiteSpace(line) || line.AsSpan().TrimStart().StartsWith("//".AsSpan(), StringComparison.Ordinal);
|
2018-04-13 09:19:50 +00:00
|
|
|
|
|
|
|
|
|
protected virtual void ParseLine(T output, Section section, string line)
|
|
|
|
|
{
|
2018-07-15 23:04:41 +00:00
|
|
|
|
line = StripComments(line);
|
|
|
|
|
|
2018-04-13 09:19:50 +00:00
|
|
|
|
switch (section)
|
|
|
|
|
{
|
|
|
|
|
case Section.Colours:
|
|
|
|
|
handleColours(output, line);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
2018-07-16 14:35:55 +00:00
|
|
|
|
|
|
|
|
|
protected string StripComments(string line)
|
2018-07-15 23:04:41 +00:00
|
|
|
|
{
|
2018-10-17 01:53:21 +00:00
|
|
|
|
var index = line.AsSpan().IndexOf("//".AsSpan());
|
2018-07-15 23:04:41 +00:00
|
|
|
|
if (index > 0)
|
|
|
|
|
return line.Substring(0, index);
|
2019-02-28 04:31:40 +00:00
|
|
|
|
|
2018-07-15 23:04:41 +00:00
|
|
|
|
return line;
|
|
|
|
|
}
|
2018-04-13 09:19:50 +00:00
|
|
|
|
|
|
|
|
|
private void handleColours(T output, string line)
|
|
|
|
|
{
|
|
|
|
|
var pair = SplitKeyVal(line);
|
|
|
|
|
|
|
|
|
|
bool isCombo = pair.Key.StartsWith(@"Combo");
|
|
|
|
|
|
2018-07-15 23:04:41 +00:00
|
|
|
|
string[] split = pair.Value.Split(',');
|
2018-04-13 09:19:50 +00:00
|
|
|
|
|
2018-10-05 02:19:01 +00:00
|
|
|
|
if (split.Length != 3 && split.Length != 4)
|
|
|
|
|
throw new InvalidOperationException($@"Color specified in incorrect format (should be R,G,B or R,G,B,A): {pair.Value}");
|
2018-04-13 09:19:50 +00:00
|
|
|
|
|
2018-10-05 02:55:59 +00:00
|
|
|
|
Color4 colour;
|
2018-10-05 02:19:01 +00:00
|
|
|
|
|
2018-10-05 02:55:59 +00:00
|
|
|
|
try
|
|
|
|
|
{
|
2018-10-05 03:06:24 +00:00
|
|
|
|
colour = new Color4(byte.Parse(split[0]), byte.Parse(split[1]), byte.Parse(split[2]), split.Length == 4 ? byte.Parse(split[3]) : (byte)255);
|
2018-10-05 02:55:59 +00:00
|
|
|
|
}
|
2019-04-25 08:36:17 +00:00
|
|
|
|
catch
|
2018-10-05 02:19:01 +00:00
|
|
|
|
{
|
2018-04-13 09:19:50 +00:00
|
|
|
|
throw new InvalidOperationException(@"Color must be specified with 8-bit integer components");
|
2018-10-05 02:19:01 +00:00
|
|
|
|
}
|
2018-04-13 09:19:50 +00:00
|
|
|
|
|
|
|
|
|
if (isCombo)
|
|
|
|
|
{
|
|
|
|
|
if (!(output is IHasComboColours tHasComboColours)) return;
|
|
|
|
|
|
2019-11-07 12:54:30 +00:00
|
|
|
|
tHasComboColours.AddComboColours(colour);
|
2018-04-13 09:19:50 +00:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
if (!(output is IHasCustomColours tHasCustomColours)) return;
|
2019-02-28 04:31:40 +00:00
|
|
|
|
|
2018-04-13 09:19:50 +00:00
|
|
|
|
tHasCustomColours.CustomColours[pair.Key] = colour;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected KeyValuePair<string, string> SplitKeyVal(string line, char separator = ':')
|
|
|
|
|
{
|
2020-01-23 15:23:53 +00:00
|
|
|
|
var split = line.Split(separator, 2);
|
2018-04-13 09:19:50 +00:00
|
|
|
|
|
|
|
|
|
return new KeyValuePair<string, string>
|
|
|
|
|
(
|
|
|
|
|
split[0].Trim(),
|
|
|
|
|
split.Length > 1 ? split[1].Trim() : string.Empty
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2020-01-23 15:23:53 +00:00
|
|
|
|
protected string CleanFilename(string path) => path.Trim('"').ToStandardisedPath();
|
|
|
|
|
|
2018-04-13 09:19:50 +00:00
|
|
|
|
protected enum Section
|
|
|
|
|
{
|
|
|
|
|
None,
|
|
|
|
|
General,
|
|
|
|
|
Editor,
|
|
|
|
|
Metadata,
|
|
|
|
|
Difficulty,
|
|
|
|
|
Events,
|
|
|
|
|
TimingPoints,
|
|
|
|
|
Colours,
|
|
|
|
|
HitObjects,
|
|
|
|
|
Variables,
|
|
|
|
|
Fonts
|
|
|
|
|
}
|
|
|
|
|
|
2019-10-28 10:10:39 +00:00
|
|
|
|
internal class LegacyDifficultyControlPoint : DifficultyControlPoint
|
|
|
|
|
{
|
|
|
|
|
public LegacyDifficultyControlPoint()
|
|
|
|
|
{
|
2019-10-30 06:51:09 +00:00
|
|
|
|
SpeedMultiplierBindable.Precision = double.Epsilon;
|
2019-10-28 10:10:39 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-10-25 08:00:56 +00:00
|
|
|
|
internal class LegacySampleControlPoint : SampleControlPoint
|
2018-06-28 09:20:43 +00:00
|
|
|
|
{
|
|
|
|
|
public int CustomSampleBank;
|
|
|
|
|
|
2019-06-30 12:58:30 +00:00
|
|
|
|
public override HitSampleInfo ApplyTo(HitSampleInfo hitSampleInfo)
|
2018-06-28 09:20:43 +00:00
|
|
|
|
{
|
2019-06-30 12:58:30 +00:00
|
|
|
|
var baseInfo = base.ApplyTo(hitSampleInfo);
|
2018-06-28 09:20:43 +00:00
|
|
|
|
|
2018-07-20 06:12:44 +00:00
|
|
|
|
if (string.IsNullOrEmpty(baseInfo.Suffix) && CustomSampleBank > 1)
|
2018-07-09 17:56:00 +00:00
|
|
|
|
baseInfo.Suffix = CustomSampleBank.ToString();
|
2018-06-28 09:20:43 +00:00
|
|
|
|
|
|
|
|
|
return baseInfo;
|
|
|
|
|
}
|
|
|
|
|
|
2019-10-25 08:00:56 +00:00
|
|
|
|
public override bool EquivalentTo(ControlPoint other) =>
|
2019-10-27 02:35:45 +00:00
|
|
|
|
base.EquivalentTo(other) && other is LegacySampleControlPoint otherTyped &&
|
|
|
|
|
CustomSampleBank == otherTyped.CustomSampleBank;
|
2018-06-28 09:20:43 +00:00
|
|
|
|
}
|
2018-04-13 09:19:50 +00:00
|
|
|
|
}
|
|
|
|
|
}
|