using System; using System.Collections.Generic; using System.Linq; using System.Runtime.Serialization; using Newtonsoft.Json; using Newtonsoft.Json.Linq; namespace StardewModdingAPI.Toolkit.Framework.ModData { /// The raw mod metadata from SMAPI's internal mod list. internal class ModDataModel { /********* ** Accessors *********/ /// The mod's current unique ID. public string ID { get; } /// The former mod IDs (if any). /// /// This uses a custom format which uniquely identifies a mod across multiple versions and /// supports matching other fields if no ID was specified. This doesn't include the latest /// ID, if any. If the mod's ID changed over time, multiple variants can be separated by the /// | character. /// public string? FormerIDs { get; } /// The mod warnings to suppress, even if they'd normally be shown. public ModWarning SuppressWarnings { get; } /// This field stores properties that aren't mapped to another field before they're parsed into . [JsonExtensionData] public IDictionary ExtensionData { get; } = new Dictionary(); /// The versioned field data. /// /// This maps field names to values. This should be accessed via . /// Format notes: /// - Each key consists of a field name prefixed with any combination of version range /// and Default, separated by pipes (whitespace trimmed). For example, Name /// will always override the name, Default | Name will only override a blank /// name, and ~1.1 | Default | Name will override blank names up to version 1.1. /// - The version format is min~max (where either side can be blank for unbounded), or /// a single version number. /// - The field name itself corresponds to a value. /// public IDictionary Fields { get; set; } = new Dictionary(); /********* ** Public methods *********/ /// Construct an instance. /// The mod's current unique ID. /// The former mod IDs (if any). /// The mod warnings to suppress, even if they'd normally be shown. public ModDataModel(string id, string? formerIds, ModWarning suppressWarnings) { this.ID = id; this.FormerIDs = formerIds; this.SuppressWarnings = suppressWarnings; } /// Get a parsed representation of the . public IEnumerable GetFields() { foreach (KeyValuePair pair in this.Fields) { // init fields string packedKey = pair.Key; string value = pair.Value; bool isDefault = false; ISemanticVersion? lowerVersion = null; ISemanticVersion? upperVersion = null; // parse string[] parts = packedKey.Split('|').Select(p => p.Trim()).ToArray(); ModDataFieldKey fieldKey = (ModDataFieldKey)Enum.Parse(typeof(ModDataFieldKey), parts.Last(), ignoreCase: true); foreach (string part in parts.Take(parts.Length - 1)) { // 'default' if (part.Equals("Default", StringComparison.OrdinalIgnoreCase)) { isDefault = true; continue; } // version range if (part.Contains("~")) { string[] versionParts = part.Split(new[] { '~' }, 2); lowerVersion = versionParts[0] != "" ? new SemanticVersion(versionParts[0]) : null; upperVersion = versionParts[1] != "" ? new SemanticVersion(versionParts[1]) : null; continue; } // single version lowerVersion = new SemanticVersion(part); upperVersion = new SemanticVersion(part); } yield return new ModDataField(fieldKey, value, isDefault, lowerVersion, upperVersion); } } /// Get the former mod IDs. public IEnumerable GetFormerIDs() { if (this.FormerIDs != null) { foreach (string id in this.FormerIDs.Split('|')) yield return id.Trim(); } } /********* ** Private methods *********/ /// The method invoked after JSON deserialization. /// The deserialization context. [OnDeserialized] private void OnDeserialized(StreamingContext context) { this.Fields = this.ExtensionData.ToDictionary(p => p.Key, p => p.Value.ToString()); this.ExtensionData.Clear(); } } }