using System; using System.Reflection; using StardewModdingAPI.Internal; #pragma warning disable CS0618 // obsolete asset interceptors deliberately supported here namespace StardewModdingAPI.Framework.Content { /// A wrapper for and for internal cache invalidation. internal class AssetInterceptorChange { /********* ** Accessors *********/ /// The mod which registered the interceptor. public IModMetadata Mod { get; } /// The interceptor instance. public object Instance { get; } /// Whether the asset interceptor was added since the last tick. Mutually exclusive with . public bool WasAdded { get; } /// Whether the asset interceptor was removed since the last tick. Mutually exclusive with . public bool WasRemoved => this.WasAdded; /********* ** Public methods *********/ /// Construct an instance. /// The mod registering the interceptor. /// The interceptor. This must be an or instance. /// Whether the asset interceptor was added since the last tick; else removed. public AssetInterceptorChange(IModMetadata mod, object instance, bool wasAdded) { this.Mod = mod ?? throw new ArgumentNullException(nameof(mod)); this.Instance = instance ?? throw new ArgumentNullException(nameof(instance)); this.WasAdded = wasAdded; if (instance is not (IAssetEditor or IAssetLoader)) throw new InvalidCastException($"The provided {nameof(instance)} value must be an {nameof(IAssetEditor)} or {nameof(IAssetLoader)} instance."); } /// Get whether this instance can intercept the given asset. /// Basic metadata about the asset being loaded. public bool CanIntercept(IAssetInfo asset) { MethodInfo? canIntercept = this.GetType().GetMethod(nameof(this.CanInterceptImpl), BindingFlags.Instance | BindingFlags.NonPublic); if (canIntercept == null) throw new InvalidOperationException($"SMAPI couldn't access the {nameof(AssetInterceptorChange)}.{nameof(this.CanInterceptImpl)} implementation."); return (bool)canIntercept.MakeGenericMethod(asset.DataType).Invoke(this, new object[] { asset })!; } /********* ** Private methods *********/ /// Get whether this instance can intercept the given asset. /// The asset type. /// Basic metadata about the asset being loaded. private bool CanInterceptImpl(IAssetInfo asset) { // check edit if (this.Instance is IAssetEditor editor) { Context.HeuristicModsRunningCode.Push(this.Mod); try { if (editor.CanEdit(asset)) return true; } catch (Exception ex) { this.Mod.LogAsMod($"Mod failed when checking whether it could edit asset '{asset.Name}'. Error details:\n{ex.GetLogSummary()}", LogLevel.Error); } finally { Context.HeuristicModsRunningCode.TryPop(out _); } } // check load if (this.Instance is IAssetLoader loader) { Context.HeuristicModsRunningCode.Push(this.Mod); try { if (loader.CanLoad(asset)) return true; } catch (Exception ex) { this.Mod.LogAsMod($"Mod failed when checking whether it could load asset '{asset.Name}'. Error details:\n{ex.GetLogSummary()}", LogLevel.Error); } finally { Context.HeuristicModsRunningCode.TryPop(out _); } } return false; } } }