2021-11-09 20:34:56 +08: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.
2021-11-19 15:07:55 +08:00
using System.IO ;
2022-10-27 15:33:50 +08:00
using System.Text.RegularExpressions ;
2021-11-09 20:34:56 +08:00
using osu.Game.Beatmaps ;
2021-11-15 13:34:50 +08:00
using osu.Game.Database ;
2021-11-19 15:07:55 +08:00
using osu.Game.IO ;
2021-11-16 11:44:20 +08:00
using osu.Game.Online.API.Requests.Responses ;
2021-11-09 20:34:56 +08:00
using osu.Game.Rulesets ;
using osu.Game.Scoring ;
using osu.Game.Users ;
namespace osu.Game.Extensions
{
public static class ModelExtensions
{
2022-10-28 17:04:28 +08:00
private static readonly Regex invalid_filename_chars = new Regex ( @"(?!$)[^A-Za-z0-9_()[\]. \-]" , RegexOptions . Compiled ) ;
2021-11-19 15:07:55 +08:00
/// <summary>
/// Get the relative path in osu! storage for this file.
/// </summary>
/// <param name="fileInfo">The file info.</param>
/// <returns>A relative file path.</returns>
public static string GetStoragePath ( this IFileInfo fileInfo ) = > Path . Combine ( fileInfo . Hash . Remove ( 1 ) , fileInfo . Hash . Remove ( 2 ) , fileInfo . Hash ) ;
2021-11-09 20:34:56 +08:00
/// <summary>
/// Returns a user-facing string representing the <paramref name="model"/>.
/// </summary>
/// <remarks>
/// <para>
/// Non-interface types without special handling will fall back to <see cref="object.ToString()"/>.
/// </para>
/// <para>
/// Warning: This method is _purposefully_ not called <c>GetDisplayTitle()</c> like the others, because otherwise
/// extension method type inference rules cause this method to call itself and cause a stack overflow.
/// </para>
/// </remarks>
2021-11-15 13:38:01 +08:00
public static string GetDisplayString ( this object? model )
2021-11-09 20:34:56 +08:00
{
2021-11-15 13:38:01 +08:00
string? result = null ;
2021-11-09 20:34:56 +08:00
switch ( model )
{
case IBeatmapSetInfo beatmapSetInfo :
2021-11-24 14:01:45 +08:00
result = beatmapSetInfo . Metadata . GetDisplayTitle ( ) ;
2021-11-09 20:34:56 +08:00
break ;
case IBeatmapInfo beatmapInfo :
result = beatmapInfo . GetDisplayTitle ( ) ;
break ;
case IBeatmapMetadataInfo metadataInfo :
result = metadataInfo . GetDisplayTitle ( ) ;
break ;
case IScoreInfo scoreInfo :
result = scoreInfo . GetDisplayTitle ( ) ;
break ;
case IRulesetInfo rulesetInfo :
result = rulesetInfo . Name ;
break ;
case IUser user :
result = user . Username ;
break ;
}
// fallback in case none of the above happens to match.
2021-11-12 14:25:59 +08:00
result ? ? = model ? . ToString ( ) ? ? @"null" ;
2021-11-09 20:34:56 +08:00
return result ;
}
2021-11-15 13:34:50 +08:00
2022-03-03 13:15:25 +08:00
/// <summary>
/// Check whether this <see cref="IRulesetInfo"/>'s online ID is within the range that defines it as a legacy ruleset (ie. either osu!, osu!taiko, osu!catch or osu!mania).
/// </summary>
public static bool IsLegacyRuleset ( this IRulesetInfo ruleset ) = > ruleset . OnlineID > = 0 & & ruleset . OnlineID < = ILegacyRuleset . MAX_LEGACY_RULESET_ID ;
2021-11-15 13:34:50 +08:00
/// <summary>
2021-11-16 11:37:47 +08:00
/// Check whether the online ID of two <see cref="IBeatmapSetInfo"/>s match.
2021-11-15 13:34:50 +08:00
/// </summary>
/// <param name="instance">The instance to compare.</param>
/// <param name="other">The other instance to compare against.</param>
/// <returns>Whether online IDs match. If either instance is missing an online ID, this will return false.</returns>
2021-11-16 11:37:47 +08:00
public static bool MatchesOnlineID ( this IBeatmapSetInfo ? instance , IBeatmapSetInfo ? other ) = > matchesOnlineID ( instance , other ) ;
/// <summary>
/// Check whether the online ID of two <see cref="IBeatmapInfo"/>s match.
/// </summary>
/// <param name="instance">The instance to compare.</param>
/// <param name="other">The other instance to compare against.</param>
/// <returns>Whether online IDs match. If either instance is missing an online ID, this will return false.</returns>
public static bool MatchesOnlineID ( this IBeatmapInfo ? instance , IBeatmapInfo ? other ) = > matchesOnlineID ( instance , other ) ;
/// <summary>
/// Check whether the online ID of two <see cref="IRulesetInfo"/>s match.
/// </summary>
/// <param name="instance">The instance to compare.</param>
/// <param name="other">The other instance to compare against.</param>
/// <returns>Whether online IDs match. If either instance is missing an online ID, this will return false.</returns>
public static bool MatchesOnlineID ( this IRulesetInfo ? instance , IRulesetInfo ? other ) = > matchesOnlineID ( instance , other ) ;
2021-11-16 11:44:20 +08:00
/// <summary>
/// Check whether the online ID of two <see cref="APIUser"/>s match.
/// </summary>
/// <param name="instance">The instance to compare.</param>
/// <param name="other">The other instance to compare against.</param>
/// <returns>Whether online IDs match. If either instance is missing an online ID, this will return false.</returns>
public static bool MatchesOnlineID ( this APIUser ? instance , APIUser ? other ) = > matchesOnlineID ( instance , other ) ;
2021-12-10 17:32:06 +08:00
/// <summary>
/// Check whether the online ID of two <see cref="IScoreInfo"/>s match.
/// </summary>
/// <param name="instance">The instance to compare.</param>
/// <param name="other">The other instance to compare against.</param>
2023-09-04 18:21:23 +08:00
/// <returns>
/// Whether online IDs match.
/// Both <see cref="IHasOnlineID{T}.OnlineID"/> and <see cref="IScoreInfo.LegacyOnlineID"/> are checked, in that order.
/// If either instance is missing an online ID, this will return false.
/// </returns>
public static bool MatchesOnlineID ( this IScoreInfo ? instance , IScoreInfo ? other )
{
if ( matchesOnlineID ( instance , other ) )
return true ;
if ( instance = = null | | other = = null )
return false ;
if ( instance . LegacyOnlineID < 0 | | other . LegacyOnlineID < 0 )
return false ;
return instance . LegacyOnlineID . Equals ( other . LegacyOnlineID ) ;
}
2021-12-10 17:32:06 +08:00
2021-11-16 11:37:47 +08:00
private static bool matchesOnlineID ( this IHasOnlineID < long > ? instance , IHasOnlineID < long > ? other )
2021-11-15 13:34:50 +08:00
{
2021-11-15 13:38:01 +08:00
if ( instance = = null | | other = = null )
return false ;
2021-11-15 13:34:50 +08:00
if ( instance . OnlineID < 0 | | other . OnlineID < 0 )
return false ;
return instance . OnlineID . Equals ( other . OnlineID ) ;
}
2021-11-16 11:37:47 +08:00
private static bool matchesOnlineID ( this IHasOnlineID < int > ? instance , IHasOnlineID < int > ? other )
2021-11-15 13:34:50 +08:00
{
2021-11-15 13:38:01 +08:00
if ( instance = = null | | other = = null )
return false ;
2021-11-15 13:34:50 +08:00
if ( instance . OnlineID < 0 | | other . OnlineID < 0 )
return false ;
return instance . OnlineID . Equals ( other . OnlineID ) ;
}
2021-11-25 15:33:35 +08:00
/// <summary>
2022-10-28 17:04:28 +08:00
/// Create a valid filename which should work across all platforms.
2021-11-25 15:33:35 +08:00
/// </summary>
2022-10-28 17:04:28 +08:00
/// <remarks>
/// This function replaces all characters not included in a very pessimistic list which should be compatible
/// across all operating systems. We are using this in place of <see cref="Path.GetInvalidFileNameChars"/> as
/// that function does not have per-platform considerations (and is only made to work on windows).
/// </remarks>
public static string GetValidFilename ( this string filename ) = > invalid_filename_chars . Replace ( filename , "_" ) ;
2021-11-09 20:34:56 +08:00
}
}