summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorJesse Plamondon-Willard <github@jplamondonw.com>2018-08-19 20:27:28 -0400
committerJesse Plamondon-Willard <github@jplamondonw.com>2018-08-19 20:27:28 -0400
commit100e303b488a36e8410ff67e32c35bff80f21ba2 (patch)
tree53ec306a14025726c35c8cebf0bd72370abf996e /src
parent826dd53ab550e5b92796c510569118beee6bd044 (diff)
downloadSMAPI-100e303b488a36e8410ff67e32c35bff80f21ba2.tar.gz
SMAPI-100e303b488a36e8410ff67e32c35bff80f21ba2.tar.bz2
SMAPI-100e303b488a36e8410ff67e32c35bff80f21ba2.zip
add recursive mod search (#583)
Diffstat (limited to 'src')
-rw-r--r--src/SMAPI/Framework/ModLoading/ModResolver.cs4
-rw-r--r--src/StardewModdingAPI.Toolkit/Framework/ModScanning/ModFolder.cs15
-rw-r--r--src/StardewModdingAPI.Toolkit/Framework/ModScanning/ModScanner.cs64
3 files changed, 65 insertions, 18 deletions
diff --git a/src/SMAPI/Framework/ModLoading/ModResolver.cs b/src/SMAPI/Framework/ModLoading/ModResolver.cs
index 09880d03..11518444 100644
--- a/src/SMAPI/Framework/ModLoading/ModResolver.cs
+++ b/src/SMAPI/Framework/ModLoading/ModResolver.cs
@@ -35,7 +35,7 @@ namespace StardewModdingAPI.Framework.ModLoading
if (string.IsNullOrWhiteSpace(displayName))
displayName = dataRecord?.DisplayName;
if (string.IsNullOrWhiteSpace(displayName))
- displayName = PathUtilities.GetRelativePath(rootPath, folder.ActualDirectory?.FullName ?? folder.SearchDirectory.FullName);
+ displayName = PathUtilities.GetRelativePath(rootPath, folder.Directory.FullName);
// apply defaults
if (manifest != null && dataRecord != null)
@@ -48,7 +48,7 @@ namespace StardewModdingAPI.Framework.ModLoading
ModMetadataStatus status = folder.ManifestParseError == null
? ModMetadataStatus.Found
: ModMetadataStatus.Failed;
- yield return new ModMetadata(displayName, folder.ActualDirectory?.FullName, manifest, dataRecord).SetStatus(status, folder.ManifestParseError);
+ yield return new ModMetadata(displayName, folder.Directory.FullName, manifest, dataRecord).SetStatus(status, folder.ManifestParseError);
}
}
diff --git a/src/StardewModdingAPI.Toolkit/Framework/ModScanning/ModFolder.cs b/src/StardewModdingAPI.Toolkit/Framework/ModScanning/ModFolder.cs
index 4aaa3f83..83c9c44d 100644
--- a/src/StardewModdingAPI.Toolkit/Framework/ModScanning/ModFolder.cs
+++ b/src/StardewModdingAPI.Toolkit/Framework/ModScanning/ModFolder.cs
@@ -11,11 +11,8 @@ namespace StardewModdingAPI.Toolkit.Framework.ModScanning
/*********
** Accessors
*********/
- /// <summary>The Mods subfolder containing this mod.</summary>
- public DirectoryInfo SearchDirectory { get; }
-
- /// <summary>The folder containing manifest.json.</summary>
- public DirectoryInfo ActualDirectory { get; }
+ /// <summary>The folder containing the mod's manifest.json.</summary>
+ public DirectoryInfo Directory { get; }
/// <summary>The mod manifest.</summary>
public Manifest Manifest { get; }
@@ -28,14 +25,12 @@ namespace StardewModdingAPI.Toolkit.Framework.ModScanning
** Public methods
*********/
/// <summary>Construct an instance.</summary>
- /// <param name="searchDirectory">The Mods subfolder containing this mod.</param>
- /// <param name="actualDirectory">The folder containing manifest.json.</param>
+ /// <param name="directory">The folder containing the mod's manifest.json.</param>
/// <param name="manifest">The mod manifest.</param>
/// <param name="manifestParseError">The error which occurred parsing the manifest, if any.</param>
- public ModFolder(DirectoryInfo searchDirectory, DirectoryInfo actualDirectory, Manifest manifest, string manifestParseError = null)
+ public ModFolder(DirectoryInfo directory, Manifest manifest, string manifestParseError = null)
{
- this.SearchDirectory = searchDirectory;
- this.ActualDirectory = actualDirectory;
+ this.Directory = directory;
this.Manifest = manifest;
this.ManifestParseError = manifestParseError;
}
diff --git a/src/StardewModdingAPI.Toolkit/Framework/ModScanning/ModScanner.cs b/src/StardewModdingAPI.Toolkit/Framework/ModScanning/ModScanner.cs
index f1cce4a4..063ec2f4 100644
--- a/src/StardewModdingAPI.Toolkit/Framework/ModScanning/ModScanner.cs
+++ b/src/StardewModdingAPI.Toolkit/Framework/ModScanning/ModScanner.cs
@@ -16,6 +16,14 @@ namespace StardewModdingAPI.Toolkit.Framework.ModScanning
/// <summary>The JSON helper with which to read manifests.</summary>
private readonly JsonHelper JsonHelper;
+ /// <summary>A list of filesystem entry names to ignore when checking whether a folder should be treated as a mod.</summary>
+ private readonly HashSet<string> IgnoreFilesystemEntries = new HashSet<string>(StringComparer.InvariantCultureIgnoreCase)
+ {
+ ".DS_Store",
+ "mcs",
+ "Thumbs.db"
+ };
+
/*********
** Public methods
@@ -31,19 +39,23 @@ namespace StardewModdingAPI.Toolkit.Framework.ModScanning
/// <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);
+ DirectoryInfo root = new DirectoryInfo(rootPath);
+ return this.GetModFolders(root, root);
}
/// <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)
+ public ModFolder ReadFolder(DirectoryInfo searchFolder)
{
// find manifest.json
FileInfo manifestFile = this.FindManifest(searchFolder);
if (manifestFile == null)
- return new ModFolder(searchFolder, null, null, "it doesn't have a manifest.");
+ {
+ bool isEmpty = !searchFolder.GetFileSystemInfos().Where(this.IsRelevant).Any();
+ if (isEmpty)
+ return new ModFolder(searchFolder, null, "it's an empty folder.");
+ return new ModFolder(searchFolder, null, "it contains files, but none of them are manifest.json.");
+ }
// read mod info
Manifest manifest = null;
@@ -64,13 +76,33 @@ namespace StardewModdingAPI.Toolkit.Framework.ModScanning
}
}
- return new ModFolder(searchFolder, manifestFile.Directory, manifest, manifestError);
+ return new ModFolder(manifestFile.Directory, manifest, manifestError);
}
/*********
** Private methods
*********/
+ /// <summary>Recursively extract information about all mods in the given folder.</summary>
+ /// <param name="root">The root mod folder.</param>
+ /// <param name="folder">The folder to search for mods.</param>
+ public IEnumerable<ModFolder> GetModFolders(DirectoryInfo root, DirectoryInfo folder)
+ {
+ // recurse into subfolders
+ if (this.IsModSearchFolder(root, folder))
+ {
+ foreach (DirectoryInfo subfolder in folder.EnumerateDirectories())
+ {
+ foreach (ModFolder match in this.GetModFolders(root, subfolder))
+ yield return match;
+ }
+ }
+
+ // treat as mod folder
+ else
+ yield return this.ReadFolder(folder);
+ }
+
/// <summary>Find the manifest for a mod folder.</summary>
/// <param name="folder">The folder to search.</param>
private FileInfo FindManifest(DirectoryInfo folder)
@@ -94,5 +126,25 @@ namespace StardewModdingAPI.Toolkit.Framework.ModScanning
return null;
}
}
+
+ /// <summary>Get whether a given folder should be treated as a search folder (i.e. look for subfolders containing mods).</summary>
+ /// <param name="root">The root mod folder.</param>
+ /// <param name="folder">The folder to search for mods.</param>
+ private bool IsModSearchFolder(DirectoryInfo root, DirectoryInfo folder)
+ {
+ if (root.FullName == folder.FullName)
+ return true;
+
+ DirectoryInfo[] subfolders = folder.GetDirectories().Where(this.IsRelevant).ToArray();
+ FileInfo[] files = folder.GetFiles().Where(this.IsRelevant).ToArray();
+ return subfolders.Any() && !files.Any();
+ }
+
+ /// <summary>Get whether a file or folder is relevant when deciding how to process a mod folder.</summary>
+ /// <param name="entry">The file or folder.</param>
+ private bool IsRelevant(FileSystemInfo entry)
+ {
+ return !this.IgnoreFilesystemEntries.Contains(entry.Name);
+ }
}
}