diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs index 2cca3ceaeb..1c01f100eb 100644 --- a/osu.Game/Beatmaps/BeatmapManager.cs +++ b/osu.Game/Beatmaps/BeatmapManager.cs @@ -225,16 +225,6 @@ namespace osu.Game.Beatmaps remove => beatmapModelManager.ItemRemoved -= value; } - public void Export(BeatmapSetInfo item) - { - beatmapModelManager.Export(item); - } - - public void ExportModelTo(BeatmapSetInfo model, Stream outputStream) - { - beatmapModelManager.ExportModelTo(model, outputStream); - } - public void Update(BeatmapSetInfo item) { beatmapModelManager.Update(item); diff --git a/osu.Game/Beatmaps/BeatmapModelManager.cs b/osu.Game/Beatmaps/BeatmapModelManager.cs index 81920ae052..d0c41e0fb8 100644 --- a/osu.Game/Beatmaps/BeatmapModelManager.cs +++ b/osu.Game/Beatmaps/BeatmapModelManager.cs @@ -212,7 +212,7 @@ namespace osu.Game.Beatmaps var fileInfo = setInfo.Files.SingleOrDefault(f => string.Equals(f.Filename, beatmapInfo.Path, StringComparison.OrdinalIgnoreCase)) ?? new BeatmapSetFileInfo(); // metadata may have changed; update the path with the standard format. - beatmapInfo.Path = GetValidFilename($"{metadata.Artist} - {metadata.Title} ({metadata.Author}) [{beatmapInfo.DifficultyName}].osu"); + beatmapInfo.Path = $"{metadata.Artist} - {metadata.Title} ({metadata.Author}) [{beatmapInfo.DifficultyName}].osu".GetValidArchiveContentFilename(); beatmapInfo.MD5Hash = stream.ComputeMD5Hash(); diff --git a/osu.Game/Beatmaps/BeatmapSetInfo.cs b/osu.Game/Beatmaps/BeatmapSetInfo.cs index a4d60d7ea0..ac7067edda 100644 --- a/osu.Game/Beatmaps/BeatmapSetInfo.cs +++ b/osu.Game/Beatmaps/BeatmapSetInfo.cs @@ -95,7 +95,7 @@ namespace osu.Game.Beatmaps IBeatmapMetadataInfo IBeatmapSetInfo.Metadata => Metadata ?? Beatmaps.FirstOrDefault()?.Metadata ?? new BeatmapMetadata(); IEnumerable IBeatmapSetInfo.Beatmaps => Beatmaps; - IEnumerable IBeatmapSetInfo.Files => Files; + IEnumerable IHasNamedFiles.Files => Files; #endregion } diff --git a/osu.Game/Beatmaps/IBeatmapSetInfo.cs b/osu.Game/Beatmaps/IBeatmapSetInfo.cs index aa114c8472..9755120457 100644 --- a/osu.Game/Beatmaps/IBeatmapSetInfo.cs +++ b/osu.Game/Beatmaps/IBeatmapSetInfo.cs @@ -12,7 +12,7 @@ namespace osu.Game.Beatmaps /// /// A representation of a collection of beatmap difficulties, generally packaged as an ".osz" archive. /// - public interface IBeatmapSetInfo : IHasOnlineID, IEquatable + public interface IBeatmapSetInfo : IHasOnlineID, IEquatable, IHasNamedFiles { /// /// The date when this beatmap was imported. @@ -29,11 +29,6 @@ namespace osu.Game.Beatmaps /// IEnumerable Beatmaps { get; } - /// - /// All files used by this set. - /// - IEnumerable Files { get; } - /// /// The maximum star difficulty of all beatmaps in this set. /// diff --git a/osu.Game/Database/ArchiveModelManager.cs b/osu.Game/Database/ArchiveModelManager.cs index e5919cb5c4..e73f4a7f6e 100644 --- a/osu.Game/Database/ArchiveModelManager.cs +++ b/osu.Game/Database/ArchiveModelManager.cs @@ -20,7 +20,6 @@ using osu.Game.IO; using osu.Game.IO.Archives; using osu.Game.IPC; using osu.Game.Overlays.Notifications; -using SharpCompress.Archives.Zip; namespace osu.Game.Database { @@ -82,8 +81,6 @@ namespace osu.Game.Database // ReSharper disable once NotAccessedField.Local (we should keep a reference to this so it is not finalised) private ArchiveImportIPCChannel ipc; - private readonly Storage exportStorage; - protected ArchiveModelManager(Storage storage, IDatabaseContextFactory contextFactory, MutableDatabaseBackedStoreWithFileIncludes modelStore, IIpcHost importHost = null) { ContextFactory = contextFactory; @@ -92,8 +89,6 @@ namespace osu.Game.Database ModelStore.ItemUpdated += item => handleEvent(() => ItemUpdated?.Invoke(item)); ModelStore.ItemRemoved += item => handleEvent(() => ItemRemoved?.Invoke(item)); - exportStorage = storage.GetStorageForDirectory(@"exports"); - Files = new FileStore(contextFactory, storage); if (importHost != null) @@ -452,41 +447,6 @@ namespace osu.Game.Database return item.ToEntityFrameworkLive(); }, cancellationToken, TaskCreationOptions.HideScheduler, lowPriority ? import_scheduler_low_priority : import_scheduler).Unwrap().ConfigureAwait(false); - /// - /// Exports an item to a legacy (.zip based) package. - /// - /// The item to export. - public void Export(TModel item) - { - var retrievedItem = ModelStore.ConsumableItems.FirstOrDefault(s => s.ID == item.ID); - - if (retrievedItem == null) - throw new ArgumentException(@"Specified model could not be found", nameof(item)); - - string filename = $"{GetValidFilename(item.ToString())}{HandledExtensions.First()}"; - - using (var stream = exportStorage.GetStream(filename, FileAccess.Write, FileMode.Create)) - ExportModelTo(retrievedItem, stream); - - exportStorage.PresentFileExternally(filename); - } - - /// - /// Exports an item to the given output stream. - /// - /// The item to export. - /// The output stream to export to. - public virtual void ExportModelTo(TModel model, Stream outputStream) - { - using (var archive = ZipArchive.Create()) - { - foreach (var file in model.Files) - archive.AddEntry(file.Filename, Files.Storage.GetStream(file.FileInfo.GetStoragePath())); - - archive.SaveTo(outputStream); - } - } - /// /// Replace an existing file with a new version. /// @@ -875,18 +835,5 @@ namespace osu.Game.Database // this doesn't follow the SHA2 hashing schema intentionally, so such entries on the data store can be identified. return Guid.NewGuid().ToString(); } - - private readonly char[] invalidFilenameCharacters = Path.GetInvalidFileNameChars() - // Backslash is added to avoid issues when exporting to zip. - // See SharpCompress filename normalisation https://github.com/adamhathcock/sharpcompress/blob/a1e7c0068db814c9aa78d86a94ccd1c761af74bd/src/SharpCompress/Writers/Zip/ZipWriter.cs#L143. - .Append('\\') - .ToArray(); - - protected string GetValidFilename(string filename) - { - foreach (char c in invalidFilenameCharacters) - filename = filename.Replace(c, '_'); - return filename; - } } } diff --git a/osu.Game/Database/IHasNamedFiles.cs b/osu.Game/Database/IHasNamedFiles.cs new file mode 100644 index 0000000000..08906aaa08 --- /dev/null +++ b/osu.Game/Database/IHasNamedFiles.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. + +using System.Collections.Generic; + +namespace osu.Game.Database +{ + public interface IHasNamedFiles + { + /// + /// All files used by this model. + /// + IEnumerable Files { get; } + } +} diff --git a/osu.Game/Database/IModelManager.cs b/osu.Game/Database/IModelManager.cs index 6c9cca7c7a..779d0522f7 100644 --- a/osu.Game/Database/IModelManager.cs +++ b/osu.Game/Database/IModelManager.cs @@ -3,7 +3,6 @@ using System; using System.Collections.Generic; -using System.IO; namespace osu.Game.Database { @@ -24,19 +23,6 @@ namespace osu.Game.Database /// event Action ItemRemoved; - /// - /// Exports an item to a legacy (.zip based) package. - /// - /// The item to export. - void Export(TModel item); - - /// - /// Exports an item to the given output stream. - /// - /// The item to export. - /// The output stream to export to. - void ExportModelTo(TModel model, Stream outputStream); - /// /// Perform an update of the specified item. /// TODO: Support file additions/removals. diff --git a/osu.Game/Database/LegacyBeatmapExporter.cs b/osu.Game/Database/LegacyBeatmapExporter.cs new file mode 100644 index 0000000000..fb8ee8f5f5 --- /dev/null +++ b/osu.Game/Database/LegacyBeatmapExporter.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. + +using osu.Framework.Platform; +using osu.Game.Beatmaps; + +namespace osu.Game.Database +{ + public class LegacyBeatmapExporter : LegacyExporter + { + protected override string FileExtension => ".osz"; + + public LegacyBeatmapExporter(Storage storage) + : base(storage) + { + } + } +} diff --git a/osu.Game/Database/LegacyExporter.cs b/osu.Game/Database/LegacyExporter.cs new file mode 100644 index 0000000000..802ccec6ed --- /dev/null +++ b/osu.Game/Database/LegacyExporter.cs @@ -0,0 +1,62 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.IO; +using osu.Framework.Platform; +using osu.Game.Extensions; +using SharpCompress.Archives.Zip; + +namespace osu.Game.Database +{ + /// + /// A class which handles exporting legacy user data of a single type from osu-stable. + /// + public abstract class LegacyExporter + where TModel : class, IHasNamedFiles + { + /// + /// The file extension for exports (including the leading '.'). + /// + protected abstract string FileExtension { get; } + + protected readonly Storage UserFileStorage; + + private readonly Storage exportStorage; + + protected LegacyExporter(Storage storage) + { + exportStorage = storage.GetStorageForDirectory(@"exports"); + UserFileStorage = storage.GetStorageForDirectory(@"files"); + } + + /// + /// Exports an item to a legacy (.zip based) package. + /// + /// The item to export. + public void Export(TModel item) + { + string filename = $"{item.ToString().GetValidArchiveContentFilename()}{FileExtension}"; + + using (var stream = exportStorage.GetStream(filename, FileAccess.Write, FileMode.Create)) + ExportModelTo(item, stream); + + exportStorage.PresentFileExternally(filename); + } + + /// + /// Exports an item to the given output stream. + /// + /// The item to export. + /// The output stream to export to. + public virtual void ExportModelTo(TModel model, Stream outputStream) + { + using (var archive = ZipArchive.Create()) + { + foreach (var file in model.Files) + archive.AddEntry(file.Filename, UserFileStorage.GetStream(file.File.GetStoragePath())); + + archive.SaveTo(outputStream); + } + } + } +} diff --git a/osu.Game/Database/LegacyScoreExporter.cs b/osu.Game/Database/LegacyScoreExporter.cs new file mode 100644 index 0000000000..41f8516880 --- /dev/null +++ b/osu.Game/Database/LegacyScoreExporter.cs @@ -0,0 +1,31 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.IO; +using System.Linq; +using osu.Framework.Platform; +using osu.Game.Extensions; +using osu.Game.Scoring; + +namespace osu.Game.Database +{ + public class LegacyScoreExporter : LegacyExporter + { + protected override string FileExtension => ".osr"; + + public LegacyScoreExporter(Storage storage) + : base(storage) + { + } + + public override void ExportModelTo(ScoreInfo model, Stream outputStream) + { + var file = model.Files.SingleOrDefault(); + if (file == null) + return; + + using (var inputStream = UserFileStorage.GetStream(file.FileInfo.GetStoragePath())) + inputStream.CopyTo(outputStream); + } + } +} diff --git a/osu.Game/Database/LegacySkinExporter.cs b/osu.Game/Database/LegacySkinExporter.cs new file mode 100644 index 0000000000..9432a1b5fc --- /dev/null +++ b/osu.Game/Database/LegacySkinExporter.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. + +using osu.Framework.Platform; +using osu.Game.Skinning; + +namespace osu.Game.Database +{ + public class LegacySkinExporter : LegacyExporter + { + protected override string FileExtension => ".osk"; + + public LegacySkinExporter(Storage storage) + : base(storage) + { + } + } +} diff --git a/osu.Game/Extensions/ModelExtensions.cs b/osu.Game/Extensions/ModelExtensions.cs index 9f00d21383..2274da0fd4 100644 --- a/osu.Game/Extensions/ModelExtensions.cs +++ b/osu.Game/Extensions/ModelExtensions.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System.IO; +using System.Linq; using osu.Game.Beatmaps; using osu.Game.Database; using osu.Game.IO; @@ -124,5 +125,21 @@ namespace osu.Game.Extensions return instance.OnlineID.Equals(other.OnlineID); } + + private static readonly char[] invalid_filename_characters = Path.GetInvalidFileNameChars() + // Backslash is added to avoid issues when exporting to zip. + // See SharpCompress filename normalisation https://github.com/adamhathcock/sharpcompress/blob/a1e7c0068db814c9aa78d86a94ccd1c761af74bd/src/SharpCompress/Writers/Zip/ZipWriter.cs#L143. + .Append('\\') + .ToArray(); + + /// + /// Get a valid filename for use inside a zip file. Avoids backslashes being incorrectly converted to directories. + /// + public static string GetValidArchiveContentFilename(this string filename) + { + foreach (char c in invalid_filename_characters) + filename = filename.Replace(c, '_'); + return filename; + } } } diff --git a/osu.Game/Models/RealmBeatmapSet.cs b/osu.Game/Models/RealmBeatmapSet.cs index fee59633f1..3566ff5321 100644 --- a/osu.Game/Models/RealmBeatmapSet.cs +++ b/osu.Game/Models/RealmBeatmapSet.cs @@ -76,7 +76,6 @@ namespace osu.Game.Models public bool Equals(IBeatmapSetInfo? other) => other is RealmBeatmapSet b && Equals(b); IEnumerable IBeatmapSetInfo.Beatmaps => Beatmaps; - - IEnumerable IBeatmapSetInfo.Files => Files; + IEnumerable IHasNamedFiles.Files => Files; } } diff --git a/osu.Game/Online/API/Requests/Responses/APIBeatmapSet.cs b/osu.Game/Online/API/Requests/Responses/APIBeatmapSet.cs index 9005fa8eb7..57c45faed3 100644 --- a/osu.Game/Online/API/Requests/Responses/APIBeatmapSet.cs +++ b/osu.Game/Online/API/Requests/Responses/APIBeatmapSet.cs @@ -136,7 +136,7 @@ namespace osu.Game.Online.API.Requests.Responses IBeatmapMetadataInfo IBeatmapSetInfo.Metadata => metadata; DateTimeOffset IBeatmapSetInfo.DateAdded => throw new NotImplementedException(); - IEnumerable IBeatmapSetInfo.Files => throw new NotImplementedException(); + IEnumerable IHasNamedFiles.Files => throw new NotImplementedException(); double IBeatmapSetInfo.MaxStarDifficulty => throw new NotImplementedException(); double IBeatmapSetInfo.MaxLength => throw new NotImplementedException(); double IBeatmapSetInfo.MaxBPM => BPM; diff --git a/osu.Game/Online/API/Requests/Responses/APIScoreInfo.cs b/osu.Game/Online/API/Requests/Responses/APIScoreInfo.cs index 0a2d6ca7b0..467d5a9f23 100644 --- a/osu.Game/Online/API/Requests/Responses/APIScoreInfo.cs +++ b/osu.Game/Online/API/Requests/Responses/APIScoreInfo.cs @@ -8,6 +8,7 @@ using JetBrains.Annotations; using Newtonsoft.Json; using Newtonsoft.Json.Converters; using osu.Game.Beatmaps; +using osu.Game.Database; using osu.Game.Rulesets; using osu.Game.Rulesets.Mods; using osu.Game.Scoring; @@ -147,6 +148,7 @@ namespace osu.Game.Online.API.Requests.Responses } public IRulesetInfo Ruleset => new RulesetInfo { OnlineID = RulesetID }; + IEnumerable IHasNamedFiles.Files => throw new NotImplementedException(); IBeatmapInfo IScoreInfo.Beatmap => Beatmap; } diff --git a/osu.Game/Online/Leaderboards/LeaderboardScore.cs b/osu.Game/Online/Leaderboards/LeaderboardScore.cs index f0c57cb953..e01c7c9e49 100644 --- a/osu.Game/Online/Leaderboards/LeaderboardScore.cs +++ b/osu.Game/Online/Leaderboards/LeaderboardScore.cs @@ -14,6 +14,8 @@ using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.UserInterface; using osu.Framework.Input.Events; using osu.Framework.Localisation; +using osu.Framework.Platform; +using osu.Game.Database; using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; @@ -66,6 +68,9 @@ namespace osu.Game.Online.Leaderboards [Resolved] private ScoreManager scoreManager { get; set; } + [Resolved] + private Storage storage { get; set; } + public LeaderboardScore(ScoreInfo score, int? rank, bool allowHighlight = true) { Score = score; @@ -395,7 +400,7 @@ namespace osu.Game.Online.Leaderboards items.Add(new OsuMenuItem("Use these mods", MenuItemType.Highlighted, () => songSelect.Mods.Value = Score.Mods)); if (Score.Files.Count > 0) - items.Add(new OsuMenuItem("Export", MenuItemType.Standard, () => scoreManager.Export(Score))); + items.Add(new OsuMenuItem("Export", MenuItemType.Standard, () => new LegacyScoreExporter(storage).Export(Score))); if (Score.ID != 0) items.Add(new OsuMenuItem("Delete", MenuItemType.Destructive, () => dialogOverlay?.Push(new LocalScoreDeleteDialog(Score)))); diff --git a/osu.Game/Overlays/Settings/Sections/SkinSection.cs b/osu.Game/Overlays/Settings/Sections/SkinSection.cs index e7a7abed59..0eb65b4b0f 100644 --- a/osu.Game/Overlays/Settings/Sections/SkinSection.cs +++ b/osu.Game/Overlays/Settings/Sections/SkinSection.cs @@ -11,7 +11,9 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Sprites; using osu.Framework.Localisation; using osu.Framework.Logging; +using osu.Framework.Platform; using osu.Game.Configuration; +using osu.Game.Database; using osu.Game.Graphics.UserInterface; using osu.Game.Localisation; using osu.Game.Skinning; @@ -167,6 +169,9 @@ namespace osu.Game.Overlays.Settings.Sections [Resolved] private SkinManager skins { get; set; } + [Resolved] + private Storage storage { get; set; } + private Bindable currentSkin; [BackgroundDependencyLoader] @@ -183,7 +188,7 @@ namespace osu.Game.Overlays.Settings.Sections { try { - skins.Export(currentSkin.Value.SkinInfo); + new LegacySkinExporter(storage).Export(currentSkin.Value.SkinInfo); } catch (Exception e) { diff --git a/osu.Game/Scoring/IScoreInfo.cs b/osu.Game/Scoring/IScoreInfo.cs index 21a402f8c3..8b5b228632 100644 --- a/osu.Game/Scoring/IScoreInfo.cs +++ b/osu.Game/Scoring/IScoreInfo.cs @@ -9,7 +9,7 @@ using osu.Game.Rulesets; namespace osu.Game.Scoring { - public interface IScoreInfo : IHasOnlineID + public interface IScoreInfo : IHasOnlineID, IHasNamedFiles { APIUser User { get; } diff --git a/osu.Game/Scoring/ScoreFileInfo.cs b/osu.Game/Scoring/ScoreFileInfo.cs index 9075fdec5b..b2e81d4b8d 100644 --- a/osu.Game/Scoring/ScoreFileInfo.cs +++ b/osu.Game/Scoring/ScoreFileInfo.cs @@ -7,7 +7,7 @@ using osu.Game.IO; namespace osu.Game.Scoring { - public class ScoreFileInfo : INamedFileInfo, IHasPrimaryKey + public class ScoreFileInfo : INamedFileInfo, IHasPrimaryKey, INamedFileUsage { public int ID { get; set; } @@ -17,5 +17,7 @@ namespace osu.Game.Scoring [Required] public string Filename { get; set; } + + IFileInfo INamedFileUsage.File => FileInfo; } } diff --git a/osu.Game/Scoring/ScoreInfo.cs b/osu.Game/Scoring/ScoreInfo.cs index b4e194cbed..564aa3b98c 100644 --- a/osu.Game/Scoring/ScoreInfo.cs +++ b/osu.Game/Scoring/ScoreInfo.cs @@ -257,5 +257,7 @@ namespace osu.Game.Scoring bool IScoreInfo.HasReplay => Files.Any(); #endregion + + IEnumerable IHasNamedFiles.Files => Files; } } diff --git a/osu.Game/Scoring/ScoreManager.cs b/osu.Game/Scoring/ScoreManager.cs index b8d0d8abc6..1564b3bcc5 100644 --- a/osu.Game/Scoring/ScoreManager.cs +++ b/osu.Game/Scoring/ScoreManager.cs @@ -3,7 +3,6 @@ using System; using System.Collections.Generic; -using System.IO; using System.Linq; using System.Linq.Expressions; using System.Threading; @@ -262,16 +261,6 @@ namespace osu.Game.Scoring remove => scoreModelManager.ItemRemoved -= value; } - public void Export(ScoreInfo item) - { - scoreModelManager.Export(item); - } - - public void ExportModelTo(ScoreInfo model, Stream outputStream) - { - scoreModelManager.ExportModelTo(model, outputStream); - } - public void Update(ScoreInfo item) { scoreModelManager.Update(item); diff --git a/osu.Game/Scoring/ScoreModelManager.cs b/osu.Game/Scoring/ScoreModelManager.cs index 9da739237b..2cbd3aded7 100644 --- a/osu.Game/Scoring/ScoreModelManager.cs +++ b/osu.Game/Scoring/ScoreModelManager.cs @@ -3,7 +3,6 @@ using System; using System.Collections.Generic; -using System.IO; using System.Linq; using System.Linq.Expressions; using System.Threading; @@ -13,7 +12,6 @@ using osu.Framework.Logging; using osu.Framework.Platform; using osu.Game.Beatmaps; using osu.Game.Database; -using osu.Game.Extensions; using osu.Game.IO.Archives; using osu.Game.Rulesets; using osu.Game.Scoring.Legacy; @@ -69,15 +67,5 @@ namespace osu.Game.Scoring protected override bool CheckLocalAvailability(ScoreInfo model, IQueryable items) => base.CheckLocalAvailability(model, items) || (model.OnlineScoreID != null && items.Any(i => i.OnlineScoreID == model.OnlineScoreID)); - - public override void ExportModelTo(ScoreInfo model, Stream outputStream) - { - var file = model.Files.SingleOrDefault(); - if (file == null) - return; - - using (var inputStream = Files.Storage.GetStream(file.FileInfo.GetStoragePath())) - inputStream.CopyTo(outputStream); - } } } diff --git a/osu.Game/Screens/Edit/Editor.cs b/osu.Game/Screens/Edit/Editor.cs index 94b6e58b67..ac71298f36 100644 --- a/osu.Game/Screens/Edit/Editor.cs +++ b/osu.Game/Screens/Edit/Editor.cs @@ -16,9 +16,11 @@ using osu.Framework.Input; using osu.Framework.Input.Bindings; using osu.Framework.Input.Events; using osu.Framework.Logging; +using osu.Framework.Platform; using osu.Framework.Screens; using osu.Game.Beatmaps; using osu.Game.Configuration; +using osu.Game.Database; using osu.Game.Graphics; using osu.Game.Graphics.Cursor; using osu.Game.Graphics.UserInterface; @@ -63,6 +65,9 @@ namespace osu.Game.Screens.Edit [Resolved] private BeatmapManager beatmapManager { get; set; } + [Resolved] + private Storage storage { get; set; } + [Resolved(canBeNull: true)] private DialogOverlay dialogOverlay { get; set; } @@ -753,7 +758,7 @@ namespace osu.Game.Screens.Edit private void exportBeatmap() { Save(); - beatmapManager.Export(Beatmap.Value.BeatmapSetInfo); + new LegacyBeatmapExporter(storage).Export(Beatmap.Value.BeatmapSetInfo); } private void updateLastSavedHash() diff --git a/osu.Game/Skinning/SkinFileInfo.cs b/osu.Game/Skinning/SkinFileInfo.cs index 8a7019e1a3..db7cd953bb 100644 --- a/osu.Game/Skinning/SkinFileInfo.cs +++ b/osu.Game/Skinning/SkinFileInfo.cs @@ -7,7 +7,7 @@ using osu.Game.IO; namespace osu.Game.Skinning { - public class SkinFileInfo : INamedFileInfo, IHasPrimaryKey + public class SkinFileInfo : INamedFileInfo, IHasPrimaryKey, INamedFileUsage { public int ID { get; set; } @@ -19,5 +19,7 @@ namespace osu.Game.Skinning [Required] public string Filename { get; set; } + + IFileInfo INamedFileUsage.File => FileInfo; } } diff --git a/osu.Game/Skinning/SkinInfo.cs b/osu.Game/Skinning/SkinInfo.cs index 4733e8cdd9..5d2d51a9b0 100644 --- a/osu.Game/Skinning/SkinInfo.cs +++ b/osu.Game/Skinning/SkinInfo.cs @@ -10,7 +10,7 @@ using osu.Game.IO; namespace osu.Game.Skinning { - public class SkinInfo : IHasFiles, IEquatable, IHasPrimaryKey, ISoftDelete + public class SkinInfo : IHasFiles, IEquatable, IHasPrimaryKey, ISoftDelete, IHasNamedFiles { internal const int DEFAULT_SKIN = 0; internal const int CLASSIC_SKIN = -1; @@ -55,5 +55,7 @@ namespace osu.Game.Skinning string author = Creator == null ? string.Empty : $"({Creator})"; return $"{Name} {author}".Trim(); } + + IEnumerable IHasNamedFiles.Files => Files; } } diff --git a/osu.Game/Skinning/SkinManager.cs b/osu.Game/Skinning/SkinManager.cs index 679b35799e..8d07dd046a 100644 --- a/osu.Game/Skinning/SkinManager.cs +++ b/osu.Game/Skinning/SkinManager.cs @@ -301,16 +301,6 @@ namespace osu.Game.Skinning remove => skinModelManager.ItemRemoved -= value; } - public void Export(SkinInfo item) - { - skinModelManager.Export(item); - } - - public void ExportModelTo(SkinInfo model, Stream outputStream) - { - skinModelManager.ExportModelTo(model, outputStream); - } - public void Update(SkinInfo item) { skinModelManager.Update(item);