Add the ability to export skins

This commit is contained in:
Dean Herbert
2020-05-24 13:44:11 +09:00
parent 224a3ff462
commit c071fe6140
3 changed files with 52 additions and 23 deletions

View File

@ -27,7 +27,6 @@ using osu.Game.Online.API.Requests;
using osu.Game.Rulesets; using osu.Game.Rulesets;
using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects;
using Decoder = osu.Game.Beatmaps.Formats.Decoder; using Decoder = osu.Game.Beatmaps.Formats.Decoder;
using ZipArchive = SharpCompress.Archives.Zip.ZipArchive;
namespace osu.Game.Beatmaps namespace osu.Game.Beatmaps
{ {
@ -66,7 +65,6 @@ namespace osu.Game.Beatmaps
private readonly AudioManager audioManager; private readonly AudioManager audioManager;
private readonly GameHost host; private readonly GameHost host;
private readonly BeatmapOnlineLookupQueue onlineLookupQueue; private readonly BeatmapOnlineLookupQueue onlineLookupQueue;
private readonly Storage exportStorage;
public BeatmapManager(Storage storage, IDatabaseContextFactory contextFactory, RulesetStore rulesets, IAPIProvider api, AudioManager audioManager, GameHost host = null, public BeatmapManager(Storage storage, IDatabaseContextFactory contextFactory, RulesetStore rulesets, IAPIProvider api, AudioManager audioManager, GameHost host = null,
WorkingBeatmap defaultBeatmap = null) WorkingBeatmap defaultBeatmap = null)
@ -83,7 +81,6 @@ namespace osu.Game.Beatmaps
beatmaps.BeatmapRestored += b => beatmapRestored.Value = new WeakReference<BeatmapInfo>(b); beatmaps.BeatmapRestored += b => beatmapRestored.Value = new WeakReference<BeatmapInfo>(b);
onlineLookupQueue = new BeatmapOnlineLookupQueue(api, storage); onlineLookupQueue = new BeatmapOnlineLookupQueue(api, storage);
exportStorage = storage.GetStorageForDirectory("exports");
} }
protected override ArchiveDownloadRequest<BeatmapSetInfo> CreateDownloadRequest(BeatmapSetInfo set, bool minimiseDownloadSize) => protected override ArchiveDownloadRequest<BeatmapSetInfo> CreateDownloadRequest(BeatmapSetInfo set, bool minimiseDownloadSize) =>
@ -214,26 +211,6 @@ namespace osu.Game.Beatmaps
workingCache.Remove(working); workingCache.Remove(working);
} }
/// <summary>
/// Exports a <see cref="BeatmapSetInfo"/> to an .osz package.
/// </summary>
/// <param name="set">The <see cref="BeatmapSetInfo"/> to export.</param>
public void Export(BeatmapSetInfo set)
{
var localSet = QueryBeatmapSet(s => s.ID == set.ID);
using (var archive = ZipArchive.Create())
{
foreach (var file in localSet.Files)
archive.AddEntry(file.Filename, Files.Storage.GetStream(file.FileInfo.StoragePath));
using (var outputStream = exportStorage.GetStream($"{set}.osz", FileAccess.Write, FileMode.Create))
archive.SaveTo(outputStream);
exportStorage.OpenInNativeExplorer();
}
}
private readonly WeakList<WorkingBeatmap> workingCache = new WeakList<WorkingBeatmap>(); private readonly WeakList<WorkingBeatmap> workingCache = new WeakList<WorkingBeatmap>();
/// <summary> /// <summary>

View File

@ -22,6 +22,7 @@ using osu.Game.IO.Archives;
using osu.Game.IPC; using osu.Game.IPC;
using osu.Game.Overlays.Notifications; using osu.Game.Overlays.Notifications;
using osu.Game.Utils; using osu.Game.Utils;
using SharpCompress.Archives.Zip;
using SharpCompress.Common; using SharpCompress.Common;
using FileInfo = osu.Game.IO.FileInfo; using FileInfo = osu.Game.IO.FileInfo;
@ -82,6 +83,8 @@ namespace osu.Game.Database
// ReSharper disable once NotAccessedField.Local (we should keep a reference to this so it is not finalised) // ReSharper disable once NotAccessedField.Local (we should keep a reference to this so it is not finalised)
private ArchiveImportIPCChannel ipc; private ArchiveImportIPCChannel ipc;
private readonly Storage exportStorage;
protected ArchiveModelManager(Storage storage, IDatabaseContextFactory contextFactory, MutableDatabaseBackedStoreWithFileIncludes<TModel, TFileModel> modelStore, IIpcHost importHost = null) protected ArchiveModelManager(Storage storage, IDatabaseContextFactory contextFactory, MutableDatabaseBackedStoreWithFileIncludes<TModel, TFileModel> modelStore, IIpcHost importHost = null)
{ {
ContextFactory = contextFactory; ContextFactory = contextFactory;
@ -90,6 +93,8 @@ namespace osu.Game.Database
ModelStore.ItemAdded += item => handleEvent(() => itemAdded.Value = new WeakReference<TModel>(item)); ModelStore.ItemAdded += item => handleEvent(() => itemAdded.Value = new WeakReference<TModel>(item));
ModelStore.ItemRemoved += item => handleEvent(() => itemRemoved.Value = new WeakReference<TModel>(item)); ModelStore.ItemRemoved += item => handleEvent(() => itemRemoved.Value = new WeakReference<TModel>(item));
exportStorage = storage.GetStorageForDirectory("exports");
Files = new FileStore(contextFactory, storage); Files = new FileStore(contextFactory, storage);
if (importHost != null) if (importHost != null)
@ -369,6 +374,29 @@ namespace osu.Game.Database
return item; return item;
}, cancellationToken, TaskCreationOptions.HideScheduler, import_scheduler).Unwrap(); }, cancellationToken, TaskCreationOptions.HideScheduler, import_scheduler).Unwrap();
/// <summary>
/// Exports an item to an 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 count not be found", nameof(item));
using (var archive = ZipArchive.Create())
{
foreach (var file in retrievedItem.Files)
archive.AddEntry(file.Filename, Files.Storage.GetStream(file.FileInfo.StoragePath));
using (var outputStream = exportStorage.GetStream($"{item}{HandledExtensions.First()}", FileAccess.Write, FileMode.Create))
archive.SaveTo(outputStream);
exportStorage.OpenInNativeExplorer();
}
}
public void UpdateFile(TModel model, TFileModel file, Stream contents) public void UpdateFile(TModel model, TFileModel file, Stream contents)
{ {
using (var usage = ContextFactory.GetForWrite()) using (var usage = ContextFactory.GetForWrite())

View File

@ -7,6 +7,7 @@ using osu.Framework.Allocation;
using osu.Framework.Bindables; using osu.Framework.Bindables;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Sprites;
using osu.Framework.Logging;
using osu.Game.Configuration; using osu.Game.Configuration;
using osu.Game.Graphics.UserInterface; using osu.Game.Graphics.UserInterface;
using osu.Game.Skinning; using osu.Game.Skinning;
@ -34,13 +35,33 @@ namespace osu.Game.Overlays.Settings.Sections
private IBindable<WeakReference<SkinInfo>> managerAdded; private IBindable<WeakReference<SkinInfo>> managerAdded;
private IBindable<WeakReference<SkinInfo>> managerRemoved; private IBindable<WeakReference<SkinInfo>> managerRemoved;
private SettingsButton exportButton;
private Bindable<Skin> currentSkin;
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load(OsuConfigManager config) private void load(OsuConfigManager config)
{ {
FlowContent.Spacing = new Vector2(0, 5); FlowContent.Spacing = new Vector2(0, 5);
Children = new Drawable[] Children = new Drawable[]
{ {
skinDropdown = new SkinSettingsDropdown(), skinDropdown = new SkinSettingsDropdown(),
exportButton = new SettingsButton
{
Text = "Export selected skin",
Action = () =>
{
try
{
skins.Export(skins.CurrentSkin.Value.SkinInfo);
}
catch (Exception)
{
Logger.Log("Could not export current skin", level: LogLevel.Error);
}
}
},
new SettingsSlider<float, SizeSlider> new SettingsSlider<float, SizeSlider>
{ {
LabelText = "Menu cursor size", LabelText = "Menu cursor size",
@ -81,6 +102,9 @@ namespace osu.Game.Overlays.Settings.Sections
skinDropdown.Bindable = dropdownBindable; skinDropdown.Bindable = dropdownBindable;
skinDropdown.Items = skins.GetAllUsableSkins().ToArray(); skinDropdown.Items = skins.GetAllUsableSkins().ToArray();
currentSkin = skins.CurrentSkin.GetBoundCopy();
currentSkin.BindValueChanged(skin => exportButton.Enabled.Value = skin.NewValue.SkinInfo.ID > 0);
// Todo: This should not be necessary when OsuConfigManager is databased // Todo: This should not be necessary when OsuConfigManager is databased
if (skinDropdown.Items.All(s => s.ID != configBindable.Value)) if (skinDropdown.Items.All(s => s.ID != configBindable.Value))
configBindable.Value = 0; configBindable.Value = 0;