mirror of
https://github.com/ppy/osu
synced 2025-01-11 08:39:31 +00:00
Merge pull request #17416 from peppy/skin-fuck
Refactor skin construction
This commit is contained in:
commit
478174dd58
@ -30,7 +30,7 @@ namespace osu.Game.Rulesets.Catch.Tests
|
||||
{
|
||||
public TestLegacySkin(SkinInfo skin, IResourceStore<byte[]> storage)
|
||||
// Bypass LegacySkinResourceStore to avoid returning null for retrieving files due to bad skin info (SkinInfo.Files = null).
|
||||
: base(skin, storage, null, "skin.ini")
|
||||
: base(skin, null, storage)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
@ -175,7 +175,7 @@ namespace osu.Game.Tests.Beatmaps.Formats
|
||||
private class TestLegacySkin : LegacySkin
|
||||
{
|
||||
public TestLegacySkin(IResourceStore<byte[]> storage, string fileName)
|
||||
: base(new SkinInfo { Name = "Test Skin", Creator = "Craftplacer" }, storage, null, fileName)
|
||||
: base(new SkinInfo { Name = "Test Skin", Creator = "Craftplacer" }, null, storage, fileName)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
@ -148,7 +148,7 @@ namespace osu.Game.Tests.Gameplay
|
||||
private class TestSkin : LegacySkin
|
||||
{
|
||||
public TestSkin(string resourceName, IStorageResourceProvider resources)
|
||||
: base(DefaultLegacySkin.CreateInfo(), new TestResourceStore(resourceName), resources, "skin.ini")
|
||||
: base(DefaultLegacySkin.CreateInfo(), resources, new TestResourceStore(resourceName))
|
||||
{
|
||||
}
|
||||
}
|
||||
|
@ -1,12 +1,21 @@
|
||||
// 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;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Graphics.OpenGL.Textures;
|
||||
using osu.Framework.Audio;
|
||||
using osu.Framework.Graphics.Textures;
|
||||
using osu.Framework.IO.Stores;
|
||||
using osu.Game.Database;
|
||||
using osu.Game.IO;
|
||||
using osu.Game.Skinning;
|
||||
using SixLabors.ImageSharp;
|
||||
using SixLabors.ImageSharp.PixelFormats;
|
||||
|
||||
namespace osu.Game.Tests.NonVisual.Skinning
|
||||
{
|
||||
@ -71,7 +80,7 @@ namespace osu.Game.Tests.NonVisual.Skinning
|
||||
var texture = legacySkin.GetTexture(requestedComponent);
|
||||
|
||||
Assert.IsNotNull(texture);
|
||||
Assert.AreEqual(textureStore.Textures[expectedTexture], texture);
|
||||
Assert.AreEqual(textureStore.Textures[expectedTexture].Width, texture.Width);
|
||||
Assert.AreEqual(expectedScale, texture.ScaleAdjust);
|
||||
}
|
||||
|
||||
@ -88,23 +97,50 @@ namespace osu.Game.Tests.NonVisual.Skinning
|
||||
|
||||
private class TestLegacySkin : LegacySkin
|
||||
{
|
||||
public TestLegacySkin(TextureStore textureStore)
|
||||
: base(new SkinInfo(), null, null, string.Empty)
|
||||
public TestLegacySkin(IResourceStore<TextureUpload> textureStore)
|
||||
: base(new SkinInfo(), new TestResourceProvider(textureStore), null, string.Empty)
|
||||
{
|
||||
Textures = textureStore;
|
||||
}
|
||||
|
||||
private class TestResourceProvider : IStorageResourceProvider
|
||||
{
|
||||
private readonly IResourceStore<TextureUpload> textureStore;
|
||||
|
||||
public TestResourceProvider(IResourceStore<TextureUpload> textureStore)
|
||||
{
|
||||
this.textureStore = textureStore;
|
||||
}
|
||||
|
||||
public AudioManager AudioManager => null;
|
||||
public IResourceStore<byte[]> Files => null;
|
||||
public IResourceStore<byte[]> Resources => null;
|
||||
public RealmAccess RealmAccess => null;
|
||||
public IResourceStore<TextureUpload> CreateTextureLoaderStore(IResourceStore<byte[]> underlyingStore) => textureStore;
|
||||
}
|
||||
}
|
||||
|
||||
private class TestTextureStore : TextureStore
|
||||
private class TestTextureStore : IResourceStore<TextureUpload>
|
||||
{
|
||||
public readonly Dictionary<string, Texture> Textures;
|
||||
public readonly Dictionary<string, TextureUpload> Textures;
|
||||
|
||||
public TestTextureStore(params string[] fileNames)
|
||||
{
|
||||
Textures = fileNames.ToDictionary(fileName => fileName, fileName => new Texture(1, 1));
|
||||
// use an incrementing width to allow assertion matching on correct textures as they turn from uploads into actual textures.
|
||||
int width = 1;
|
||||
Textures = fileNames.ToDictionary(fileName => fileName, fileName => new TextureUpload(new Image<Rgba32>(width, width++)));
|
||||
}
|
||||
|
||||
public override Texture Get(string name, WrapMode wrapModeS, WrapMode wrapModeT) => Textures.GetValueOrDefault(name);
|
||||
public TextureUpload Get(string name) => Textures.GetValueOrDefault(name);
|
||||
|
||||
public Task<TextureUpload> GetAsync(string name, CancellationToken cancellationToken = new CancellationToken()) => Task.FromResult(Get(name));
|
||||
|
||||
public Stream GetStream(string name) => throw new NotImplementedException();
|
||||
|
||||
public IEnumerable<string> GetAvailableResources() => throw new NotImplementedException();
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -77,7 +77,7 @@ namespace osu.Game.Tests.Skins
|
||||
public class BeatmapSkinSource : LegacyBeatmapSkin
|
||||
{
|
||||
public BeatmapSkinSource()
|
||||
: base(new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo, null, null)
|
||||
: base(new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo, null)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -202,7 +202,7 @@ namespace osu.Game.Tests.Skins
|
||||
public class BeatmapSkinSource : LegacyBeatmapSkin
|
||||
{
|
||||
public BeatmapSkinSource()
|
||||
: base(new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo, null, null)
|
||||
: base(new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo, null)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
@ -50,7 +50,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
[Test]
|
||||
public void TestEmptyLegacyBeatmapSkinFallsBack()
|
||||
{
|
||||
CreateSkinTest(DefaultSkin.CreateInfo(), () => new LegacyBeatmapSkin(new BeatmapInfo(), null, null));
|
||||
CreateSkinTest(DefaultSkin.CreateInfo(), () => new LegacyBeatmapSkin(new BeatmapInfo(), null));
|
||||
AddUntilStep("wait for hud load", () => Player.ChildrenOfType<SkinnableTargetContainer>().All(c => c.ComponentsLoaded));
|
||||
AddAssert("hud from default skin", () => AssertComponentsFromExpectedSource(SkinnableTarget.MainHUDComponents, skinManager.CurrentSkin.Value));
|
||||
}
|
||||
|
@ -225,7 +225,7 @@ namespace osu.Game.Beatmaps
|
||||
{
|
||||
try
|
||||
{
|
||||
return new LegacyBeatmapSkin(BeatmapInfo, resources.Files, resources);
|
||||
return new LegacyBeatmapSkin(BeatmapInfo, resources);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
|
@ -30,11 +30,9 @@ namespace osu.Game.Skinning
|
||||
public DefaultLegacySkin(SkinInfo skin, IStorageResourceProvider resources)
|
||||
: base(
|
||||
skin,
|
||||
new NamespacedResourceStore<byte[]>(resources.Resources, "Skins/Legacy"),
|
||||
resources,
|
||||
// A default legacy skin may still have a skin.ini if it is modified by the user.
|
||||
// We must specify the stream directly as we are redirecting storage to the osu-resources location for other files.
|
||||
new LegacyDatabasedSkinResourceStore(skin, resources.Files).GetStream("skin.ini")
|
||||
// In the case of the actual default legacy skin (ie. the fallback one, which a user hasn't applied any modifications to) we want to use the game provided resources.
|
||||
skin.Protected ? new NamespacedResourceStore<byte[]>(resources.Resources, "Skins/Legacy") : null
|
||||
)
|
||||
{
|
||||
Configuration.CustomColours["SliderBall"] = new Color4(2, 170, 255, 255);
|
||||
|
@ -1,6 +1,7 @@
|
||||
// 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 JetBrains.Annotations;
|
||||
using osu.Framework.Audio.Sample;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
@ -20,13 +21,27 @@ namespace osu.Game.Skinning
|
||||
protected override bool AllowManiaSkin => false;
|
||||
protected override bool UseCustomSampleBanks => true;
|
||||
|
||||
public LegacyBeatmapSkin(BeatmapInfo beatmapInfo, IResourceStore<byte[]> storage, IStorageResourceProvider resources)
|
||||
: base(createSkinInfo(beatmapInfo), new LegacySkinResourceStore(beatmapInfo.BeatmapSet, storage), resources, beatmapInfo.Path)
|
||||
/// <summary>
|
||||
/// Construct a new legacy beatmap skin instance.
|
||||
/// </summary>
|
||||
/// <param name="beatmapInfo">The model for this beatmap.</param>
|
||||
/// <param name="resources">Access to raw game resources.</param>
|
||||
public LegacyBeatmapSkin(BeatmapInfo beatmapInfo, [CanBeNull] IStorageResourceProvider resources)
|
||||
: base(createSkinInfo(beatmapInfo), resources, createRealmBackedStore(beatmapInfo, resources), beatmapInfo.Path)
|
||||
{
|
||||
// Disallow default colours fallback on beatmap skins to allow using parent skin combo colours. (via SkinProvidingContainer)
|
||||
Configuration.AllowDefaultComboColoursFallback = false;
|
||||
}
|
||||
|
||||
private static IResourceStore<byte[]> createRealmBackedStore(BeatmapInfo beatmapInfo, [CanBeNull] IStorageResourceProvider resources)
|
||||
{
|
||||
if (resources == null)
|
||||
// should only ever be used in tests.
|
||||
return new ResourceStore<byte[]>();
|
||||
|
||||
return new RealmBackedResourceStore(beatmapInfo.BeatmapSet, resources.Files, new[] { @"ogg" });
|
||||
}
|
||||
|
||||
public override Drawable GetDrawableComponent(ISkinComponent component)
|
||||
{
|
||||
if (component is SkinnableTargetComponent targetComponent)
|
||||
@ -77,6 +92,10 @@ namespace osu.Game.Skinning
|
||||
}
|
||||
|
||||
private static SkinInfo createSkinInfo(BeatmapInfo beatmapInfo) =>
|
||||
new SkinInfo { Name = beatmapInfo.ToString(), Creator = beatmapInfo.Metadata.Author.Username ?? string.Empty };
|
||||
new SkinInfo
|
||||
{
|
||||
Name = beatmapInfo.ToString(),
|
||||
Creator = beatmapInfo.Metadata.Author.Username ?? string.Empty
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -15,6 +15,7 @@ using osu.Framework.Graphics.Textures;
|
||||
using osu.Framework.IO.Stores;
|
||||
using osu.Game.Audio;
|
||||
using osu.Game.Beatmaps.Formats;
|
||||
using osu.Game.Database;
|
||||
using osu.Game.IO;
|
||||
using osu.Game.Rulesets.Objects.Types;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
@ -27,12 +28,6 @@ namespace osu.Game.Skinning
|
||||
{
|
||||
public class LegacySkin : Skin
|
||||
{
|
||||
[CanBeNull]
|
||||
protected TextureStore Textures;
|
||||
|
||||
[CanBeNull]
|
||||
protected ISampleStore Samples;
|
||||
|
||||
/// <summary>
|
||||
/// Whether texture for the keys exists.
|
||||
/// Used to determine if the mania ruleset is skinned.
|
||||
@ -51,7 +46,7 @@ namespace osu.Game.Skinning
|
||||
|
||||
[UsedImplicitly(ImplicitUseKindFlags.InstantiatedWithFixedConstructorSignature)]
|
||||
public LegacySkin(SkinInfo skin, IStorageResourceProvider resources)
|
||||
: this(skin, new LegacyDatabasedSkinResourceStore(skin, resources.Files), resources, "skin.ini")
|
||||
: this(skin, resources, null)
|
||||
{
|
||||
}
|
||||
|
||||
@ -59,36 +54,12 @@ namespace osu.Game.Skinning
|
||||
/// Construct a new legacy skin instance.
|
||||
/// </summary>
|
||||
/// <param name="skin">The model for this skin.</param>
|
||||
/// <param name="storage">A storage for looking up files within this skin using user-facing filenames.</param>
|
||||
/// <param name="resources">Access to raw game resources.</param>
|
||||
/// <param name="storage">An optional store which will be used for looking up skin resources. If null, one will be created from realm <see cref="IHasRealmFiles"/> pattern.</param>
|
||||
/// <param name="configurationFilename">The user-facing filename of the configuration file to be parsed. Can accept an .osu or skin.ini file.</param>
|
||||
protected LegacySkin(SkinInfo skin, [CanBeNull] IResourceStore<byte[]> storage, [CanBeNull] IStorageResourceProvider resources, string configurationFilename)
|
||||
: this(skin, storage, resources, string.IsNullOrEmpty(configurationFilename) ? null : storage?.GetStream(configurationFilename))
|
||||
protected LegacySkin(SkinInfo skin, [CanBeNull] IStorageResourceProvider resources, [CanBeNull] IResourceStore<byte[]> storage, string configurationFilename = @"skin.ini")
|
||||
: base(skin, resources, storage, configurationFilename)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Construct a new legacy skin instance.
|
||||
/// </summary>
|
||||
/// <param name="skin">The model for this skin.</param>
|
||||
/// <param name="storage">A storage for looking up files within this skin using user-facing filenames.</param>
|
||||
/// <param name="resources">Access to raw game resources.</param>
|
||||
/// <param name="configurationStream">An optional stream containing the contents of a skin.ini file.</param>
|
||||
protected LegacySkin(SkinInfo skin, [CanBeNull] IResourceStore<byte[]> storage, [CanBeNull] IStorageResourceProvider resources, [CanBeNull] Stream configurationStream)
|
||||
: base(skin, resources, configurationStream)
|
||||
{
|
||||
if (storage != null)
|
||||
{
|
||||
var samples = resources?.AudioManager?.GetSampleStore(storage);
|
||||
if (samples != null)
|
||||
samples.PlaybackConcurrency = OsuGameBase.SAMPLE_CONCURRENCY;
|
||||
|
||||
Samples = samples;
|
||||
Textures = new TextureStore(resources?.CreateTextureLoaderStore(storage));
|
||||
|
||||
(storage as ResourceStore<byte[]>)?.AddExtension("ogg");
|
||||
}
|
||||
|
||||
// todo: this shouldn't really be duplicated here (from ManiaLegacySkinTransformer). we need to come up with a better solution.
|
||||
hasKeyTexture = new Lazy<bool>(() => this.GetAnimation(
|
||||
lookupForMania<string>(new LegacyManiaSkinConfigurationLookup(4, LegacyManiaSkinConfigurationLookups.KeyImage, 0))?.Value ?? "mania-key1", true,
|
||||
@ -385,26 +356,15 @@ namespace osu.Game.Skinning
|
||||
}
|
||||
})
|
||||
{
|
||||
Children = this.HasFont(LegacyFont.Score)
|
||||
? new Drawable[]
|
||||
{
|
||||
new LegacyComboCounter(),
|
||||
new LegacyScoreCounter(),
|
||||
new LegacyAccuracyCounter(),
|
||||
new LegacyHealthDisplay(),
|
||||
new SongProgress(),
|
||||
new BarHitErrorMeter(),
|
||||
}
|
||||
: new Drawable[]
|
||||
{
|
||||
// TODO: these should fallback to using osu!classic skin textures, rather than doing this.
|
||||
new DefaultComboCounter(),
|
||||
new DefaultScoreCounter(),
|
||||
new DefaultAccuracyCounter(),
|
||||
new DefaultHealthDisplay(),
|
||||
new SongProgress(),
|
||||
new BarHitErrorMeter(),
|
||||
}
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new LegacyComboCounter(),
|
||||
new LegacyScoreCounter(),
|
||||
new LegacyAccuracyCounter(),
|
||||
new LegacyHealthDisplay(),
|
||||
new SongProgress(),
|
||||
new BarHitErrorMeter(),
|
||||
}
|
||||
};
|
||||
|
||||
return skinnableTargetWrapper;
|
||||
@ -551,12 +511,5 @@ namespace osu.Game.Skinning
|
||||
// Fall back to using the last piece for components coming from lazer (e.g. "Gameplay/osu/approachcircle" -> "approachcircle").
|
||||
yield return componentName.Split('/').Last();
|
||||
}
|
||||
|
||||
protected override void Dispose(bool isDisposing)
|
||||
{
|
||||
base.Dispose(isDisposing);
|
||||
Textures?.Dispose();
|
||||
Samples?.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,39 +0,0 @@
|
||||
// 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;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using osu.Framework.Extensions;
|
||||
using osu.Framework.IO.Stores;
|
||||
using osu.Game.Database;
|
||||
using osu.Game.Extensions;
|
||||
|
||||
namespace osu.Game.Skinning
|
||||
{
|
||||
public class LegacySkinResourceStore : ResourceStore<byte[]>
|
||||
{
|
||||
private readonly IHasNamedFiles source;
|
||||
|
||||
public LegacySkinResourceStore(IHasNamedFiles source, IResourceStore<byte[]> underlyingStore)
|
||||
: base(underlyingStore)
|
||||
{
|
||||
this.source = source;
|
||||
}
|
||||
|
||||
protected override IEnumerable<string> GetFilenames(string name)
|
||||
{
|
||||
foreach (string filename in base.GetFilenames(name))
|
||||
{
|
||||
string path = getPathForFile(filename.ToStandardisedPath());
|
||||
if (path != null)
|
||||
yield return path;
|
||||
}
|
||||
}
|
||||
|
||||
private string getPathForFile(string filename) =>
|
||||
source.Files.FirstOrDefault(f => string.Equals(f.Filename, filename, StringComparison.OrdinalIgnoreCase))?.File.GetStoragePath();
|
||||
|
||||
public override IEnumerable<string> GetAvailableResources() => source.Files.Select(f => f.Filename);
|
||||
}
|
||||
}
|
@ -4,21 +4,29 @@
|
||||
using System.Collections.Generic;
|
||||
using osu.Framework.Extensions;
|
||||
using osu.Framework.IO.Stores;
|
||||
using osu.Game.Database;
|
||||
using osu.Game.Extensions;
|
||||
|
||||
namespace osu.Game.Skinning
|
||||
{
|
||||
public class LegacyDatabasedSkinResourceStore : ResourceStore<byte[]>
|
||||
public class RealmBackedResourceStore : ResourceStore<byte[]>
|
||||
{
|
||||
private readonly Dictionary<string, string> fileToStoragePathMapping = new Dictionary<string, string>();
|
||||
|
||||
public LegacyDatabasedSkinResourceStore(SkinInfo source, IResourceStore<byte[]> underlyingStore)
|
||||
public RealmBackedResourceStore(IHasRealmFiles source, IResourceStore<byte[]> underlyingStore, string[] extensions = null)
|
||||
: base(underlyingStore)
|
||||
{
|
||||
// Must be initialised before the file cache.
|
||||
if (extensions != null)
|
||||
{
|
||||
foreach (string extension in extensions)
|
||||
AddExtension(extension);
|
||||
}
|
||||
|
||||
initialiseFileCache(source);
|
||||
}
|
||||
|
||||
private void initialiseFileCache(SkinInfo source)
|
||||
private void initialiseFileCache(IHasRealmFiles source)
|
||||
{
|
||||
fileToStoragePathMapping.Clear();
|
||||
foreach (var f in source.Files)
|
@ -13,10 +13,10 @@ using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.OpenGL.Textures;
|
||||
using osu.Framework.Graphics.Textures;
|
||||
using osu.Framework.IO.Stores;
|
||||
using osu.Framework.Logging;
|
||||
using osu.Game.Audio;
|
||||
using osu.Game.Database;
|
||||
using osu.Game.Extensions;
|
||||
using osu.Game.IO;
|
||||
using osu.Game.Screens.Play.HUD;
|
||||
|
||||
@ -24,8 +24,19 @@ namespace osu.Game.Skinning
|
||||
{
|
||||
public abstract class Skin : IDisposable, ISkin
|
||||
{
|
||||
/// <summary>
|
||||
/// A texture store which can be used to perform user file lookups for this skin.
|
||||
/// </summary>
|
||||
[CanBeNull]
|
||||
protected TextureStore Textures { get; }
|
||||
|
||||
/// <summary>
|
||||
/// A sample store which can be used to perform user file lookups for this skin.
|
||||
/// </summary>
|
||||
[CanBeNull]
|
||||
protected ISampleStore Samples { get; }
|
||||
|
||||
public readonly Live<SkinInfo> SkinInfo;
|
||||
private readonly IStorageResourceProvider resources;
|
||||
|
||||
public SkinConfiguration Configuration { get; set; }
|
||||
|
||||
@ -41,16 +52,32 @@ namespace osu.Game.Skinning
|
||||
|
||||
public abstract IBindable<TValue> GetConfig<TLookup, TValue>(TLookup lookup);
|
||||
|
||||
protected Skin(SkinInfo skin, IStorageResourceProvider resources, [CanBeNull] Stream configurationStream = null)
|
||||
/// <summary>
|
||||
/// Construct a new skin.
|
||||
/// </summary>
|
||||
/// <param name="skin">The skin's metadata. Usually a live realm object.</param>
|
||||
/// <param name="resources">Access to game-wide resources.</param>
|
||||
/// <param name="storage">An optional store which will *replace* all file lookups that are usually sourced from <paramref name="skin"/>.</param>
|
||||
/// <param name="configurationFilename">An optional filename to read the skin configuration from. If not provided, the configuration will be retrieved from the storage using "skin.ini".</param>
|
||||
protected Skin(SkinInfo skin, [CanBeNull] IStorageResourceProvider resources, [CanBeNull] IResourceStore<byte[]> storage = null, [CanBeNull] string configurationFilename = @"skin.ini")
|
||||
{
|
||||
SkinInfo = resources?.RealmAccess != null
|
||||
? skin.ToLive(resources.RealmAccess)
|
||||
// This path should only be used in some tests.
|
||||
: skin.ToLiveUnmanaged();
|
||||
if (resources != null)
|
||||
{
|
||||
SkinInfo = resources.RealmAccess != null
|
||||
? skin.ToLive(resources.RealmAccess)
|
||||
: skin.ToLiveUnmanaged();
|
||||
|
||||
this.resources = resources;
|
||||
storage ??= new RealmBackedResourceStore(skin, resources.Files, new[] { @"ogg" });
|
||||
|
||||
configurationStream ??= getConfigurationStream();
|
||||
var samples = resources.AudioManager?.GetSampleStore(storage);
|
||||
if (samples != null)
|
||||
samples.PlaybackConcurrency = OsuGameBase.SAMPLE_CONCURRENCY;
|
||||
|
||||
Samples = samples;
|
||||
Textures = new TextureStore(resources.CreateTextureLoaderStore(storage));
|
||||
}
|
||||
|
||||
var configurationStream = storage?.GetStream(configurationFilename);
|
||||
|
||||
if (configurationStream != null)
|
||||
// stream will be closed after use by LineBufferedReader.
|
||||
@ -59,40 +86,30 @@ namespace osu.Game.Skinning
|
||||
Configuration = new SkinConfiguration();
|
||||
|
||||
// skininfo files may be null for default skin.
|
||||
SkinInfo.PerformRead(s =>
|
||||
foreach (SkinnableTarget skinnableTarget in Enum.GetValues(typeof(SkinnableTarget)))
|
||||
{
|
||||
// we may want to move this to some kind of async operation in the future.
|
||||
foreach (SkinnableTarget skinnableTarget in Enum.GetValues(typeof(SkinnableTarget)))
|
||||
string filename = $"{skinnableTarget}.json";
|
||||
|
||||
byte[] bytes = storage?.Get(filename);
|
||||
|
||||
if (bytes == null)
|
||||
continue;
|
||||
|
||||
try
|
||||
{
|
||||
string filename = $"{skinnableTarget}.json";
|
||||
string jsonContent = Encoding.UTF8.GetString(bytes);
|
||||
var deserializedContent = JsonConvert.DeserializeObject<IEnumerable<SkinnableInfo>>(jsonContent);
|
||||
|
||||
// skininfo files may be null for default skin.
|
||||
var fileInfo = s.Files.FirstOrDefault(f => f.Filename == filename);
|
||||
|
||||
if (fileInfo == null)
|
||||
if (deserializedContent == null)
|
||||
continue;
|
||||
|
||||
byte[] bytes = resources?.Files.Get(fileInfo.File.GetStoragePath());
|
||||
|
||||
if (bytes == null)
|
||||
continue;
|
||||
|
||||
try
|
||||
{
|
||||
string jsonContent = Encoding.UTF8.GetString(bytes);
|
||||
var deserializedContent = JsonConvert.DeserializeObject<IEnumerable<SkinnableInfo>>(jsonContent);
|
||||
|
||||
if (deserializedContent == null)
|
||||
continue;
|
||||
|
||||
DrawableComponentInfo[skinnableTarget] = deserializedContent.ToArray();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.Error(ex, "Failed to load skin configuration.");
|
||||
}
|
||||
DrawableComponentInfo[skinnableTarget] = deserializedContent.ToArray();
|
||||
}
|
||||
});
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.Error(ex, "Failed to load skin configuration.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void ParseConfigurationStream(Stream stream)
|
||||
@ -101,16 +118,6 @@ namespace osu.Game.Skinning
|
||||
Configuration = new LegacySkinDecoder().Decode(reader);
|
||||
}
|
||||
|
||||
private Stream getConfigurationStream()
|
||||
{
|
||||
string path = SkinInfo.PerformRead(s => s.Files.SingleOrDefault(f => f.Filename.Equals(@"skin.ini", StringComparison.OrdinalIgnoreCase))?.File.GetStoragePath());
|
||||
|
||||
if (string.IsNullOrEmpty(path))
|
||||
return null;
|
||||
|
||||
return resources?.Files.GetStream(path);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Remove all stored customisations for the provided target.
|
||||
/// </summary>
|
||||
@ -168,6 +175,9 @@ namespace osu.Game.Skinning
|
||||
return;
|
||||
|
||||
isDisposed = true;
|
||||
|
||||
Textures?.Dispose();
|
||||
Samples?.Dispose();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
@ -96,12 +96,14 @@ namespace osu.Game.Tests.Beatmaps
|
||||
AddStep("setup skins", () =>
|
||||
{
|
||||
userSkinInfo.Files.Clear();
|
||||
userSkinInfo.Files.Add(new RealmNamedFileUsage(new RealmFile { Hash = userFile }, userFile));
|
||||
if (!string.IsNullOrEmpty(userFile))
|
||||
userSkinInfo.Files.Add(new RealmNamedFileUsage(new RealmFile { Hash = userFile }, userFile));
|
||||
|
||||
Debug.Assert(beatmapInfo.BeatmapSet != null);
|
||||
|
||||
beatmapInfo.BeatmapSet.Files.Clear();
|
||||
beatmapInfo.BeatmapSet.Files.Add(new RealmNamedFileUsage(new RealmFile { Hash = beatmapFile }, beatmapFile));
|
||||
if (!string.IsNullOrEmpty(beatmapFile))
|
||||
beatmapInfo.BeatmapSet.Files.Add(new RealmNamedFileUsage(new RealmFile { Hash = beatmapFile }, beatmapFile));
|
||||
|
||||
// Need to refresh the cached skin source to refresh the skin resource store.
|
||||
dependencies.SkinSource = new SkinProvidingContainer(Skin = new LegacySkin(userSkinInfo, this));
|
||||
@ -191,22 +193,32 @@ namespace osu.Game.Tests.Beatmaps
|
||||
}
|
||||
}
|
||||
|
||||
private class TestWorkingBeatmap : ClockBackedTestWorkingBeatmap
|
||||
private class TestWorkingBeatmap : ClockBackedTestWorkingBeatmap, IStorageResourceProvider
|
||||
{
|
||||
private readonly BeatmapInfo skinBeatmapInfo;
|
||||
private readonly IResourceStore<byte[]> resourceStore;
|
||||
|
||||
private readonly IStorageResourceProvider resources;
|
||||
|
||||
public TestWorkingBeatmap(BeatmapInfo skinBeatmapInfo, IResourceStore<byte[]> resourceStore, IBeatmap beatmap, Storyboard storyboard, IFrameBasedClock referenceClock, IStorageResourceProvider resources)
|
||||
public TestWorkingBeatmap(BeatmapInfo skinBeatmapInfo, IResourceStore<byte[]> accessMarkingResourceStore, IBeatmap beatmap, Storyboard storyboard, IFrameBasedClock referenceClock,
|
||||
IStorageResourceProvider resources)
|
||||
: base(beatmap, storyboard, referenceClock, resources.AudioManager)
|
||||
{
|
||||
this.skinBeatmapInfo = skinBeatmapInfo;
|
||||
this.resourceStore = resourceStore;
|
||||
Files = accessMarkingResourceStore;
|
||||
this.resources = resources;
|
||||
}
|
||||
|
||||
protected internal override ISkin GetSkin() => new LegacyBeatmapSkin(skinBeatmapInfo, resourceStore, resources);
|
||||
protected internal override ISkin GetSkin() => new LegacyBeatmapSkin(skinBeatmapInfo, this);
|
||||
|
||||
public AudioManager AudioManager => resources.AudioManager;
|
||||
|
||||
public IResourceStore<byte[]> Files { get; }
|
||||
|
||||
public IResourceStore<byte[]> Resources => resources.Resources;
|
||||
|
||||
public RealmAccess RealmAccess => resources.RealmAccess;
|
||||
|
||||
public IResourceStore<TextureUpload> CreateTextureLoaderStore(IResourceStore<byte[]> underlyingStore) => resources.CreateTextureLoaderStore(underlyingStore);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -7,7 +7,6 @@ using System.Linq;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Audio;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.IO.Stores;
|
||||
using osu.Framework.Testing;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Skinning;
|
||||
@ -112,7 +111,7 @@ namespace osu.Game.Tests.Beatmaps
|
||||
public static readonly Color4 HYPER_DASH_FRUIT_COLOUR = Color4.DarkGoldenrod;
|
||||
|
||||
public TestBeatmapSkin(BeatmapInfo beatmapInfo, bool hasColours)
|
||||
: base(beatmapInfo, new ResourceStore<byte[]>(), null)
|
||||
: base(beatmapInfo, null)
|
||||
{
|
||||
if (hasColours)
|
||||
{
|
||||
@ -141,7 +140,7 @@ namespace osu.Game.Tests.Beatmaps
|
||||
public static readonly Color4 HYPER_DASH_FRUIT_COLOUR = Color4.LightCyan;
|
||||
|
||||
public TestSkin(bool hasCustomColours)
|
||||
: base(new SkinInfo(), new ResourceStore<byte[]>(), null, string.Empty)
|
||||
: base(new SkinInfo(), null, null)
|
||||
{
|
||||
if (hasCustomColours)
|
||||
{
|
||||
|
@ -187,7 +187,7 @@ namespace osu.Game.Tests.Visual
|
||||
private readonly bool extrapolateAnimations;
|
||||
|
||||
public TestLegacySkin(SkinInfo skin, IResourceStore<byte[]> storage, IStorageResourceProvider resources, bool extrapolateAnimations)
|
||||
: base(skin, storage, resources, "skin.ini")
|
||||
: base(skin, resources, storage)
|
||||
{
|
||||
this.extrapolateAnimations = extrapolateAnimations;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user