// 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; using System.Threading; using Newtonsoft.Json; using osu.Framework.Logging; using osu.Framework.Platform; using osu.Game.Beatmaps; using osu.Game.Database; using osu.Game.IO.Archives; using osu.Game.Rulesets; using osu.Game.Scoring.Legacy; using osu.Game.Online.API; using osu.Game.Online.API.Requests; using osu.Game.Online.API.Requests.Responses; using Realms; namespace osu.Game.Scoring { public class ScoreImporter : RealmArchiveModelImporter { public override IEnumerable HandledExtensions => new[] { ".osr" }; protected override string[] HashableFileTypes => new[] { ".osr" }; private readonly RulesetStore rulesets; private readonly Func beatmaps; private readonly IAPIProvider api; public ScoreImporter(RulesetStore rulesets, Func beatmaps, Storage storage, RealmAccess realm, IAPIProvider api) : base(storage, realm) { this.rulesets = rulesets; this.beatmaps = beatmaps; this.api = api; } protected override ScoreInfo? CreateModel(ArchiveReader archive) { using (var stream = archive.GetStream(archive.Filenames.First(f => f.EndsWith(".osr", StringComparison.OrdinalIgnoreCase)))) { try { return new DatabasedLegacyScoreDecoder(rulesets, beatmaps()).Parse(stream).ScoreInfo; } catch (LegacyScoreDecoder.BeatmapNotFoundException e) { Logger.Log(e.Message, LoggingTarget.Information, LogLevel.Error); return null; } } } public Score GetScore(ScoreInfo score) => new LegacyDatabasedScore(score, rulesets, beatmaps(), Files.Store); protected override void Populate(ScoreInfo model, ArchiveReader? archive, Realm realm, CancellationToken cancellationToken = default) { // Ensure the beatmap is not detached. if (!model.BeatmapInfo.IsManaged) model.BeatmapInfo = realm.Find(model.BeatmapInfo.ID); if (!model.Ruleset.IsManaged) model.Ruleset = realm.Find(model.Ruleset.ShortName); // These properties are known to be non-null, but these final checks ensure a null hasn't come from somewhere (or the refetch has failed). // Under no circumstance do we want these to be written to realm as null. if (model.BeatmapInfo == null) throw new ArgumentNullException(nameof(model.BeatmapInfo)); if (model.Ruleset == null) throw new ArgumentNullException(nameof(model.Ruleset)); if (string.IsNullOrEmpty(model.StatisticsJson)) model.StatisticsJson = JsonConvert.SerializeObject(model.Statistics); } protected override void PostImport(ScoreInfo model, Realm realm) { base.PostImport(model, realm); var userRequest = new GetUserRequest(model.RealmUser.Username); api.Perform(userRequest); if (userRequest.Response is APIUser user) model.User = user; } } }