using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Runtime.CompilerServices; using System.Threading.Tasks; using Newtonsoft.Json; using StardewModdingAPI.Toolkit.Framework.Clients.Wiki; using StardewModdingAPI.Toolkit.Framework.GameScanning; using StardewModdingAPI.Toolkit.Framework.ModData; using StardewModdingAPI.Toolkit.Framework.ModScanning; using StardewModdingAPI.Toolkit.Serialization; [assembly: InternalsVisibleTo("StardewModdingAPI")] [assembly: InternalsVisibleTo("SMAPI.Web")] namespace StardewModdingAPI.Toolkit { /// A convenience wrapper for the various tools. public class ModToolkit { /********* ** Fields *********/ /// The default HTTP user agent for the toolkit. private readonly string UserAgent; /// Maps vendor keys (like Nexus) to their mod URL template (where {0} is the mod ID). This doesn't affect update checks, which defer to the remote web API. private readonly IDictionary VendorModUrls = new Dictionary(StringComparer.InvariantCultureIgnoreCase) { ["Chucklefish"] = "https://community.playstarbound.com/resources/{0}", ["GitHub"] = "https://github.com/{0}/releases", ["Nexus"] = "https://www.nexusmods.com/stardewvalley/mods/{0}" }; /********* ** Accessors *********/ /// Encapsulates SMAPI's JSON parsing. public JsonHelper JsonHelper { get; } = new JsonHelper(); /********* ** Public methods *********/ /// Construct an instance. public ModToolkit() { ISemanticVersion version = new SemanticVersion(this.GetType().Assembly.GetName().Version); this.UserAgent = $"SMAPI Mod Handler Toolkit/{version}"; } /// Find valid Stardew Valley install folders. /// This checks default game locations, and on Windows checks the Windows registry for GOG/Steam install data. A folder is considered 'valid' if it contains the Stardew Valley executable for the current OS. public IEnumerable GetGameFolders() { return new GameScanner().Scan(); } /// Extract mod metadata from the wiki compatibility list. public async Task GetWikiCompatibilityListAsync() { var client = new WikiClient(this.UserAgent); return await client.FetchModsAsync(); } /// Get SMAPI's internal mod database. /// The file path for the SMAPI metadata file. public ModDatabase GetModDatabase(string metadataPath) { MetadataModel metadata = JsonConvert.DeserializeObject(File.ReadAllText(metadataPath)); ModDataRecord[] records = metadata.ModData.Select(pair => new ModDataRecord(pair.Key, pair.Value)).ToArray(); return new ModDatabase(records, this.GetUpdateUrl); } /// Extract information about all mods in the given folder. /// The root folder containing mods. public IEnumerable GetModFolders(string rootPath) { return new ModScanner(this.JsonHelper).GetModFolders(rootPath); } /// Extract information about all mods in the given folder. /// The root folder containing mods. Only the will be searched, but this field allows it to be treated as a potential mod folder of its own. /// The mod path to search. public IEnumerable GetModFolders(string rootPath, string modPath) { return new ModScanner(this.JsonHelper).GetModFolders(rootPath, modPath); } /// Get an update URL for an update key (if valid). /// The update key. public string GetUpdateUrl(string updateKey) { string[] parts = updateKey.Split(new[] { ':' }, 2); if (parts.Length != 2) return null; string vendorKey = parts[0].Trim(); string modID = parts[1].Trim(); if (this.VendorModUrls.TryGetValue(vendorKey, out string urlTemplate)) return string.Format(urlTemplate, modID); return null; } } }