From 40656e5058005d2932e3c7d147b4b4015e3053d6 Mon Sep 17 00:00:00 2001 From: bcssov Date: Thu, 1 Dec 2022 23:43:12 +0100 Subject: [PATCH] Fix Victoria 3 import and mod type indicators Make Irony able to locate mods with differently named descriptors but still pointing towards the same path --- .../Mods/Exporter/BaseExporter.cs | 20 ++- .../Mods/Exporter/JsonExporter.cs | 12 +- .../Mods/Exporter/SQLiteExporter.cs | 87 +++++++----- .../Mods/Importers/ParadoxLauncherImporter.cs | 20 ++- .../IModCollection.cs | 8 +- src/IronyModManager.Models/ModCollection.cs | 9 +- .../ModBaseService.cs | 17 ++- .../ModCollectionService.cs | 31 ++++- src/IronyModManager.Services/ModService.cs | 28 ++-- .../AddNewCollectionControlViewModel.cs | 3 +- .../CollectionModsControlViewModel.cs | 127 +++++++++++++----- .../ModifyCollectionControlViewModel.cs | 5 +- 12 files changed, 253 insertions(+), 114 deletions(-) diff --git a/src/IronyModManager.IO/Mods/Exporter/BaseExporter.cs b/src/IronyModManager.IO/Mods/Exporter/BaseExporter.cs index 9ec2d69b4..ed512c711 100644 --- a/src/IronyModManager.IO/Mods/Exporter/BaseExporter.cs +++ b/src/IronyModManager.IO/Mods/Exporter/BaseExporter.cs @@ -4,15 +4,15 @@ // Created : 08-11-2020 // // Last Modified By : Mario -// Last Modified On : 10-06-2020 +// Last Modified On : 12-01-2022 // *********************************************************************** // // Mario // // // *********************************************************************** -using System.Collections.Generic; using System; +using System.Collections.Generic; using System.IO; using System.Linq; using IronyModManager.IO.Mods.Models.Paradox; @@ -29,6 +29,16 @@ internal abstract class BaseExporter { #region Methods + /// + /// Maps the name of the file. + /// + /// The mod. + /// System.String. + protected virtual string MapFileName(IMod mod) + { + return mod.FileName; + } + /// /// Maps the mod data. /// @@ -79,15 +89,15 @@ protected virtual void MapPdxPath(IPdxMod pdxMod, IMod mod) if (mod.FileName.EndsWith(Shared.Constants.ZipExtension, StringComparison.OrdinalIgnoreCase) || mod.FileName.EndsWith(Shared.Constants.BinExtension, StringComparison.OrdinalIgnoreCase)) { - pdxMod.ArchivePath = mod.FileName; + pdxMod.ArchivePath = MapFileName(mod); if (mod.Source != ModSource.Local) { - pdxMod.DirPath = Path.GetDirectoryName(mod.FileName); + pdxMod.DirPath = Path.GetDirectoryName(MapFileName(mod)); } } else { - pdxMod.DirPath = mod.FileName; + pdxMod.DirPath = MapFileName(mod); } } diff --git a/src/IronyModManager.IO/Mods/Exporter/JsonExporter.cs b/src/IronyModManager.IO/Mods/Exporter/JsonExporter.cs index 07aad15d6..1938c058e 100644 --- a/src/IronyModManager.IO/Mods/Exporter/JsonExporter.cs +++ b/src/IronyModManager.IO/Mods/Exporter/JsonExporter.cs @@ -4,7 +4,7 @@ // Created : 08-11-2020 // // Last Modified By : Mario -// Last Modified On : 10-29-2022 +// Last Modified On : 12-01-2022 // *********************************************************************** // // Mario @@ -175,26 +175,20 @@ private async Task ExportContentLoadModsAsync(ModWriterParameters paramete contentLoad.EnabledMods.Clear(); } - if (parameters.EnabledMods != null) - { - parameters.EnabledMods.ToList().ForEach(p => + parameters.EnabledMods?.ToList().ForEach(p => { contentLoad.EnabledMods.Add(new EnabledMod() { Path = p.FullPath }); }); - } - if (parameters.TopPriorityMods != null) - { - parameters.TopPriorityMods.ToList().ForEach(p => + parameters.TopPriorityMods?.ToList().ForEach(p => { contentLoad.EnabledMods.Add(new EnabledMod() { Path = p.FullPath }); }); - } return await WritePdxModelAsync(contentLoad, contentPath); } diff --git a/src/IronyModManager.IO/Mods/Exporter/SQLiteExporter.cs b/src/IronyModManager.IO/Mods/Exporter/SQLiteExporter.cs index 2a76383cb..b6cab352c 100644 --- a/src/IronyModManager.IO/Mods/Exporter/SQLiteExporter.cs +++ b/src/IronyModManager.IO/Mods/Exporter/SQLiteExporter.cs @@ -4,7 +4,7 @@ // Created : 08-11-2020 // // Last Modified By : Mario -// Last Modified On : 10-29-2022 +// Last Modified On : 12-01-2022 // *********************************************************************** // // Mario @@ -18,6 +18,7 @@ using System.IO; using System.Linq; using System.Threading.Tasks; +using IronyModManager.IO.Common; using IronyModManager.IO.Common.Mods; using IronyModManager.Models.Common; using IronyModManager.Shared; @@ -451,17 +452,19 @@ Models.Paradox.v4.Playsets getDefaultIronyCollection() /// /// The PDX mod. /// The mod. + /// Type of the descriptor. /// Models.Paradox.v2.Mods. - private Models.Paradox.v2.Mods MapPdxModV2(Models.Paradox.v2.Mods pdxMod, IMod mod) + private Models.Paradox.v2.Mods MapPdxModV2(Models.Paradox.v2.Mods pdxMod, IMod mod, DescriptorType descriptorType) { - if (pdxMod == null) + pdxMod ??= new Models.Paradox.v2.Mods() { - pdxMod = new Models.Paradox.v2.Mods() - { - Id = Guid.NewGuid().ToString() - }; - } + Id = Guid.NewGuid().ToString() + }; MapModData(pdxMod, mod); + if (descriptorType == DescriptorType.JsonMetadata) + { + pdxMod.GameRegistryId = null; + } return pdxMod; } @@ -470,20 +473,32 @@ private Models.Paradox.v2.Mods MapPdxModV2(Models.Paradox.v2.Mods pdxMod, IMod m /// /// The PDX mod. /// The mod. + /// Type of the descriptor. /// Models.Paradox.v4.Mods. - private Models.Paradox.v4.Mods MapPdxModV4(Models.Paradox.v4.Mods pdxMod, IMod mod) + private Models.Paradox.v4.Mods MapPdxModV4(Models.Paradox.v4.Mods pdxMod, IMod mod, DescriptorType descriptorType) { - if (pdxMod == null) + pdxMod ??= new Models.Paradox.v4.Mods() { - pdxMod = new Models.Paradox.v4.Mods() - { - Id = Guid.NewGuid().ToString() - }; - } + Id = Guid.NewGuid().ToString() + }; MapModData(pdxMod, mod); + if (descriptorType == DescriptorType.JsonMetadata) + { + pdxMod.GameRegistryId = null; + } return pdxMod; } + /// + /// Maps the name of the file. + /// + /// The mod. + /// System.String. + protected override string MapFileName(IMod mod) + { + return mod.FileName.StandardizeDirectorySeparator(); + } + /// /// prepare mods transaction as an asynchronous operation. /// @@ -492,8 +507,9 @@ private Models.Paradox.v4.Mods MapPdxModV4(Models.Paradox.v4.Mods pdxMod, IMod m /// The export mods. /// The mods. /// if set to true [remove invalid]. + /// Type of the descriptor. /// IEnumerable<Models.Paradox.v2.Mods>. - private async Task> PrepareModsTransactionV2Async(IDbConnection con, IDbTransaction transaction, List exportMods, IEnumerable mods, bool removeInvalid) + private async Task> PrepareModsTransactionV2Async(IDbConnection con, IDbTransaction transaction, List exportMods, IEnumerable mods, bool removeInvalid, DescriptorType descriptorType) { var result = new HashSet(); if (mods?.Count() > 0) @@ -513,10 +529,10 @@ private Models.Paradox.v4.Mods MapPdxModV4(Models.Paradox.v4.Mods pdxMod, IMod m } foreach (var item in exportMods) { - var pdxMod = mods.FirstOrDefault(p => p.GameRegistryId.Equals(item.DescriptorFile, StringComparison.OrdinalIgnoreCase)); + var pdxMod = descriptorType == DescriptorType.DescriptorMod ? mods.FirstOrDefault(p => p.GameRegistryId.Equals(item.DescriptorFile, StringComparison.OrdinalIgnoreCase)) : mods.FirstOrDefault(p => p.DirPath.StandardizeDirectorySeparator().Equals(item.FileName.StandardizeDirectorySeparator(), StringComparison.OrdinalIgnoreCase)); if (pdxMod == null) { - var newPdxMod = MapPdxModV2(null, item); + var newPdxMod = MapPdxModV2(null, item, descriptorType); toInsert.Add(newPdxMod); result.Add(newPdxMod); } @@ -525,13 +541,13 @@ private Models.Paradox.v4.Mods MapPdxModV4(Models.Paradox.v4.Mods pdxMod, IMod m // Pending delete, insert a new entry instead if (toRemove.Contains(pdxMod)) { - var newPdxMod = MapPdxModV2(null, item); + var newPdxMod = MapPdxModV2(null, item, descriptorType); toInsert.Add(newPdxMod); result.Add(newPdxMod); } else { - var updatedPdxMod = MapPdxModV2(pdxMod, item); + var updatedPdxMod = MapPdxModV2(pdxMod, item, descriptorType); toUpdate.Add(updatedPdxMod); result.Add(updatedPdxMod); } @@ -555,7 +571,7 @@ private Models.Paradox.v4.Mods MapPdxModV4(Models.Paradox.v4.Mods pdxMod, IMod m var toInsert = new HashSet(); foreach (var mod in exportMods) { - var pdxMod = MapPdxModV2(null, mod); + var pdxMod = MapPdxModV2(null, mod, descriptorType); toInsert.Add(pdxMod); result.Add(pdxMod); } @@ -575,8 +591,9 @@ private Models.Paradox.v4.Mods MapPdxModV4(Models.Paradox.v4.Mods pdxMod, IMod m /// The export mods. /// The mods. /// if set to true [remove invalid]. + /// Type of the descriptor. /// A Task<IEnumerable`1> representing the asynchronous operation. - private async Task> PrepareModsTransactionV4Async(IDbConnection con, IDbTransaction transaction, List exportMods, IEnumerable mods, bool removeInvalid) + private async Task> PrepareModsTransactionV4Async(IDbConnection con, IDbTransaction transaction, List exportMods, IEnumerable mods, bool removeInvalid, DescriptorType descriptorType) { var result = new HashSet(); if (mods?.Count() > 0) @@ -596,10 +613,10 @@ private Models.Paradox.v4.Mods MapPdxModV4(Models.Paradox.v4.Mods pdxMod, IMod m } foreach (var item in exportMods) { - var pdxMod = mods.FirstOrDefault(p => p.GameRegistryId.Equals(item.DescriptorFile, StringComparison.OrdinalIgnoreCase)); + var pdxMod = descriptorType == DescriptorType.DescriptorMod ? mods.FirstOrDefault(p => p.GameRegistryId.Equals(item.DescriptorFile, StringComparison.OrdinalIgnoreCase)) : mods.FirstOrDefault(p => p.DirPath.StandardizeDirectorySeparator().Equals(item.FileName.StandardizeDirectorySeparator(), StringComparison.OrdinalIgnoreCase)); if (pdxMod == null) { - var newPdxMod = MapPdxModV4(null, item); + var newPdxMod = MapPdxModV4(null, item, descriptorType); toInsert.Add(newPdxMod); result.Add(newPdxMod); } @@ -608,13 +625,13 @@ private Models.Paradox.v4.Mods MapPdxModV4(Models.Paradox.v4.Mods pdxMod, IMod m // Pending delete, insert a new entry instead if (toRemove.Contains(pdxMod)) { - var newPdxMod = MapPdxModV4(null, item); + var newPdxMod = MapPdxModV4(null, item, descriptorType); toInsert.Add(newPdxMod); result.Add(newPdxMod); } else { - var updatedPdxMod = MapPdxModV4(pdxMod, item); + var updatedPdxMod = MapPdxModV4(pdxMod, item, descriptorType); toUpdate.Add(updatedPdxMod); result.Add(updatedPdxMod); } @@ -638,7 +655,7 @@ private Models.Paradox.v4.Mods MapPdxModV4(Models.Paradox.v4.Mods pdxMod, IMod m var toInsert = new HashSet(); foreach (var mod in exportMods) { - var pdxMod = MapPdxModV4(null, mod); + var pdxMod = MapPdxModV4(null, mod, descriptorType); toInsert.Add(pdxMod); result.Add(pdxMod); } @@ -864,8 +881,12 @@ private async Task SyncModsV2Async(Models.Paradox.v2.Playsets collection, ModWri using var transaction = con.BeginTransaction(); try { - var allMods = await PrepareModsTransactionV2Async(con, transaction, exportMods, mods, !parameters.AppendOnly); - await PreparePlaysetModsTransactionV2Async(con, transaction, collection, allMods.Where(p => enabledMods.Any(s => s.DescriptorFile.Equals(p.GameRegistryId))), !parameters.AppendOnly); + var allMods = await PrepareModsTransactionV2Async(con, transaction, exportMods, mods, !parameters.AppendOnly, parameters.DescriptorType); + await PreparePlaysetModsTransactionV2Async(con, transaction, collection, + parameters.DescriptorType == DescriptorType.DescriptorMod ? + allMods.Where(p => enabledMods.Any(s => s.DescriptorFile.Equals(p.GameRegistryId, StringComparison.OrdinalIgnoreCase))) : + allMods.Where(p => enabledMods.Any(s => s.FileName.StandardizeDirectorySeparator().Equals(p.DirPath.StandardizeDirectorySeparator(), StringComparison.OrdinalIgnoreCase))), + !parameters.AppendOnly); transaction.Commit(); } catch (Exception ex) @@ -906,8 +927,12 @@ private async Task SyncModsV4Async(Models.Paradox.v4.Playsets collection, ModWri using var transaction = con.BeginTransaction(); try { - var allMods = await PrepareModsTransactionV4Async(con, transaction, exportMods, mods, !parameters.AppendOnly); - await PreparePlaysetModsTransactionV4Async(con, transaction, collection, allMods.Where(p => enabledMods.Any(s => s.DescriptorFile.Equals(p.GameRegistryId))), !parameters.AppendOnly); + var allMods = await PrepareModsTransactionV4Async(con, transaction, exportMods, mods, !parameters.AppendOnly, parameters.DescriptorType); + await PreparePlaysetModsTransactionV4Async(con, transaction, collection, + parameters.DescriptorType == DescriptorType.DescriptorMod ? + allMods.Where(p => enabledMods.Any(s => s.DescriptorFile.Equals(p.GameRegistryId, StringComparison.OrdinalIgnoreCase))) : + allMods.Where(p => enabledMods.Any(s => s.FileName.StandardizeDirectorySeparator().Equals(p.DirPath.StandardizeDirectorySeparator(), StringComparison.OrdinalIgnoreCase))), + !parameters.AppendOnly); transaction.Commit(); } catch (Exception ex) diff --git a/src/IronyModManager.IO/Mods/Importers/ParadoxLauncherImporter.cs b/src/IronyModManager.IO/Mods/Importers/ParadoxLauncherImporter.cs index 77b6fc5f1..ed07bd1db 100644 --- a/src/IronyModManager.IO/Mods/Importers/ParadoxLauncherImporter.cs +++ b/src/IronyModManager.IO/Mods/Importers/ParadoxLauncherImporter.cs @@ -4,7 +4,7 @@ // Created : 08-12-2020 // // Last Modified By : Mario -// Last Modified On : 11-29-2022 +// Last Modified On : 12-01-2022 // *********************************************************************** // // Mario @@ -229,7 +229,14 @@ protected virtual async Task DatabaseImportv2Async(ModC { var result = DIResolver.Get(); result.Name = activeCollection.Name; - result.Descriptors = validMods.Select(p => p.GameRegistryId).ToList(); + if (parameters.DescriptorType == Common.DescriptorType.DescriptorMod) + { + result.Descriptors = validMods.Select(p => p.GameRegistryId).ToList(); + } + else + { + result.FullPaths = validMods.Select(p => p.DirPath.StandardizeDirectorySeparator()).ToList(); + } result.ModNames = validMods.Select(p => p.DisplayName).ToList(); return result; } @@ -266,7 +273,14 @@ protected virtual async Task DatabaseImportv3Async(ModC { var result = DIResolver.Get(); result.Name = activeCollection.Name; - result.Descriptors = validMods.Select(p => p.GameRegistryId).ToList(); + if (parameters.DescriptorType == Common.DescriptorType.DescriptorMod) + { + result.Descriptors = validMods.Select(p => p.GameRegistryId).ToList(); + } + else + { + result.FullPaths = validMods.Select(p => p.DirPath.StandardizeDirectorySeparator()).ToList(); + } result.ModNames = validMods.Select(p => p.DisplayName).ToList(); return result; } diff --git a/src/IronyModManager.Models.Common/IModCollection.cs b/src/IronyModManager.Models.Common/IModCollection.cs index b02bc3411..774da4b64 100644 --- a/src/IronyModManager.Models.Common/IModCollection.cs +++ b/src/IronyModManager.Models.Common/IModCollection.cs @@ -4,7 +4,7 @@ // Created : 03-04-2020 // // Last Modified By : Mario -// Last Modified On : 07-12-2022 +// Last Modified On : 12-01-2022 // *********************************************************************** // // Mario @@ -57,6 +57,12 @@ public interface IModCollection : IModel, IQueryableModel /// The mod names. IEnumerable ModNames { get; set; } + /// + /// Gets or sets the mod paths. + /// + /// The mod paths. + IEnumerable ModPaths { get; set; } + /// /// Gets or sets the mods. /// diff --git a/src/IronyModManager.Models/ModCollection.cs b/src/IronyModManager.Models/ModCollection.cs index 9692eab8a..b556b939e 100644 --- a/src/IronyModManager.Models/ModCollection.cs +++ b/src/IronyModManager.Models/ModCollection.cs @@ -4,7 +4,7 @@ // Created : 03-04-2020 // // Last Modified By : Mario -// Last Modified On : 07-12-2022 +// Last Modified On : 12-01-2022 // *********************************************************************** // // Mario @@ -36,6 +36,7 @@ public ModCollection() Mods = new List(); ModNames = new List(); ModIds = new List(); + ModPaths = new List(); } #endregion Constructors @@ -72,6 +73,12 @@ public ModCollection() /// The mod names. public virtual IEnumerable ModNames { get; set; } + /// + /// Gets or sets the mod paths. + /// + /// The mod paths. + public virtual IEnumerable ModPaths { get; set; } + /// /// Gets or sets the mods. /// diff --git a/src/IronyModManager.Services/ModBaseService.cs b/src/IronyModManager.Services/ModBaseService.cs index fbdb7b93b..73c00764a 100644 --- a/src/IronyModManager.Services/ModBaseService.cs +++ b/src/IronyModManager.Services/ModBaseService.cs @@ -4,7 +4,7 @@ // Created : 04-07-2020 // // Last Modified By : Mario -// Last Modified On : 11-12-2022 +// Last Modified On : 12-01-2022 // *********************************************************************** // // Mario @@ -553,9 +553,17 @@ protected virtual IEnumerable GetCollectionMods(IEnumerable mods = n if (collection != null) { - foreach (var item in collection.Mods) + var colMods = collection.Mods.ToList(); + var colModPaths = collection.ModPaths.ToList(); + for (int i = 0; i < colMods.Count; i++) { + var item = colMods[i]; var mod = mods.FirstOrDefault(p => p.DescriptorFile.Equals(item, StringComparison.OrdinalIgnoreCase)); + if (mod == null && colModPaths.Count == colMods.Count) + { + item = colModPaths[i]; + mod = mods.FirstOrDefault(p => p.FullPath.Equals(item, StringComparison.OrdinalIgnoreCase)); + } if (mod != null) { collectionMods.Add(mod); @@ -735,11 +743,10 @@ protected virtual string GetPatchModDirectory(IGame game, IModCollection modColl /// Gets the PDX mod identifier. /// /// The path. - /// if set to true [is directory]. /// System.Int32. - protected virtual long GetPdxModId(string path, bool isDirectory = false) + protected virtual long GetPdxModId(string path) { - var name = !isDirectory ? Path.GetFileNameWithoutExtension(path) : path; + var name = Path.GetFileNameWithoutExtension(path); #pragma warning disable CA1806 // Do not ignore method results long.TryParse(name.Replace(Constants.Paradox_mod_id, string.Empty), out var id); #pragma warning restore CA1806 // Do not ignore method results diff --git a/src/IronyModManager.Services/ModCollectionService.cs b/src/IronyModManager.Services/ModCollectionService.cs index 9615d4acb..086a2faca 100644 --- a/src/IronyModManager.Services/ModCollectionService.cs +++ b/src/IronyModManager.Services/ModCollectionService.cs @@ -4,7 +4,7 @@ // Created : 03-04-2020 // // Last Modified By : Mario -// Last Modified On : 11-05-2022 +// Last Modified On : 12-01-2022 // *********************************************************************** // // Mario @@ -222,6 +222,8 @@ public virtual Task ExportAsync(string file, IModCollection modCollection, collection.ModNames.ToList().ForEach(p => prefixModNames.Add(ModWriter.FormatPrefixModName(modNameOverride, p))); collection.ModNames = prefixModNames; } + // Privacy + collection.ModPaths = null; return modCollectionExporter.ExportAsync(parameters); } @@ -647,9 +649,11 @@ protected virtual void MapImportResult(IModCollection modCollection, ICollection { var sort = importResult.ModIds.ToList(); var collectionMods = mods.Where(p => importResult.ModIds.Any(x => (x.ParadoxId.HasValue && x.ParadoxId.GetValueOrDefault() == p.RemoteId.GetValueOrDefault()) || (x.SteamId.HasValue && x.SteamId.GetValueOrDefault() == p.RemoteId.GetValueOrDefault()))). - OrderBy(p => sort.IndexOf(sort.FirstOrDefault(x => (x.ParadoxId.HasValue && x.ParadoxId.GetValueOrDefault() == p.RemoteId.GetValueOrDefault()) || (x.SteamId.HasValue && x.SteamId.GetValueOrDefault() == p.RemoteId.GetValueOrDefault())))); + OrderBy(p => sort.IndexOf(sort.FirstOrDefault(x => (x.ParadoxId.HasValue && x.ParadoxId.GetValueOrDefault() == p.RemoteId.GetValueOrDefault()) || (x.SteamId.HasValue && x.SteamId.GetValueOrDefault() == p.RemoteId.GetValueOrDefault())))).ToList(); + collectionMods = collectionMods.GroupBy(p => p.FullPath).Select(p => p.FirstOrDefault()).ToList(); modCollection.Mods = collectionMods.Select(p => p.DescriptorFile).ToList(); modCollection.ModNames = collectionMods.Select(p => p.Name).ToList(); + modCollection.ModPaths = collectionMods.Select(p => p.FullPath).ToList(); } } else if (importResult.FullPaths != null && importResult.FullPaths.Any()) @@ -658,10 +662,29 @@ protected virtual void MapImportResult(IModCollection modCollection, ICollection if (mods.Any()) { var sort = importResult.FullPaths.ToList(); - var collectionMods = mods.Where(p => importResult.FullPaths.Any(x => x.Equals(p.FullPath))). - OrderBy(p => sort.IndexOf(sort.FirstOrDefault(x => x.Equals(p.FullPath)))); + var collectionMods = mods.Where(p => importResult.FullPaths.Any(x => x.StandardizeDirectorySeparator().Equals(p.FullPath.StandardizeDirectorySeparator(), StringComparison.OrdinalIgnoreCase))). + OrderBy(p => sort.IndexOf(sort.FirstOrDefault(x => x.StandardizeDirectorySeparator().Equals(p.FullPath.StandardizeDirectorySeparator(), StringComparison.OrdinalIgnoreCase)))).ToList(); + collectionMods = collectionMods.GroupBy(p => p.FullPath).Select(p => p.FirstOrDefault()).ToList(); modCollection.Mods = collectionMods.Select(p => p.DescriptorFile).ToList(); modCollection.ModNames = collectionMods.Select(p => p.Name).ToList(); + modCollection.ModPaths = collectionMods.Select(p => p.FullPath).ToList(); + } + } + } + else + { + var mods = GetInstalledModsInternal(modCollection.Game, false); + if (mods.Any()) + { + var sort = importResult.Descriptors.ToList(); + var collectionMods = mods.Where(p => importResult.Descriptors.Any(x => x.Equals(p.DescriptorFile, StringComparison.OrdinalIgnoreCase))).OrderBy(p => sort.IndexOf(sort.FirstOrDefault(x => x.Equals(p.DescriptorFile, StringComparison.OrdinalIgnoreCase)))).ToList(); + modCollection.ModPaths = collectionMods.Select(p => p.FullPath).ToList(); + if (modCollection.ModPaths.Count() != modCollection.Mods.Count() && importResult.ModNames.Any()) + { + sort = importResult.ModNames.ToList(); + collectionMods = mods.Where(p => importResult.ModNames.Any(x => x.Equals(p.Name))).OrderBy(p => sort.IndexOf(sort.FirstOrDefault(x => x.Equals(p.Name)))).ToList(); + collectionMods = collectionMods.GroupBy(p => p.FullPath).Select(p => p.FirstOrDefault()).ToList(); + modCollection.ModPaths = collectionMods.Select(p => p.FullPath).ToList(); } } } diff --git a/src/IronyModManager.Services/ModService.cs b/src/IronyModManager.Services/ModService.cs index 66aadee15..0c6a0a1e2 100644 --- a/src/IronyModManager.Services/ModService.cs +++ b/src/IronyModManager.Services/ModService.cs @@ -4,7 +4,7 @@ // Created : 02-24-2020 // // Last Modified By : Mario -// Last Modified On : 11-12-2022 +// Last Modified On : 12-01-2022 // *********************************************************************** // // Mario @@ -819,17 +819,14 @@ void parseModFiles(string path, ModSource source, bool isDirectory, string modNa break; case ModSource.Steam: - if (mod.RemoteId.GetValueOrDefault() == 0) + if (!isDirectory) + { + var modParentDirectory = Path.GetDirectoryName(path); + mod.RemoteId = GetSteamModId(modParentDirectory); + } + else { - if (!isDirectory) - { - var modParentDirectory = Path.GetDirectoryName(path); - mod.RemoteId = GetSteamModId(modParentDirectory, isDirectory); - } - else - { - mod.RemoteId = GetSteamModId(path, isDirectory); - } + mod.RemoteId = GetSteamModId(path); } string steamPath; if (modDescriptorType == ModDescriptorType.DescriptorMod) @@ -847,11 +844,11 @@ void parseModFiles(string path, ModSource source, bool isDirectory, string modNa if (!isDirectory) { var modParentDirectory = Path.GetDirectoryName(path); - mod.RemoteId = GetPdxModId(modParentDirectory, isDirectory); + mod.RemoteId = GetPdxModId(modParentDirectory); } else { - mod.RemoteId = GetPdxModId(path, isDirectory); + mod.RemoteId = GetPdxModId(path); } string pdxPath; if (modDescriptorType == ModDescriptorType.DescriptorMod) @@ -933,11 +930,10 @@ void parseModFiles(string path, ModSource source, bool isDirectory, string modNa /// Gets the steam mod identifier. /// /// The path. - /// if set to true [is directory]. /// System.Int32. - protected virtual long GetSteamModId(string path, bool isDirectory = false) + protected virtual long GetSteamModId(string path) { - var name = !isDirectory ? Path.GetFileNameWithoutExtension(path) : path; + var name = Path.GetFileNameWithoutExtension(path); #pragma warning disable CA1806 // Do not ignore method results long.TryParse(name.Replace(Constants.Steam_mod_id, string.Empty), out var id); #pragma warning restore CA1806 // Do not ignore method results diff --git a/src/IronyModManager/ViewModels/Controls/AddNewCollectionControlViewModel.cs b/src/IronyModManager/ViewModels/Controls/AddNewCollectionControlViewModel.cs index 4c4ec1f40..b1920fcbb 100644 --- a/src/IronyModManager/ViewModels/Controls/AddNewCollectionControlViewModel.cs +++ b/src/IronyModManager/ViewModels/Controls/AddNewCollectionControlViewModel.cs @@ -4,7 +4,7 @@ // Created : 03-05-2020 // // Last Modified By : Mario -// Last Modified On : 05-31-2021 +// Last Modified On : 12-01-2022 // *********************************************************************** // // Mario @@ -153,6 +153,7 @@ protected override void OnActivated(CompositeDisposable disposables) if (RenamingCollection != null) { collection.Mods = RenamingCollection.Mods; + collection.ModPaths = RenamingCollection.ModPaths; collection.PatchModEnabled = RenamingCollection.PatchModEnabled; modCollectionService.Delete(RenamingCollection.Name); modPatchCollectionService.InvalidatePatchModState(RenamingCollection.Name); diff --git a/src/IronyModManager/ViewModels/Controls/CollectionModsControlViewModel.cs b/src/IronyModManager/ViewModels/Controls/CollectionModsControlViewModel.cs index d617020ba..1a72a6f3e 100644 --- a/src/IronyModManager/ViewModels/Controls/CollectionModsControlViewModel.cs +++ b/src/IronyModManager/ViewModels/Controls/CollectionModsControlViewModel.cs @@ -916,10 +916,7 @@ public virtual void InstantReorderSelectedItems(IMod mod, int newOrder) { async Task reorder() { - if (reorderToken != null) - { - reorderToken.Cancel(); - } + reorderToken?.Cancel(); reorderToken = new CancellationTokenSource(); mod.Order = newOrder; if (!reorderQueue.Contains(mod)) @@ -1172,11 +1169,18 @@ protected virtual void HandleModCollectionChange(bool resetStack) { var missingMods = new List(); var hasModNames = existingCollection.ModNames != null && existingCollection.ModNames.Count() == existingCollection.Mods.Count(); - var index = -1; - foreach (var item in existingCollection.Mods) + var mods = existingCollection.Mods.ToList(); + var modPaths = existingCollection.ModPaths != null ? existingCollection.ModPaths.ToList() : new List(); + var modNames = hasModNames ? existingCollection.ModNames.ToList() : new List(); + for (int i = 0; i < mods.Count; i++) { - index++; + var item = mods[i]; var mod = Mods.FirstOrDefault(p => p.DescriptorFile.Equals(item, StringComparison.InvariantCultureIgnoreCase)); + if (mod == null && mods.Count == modPaths.Count) + { + item = modPaths[i]; + mod = Mods.FirstOrDefault(p => p.FullPath.Equals(item, StringComparison.OrdinalIgnoreCase)); + } if (mod != null) { mod.IsSelected = true; @@ -1186,7 +1190,7 @@ protected virtual void HandleModCollectionChange(bool resetStack) { if (hasModNames) { - missingMods.Add($"{existingCollection.ModNames.ElementAt(index)} ({item})"); + missingMods.Add($"{modNames[i]} ({item})"); } else { @@ -1314,49 +1318,85 @@ Task importInstance(IModCollection importData) { var importedMods = new List(); var descriptors = result.Mods.ToList(); + var paths = result.ModPaths != null ? result.ModPaths.ToList() : new List(); for (int i = 0; i < descriptors.Count; i++) { var descriptor = descriptors[i]; var name = modNames[i]; var mod = mods.FirstOrDefault(p => p.Name.Equals(name) && System.IO.Path.GetDirectoryName(p.FullPath).EndsWith(System.IO.Path.DirectorySeparatorChar + result.MergedFolderName)); - importedMods.Add(mod == null ? descriptor : mod.DescriptorFile); + if (mod != null) + { + importedMods.Add(mod.DescriptorFile); + } + else + { + if (descriptors.Count == paths.Count) + { + var existingMod = mods.FirstOrDefault(p => p.DescriptorFile.Equals(descriptor, StringComparison.OrdinalIgnoreCase)); + if (existingMod == null) + { + existingMod = mods.FirstOrDefault(p => p.FullPath.Equals(paths[i], StringComparison.OrdinalIgnoreCase)); + } + importedMods.Add(existingMod != null ? existingMod.DescriptorFile : descriptor); + } + else + { + importedMods.Add(descriptor); + } + } } result.Mods = importedMods; modCollectionService.Save(result); } - var modPaths = result.Mods != null ? result.Mods.ToList() : new List(); + var modDescriptorPaths = result.Mods != null ? result.Mods.ToList() : new List(); + var modPaths = result.ModPaths != null ? result.ModPaths.ToList() : new List(); restoreCollectionSelection = result.Name; LoadModCollections(); var showImportNotification = true; // Check if any mods do not exist if (hasMods && mods.Any()) { - var nonExistingModPaths = modPaths.Where(p => !mods.Any(m => m.DescriptorFile.Equals(p))); + var nonExistingModPaths = modDescriptorPaths.Where(p => !mods.Any(m => m.DescriptorFile.Equals(p, StringComparison.OrdinalIgnoreCase))); if (nonExistingModPaths.Any()) { var nonExistingModNames = new List(); - var hasModNames = modNames != null && modNames.Any() && modPaths.Count == modNames.Count; + var hasModNames = modNames != null && modNames.Any() && modDescriptorPaths.Count == modNames.Count; foreach (var item in nonExistingModPaths) { - var index = modPaths.IndexOf(item); - if (hasModNames) + var index = modDescriptorPaths.IndexOf(item); + var isInvalid = true; + if (modPaths.Count == modDescriptorPaths.Count) { - nonExistingModNames.Add($"{modNames[index]} ({item})"); + var modPath = modPaths[index]; + if (mods.Any(p => p.FullPath.Equals(modPath, StringComparison.OrdinalIgnoreCase))) + { + isInvalid = false; + } } - else + if (isInvalid) { - nonExistingModNames.Add(item); + if (hasModNames) + { + nonExistingModNames.Add($"{modNames[index]} ({item})"); + } + else + { + nonExistingModNames.Add(item); + } } } - var notExistingModTitle = localizationManager.GetResource(LocalizationResources.Collection_Mods.ImportNonExistingMods.Title); - var nonExistingModMessage = IronyFormatter.Format(localizationManager.GetResource(LocalizationResources.Collection_Mods.ImportNonExistingMods.Message), new { Environment.NewLine, Mods = string.Join(Environment.NewLine, nonExistingModNames) }); - endOverlay = false; - showImportNotification = false; - var title = localizationManager.GetResource(LocalizationResources.Notifications.CollectionImported.Title); - var message = IronyFormatter.Format(localizationManager.GetResource(LocalizationResources.Notifications.CollectionImported.Message), new { CollectionName = result.Name }); - notificationAction.ShowNotification(title, message, NotificationType.Warning); - await TriggerOverlayAsync(id, false); - await notificationAction.ShowPromptAsync(notExistingModTitle, notExistingModTitle, nonExistingModMessage, NotificationType.Warning, PromptType.OK); + if (nonExistingModNames.Any()) + { + var notExistingModTitle = localizationManager.GetResource(LocalizationResources.Collection_Mods.ImportNonExistingMods.Title); + var nonExistingModMessage = IronyFormatter.Format(localizationManager.GetResource(LocalizationResources.Collection_Mods.ImportNonExistingMods.Message), new { Environment.NewLine, Mods = string.Join(Environment.NewLine, nonExistingModNames) }); + endOverlay = false; + showImportNotification = false; + var title = localizationManager.GetResource(LocalizationResources.Notifications.CollectionImported.Title); + var message = IronyFormatter.Format(localizationManager.GetResource(LocalizationResources.Notifications.CollectionImported.Message), new { CollectionName = result.Name }); + notificationAction.ShowNotification(title, message, NotificationType.Warning); + await TriggerOverlayAsync(id, false); + await notificationAction.ShowPromptAsync(notExistingModTitle, notExistingModTitle, nonExistingModMessage, NotificationType.Warning, PromptType.OK); + } } } if (showImportNotification) @@ -1390,7 +1430,7 @@ protected virtual void InitSortersAndFilters(IAppState state, bool setFlag = tru ModNameSortOrder.Text = ModName; if (!string.IsNullOrWhiteSpace(state.CollectionModsSelectedMod) && SelectedMods != null) { - SelectedMod = SelectedMods.FirstOrDefault(p => p.DescriptorFile.Equals(state.CollectionModsSelectedMod)); + SelectedMod = SelectedMods.FirstOrDefault(p => p.DescriptorFile.Equals(state.CollectionModsSelectedMod, StringComparison.OrdinalIgnoreCase)); } RecognizeSortOrder(SelectedModCollection); if (setFlag) @@ -2094,10 +2134,7 @@ protected virtual void PerformRedoUndoOrdering(IEnumerable descriptors) skipModSelectionSave = true; skipModCollectionSave = true; reorderQueue.Clear(); - if (reorderToken != null) - { - reorderToken.Cancel(); - } + reorderToken?.Cancel(); foreach (var item in Mods) { item.IsSelected = false; @@ -2148,7 +2185,23 @@ protected virtual void RecognizeSortOrder(IModCollection modCollection) // modCollection sort order if (modCollection?.Mods.Count() > 0 && Mods?.Count() > 0) { - var mods = Mods.Where(p => modCollection.Mods.Any(a => a.Equals(p.DescriptorFile, StringComparison.OrdinalIgnoreCase))); + var mods = new List(); + var colMods = modCollection.Mods.ToList(); + var colPaths = modCollection.ModPaths != null ? modCollection.ModPaths.ToList() : new List(); + for (int i = 0; i < colMods.Count; i++) + { + var item = colMods[i]; + var mod = Mods.FirstOrDefault(p => p.DescriptorFile.Equals(item, StringComparison.OrdinalIgnoreCase)); + if (mod == null && colMods.Count == colPaths.Count) + { + item = colPaths[i]; + mod = Mods.FirstOrDefault(p => p.FullPath.Equals(item, StringComparison.OrdinalIgnoreCase)); + } + if (mod != null) + { + mods.Add(mod); + } + } if (mods.Any()) { var ascending = mods.OrderBy(p => p.Name, StringComparer.OrdinalIgnoreCase).Select(p => p.DescriptorFile); @@ -2239,7 +2292,9 @@ protected virtual void SaveSelectedCollection() } collection.Game = game; collection.Name = SelectedModCollection.Name; - collection.Mods = SelectedMods?.Where(p => p.IsSelected).Select(p => p.DescriptorFile).ToList(); + var selectedMods = SelectedMods?.Where(p => p.IsSelected); + collection.Mods = selectedMods.Select(p => p.DescriptorFile).ToList(); + collection.ModPaths = selectedMods.Select(p => p.FullPath).ToList(); collection.IsSelected = true; collection.MergedFolderName = SelectedModCollection.MergedFolderName; collection.PatchModEnabled = SelectedModCollection.PatchModEnabled; @@ -2249,6 +2304,7 @@ protected virtual void SaveSelectedCollection() SelectedModCollection.Mods = collection.Mods.ToList(); SelectedModCollection.ModIds = collection.ModIds; SelectedModCollection.ModNames = collection.ModNames; + SelectedModCollection.ModPaths = collection.ModPaths; } } } @@ -2278,10 +2334,7 @@ protected virtual async Task ScheduleToReorderQueueAsync(IMod mod) { reorderQueue.Add(mod); } - if (reorderToken != null) - { - reorderToken.Cancel(); - } + reorderToken?.Cancel(); reorderToken = new CancellationTokenSource(); await PerformModReorderAsync(false, reorderToken.Token); } diff --git a/src/IronyModManager/ViewModels/Controls/ModifyCollectionControlViewModel.cs b/src/IronyModManager/ViewModels/Controls/ModifyCollectionControlViewModel.cs index fe95688df..afeab243c 100644 --- a/src/IronyModManager/ViewModels/Controls/ModifyCollectionControlViewModel.cs +++ b/src/IronyModManager/ViewModels/Controls/ModifyCollectionControlViewModel.cs @@ -4,7 +4,7 @@ // Created : 05-09-2020 // // Last Modified By : Mario -// Last Modified On : 11-30-2022 +// Last Modified On : 12-01-2022 // *********************************************************************** // // Mario @@ -372,6 +372,7 @@ protected virtual IModCollection CopyCollection(string requestedName, bool skipN var copied = modCollectionService.Create(); copied.IsSelected = true; copied.Mods = ActiveCollection.Mods; + copied.ModPaths = ActiveCollection.ModPaths; copied.Name = name; copied.PatchModEnabled = true; return copied; @@ -523,6 +524,7 @@ await Task.Run(async () => return await modMergeService.MergeCollectionByFilesAsync(copy.Name); }).ConfigureAwait(false); copy.Mods = new List() { mergeMod.DescriptorFile }; + copy.ModPaths = new List() { mergeMod.FullPath }; await TriggerOverlayAsync(id, false); freeSpaceCheckHandler?.Dispose(); @@ -601,6 +603,7 @@ await Task.Run(async () => })); }).ConfigureAwait(false); copy.Mods = mergeMods.Select(p => p.DescriptorFile).ToList(); + copy.ModPaths = mergeMods.Select(p => p.FullPath).ToList(); copy.PatchModEnabled = ActiveCollection.PatchModEnabled; // I know bad