using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using Newtonsoft.Json;
namespace StardewModdingAPI.Framework.ModData
{
/// Handles access to SMAPI's internal mod metadata list.
internal class ModDatabase
{
/*********
** Properties
*********/
/// The underlying mod data records indexed by default display name.
private readonly IDictionary Records;
/*********
** Public methods
*********/
/// Construct an empty instance.
public ModDatabase()
: this(new Dictionary()) { }
/// Construct an instance.
/// The underlying mod data records indexed by default display name.
public ModDatabase(IDictionary records)
{
this.Records = records;
}
/// Get a parsed representation of the which match a given manifest.
/// The manifest to match.
public ParsedModDataRecord GetParsed(IManifest manifest)
{
// get raw record
if (!this.TryGetRaw(manifest, out string displayName, out ModDataRecord record))
return null;
// parse fields
ParsedModDataRecord parsed = new ParsedModDataRecord { DisplayName = displayName, DataRecord = record };
foreach (ModDataField field in record.GetFields().Where(field => field.IsMatch(manifest)))
{
switch (field.Key)
{
// update key
case ModDataFieldKey.UpdateKey:
parsed.UpdateKey = field.Value;
break;
// alternative URL
case ModDataFieldKey.AlternativeUrl:
parsed.AlternativeUrl = field.Value;
break;
// status
case ModDataFieldKey.Status:
parsed.Status = (ModStatus)Enum.Parse(typeof(ModStatus), field.Value, ignoreCase: true);
parsed.StatusUpperVersion = field.UpperVersion;
break;
// status reason phrase
case ModDataFieldKey.StatusReasonPhrase:
parsed.StatusReasonPhrase = field.Value;
break;
}
}
return parsed;
}
/// Get the display name for a given mod ID (if available).
/// The unique mod ID.
public string GetDisplayNameFor(string id)
{
foreach (var entry in this.Records)
{
if (entry.Value.ID != null && entry.Value.ID.Equals(id, StringComparison.InvariantCultureIgnoreCase))
return entry.Key;
}
return null;
}
/*********
** Private models
*********/
/// Get the data record matching a given manifest.
/// The mod manifest.
/// The mod's default display name.
/// The raw mod record.
private bool TryGetRaw(IManifest manifest, out string displayName, out ModDataRecord record)
{
if (manifest != null)
{
foreach (var entry in this.Records)
{
displayName = entry.Key;
record = entry.Value;
// try main ID
if (record.ID != null && record.ID.Equals(manifest.UniqueID, StringComparison.InvariantCultureIgnoreCase))
return true;
// try former IDs
if (record.FormerIDs != null)
{
foreach (string part in record.FormerIDs.Split('|'))
{
// packed field snapshot
if (part.StartsWith("{"))
{
FieldSnapshot snapshot = JsonConvert.DeserializeObject(part);
bool isMatch =
(snapshot.ID == null || snapshot.ID.Equals(manifest.UniqueID, StringComparison.InvariantCultureIgnoreCase))
&& (snapshot.EntryDll == null || snapshot.EntryDll.Equals(manifest.EntryDll, StringComparison.InvariantCultureIgnoreCase))
&& (
snapshot.Author == null
|| snapshot.Author.Equals(manifest.Author, StringComparison.InvariantCultureIgnoreCase)
|| (manifest.ExtraFields.ContainsKey("Authour") && snapshot.Author.Equals(manifest.ExtraFields["Authour"].ToString(), StringComparison.InvariantCultureIgnoreCase))
)
&& (snapshot.Name == null || snapshot.Name.Equals(manifest.Name, StringComparison.InvariantCultureIgnoreCase));
if (isMatch)
return true;
}
// plain ID
else if (part.Equals(manifest.UniqueID, StringComparison.InvariantCultureIgnoreCase))
return true;
}
}
}
}
displayName = null;
record = null;
return false;
}
/*********
** Private models
*********/
/// A unique set of fields which identifies the mod.
[SuppressMessage("ReSharper", "ClassNeverInstantiated.Local", Justification = "Used via JSON deserialisation.")]
[SuppressMessage("ReSharper", "UnusedAutoPropertyAccessor.Local", Justification = "Used via JSON deserialisation.")]
private class FieldSnapshot
{
/*********
** Accessors
*********/
/// The unique mod ID (or null to ignore it).
public string ID { get; set; }
/// The entry DLL (or null to ignore it).
public string EntryDll { get; set; }
/// The mod name (or null to ignore it).
public string Name { get; set; }
/// The author name (or null to ignore it).
public string Author { get; set; }
}
}
}