mirror of
https://github.com/ppy/osu.git
synced 2024-12-16 15:42:54 +08:00
Merge branch 'master' into hud_visibility
This commit is contained in:
commit
aa43b138b0
@ -3,10 +3,7 @@
|
|||||||
|
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using osu.Game.Beatmaps.Formats;
|
|
||||||
using osu.Game.Beatmaps.IO;
|
using osu.Game.Beatmaps.IO;
|
||||||
using osu.Game.Beatmaps;
|
|
||||||
using osu.Game.Database;
|
|
||||||
|
|
||||||
namespace osu.Desktop.Beatmaps.IO
|
namespace osu.Desktop.Beatmaps.IO
|
||||||
{
|
{
|
||||||
@ -18,20 +15,17 @@ namespace osu.Desktop.Beatmaps.IO
|
|||||||
public static void Register() => AddReader<LegacyFilesystemReader>((storage, path) => Directory.Exists(path));
|
public static void Register() => AddReader<LegacyFilesystemReader>((storage, path) => Directory.Exists(path));
|
||||||
|
|
||||||
private string basePath { get; }
|
private string basePath { get; }
|
||||||
private Beatmap firstMap { get; }
|
|
||||||
|
|
||||||
public LegacyFilesystemReader(string path)
|
public LegacyFilesystemReader(string path)
|
||||||
{
|
{
|
||||||
basePath = path;
|
basePath = path;
|
||||||
|
|
||||||
BeatmapFilenames = Directory.GetFiles(basePath, @"*.osu").Select(Path.GetFileName).ToArray();
|
BeatmapFilenames = Directory.GetFiles(basePath, @"*.osu").Select(Path.GetFileName).ToArray();
|
||||||
|
|
||||||
if (BeatmapFilenames.Length == 0)
|
if (BeatmapFilenames.Length == 0)
|
||||||
throw new FileNotFoundException(@"This directory contains no beatmaps");
|
throw new FileNotFoundException(@"This directory contains no beatmaps");
|
||||||
|
|
||||||
StoryboardFilename = Directory.GetFiles(basePath, @"*.osb").Select(Path.GetFileName).FirstOrDefault();
|
StoryboardFilename = Directory.GetFiles(basePath, @"*.osb").Select(Path.GetFileName).FirstOrDefault();
|
||||||
using (var stream = new StreamReader(GetStream(BeatmapFilenames[0])))
|
|
||||||
{
|
|
||||||
var decoder = BeatmapDecoder.GetDecoder(stream);
|
|
||||||
firstMap = decoder.Decode(stream);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public override Stream GetStream(string name)
|
public override Stream GetStream(string name)
|
||||||
@ -39,11 +33,6 @@ namespace osu.Desktop.Beatmaps.IO
|
|||||||
return File.OpenRead(Path.Combine(basePath, name));
|
return File.OpenRead(Path.Combine(basePath, name));
|
||||||
}
|
}
|
||||||
|
|
||||||
public override BeatmapMetadata ReadMetadata()
|
|
||||||
{
|
|
||||||
return firstMap.BeatmapInfo.Metadata;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void Dispose()
|
public override void Dispose()
|
||||||
{
|
{
|
||||||
// no-op
|
// no-op
|
||||||
|
@ -2,7 +2,6 @@
|
|||||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||||
|
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Beatmaps.Legacy;
|
|
||||||
using osu.Game.Beatmaps.Samples;
|
using osu.Game.Beatmaps.Samples;
|
||||||
using osu.Game.Modes.Objects;
|
using osu.Game.Modes.Objects;
|
||||||
using osu.Game.Modes.Objects.Types;
|
using osu.Game.Modes.Objects.Types;
|
||||||
@ -16,14 +15,30 @@ namespace osu.Game.Modes.Taiko.Beatmaps
|
|||||||
{
|
{
|
||||||
internal class TaikoBeatmapConverter : IBeatmapConverter<TaikoHitObject>
|
internal class TaikoBeatmapConverter : IBeatmapConverter<TaikoHitObject>
|
||||||
{
|
{
|
||||||
private const float legacy_velocity_scale = 1.4f;
|
/// <summary>
|
||||||
private const float bash_convert_factor = 1.65f;
|
/// osu! is generally slower than taiko, so a factor is added to increase
|
||||||
|
/// speed. This must be used everywhere slider length or beat length is used.
|
||||||
|
/// </summary>
|
||||||
|
private const float legacy_velocity_multiplier = 1.4f;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Because swells are easier in taiko than spinners are in osu!,
|
||||||
|
/// legacy taiko multiplies a factor when converting the number of required hits.
|
||||||
|
/// </summary>
|
||||||
|
private const float swell_hit_multiplier = 1.65f;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Base osu! slider scoring distance.
|
||||||
|
/// </summary>
|
||||||
|
private const float osu_base_scoring_distance = 100;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Drum roll distance that results in a duration of 1 speed-adjusted beat length.
|
||||||
|
/// </summary>
|
||||||
|
private const float taiko_base_distance = 100;
|
||||||
|
|
||||||
public Beatmap<TaikoHitObject> Convert(Beatmap original)
|
public Beatmap<TaikoHitObject> Convert(Beatmap original)
|
||||||
{
|
{
|
||||||
if (original is LegacyBeatmap)
|
|
||||||
original.TimingInfo.ControlPoints.ForEach(c => c.VelocityAdjustment /= legacy_velocity_scale);
|
|
||||||
|
|
||||||
return new Beatmap<TaikoHitObject>(original)
|
return new Beatmap<TaikoHitObject>(original)
|
||||||
{
|
{
|
||||||
HitObjects = original.HitObjects.SelectMany(h => convertHitObject(h, original)).ToList()
|
HitObjects = original.HitObjects.SelectMany(h => convertHitObject(h, original)).ToList()
|
||||||
@ -48,27 +63,44 @@ namespace osu.Game.Modes.Taiko.Beatmaps
|
|||||||
|
|
||||||
if (distanceData != null)
|
if (distanceData != null)
|
||||||
{
|
{
|
||||||
double sv = beatmap.TimingInfo.SliderVelocityAt(obj.StartTime) * beatmap.BeatmapInfo.Difficulty.SliderMultiplier;
|
|
||||||
|
|
||||||
double l = distanceData.Distance * legacy_velocity_scale;
|
|
||||||
double v = sv * legacy_velocity_scale;
|
|
||||||
double bl = beatmap.TimingInfo.BeatLengthAt(obj.StartTime);
|
|
||||||
|
|
||||||
int repeats = repeatsData?.RepeatCount ?? 1;
|
int repeats = repeatsData?.RepeatCount ?? 1;
|
||||||
|
|
||||||
double skipPeriod = Math.Min(bl / beatmap.BeatmapInfo.Difficulty.SliderTickRate, distanceData.Duration / repeats);
|
double speedAdjustment = beatmap.TimingInfo.SpeedMultiplierAt(obj.StartTime);
|
||||||
|
double speedAdjustedBeatLength = beatmap.TimingInfo.BeatLengthAt(obj.StartTime) * speedAdjustment;
|
||||||
|
|
||||||
if (skipPeriod > 0 && l / v * 1000 < 2 * bl)
|
// The true distance, accounting for any repeats. This ends up being the drum roll distance later
|
||||||
|
double distance = distanceData.Distance * repeats * legacy_velocity_multiplier;
|
||||||
|
|
||||||
|
// The velocity of the taiko hit object - calculated as the velocity of a drum roll
|
||||||
|
double taikoVelocity = taiko_base_distance * beatmap.BeatmapInfo.Difficulty.SliderMultiplier / speedAdjustedBeatLength * legacy_velocity_multiplier;
|
||||||
|
// The duration of the taiko hit object
|
||||||
|
double taikoDuration = distance / taikoVelocity;
|
||||||
|
|
||||||
|
// For some reason, old osu! always uses speedAdjustment to determine the taiko velocity, but
|
||||||
|
// only uses it to determine osu! velocity if beatmap version < 8. Let's account for that here.
|
||||||
|
if (beatmap.BeatmapInfo.BeatmapVersion >= 8)
|
||||||
|
speedAdjustedBeatLength /= speedAdjustment;
|
||||||
|
|
||||||
|
// The velocity of the osu! hit object - calculated as the velocity of a slider
|
||||||
|
double osuVelocity = osu_base_scoring_distance * beatmap.BeatmapInfo.Difficulty.SliderMultiplier / speedAdjustedBeatLength * legacy_velocity_multiplier;
|
||||||
|
// The duration of the osu! hit object
|
||||||
|
double osuDuration = distance / osuVelocity;
|
||||||
|
|
||||||
|
// If the drum roll is to be split into hit circles, assume the ticks are 1/8 spaced within the duration of one beat
|
||||||
|
double tickSpacing = Math.Min(speedAdjustedBeatLength / beatmap.BeatmapInfo.Difficulty.SliderTickRate, taikoDuration / repeats) / 8;
|
||||||
|
|
||||||
|
if (tickSpacing > 0 && osuDuration < 2 * speedAdjustedBeatLength)
|
||||||
{
|
{
|
||||||
for (double j = obj.StartTime; j <= distanceData.EndTime + skipPeriod / 8; j += skipPeriod)
|
for (double j = obj.StartTime; j <= distanceData.EndTime + tickSpacing; j += tickSpacing)
|
||||||
{
|
{
|
||||||
// Todo: This should generate different type of hits (including strongs)
|
// Todo: This should generate different type of hits (including strongs)
|
||||||
// depending on hitobject sound additions (not implemented fully yet)
|
// depending on hitobject sound additions (not implemented fully yet)
|
||||||
yield return new CentreHit
|
yield return new CentreHit
|
||||||
{
|
{
|
||||||
StartTime = obj.StartTime,
|
StartTime = j,
|
||||||
Sample = obj.Sample,
|
Sample = obj.Sample,
|
||||||
IsStrong = strong
|
IsStrong = strong,
|
||||||
|
VelocityMultiplier = legacy_velocity_multiplier
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -79,22 +111,24 @@ namespace osu.Game.Modes.Taiko.Beatmaps
|
|||||||
StartTime = obj.StartTime,
|
StartTime = obj.StartTime,
|
||||||
Sample = obj.Sample,
|
Sample = obj.Sample,
|
||||||
IsStrong = strong,
|
IsStrong = strong,
|
||||||
Distance = distanceData.Distance * (repeatsData?.RepeatCount ?? 1) * legacy_velocity_scale
|
Distance = distance,
|
||||||
|
TickRate = beatmap.BeatmapInfo.Difficulty.SliderTickRate == 3 ? 3 : 4,
|
||||||
|
VelocityMultiplier = legacy_velocity_multiplier
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (endTimeData != null)
|
else if (endTimeData != null)
|
||||||
{
|
{
|
||||||
double hitMultiplier = BeatmapDifficulty.DifficultyRange(beatmap.BeatmapInfo.Difficulty.OverallDifficulty, 3, 5, 7.5) * bash_convert_factor;
|
double hitMultiplier = BeatmapDifficulty.DifficultyRange(beatmap.BeatmapInfo.Difficulty.OverallDifficulty, 3, 5, 7.5) * swell_hit_multiplier;
|
||||||
|
|
||||||
yield return new Swell
|
yield return new Swell
|
||||||
{
|
{
|
||||||
StartTime = obj.StartTime,
|
StartTime = obj.StartTime,
|
||||||
Sample = obj.Sample,
|
Sample = obj.Sample,
|
||||||
IsStrong = strong,
|
IsStrong = strong,
|
||||||
|
|
||||||
EndTime = endTimeData.EndTime,
|
EndTime = endTimeData.EndTime,
|
||||||
RequiredHits = (int)Math.Max(1, endTimeData.Duration / 1000 * hitMultiplier)
|
RequiredHits = (int)Math.Max(1, endTimeData.Duration / 1000 * hitMultiplier),
|
||||||
|
VelocityMultiplier = legacy_velocity_multiplier
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -107,7 +141,8 @@ namespace osu.Game.Modes.Taiko.Beatmaps
|
|||||||
{
|
{
|
||||||
StartTime = obj.StartTime,
|
StartTime = obj.StartTime,
|
||||||
Sample = obj.Sample,
|
Sample = obj.Sample,
|
||||||
IsStrong = strong
|
IsStrong = strong,
|
||||||
|
VelocityMultiplier = legacy_velocity_multiplier
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -117,6 +152,7 @@ namespace osu.Game.Modes.Taiko.Beatmaps
|
|||||||
StartTime = obj.StartTime,
|
StartTime = obj.StartTime,
|
||||||
Sample = obj.Sample,
|
Sample = obj.Sample,
|
||||||
IsStrong = strong,
|
IsStrong = strong,
|
||||||
|
VelocityMultiplier = legacy_velocity_multiplier
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -33,10 +33,9 @@ namespace osu.Game.Modes.Taiko.Objects
|
|||||||
public double Velocity { get; protected set; } = 5;
|
public double Velocity { get; protected set; } = 5;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The distance between ticks of this drumroll.
|
/// Numer of ticks per beat length.
|
||||||
/// <para>Half of this value is the hit window of the ticks.</para>
|
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public double TickTimeDistance { get; protected set; } = 100;
|
public int TickRate = 1;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Number of drum roll ticks required for a "Good" hit.
|
/// Number of drum roll ticks required for a "Good" hit.
|
||||||
@ -60,18 +59,20 @@ namespace osu.Game.Modes.Taiko.Objects
|
|||||||
|
|
||||||
private List<DrumRollTick> ticks;
|
private List<DrumRollTick> ticks;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The length (in milliseconds) between ticks of this drumroll.
|
||||||
|
/// <para>Half of this value is the hit window of the ticks.</para>
|
||||||
|
/// </summary>
|
||||||
|
private double tickSpacing = 100;
|
||||||
|
|
||||||
public override void ApplyDefaults(TimingInfo timing, BeatmapDifficulty difficulty)
|
public override void ApplyDefaults(TimingInfo timing, BeatmapDifficulty difficulty)
|
||||||
{
|
{
|
||||||
base.ApplyDefaults(timing, difficulty);
|
base.ApplyDefaults(timing, difficulty);
|
||||||
|
|
||||||
Velocity = base_distance * difficulty.SliderMultiplier * difficulty.SliderTickRate * timing.BeatLengthAt(StartTime) * timing.SpeedMultiplierAt(StartTime);
|
double speedAdjutedBeatLength = timing.SpeedMultiplierAt(StartTime) * timing.BeatLengthAt(StartTime);
|
||||||
TickTimeDistance = timing.BeatLengthAt(StartTime);
|
|
||||||
|
|
||||||
//TODO: move this to legacy conversion code to allow for direct division without special case.
|
Velocity = base_distance * difficulty.SliderMultiplier / speedAdjutedBeatLength * VelocityMultiplier;
|
||||||
if (difficulty.SliderTickRate == 3)
|
tickSpacing = timing.BeatLengthAt(StartTime) / TickRate;
|
||||||
TickTimeDistance /= 3;
|
|
||||||
else
|
|
||||||
TickTimeDistance /= 4;
|
|
||||||
|
|
||||||
RequiredGoodHits = TotalTicks * Math.Min(0.15, 0.05 + 0.10 / 6 * difficulty.OverallDifficulty);
|
RequiredGoodHits = TotalTicks * Math.Min(0.15, 0.05 + 0.10 / 6 * difficulty.OverallDifficulty);
|
||||||
RequiredGreatHits = TotalTicks * Math.Min(0.30, 0.10 + 0.20 / 6 * difficulty.OverallDifficulty);
|
RequiredGreatHits = TotalTicks * Math.Min(0.30, 0.10 + 0.20 / 6 * difficulty.OverallDifficulty);
|
||||||
@ -81,17 +82,17 @@ namespace osu.Game.Modes.Taiko.Objects
|
|||||||
{
|
{
|
||||||
var ret = new List<DrumRollTick>();
|
var ret = new List<DrumRollTick>();
|
||||||
|
|
||||||
if (TickTimeDistance == 0)
|
if (tickSpacing == 0)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
bool first = true;
|
bool first = true;
|
||||||
for (double t = StartTime; t < EndTime + (int)TickTimeDistance; t += TickTimeDistance)
|
for (double t = StartTime; t < EndTime + (int)tickSpacing; t += tickSpacing)
|
||||||
{
|
{
|
||||||
ret.Add(new DrumRollTick
|
ret.Add(new DrumRollTick
|
||||||
{
|
{
|
||||||
FirstTick = first,
|
FirstTick = first,
|
||||||
PreEmpt = PreEmpt,
|
PreEmpt = PreEmpt,
|
||||||
TickTimeDistance = TickTimeDistance,
|
TickSpacing = tickSpacing,
|
||||||
StartTime = t,
|
StartTime = t,
|
||||||
IsStrong = IsStrong,
|
IsStrong = IsStrong,
|
||||||
Sample = new HitSampleInfo
|
Sample = new HitSampleInfo
|
||||||
|
@ -11,14 +11,14 @@ namespace osu.Game.Modes.Taiko.Objects
|
|||||||
public bool FirstTick;
|
public bool FirstTick;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The distance between this tick and the next in milliseconds.
|
/// The length (in milliseconds) between this tick and the next.
|
||||||
/// <para>Half of this value is the hit window of the tick.</para>
|
/// <para>Half of this value is the hit window of the tick.</para>
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public double TickTimeDistance;
|
public double TickSpacing;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The time allowed to hit this tick.
|
/// The time allowed to hit this tick.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public double HitWindow => TickTimeDistance / 2;
|
public double HitWindow => TickSpacing / 2;
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -19,6 +19,11 @@ namespace osu.Game.Modes.Taiko.Objects
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
private const double base_scroll_time = 6000;
|
private const double base_scroll_time = 6000;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The velocity multiplier applied to this hit object.
|
||||||
|
/// </summary>
|
||||||
|
public float VelocityMultiplier = 1;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The time to scroll in the HitObject.
|
/// The time to scroll in the HitObject.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -39,7 +44,7 @@ namespace osu.Game.Modes.Taiko.Objects
|
|||||||
{
|
{
|
||||||
base.ApplyDefaults(timing, difficulty);
|
base.ApplyDefaults(timing, difficulty);
|
||||||
|
|
||||||
PreEmpt = base_scroll_time / difficulty.SliderMultiplier * timing.BeatLengthAt(StartTime) * timing.SpeedMultiplierAt(StartTime) / 1000;
|
PreEmpt = base_scroll_time / difficulty.SliderMultiplier * timing.BeatLengthAt(StartTime) * timing.SpeedMultiplierAt(StartTime) / VelocityMultiplier / 1000;
|
||||||
|
|
||||||
ControlPoint overridePoint;
|
ControlPoint overridePoint;
|
||||||
Kiai = timing.TimingPointAt(StartTime, out overridePoint).KiaiMode;
|
Kiai = timing.TimingPointAt(StartTime, out overridePoint).KiaiMode;
|
||||||
|
@ -72,14 +72,9 @@ namespace osu.Game.Modes.Taiko.Replays
|
|||||||
}
|
}
|
||||||
else if (drumRoll != null)
|
else if (drumRoll != null)
|
||||||
{
|
{
|
||||||
double delay = drumRoll.TickTimeDistance;
|
foreach (var tick in drumRoll.Ticks)
|
||||||
|
|
||||||
double time = drumRoll.StartTime;
|
|
||||||
|
|
||||||
for (int j = 0; j < drumRoll.TotalTicks; j++)
|
|
||||||
{
|
{
|
||||||
Frames.Add(new ReplayFrame((int)time, 0, 0, hitButton ? ReplayButtonState.Left1 : ReplayButtonState.Left2));
|
Frames.Add(new ReplayFrame(tick.StartTime, 0, 0, hitButton ? ReplayButtonState.Left1 : ReplayButtonState.Left2));
|
||||||
time += delay;
|
|
||||||
hitButton = !hitButton;
|
hitButton = !hitButton;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,6 +7,8 @@ using osu.Game.Beatmaps.IO;
|
|||||||
using osu.Game.Modes;
|
using osu.Game.Modes;
|
||||||
using osu.Game.Modes.Osu;
|
using osu.Game.Modes.Osu;
|
||||||
using osu.Game.Tests.Resources;
|
using osu.Game.Tests.Resources;
|
||||||
|
using osu.Game.Beatmaps.Formats;
|
||||||
|
using osu.Game.Database;
|
||||||
|
|
||||||
namespace osu.Game.Tests.Beatmaps.IO
|
namespace osu.Game.Tests.Beatmaps.IO
|
||||||
{
|
{
|
||||||
@ -53,7 +55,11 @@ namespace osu.Game.Tests.Beatmaps.IO
|
|||||||
using (var osz = Resource.OpenResource("Beatmaps.241526 Soleily - Renatus.osz"))
|
using (var osz = Resource.OpenResource("Beatmaps.241526 Soleily - Renatus.osz"))
|
||||||
{
|
{
|
||||||
var reader = new OszArchiveReader(osz);
|
var reader = new OszArchiveReader(osz);
|
||||||
var meta = reader.ReadMetadata();
|
|
||||||
|
BeatmapMetadata meta;
|
||||||
|
using (var stream = new StreamReader(reader.GetStream("Soleily - Renatus (Deif) [Platter].osu")))
|
||||||
|
meta = BeatmapDecoder.GetDecoder(stream).Decode(stream).Metadata;
|
||||||
|
|
||||||
Assert.AreEqual(241526, meta.OnlineBeatmapSetID);
|
Assert.AreEqual(241526, meta.OnlineBeatmapSetID);
|
||||||
Assert.AreEqual("Soleily", meta.Artist);
|
Assert.AreEqual("Soleily", meta.Artist);
|
||||||
Assert.AreEqual("Soleily", meta.ArtistUnicode);
|
Assert.AreEqual("Soleily", meta.ArtistUnicode);
|
||||||
|
@ -13,13 +13,13 @@ namespace osu.Game.Beatmaps.Formats
|
|||||||
{
|
{
|
||||||
private static Dictionary<string, Type> decoders { get; } = new Dictionary<string, Type>();
|
private static Dictionary<string, Type> decoders { get; } = new Dictionary<string, Type>();
|
||||||
|
|
||||||
public static BeatmapDecoder GetDecoder(TextReader stream)
|
public static BeatmapDecoder GetDecoder(StreamReader stream)
|
||||||
{
|
{
|
||||||
var line = stream.ReadLine()?.Trim();
|
string line = stream.ReadLine()?.Trim();
|
||||||
|
|
||||||
if (line == null || !decoders.ContainsKey(line))
|
if (line == null || !decoders.ContainsKey(line))
|
||||||
throw new IOException(@"Unknown file format");
|
throw new IOException(@"Unknown file format");
|
||||||
return (BeatmapDecoder)Activator.CreateInstance(decoders[line]);
|
return (BeatmapDecoder)Activator.CreateInstance(decoders[line], line);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected static void AddDecoder<T>(string magic) where T : BeatmapDecoder
|
protected static void AddDecoder<T>(string magic) where T : BeatmapDecoder
|
||||||
@ -27,17 +27,17 @@ namespace osu.Game.Beatmaps.Formats
|
|||||||
decoders[magic] = typeof(T);
|
decoders[magic] = typeof(T);
|
||||||
}
|
}
|
||||||
|
|
||||||
public virtual Beatmap Decode(TextReader stream)
|
public virtual Beatmap Decode(StreamReader stream)
|
||||||
{
|
{
|
||||||
return ParseFile(stream);
|
return ParseFile(stream);
|
||||||
}
|
}
|
||||||
|
|
||||||
public virtual void Decode(TextReader stream, Beatmap beatmap)
|
public virtual void Decode(StreamReader stream, Beatmap beatmap)
|
||||||
{
|
{
|
||||||
ParseFile(stream, beatmap);
|
ParseFile(stream, beatmap);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected virtual Beatmap ParseFile(TextReader stream)
|
protected virtual Beatmap ParseFile(StreamReader stream)
|
||||||
{
|
{
|
||||||
var beatmap = new Beatmap
|
var beatmap = new Beatmap
|
||||||
{
|
{
|
||||||
@ -48,9 +48,11 @@ namespace osu.Game.Beatmaps.Formats
|
|||||||
Difficulty = new BeatmapDifficulty(),
|
Difficulty = new BeatmapDifficulty(),
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
ParseFile(stream, beatmap);
|
ParseFile(stream, beatmap);
|
||||||
return beatmap;
|
return beatmap;
|
||||||
}
|
}
|
||||||
protected abstract void ParseFile(TextReader stream, Beatmap beatmap);
|
|
||||||
|
protected abstract void ParseFile(StreamReader stream, Beatmap beatmap);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,16 +0,0 @@
|
|||||||
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
|
||||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.IO;
|
|
||||||
|
|
||||||
namespace osu.Game.Beatmaps.Formats
|
|
||||||
{
|
|
||||||
public class ConstructableBeatmapDecoder : BeatmapDecoder
|
|
||||||
{
|
|
||||||
protected override void ParseFile(TextReader stream, Beatmap beatmap)
|
|
||||||
{
|
|
||||||
throw new NotImplementedException();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -31,6 +31,17 @@ namespace osu.Game.Beatmaps.Formats
|
|||||||
// TODO: Not sure how far back to go, or differences between versions
|
// TODO: Not sure how far back to go, or differences between versions
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private readonly int beatmapVersion;
|
||||||
|
|
||||||
|
public OsuLegacyDecoder()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public OsuLegacyDecoder(string header)
|
||||||
|
{
|
||||||
|
beatmapVersion = int.Parse(header.Substring(17));
|
||||||
|
}
|
||||||
|
|
||||||
private enum Section
|
private enum Section
|
||||||
{
|
{
|
||||||
None,
|
None,
|
||||||
@ -246,32 +257,36 @@ namespace osu.Game.Beatmaps.Formats
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override Beatmap ParseFile(TextReader stream)
|
protected override Beatmap ParseFile(StreamReader stream)
|
||||||
{
|
{
|
||||||
return new LegacyBeatmap(base.ParseFile(stream));
|
return new LegacyBeatmap(base.ParseFile(stream));
|
||||||
}
|
}
|
||||||
|
|
||||||
public override Beatmap Decode(TextReader stream)
|
public override Beatmap Decode(StreamReader stream)
|
||||||
{
|
{
|
||||||
return new LegacyBeatmap(base.Decode(stream));
|
return new LegacyBeatmap(base.Decode(stream));
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void ParseFile(TextReader stream, Beatmap beatmap)
|
protected override void ParseFile(StreamReader stream, Beatmap beatmap)
|
||||||
{
|
{
|
||||||
|
beatmap.BeatmapInfo.BeatmapVersion = beatmapVersion;
|
||||||
|
|
||||||
HitObjectParser parser = null;
|
HitObjectParser parser = null;
|
||||||
|
|
||||||
|
Section section = Section.None;
|
||||||
bool hasCustomColours = false;
|
bool hasCustomColours = false;
|
||||||
|
|
||||||
var section = Section.None;
|
string line;
|
||||||
while (true)
|
while ((line = stream.ReadLine()) != null)
|
||||||
{
|
{
|
||||||
var line = stream.ReadLine();
|
|
||||||
if (line == null)
|
|
||||||
break;
|
|
||||||
if (string.IsNullOrEmpty(line))
|
if (string.IsNullOrEmpty(line))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (line.StartsWith(@"osu file format v"))
|
if (line.StartsWith(@"osu file format v"))
|
||||||
|
{
|
||||||
|
beatmap.BeatmapInfo.BeatmapVersion = int.Parse(line.Substring(17));
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (line.StartsWith(@"[") && line.EndsWith(@"]"))
|
if (line.StartsWith(@"[") && line.EndsWith(@"]"))
|
||||||
{
|
{
|
||||||
|
@ -6,7 +6,6 @@ using System.Collections.Generic;
|
|||||||
using System.IO;
|
using System.IO;
|
||||||
using osu.Framework.IO.Stores;
|
using osu.Framework.IO.Stores;
|
||||||
using osu.Framework.Platform;
|
using osu.Framework.Platform;
|
||||||
using osu.Game.Database;
|
|
||||||
|
|
||||||
namespace osu.Game.Beatmaps.IO
|
namespace osu.Game.Beatmaps.IO
|
||||||
{
|
{
|
||||||
@ -35,11 +34,6 @@ namespace osu.Game.Beatmaps.IO
|
|||||||
readers.Add(new Reader { Test = test, Type = typeof(T) });
|
readers.Add(new Reader { Test = test, Type = typeof(T) });
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Reads the beatmap metadata from this archive.
|
|
||||||
/// </summary>
|
|
||||||
public abstract BeatmapMetadata ReadMetadata();
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets a list of beatmap file names.
|
/// Gets a list of beatmap file names.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -5,7 +5,6 @@ using System.IO;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using Ionic.Zip;
|
using Ionic.Zip;
|
||||||
using osu.Game.Beatmaps.Formats;
|
using osu.Game.Beatmaps.Formats;
|
||||||
using osu.Game.Database;
|
|
||||||
|
|
||||||
namespace osu.Game.Beatmaps.IO
|
namespace osu.Game.Beatmaps.IO
|
||||||
{
|
{
|
||||||
@ -23,23 +22,18 @@ namespace osu.Game.Beatmaps.IO
|
|||||||
|
|
||||||
private readonly Stream archiveStream;
|
private readonly Stream archiveStream;
|
||||||
private readonly ZipFile archive;
|
private readonly ZipFile archive;
|
||||||
private readonly Beatmap firstMap;
|
|
||||||
|
|
||||||
public OszArchiveReader(Stream archiveStream)
|
public OszArchiveReader(Stream archiveStream)
|
||||||
{
|
{
|
||||||
this.archiveStream = archiveStream;
|
this.archiveStream = archiveStream;
|
||||||
archive = ZipFile.Read(archiveStream);
|
archive = ZipFile.Read(archiveStream);
|
||||||
BeatmapFilenames = archive.Entries.Where(e => e.FileName.EndsWith(@".osu"))
|
|
||||||
.Select(e => e.FileName).ToArray();
|
BeatmapFilenames = archive.Entries.Where(e => e.FileName.EndsWith(@".osu")).Select(e => e.FileName).ToArray();
|
||||||
|
|
||||||
if (BeatmapFilenames.Length == 0)
|
if (BeatmapFilenames.Length == 0)
|
||||||
throw new FileNotFoundException(@"This directory contains no beatmaps");
|
throw new FileNotFoundException(@"This directory contains no beatmaps");
|
||||||
StoryboardFilename = archive.Entries.Where(e => e.FileName.EndsWith(@".osb"))
|
|
||||||
.Select(e => e.FileName).FirstOrDefault();
|
StoryboardFilename = archive.Entries.Where(e => e.FileName.EndsWith(@".osb")).Select(e => e.FileName).FirstOrDefault();
|
||||||
using (var stream = new StreamReader(GetStream(BeatmapFilenames[0])))
|
|
||||||
{
|
|
||||||
var decoder = BeatmapDecoder.GetDecoder(stream);
|
|
||||||
firstMap = decoder.Decode(stream);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public override Stream GetStream(string name)
|
public override Stream GetStream(string name)
|
||||||
@ -50,11 +44,6 @@ namespace osu.Game.Beatmaps.IO
|
|||||||
return entry.OpenReader();
|
return entry.OpenReader();
|
||||||
}
|
}
|
||||||
|
|
||||||
public override BeatmapMetadata ReadMetadata()
|
|
||||||
{
|
|
||||||
return firstMap.BeatmapInfo.Metadata;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void Dispose()
|
public override void Dispose()
|
||||||
{
|
{
|
||||||
archive.Dispose();
|
archive.Dispose();
|
||||||
|
@ -175,7 +175,10 @@ namespace osu.Game.Database
|
|||||||
BeatmapMetadata metadata;
|
BeatmapMetadata metadata;
|
||||||
|
|
||||||
using (var reader = ArchiveReader.GetReader(storage, path))
|
using (var reader = ArchiveReader.GetReader(storage, path))
|
||||||
metadata = reader.ReadMetadata();
|
{
|
||||||
|
using (var stream = new StreamReader(reader.GetStream(reader.BeatmapFilenames[0])))
|
||||||
|
metadata = BeatmapDecoder.GetDecoder(stream).Decode(stream).Metadata;
|
||||||
|
}
|
||||||
|
|
||||||
if (File.Exists(path)) // Not always the case, i.e. for LegacyFilesystemReader
|
if (File.Exists(path)) // Not always the case, i.e. for LegacyFilesystemReader
|
||||||
{
|
{
|
||||||
|
@ -15,6 +15,8 @@ namespace osu.Game.Database
|
|||||||
[PrimaryKey, AutoIncrement]
|
[PrimaryKey, AutoIncrement]
|
||||||
public int ID { get; set; }
|
public int ID { get; set; }
|
||||||
|
|
||||||
|
public int BeatmapVersion;
|
||||||
|
|
||||||
public int? OnlineBeatmapID { get; set; }
|
public int? OnlineBeatmapID { get; set; }
|
||||||
|
|
||||||
public int? OnlineBeatmapSetID { get; set; }
|
public int? OnlineBeatmapSetID { get; set; }
|
||||||
|
@ -34,10 +34,12 @@ namespace osu.Game.Database
|
|||||||
using (var stream = new StreamReader(reader.GetStream(BeatmapInfo.Path)))
|
using (var stream = new StreamReader(reader.GetStream(BeatmapInfo.Path)))
|
||||||
{
|
{
|
||||||
decoder = BeatmapDecoder.GetDecoder(stream);
|
decoder = BeatmapDecoder.GetDecoder(stream);
|
||||||
beatmap = decoder?.Decode(stream);
|
beatmap = decoder.Decode(stream);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (WithStoryboard && beatmap != null && BeatmapSetInfo.StoryboardFile != null)
|
if (beatmap == null || !WithStoryboard || BeatmapSetInfo.StoryboardFile == null)
|
||||||
|
return beatmap;
|
||||||
|
|
||||||
using (var stream = new StreamReader(reader.GetStream(BeatmapSetInfo.StoryboardFile)))
|
using (var stream = new StreamReader(reader.GetStream(BeatmapSetInfo.StoryboardFile)))
|
||||||
decoder.Decode(stream, beatmap);
|
decoder.Decode(stream, beatmap);
|
||||||
}
|
}
|
||||||
|
@ -142,7 +142,6 @@
|
|||||||
<Compile Include="Overlays\LoginOverlay.cs" />
|
<Compile Include="Overlays\LoginOverlay.cs" />
|
||||||
<Compile Include="Overlays\MusicController.cs" />
|
<Compile Include="Overlays\MusicController.cs" />
|
||||||
<Compile Include="Beatmaps\Beatmap.cs" />
|
<Compile Include="Beatmaps\Beatmap.cs" />
|
||||||
<Compile Include="Beatmaps\Formats\ConstructableBeatmapDecoder.cs" />
|
|
||||||
<Compile Include="Beatmaps\WorkingBeatmap.cs" />
|
<Compile Include="Beatmaps\WorkingBeatmap.cs" />
|
||||||
<Compile Include="Beatmaps\Drawables\BeatmapSetHeader.cs" />
|
<Compile Include="Beatmaps\Drawables\BeatmapSetHeader.cs" />
|
||||||
<Compile Include="Beatmaps\Drawables\DifficultyIcon.cs" />
|
<Compile Include="Beatmaps\Drawables\DifficultyIcon.cs" />
|
||||||
|
Loading…
Reference in New Issue
Block a user