1
0
mirror of https://github.com/ppy/osu.git synced 2024-12-14 06:52:55 +08:00

Split out legacy model export logic into LegacyModelExporter classes

This commit is contained in:
Dean Herbert 2021-11-25 16:36:30 +09:00
parent 7488ccd5fe
commit cc1b91e4bd
13 changed files with 151 additions and 112 deletions

View File

@ -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);

View File

@ -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<TModel, TFileModel> 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);
/// <summary>
/// Exports an item to a legacy (.zip based) package.
/// </summary>
/// <param name="item">The item to export.</param>
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);
}
/// <summary>
/// Exports an item to the given output stream.
/// </summary>
/// <param name="model">The item to export.</param>
/// <param name="outputStream">The output stream to export to.</param>
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);
}
}
/// <summary>
/// Replace an existing file with a new version.
/// </summary>
@ -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;
}
}
}

View File

@ -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
/// </summary>
event Action<TModel> ItemRemoved;
/// <summary>
/// Exports an item to a legacy (.zip based) package.
/// </summary>
/// <param name="item">The item to export.</param>
void Export(TModel item);
/// <summary>
/// Exports an item to the given output stream.
/// </summary>
/// <param name="model">The item to export.</param>
/// <param name="outputStream">The output stream to export to.</param>
void ExportModelTo(TModel model, Stream outputStream);
/// <summary>
/// Perform an update of the specified item.
/// TODO: Support file additions/removals.

View File

@ -0,0 +1,18 @@
// 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.
using osu.Framework.Platform;
using osu.Game.Beatmaps;
namespace osu.Game.Database
{
public class LegacyBeatmapExporter : LegacyExporter<BeatmapSetInfo>
{
protected override string FileExtension => ".osz";
public LegacyBeatmapExporter(Storage storage, IModelManager<BeatmapSetInfo> manager)
: base(storage, manager)
{
}
}
}

View File

@ -0,0 +1,66 @@
// 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.
using System.IO;
using osu.Framework.Platform;
using osu.Game.Extensions;
using SharpCompress.Archives.Zip;
namespace osu.Game.Database
{
/// <summary>
/// A class which handles exporting legacy user data of a single type from osu-stable.
/// </summary>
public abstract class LegacyExporter<TModel>
where TModel : class, IHasNamedFiles
{
/// <summary>
/// The file extension for exports (including the leading '.').
/// </summary>
protected abstract string FileExtension { get; }
protected readonly IModelManager<TModel> Manager;
protected readonly Storage UserFileStorage;
private readonly Storage exportStorage;
protected LegacyExporter(Storage storage, IModelManager<TModel> manager)
{
Manager = manager;
exportStorage = storage.GetStorageForDirectory(@"exports");
UserFileStorage = storage.GetStorageForDirectory(@"files");
}
/// <summary>
/// Exports an item to a legacy (.zip based) package.
/// </summary>
/// <param name="item">The item to export.</param>
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);
}
/// <summary>
/// Exports an item to the given output stream.
/// </summary>
/// <param name="model">The item to export.</param>
/// <param name="outputStream">The output stream to export to.</param>
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);
}
}
}
}

View File

@ -0,0 +1,31 @@
// 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.
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<ScoreInfo>
{
protected override string FileExtension => ".osr";
public LegacyScoreExporter(Storage storage, IModelManager<ScoreInfo> manager)
: base(storage, manager)
{
}
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);
}
}
}

View File

@ -0,0 +1,18 @@
// 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.
using osu.Framework.Platform;
using osu.Game.Skinning;
namespace osu.Game.Database
{
public class LegacySkinExporter : LegacyExporter<SkinInfo>
{
protected override string FileExtension => ".osk";
public LegacySkinExporter(Storage storage, IModelManager<SkinInfo> manager)
: base(storage, manager)
{
}
}
}

View File

@ -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, scoreManager).Export(Score)));
if (Score.ID != 0)
items.Add(new OsuMenuItem("Delete", MenuItemType.Destructive, () => dialogOverlay?.Push(new LocalScoreDeleteDialog(Score))));

View File

@ -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<Skin> currentSkin;
[BackgroundDependencyLoader]
@ -183,7 +188,7 @@ namespace osu.Game.Overlays.Settings.Sections
{
try
{
skins.Export(currentSkin.Value.SkinInfo);
new LegacySkinExporter(storage, skins).Export(currentSkin.Value.SkinInfo);
}
catch (Exception e)
{

View File

@ -262,16 +262,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);

View File

@ -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<ScoreInfo> 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);
}
}
}

View File

@ -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, beatmapManager).Export(Beatmap.Value.BeatmapSetInfo);
}
private void updateLastSavedHash()

View File

@ -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);