#nullable enable using IPA.Logging; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using System; using System.IO; namespace IPA.Loader.Features { internal class DefineFeature : Feature { public static bool NewFeature = true; private class DataModel { [JsonProperty("type", Required = Required.Always)] public string TypeName = ""; [JsonProperty("name", Required = Required.DisallowNull)] public string? ActualName = null; public string Name => ActualName ?? TypeName; } private DataModel data = null!; protected override bool Initialize(PluginMetadata meta, JObject featureData) { Logger.Features.Debug("Executing DefineFeature Init"); try { data = featureData.ToObject() ?? throw new InvalidOperationException("Feature data is null"); } catch (Exception e) { InvalidMessage = $"Invalid data: {e}"; return false; } InvalidMessage = $"Feature {data.Name} already exists"; return PreregisterFeature(meta, data.Name); } public override void BeforeInit(PluginMetadata meta) { Logger.Features.Debug("Executing DefineFeature AfterInit"); Type type; try { type = meta.Assembly.GetType(data.TypeName); } catch (ArgumentException) { Logger.Features.Error($"Invalid type name {data.TypeName}"); return; } catch (Exception e) when (e is FileNotFoundException or FileLoadException or BadImageFormatException) { var filename = ""; switch (e) { case FileNotFoundException fn: filename = fn.FileName; break; case FileLoadException fl: filename = fl.FileName; break; case BadImageFormatException bi: filename = bi.FileName; break; } Logger.Features.Error($"Could not find {filename} while loading type"); return; } if (type == null) { Logger.Features.Error($"Invalid type name {data.TypeName}"); return; } try { if (RegisterFeature(meta, data.Name, type)) { NewFeature = true; return; } Logger.Features.Error($"Feature with name {data.Name} already exists"); return; } catch (ArgumentException) { Logger.Features.Error($"{type.FullName} not a subclass of {nameof(Feature)}"); return; } } } }