diff --git a/osu.Game/Beatmaps/IBeatmapDifficultyInfo.cs b/osu.Game/Beatmaps/IBeatmapDifficultyInfo.cs new file mode 100644 index 0000000000..6d9fcfcb06 --- /dev/null +++ b/osu.Game/Beatmaps/IBeatmapDifficultyInfo.cs @@ -0,0 +1,90 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +#nullable enable + +namespace osu.Game.Beatmaps +{ + /// + /// A representation of all top-level difficulty settings for a beatmap. + /// + public interface IBeatmapDifficultyInfo + { + /// + /// The default value used for all difficulty settings except and . + /// + const float DEFAULT_DIFFICULTY = 5; + + /// + /// The drain rate of the associated beatmap. + /// + float DrainRate { get; } + + /// + /// The circle size of the associated beatmap. + /// + float CircleSize { get; } + + /// + /// The overall difficulty of the associated beatmap. + /// + float OverallDifficulty { get; } + + /// + /// The approach rate of the associated beatmap. + /// + float ApproachRate { get; } + + /// + /// The slider multiplier of the associated beatmap. + /// + double SliderMultiplier { get; } + + /// + /// The slider tick rate of the associated beatmap. + /// + double SliderTickRate { get; } + + /// + /// Maps a difficulty value [0, 10] to a two-piece linear range of values. + /// + /// The difficulty value to be mapped. + /// Minimum of the resulting range which will be achieved by a difficulty value of 0. + /// Midpoint of the resulting range which will be achieved by a difficulty value of 5. + /// Maximum of the resulting range which will be achieved by a difficulty value of 10. + /// Value to which the difficulty value maps in the specified range. + static double DifficultyRange(double difficulty, double min, double mid, double max) + { + if (difficulty > 5) + return mid + (max - mid) * (difficulty - 5) / 5; + if (difficulty < 5) + return mid - (mid - min) * (5 - difficulty) / 5; + + return mid; + } + + /// + /// Maps a difficulty value [0, 10] to a two-piece linear range of values. + /// + /// The difficulty value to be mapped. + /// The values that define the two linear ranges. + /// + /// + /// od0 + /// Minimum of the resulting range which will be achieved by a difficulty value of 0. + /// + /// + /// od5 + /// Midpoint of the resulting range which will be achieved by a difficulty value of 5. + /// + /// + /// od10 + /// Maximum of the resulting range which will be achieved by a difficulty value of 10. + /// + /// + /// + /// Value to which the difficulty value maps in the specified range. + public static double DifficultyRange(double difficulty, (double od0, double od5, double od10) range) + => DifficultyRange(difficulty, range.od0, range.od5, range.od10); + } +} diff --git a/osu.Game/Beatmaps/IBeatmapInfo.cs b/osu.Game/Beatmaps/IBeatmapInfo.cs new file mode 100644 index 0000000000..72a04621f2 --- /dev/null +++ b/osu.Game/Beatmaps/IBeatmapInfo.cs @@ -0,0 +1,74 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Localisation; + +#nullable enable + +namespace osu.Game.Beatmaps +{ + /// + /// A single beatmap difficulty. + /// + public interface IBeatmapInfo : IHasOnlineID + { + /// + /// The user-specified name given to this beatmap. + /// + string DifficultyName { get; } + + /// + /// The metadata representing this beatmap. May be shared between multiple beatmaps. + /// + IBeatmapMetadataInfo Metadata { get; } + + /// + /// The difficulty settings for this beatmap. + /// + IBeatmapDifficultyInfo Difficulty { get; } + + /// + /// The playable length in milliseconds of this beatmap. + /// + double Length { get; } + + /// + /// The most common BPM of this beatmap. + /// + double BPM { get; } + + /// + /// The SHA-256 hash representing this beatmap's contents. + /// + string Hash { get; } + + /// + /// MD5 is kept for legacy support (matching against replays, osu-web-10 etc.). + /// + string MD5Hash { get; } + + /// + /// The ruleset this beatmap was made for. + /// + IRulesetInfo Ruleset { get; } + + /// + /// The basic star rating for this beatmap (with no mods applied). + /// + double StarRating { get; } + + string DisplayTitle => $"{Metadata} {versionString}".Trim(); + + RomanisableString DisplayTitleRomanisable + { + get + { + var metadata = Metadata.DisplayTitleRomanisable; + + return new RomanisableString($"{metadata.GetPreferred(true)} {versionString}".Trim(), $"{metadata.GetPreferred(false)} {versionString}".Trim()); + } + } + + private string versionString => string.IsNullOrEmpty(DifficultyName) ? string.Empty : $"[{DifficultyName}]"; + } +} diff --git a/osu.Game/Beatmaps/IBeatmapMetadataInfo.cs b/osu.Game/Beatmaps/IBeatmapMetadataInfo.cs new file mode 100644 index 0000000000..18dd38b404 --- /dev/null +++ b/osu.Game/Beatmaps/IBeatmapMetadataInfo.cs @@ -0,0 +1,117 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using System.Linq; +using osu.Framework.Localisation; + +#nullable enable + +namespace osu.Game.Beatmaps +{ + /// + /// Metadata representing a beatmap. May be shared between multiple beatmap difficulties. + /// + public interface IBeatmapMetadataInfo : IEquatable + { + /// + /// The romanised title of this beatmap. + /// + string Title { get; } + + /// + /// The unicode title of this beatmap. + /// + string TitleUnicode { get; } + + /// + /// The romanised artist of this beatmap. + /// + string Artist { get; } + + /// + /// The unicode artist of this beatmap. + /// + string ArtistUnicode { get; } + + /// + /// The author of this beatmap. + /// + string Author { get; } // eventually should be linked to a persisted User. + + /// + /// The source of this beatmap. + /// + string Source { get; } + + /// + /// The tags of this beatmap. + /// + string Tags { get; } + + /// + /// The time in milliseconds to begin playing the track for preview purposes. + /// If -1, the track should begin playing at 40% of its length. + /// + int PreviewTime { get; } + + /// + /// The filename of the audio file consumed by this beatmap. + /// + string AudioFile { get; } + + /// + /// The filename of the background image file consumed by this beatmap. + /// + string BackgroundFile { get; } + + string DisplayTitle + { + get + { + string author = string.IsNullOrEmpty(Author) ? string.Empty : $"({Author})"; + return $"{Artist} - {Title} {author}".Trim(); + } + } + + RomanisableString DisplayTitleRomanisable + { + get + { + string author = string.IsNullOrEmpty(Author) ? string.Empty : $"({Author})"; + var artistUnicode = string.IsNullOrEmpty(ArtistUnicode) ? Artist : ArtistUnicode; + var titleUnicode = string.IsNullOrEmpty(TitleUnicode) ? Title : TitleUnicode; + + return new RomanisableString($"{artistUnicode} - {titleUnicode} {author}".Trim(), $"{Artist} - {Title} {author}".Trim()); + } + } + + string[] SearchableTerms => new[] + { + Author, + Artist, + ArtistUnicode, + Title, + TitleUnicode, + Source, + Tags + }.Where(s => !string.IsNullOrEmpty(s)).ToArray(); + + bool IEquatable.Equals(IBeatmapMetadataInfo? other) + { + if (other == null) + return false; + + return Title == other.Title + && TitleUnicode == other.TitleUnicode + && Artist == other.Artist + && ArtistUnicode == other.ArtistUnicode + && Author == other.Author + && Source == other.Source + && Tags == other.Tags + && PreviewTime == other.PreviewTime + && AudioFile == other.AudioFile + && BackgroundFile == other.BackgroundFile; + } + } +} diff --git a/osu.Game/Beatmaps/IBeatmapSetInfo.cs b/osu.Game/Beatmaps/IBeatmapSetInfo.cs new file mode 100644 index 0000000000..f22115e08f --- /dev/null +++ b/osu.Game/Beatmaps/IBeatmapSetInfo.cs @@ -0,0 +1,57 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using System.Collections.Generic; +using System.Linq; + +#nullable enable + +namespace osu.Game.Beatmaps +{ + /// + /// A representation of a collection of beatmap difficulties, generally packaged as an ".osz" archive. + /// + public interface IBeatmapSetInfo : IHasOnlineID + { + /// + /// The date when this beatmap was imported. + /// + DateTimeOffset DateAdded { get; } + + /// + /// The best-effort metadata representing this set. In the case metadata differs between contained beatmaps, one is arbitrarily chosen. + /// + IBeatmapMetadataInfo? Metadata { get; } + + /// + /// All beatmaps contained in this set. + /// + IEnumerable Beatmaps { get; } + + /// + /// All files used by this set. + /// + IEnumerable Files { get; } + + /// + /// The maximum star difficulty of all beatmaps in this set. + /// + double MaxStarDifficulty { get; } + + /// + /// The maximum playable length in milliseconds of all beatmaps in this set. + /// + double MaxLength { get; } + + /// + /// The maximum BPM of all beatmaps in this set. + /// + double MaxBPM { get; } + + /// + /// The filename for the storyboard. + /// + string StoryboardFile => Files.FirstOrDefault(f => f.Filename.EndsWith(".osb", StringComparison.OrdinalIgnoreCase))?.Filename ?? string.Empty; + } +} diff --git a/osu.Game/Beatmaps/IFileInfo.cs b/osu.Game/Beatmaps/IFileInfo.cs new file mode 100644 index 0000000000..50eb223fc4 --- /dev/null +++ b/osu.Game/Beatmaps/IFileInfo.cs @@ -0,0 +1,18 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +#nullable enable + +namespace osu.Game.Beatmaps +{ + /// + /// A representation of a tracked file. + /// + public interface IFileInfo + { + /// + /// SHA-256 hash of the file content. + /// + string Hash { get; } + } +} diff --git a/osu.Game/Beatmaps/IHasOnlineID.cs b/osu.Game/Beatmaps/IHasOnlineID.cs new file mode 100644 index 0000000000..dc2793afe5 --- /dev/null +++ b/osu.Game/Beatmaps/IHasOnlineID.cs @@ -0,0 +1,15 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +#nullable enable + +namespace osu.Game.Beatmaps +{ + public interface IHasOnlineID + { + /// + /// The server-side ID representing this instance, if one exists. + /// + int? OnlineID { get; } + } +} diff --git a/osu.Game/Beatmaps/INamedFileUsage.cs b/osu.Game/Beatmaps/INamedFileUsage.cs new file mode 100644 index 0000000000..aa7a3852a7 --- /dev/null +++ b/osu.Game/Beatmaps/INamedFileUsage.cs @@ -0,0 +1,23 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +#nullable enable + +namespace osu.Game.Beatmaps +{ + /// + /// A usage of a file, with a local filename attached. + /// + public interface INamedFileUsage + { + /// + /// The underlying file on disk. + /// + IFileInfo File { get; } + + /// + /// The filename for this usage. + /// + string Filename { get; } + } +} diff --git a/osu.Game/Beatmaps/IRulesetInfo.cs b/osu.Game/Beatmaps/IRulesetInfo.cs new file mode 100644 index 0000000000..b31ebdfbfd --- /dev/null +++ b/osu.Game/Beatmaps/IRulesetInfo.cs @@ -0,0 +1,46 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using osu.Game.Rulesets; + +#nullable enable + +namespace osu.Game.Beatmaps +{ + /// + /// A representation of a ruleset's metadata. + /// + public interface IRulesetInfo : IHasOnlineID + { + /// + /// The user-exposed name of this ruleset. + /// + string Name { get; } + + /// + /// An acronym defined by the ruleset that can be used as a permanent identifier. + /// + string ShortName { get; } + + /// + /// A string representation of this ruleset, to be used with reflection to instantiate the ruleset represented by this metadata. + /// + string InstantiationInfo { get; } + + public Ruleset? CreateInstance() + { + var type = Type.GetType(InstantiationInfo); + + if (type == null) + return null; + + var ruleset = Activator.CreateInstance(type) as Ruleset; + + // overwrite the pre-populated RulesetInfo with a potentially database attached copy. + // ruleset.RulesetInfo = this; + + return ruleset; + } + } +}