using System; using System.Collections.Generic; using System.IO; using System.Linq; using StardewModdingAPI.Toolkit.Serialisation; using StardewModdingAPI.Toolkit.Serialisation.Models; namespace StardewModdingAPI.Toolkit.Framework.ModScanning { /// Scans folders for mod data. public class ModScanner { /********* ** Properties *********/ /// The JSON helper with which to read manifests. private readonly JsonHelper JsonHelper; /********* ** Public methods *********/ /// Construct an instance. /// The JSON helper with which to read manifests. public ModScanner(JsonHelper jsonHelper) { this.JsonHelper = jsonHelper; } /// Extract information about all mods in the given folder. /// The root folder containing mods. public IEnumerable GetModFolders(string rootPath) { foreach (DirectoryInfo folder in new DirectoryInfo(rootPath).EnumerateDirectories()) yield return this.ReadFolder(rootPath, folder); } /// Extract information from a mod folder. /// The root folder containing mods. /// The folder to search for a mod. public ModFolder ReadFolder(string rootPath, DirectoryInfo searchFolder) { // find manifest.json FileInfo manifestFile = this.FindManifest(searchFolder); if (manifestFile == null) return new ModFolder(searchFolder); // read mod info Manifest manifest = null; string manifestError = null; { try { manifest = this.JsonHelper.ReadJsonFile(manifestFile.FullName); if (manifest == null) { manifestError = File.Exists(manifestFile.FullName) ? "its manifest is invalid." : "it doesn't have a manifest."; } } catch (SParseException ex) { manifestError = $"parsing its manifest failed: {ex.Message}"; } catch (Exception ex) { manifestError = $"parsing its manifest failed:\n{ex}"; } } return new ModFolder(searchFolder, manifestFile.Directory, manifest, manifestError); } /********* ** Private methods *********/ /// Find the manifest for a mod folder. /// The folder to search. private FileInfo FindManifest(DirectoryInfo folder) { while (true) { // check for manifest in current folder FileInfo file = new FileInfo(Path.Combine(folder.FullName, "manifest.json")); if (file.Exists) return file; // check for single subfolder FileSystemInfo[] entries = folder.EnumerateFileSystemInfos().Take(2).ToArray(); if (entries.Length == 1 && entries[0] is DirectoryInfo subfolder) { folder = subfolder; continue; } // not found return null; } } } }