// 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.IO; using System.Threading.Tasks; using osu.Framework.Allocation; using osu.Framework.Audio; using osu.Framework.Audio.Sample; using osu.Framework.Bindables; using osu.Framework.Graphics.OpenGL.Textures; using osu.Framework.Graphics.Textures; using osu.Framework.IO.Stores; using osu.Framework.Platform; using osu.Game.Rulesets.Configuration; namespace osu.Game.Rulesets.UI { public class DrawableRulesetDependencies : DependencyContainer, IDisposable { /// /// The texture store to be used for the ruleset. /// public TextureStore TextureStore { get; } /// /// The sample store to be used for the ruleset. /// /// /// This is the local sample store pointing to the ruleset sample resources, /// the cached sample store () retrieves from /// this store and falls back to the parent store if this store doesn't have the requested sample. /// public ISampleStore SampleStore { get; } /// /// The ruleset config manager. /// public IRulesetConfigManager RulesetConfigManager { get; private set; } public DrawableRulesetDependencies(Ruleset ruleset, IReadOnlyDependencyContainer parent) : base(parent) { var resources = ruleset.CreateResourceStore(); if (resources != null) { TextureStore = new TextureStore(parent.Get().CreateTextureLoaderStore(new NamespacedResourceStore(resources, @"Textures"))); CacheAs(TextureStore = new FallbackTextureStore(TextureStore, parent.Get())); SampleStore = parent.Get().GetSampleStore(new NamespacedResourceStore(resources, @"Samples")); SampleStore.PlaybackConcurrency = OsuGameBase.SAMPLE_CONCURRENCY; CacheAs(SampleStore = new FallbackSampleStore(SampleStore, parent.Get())); } RulesetConfigManager = parent.Get().GetConfigFor(ruleset); if (RulesetConfigManager != null) Cache(RulesetConfigManager); } #region Disposal ~DrawableRulesetDependencies() { // required to potentially clean up sample store from audio hierarchy. Dispose(false); } public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } private bool isDisposed; protected void Dispose(bool disposing) { if (isDisposed) return; isDisposed = true; SampleStore?.Dispose(); TextureStore?.Dispose(); RulesetConfigManager = null; } #endregion /// /// A sample store which adds a fallback source and prevents disposal of the fallback source. /// private class FallbackSampleStore : ISampleStore { private readonly ISampleStore primary; private readonly ISampleStore fallback; public FallbackSampleStore(ISampleStore primary, ISampleStore fallback) { this.primary = primary; this.fallback = fallback; } public Sample Get(string name) => primary.Get(name) ?? fallback.Get(name); public Task GetAsync(string name) => primary.GetAsync(name) ?? fallback.GetAsync(name); public Stream GetStream(string name) => primary.GetStream(name) ?? fallback.GetStream(name); public IEnumerable GetAvailableResources() => throw new NotSupportedException(); public void AddAdjustment(AdjustableProperty type, IBindable adjustBindable) => throw new NotSupportedException(); public void RemoveAdjustment(AdjustableProperty type, IBindable adjustBindable) => throw new NotSupportedException(); public void RemoveAllAdjustments(AdjustableProperty type) => throw new NotSupportedException(); public BindableNumber Volume => throw new NotSupportedException(); public BindableNumber Balance => throw new NotSupportedException(); public BindableNumber Frequency => throw new NotSupportedException(); public BindableNumber Tempo => throw new NotSupportedException(); public IBindable GetAggregate(AdjustableProperty type) => throw new NotSupportedException(); public IBindable AggregateVolume => throw new NotSupportedException(); public IBindable AggregateBalance => throw new NotSupportedException(); public IBindable AggregateFrequency => throw new NotSupportedException(); public IBindable AggregateTempo => throw new NotSupportedException(); public void BindAdjustments(IAggregateAudioAdjustment component) => throw new NotImplementedException(); public void UnbindAdjustments(IAggregateAudioAdjustment component) => throw new NotImplementedException(); public int PlaybackConcurrency { get => throw new NotSupportedException(); set => throw new NotSupportedException(); } public void Dispose() { primary?.Dispose(); } } /// /// A texture store which adds a fallback source and prevents disposal of the fallback source. /// private class FallbackTextureStore : TextureStore { private readonly TextureStore primary; private readonly TextureStore fallback; public FallbackTextureStore(TextureStore primary, TextureStore fallback) { this.primary = primary; this.fallback = fallback; } public override Texture Get(string name, WrapMode wrapModeS, WrapMode wrapModeT) => primary.Get(name, wrapModeS, wrapModeT) ?? fallback.Get(name, wrapModeS, wrapModeT); protected override void Dispose(bool disposing) { base.Dispose(disposing); primary?.Dispose(); } } } }