using System.Collections.Generic; using System.IO; using System.Linq; 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.Framework.UpdateData; using StardewModdingAPI.Toolkit.Serialization; 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 Dictionary VendorModUrls = new() { [ModSiteKey.Chucklefish] = "https://community.playstarbound.com/resources/{0}", [ModSiteKey.GitHub] = "https://github.com/{0}/releases", [ModSiteKey.Nexus] = "https://www.nexusmods.com/stardewvalley/mods/{0}" }; /********* ** Accessors *********/ /// Encapsulates SMAPI's JSON parsing. public JsonHelper JsonHelper { get; } = new(); /********* ** 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() { WikiClient client = new(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. /// Whether to match file paths case-insensitively, even on Linux. public IEnumerable GetModFolders(string rootPath, bool useCaseInsensitiveFilePaths) { return new ModScanner(this.JsonHelper).GetModFolders(rootPath, useCaseInsensitiveFilePaths); } /// 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. /// Whether to match file paths case-insensitively, even on Linux. public IEnumerable GetModFolders(string rootPath, string modPath, bool useCaseInsensitiveFilePaths) { return new ModScanner(this.JsonHelper).GetModFolders(rootPath, modPath, useCaseInsensitiveFilePaths); } /// Get an update URL for an update key (if valid). /// The update key. public string? GetUpdateUrl(string updateKey) { UpdateKey parsed = UpdateKey.Parse(updateKey); if (!parsed.LooksValid) return null; if (this.VendorModUrls.TryGetValue(parsed.Site, out string? urlTemplate)) return string.Format(urlTemplate, parsed.ID); return null; } } }