summaryrefslogtreecommitdiff
path: root/src/StardewModdingAPI.Toolkit/Framework/ModScanning/ModScanner.cs
blob: d3662c9cf51afaf389ea1be7738edec644ed4a3d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
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
{
    /// <summary>Scans folders for mod data.</summary>
    public class ModScanner
    {
        /*********
        ** Properties
        *********/
        /// <summary>The JSON helper with which to read manifests.</summary>
        private readonly JsonHelper JsonHelper;


        /*********
        ** Public methods
        *********/
        /// <summary>Construct an instance.</summary>
        /// <param name="jsonHelper">The JSON helper with which to read manifests.</param>
        public ModScanner(JsonHelper jsonHelper)
        {
            this.JsonHelper = jsonHelper;
        }

        /// <summary>Extract information about all mods in the given folder.</summary>
        /// <param name="rootPath">The root folder containing mods.</param>
        public IEnumerable<ModFolder> GetModFolders(string rootPath)
        {
            foreach (DirectoryInfo folder in new DirectoryInfo(rootPath).EnumerateDirectories())
                yield return this.ReadFolder(rootPath, folder);
        }

        /// <summary>Extract information from a mod folder.</summary>
        /// <param name="rootPath">The root folder containing mods.</param>
        /// <param name="searchFolder">The folder to search for a mod.</param>
        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<Manifest>(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
        *********/
        /// <summary>Find the manifest for a mod folder.</summary>
        /// <param name="folder">The folder to search.</param>
        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;
            }
        }
    }
}