diff --git a/BSIPA-ModList/BSIPA-ModList.csproj b/BSIPA-ModList/BSIPA-ModList.csproj index 77fb4b2e..126b0af5 100644 --- a/BSIPA-ModList/BSIPA-ModList.csproj +++ b/BSIPA-ModList/BSIPA-ModList.csproj @@ -97,6 +97,9 @@ + + + diff --git a/BSIPA-ModList/Icons/Thumbs.db b/BSIPA-ModList/Icons/Thumbs.db index 3f84cf47..f50e0834 100644 Binary files a/BSIPA-ModList/Icons/Thumbs.db and b/BSIPA-ModList/Icons/Thumbs.db differ diff --git a/BSIPA-ModList/Icons/library.png b/BSIPA-ModList/Icons/library.png new file mode 100644 index 00000000..1b9e0aa7 Binary files /dev/null and b/BSIPA-ModList/Icons/library.png differ diff --git a/BSIPA-ModList/UI/ViewControllers/ModListController.cs b/BSIPA-ModList/UI/ViewControllers/ModListController.cs index 48a056dd..91a19968 100644 --- a/BSIPA-ModList/UI/ViewControllers/ModListController.cs +++ b/BSIPA-ModList/UI/ViewControllers/ModListController.cs @@ -118,6 +118,56 @@ namespace BSIPA_ModList.UI list.flow.SetSelected(infoView, immediate: list.flow.HasSelected); } } + private class LibraryModCell : CustomCellInfo, IClickableCell + { + private static Sprite _defaultIcon; + public static Sprite DefaultIcon + { + get + { + if (_defaultIcon == null) + _defaultIcon = UIUtilities.LoadSpriteFromResources("BSIPA_ModList.Icons.library.png"); + return _defaultIcon; + } + } + + internal PluginLoader.PluginInfo Plugin; + private ModListController list; + + public LibraryModCell(ModListController list, PluginLoader.PluginInfo plugin) + : base($"{plugin.Metadata.Name} v{plugin.Metadata.Version}", plugin.Metadata.Manifest.Author, null) + { + Plugin = plugin; + this.list = list; + + if (string.IsNullOrWhiteSpace(subtext)) + subtext = "Unspecified Author"; + + icon = DefaultIcon; + + Logger.log.Debug($"LibraryModCell {plugin.Metadata.Name} {plugin.Metadata.Version}"); + } + + private ModInfoViewController infoView; + + public void OnSelect(ModListController cntrl) + { + Logger.log.Debug($"Selected LibraryModCell {Plugin.Metadata.Name} {Plugin.Metadata.Version}"); + + if (infoView == null) + { + var desc = Plugin.Metadata.Manifest.Description; + if (string.IsNullOrWhiteSpace(desc)) + desc = "No description"; + + infoView = BeatSaberUI.CreateViewController(); + infoView.Init(icon, Plugin.Metadata.Name, "v" + Plugin.Metadata.Version.ToString(), subtext, + desc, Plugin.Metadata.Features.FirstOrDefault(f => f is NoUpdateFeature) == null); + } + + list.flow.SetSelected(infoView, immediate: list.flow.HasSelected); + } + } #pragma warning disable CS0618 private class IPAModCell : CustomCellInfo, IClickableCell @@ -190,10 +240,12 @@ namespace BSIPA_ModList.UI reuseIdentifier = "BSIPAModListTableCell"; - foreach (var plugin in bsipaPlugins) + foreach (var plugin in bsipaPlugins.Where(p => !p.Metadata.IsBare)) Data.Add(new BSIPAModCell(this, plugin)); foreach (var plugin in ignoredPlugins) Data.Add(new BSIPAIgnoredModCell(this, plugin)); + foreach (var plugin in bsipaPlugins.Where(p => p.Metadata.IsBare)) + Data.Add(new LibraryModCell(this, plugin)); foreach (var plugin in ipaPlugins) Data.Add(new IPAModCell(this, plugin)); } diff --git a/IPA.Loader/Loader/PluginLoader.cs b/IPA.Loader/Loader/PluginLoader.cs index a66ee4af..dd4756a4 100644 --- a/IPA.Loader/Loader/PluginLoader.cs +++ b/IPA.Loader/Loader/PluginLoader.cs @@ -71,6 +71,8 @@ namespace IPA.Loader internal bool IsSelf; + internal bool IsBare; + private PluginManifest manifest; internal HashSet Dependencies { get; } = new HashSet(); @@ -212,6 +214,31 @@ namespace IPA.Loader Logger.loader.Error(e); } } + + IEnumerable bareManifests = Directory.GetFiles(BeatSaber.PluginsPath, "*.json"); + bareManifests = bareManifests.Concat(Directory.GetFiles(BeatSaber.PluginsPath, "*.manifest")); + foreach (var manifest in bareManifests) + { + try + { + var metadata = new PluginMetadata + { + File = new FileInfo(Path.Combine(BeatSaber.PluginsPath, manifest)), + IsSelf = false, + IsBare = true, + }; + + metadata.Manifest = JsonConvert.DeserializeObject(File.ReadAllText(manifest)); + + Logger.loader.Debug($"Adding info for bare manifest {Path.GetFileName(manifest)}"); + PluginsMetadata.Add(metadata); + } + catch (Exception e) + { + Logger.loader.Error($"Could not load data for bare manifest {Path.GetFileName(manifest)}"); + Logger.loader.Error(e); + } + } } // keep track of these for the updater; it should still be able to update mods not loaded @@ -219,7 +246,7 @@ namespace IPA.Loader internal static void Resolve() { // resolves duplicates and conflicts, etc - PluginsMetadata.Sort((a, b) => a.Version.CompareTo(b.Version)); + PluginsMetadata.Sort((a, b) => b.Version.CompareTo(a.Version)); var ids = new HashSet(); var ignore = new HashSet(); @@ -431,7 +458,7 @@ namespace IPA.Loader internal static void Load(PluginMetadata meta) { - if (meta.Assembly == null) + if (meta.Assembly == null && meta.PluginType != null) meta.Assembly = Assembly.LoadFrom(meta.File.FullName); } diff --git a/IPA.Loader/Loader/manifest.json b/IPA.Loader/Loader/manifest.json index 3c55f6cb..b53f052b 100644 --- a/IPA.Loader/Loader/manifest.json +++ b/IPA.Loader/Loader/manifest.json @@ -1,5 +1,5 @@ { - "$schema": "https://raw.githubusercontent.com/nike4613/ModSaber-MetadataFileSchema/master/Schema.json", + "$schema": "https://raw.githubusercontent.com/beat-saber-modding-group/BSIPA-MetadataFileSchema/master/Schema.json", "author": "DaNike", "description": "A mod loader specifically for Beat Saber.", "gameVersion": "0.13.2", diff --git a/Refs/BeatSaberCustomUI.dll b/Refs/BeatSaberCustomUI.dll index 297e4173..b22737ac 100644 Binary files a/Refs/BeatSaberCustomUI.dll and b/Refs/BeatSaberCustomUI.dll differ