MO2Compiler nullability enabled

This commit is contained in:
Justin Swanson
2020-04-09 16:05:07 -05:00
parent a29eb93caf
commit 4e1a32caac
5 changed files with 91 additions and 43 deletions

View File

@ -21,6 +21,9 @@ namespace Wabbajack
return ret; return ret;
} }
/// <summary>
/// Adds the given values to the dictionary. If a key already exists, it will throw an exception
/// </summary>
public static void Add<K, V>(this IDictionary<K, V> dict, IEnumerable<KeyValuePair<K, V>> vals) public static void Add<K, V>(this IDictionary<K, V> dict, IEnumerable<KeyValuePair<K, V>> vals)
where K : notnull where K : notnull
{ {
@ -30,6 +33,9 @@ namespace Wabbajack
} }
} }
/// <summary>
/// Adds the given values to the dictionary. If a key already exists, it will be replaced
/// </summary>
public static void Set<K, V>(this IDictionary<K, V> dict, IEnumerable<KeyValuePair<K, V>> vals) public static void Set<K, V>(this IDictionary<K, V> dict, IEnumerable<KeyValuePair<K, V>> vals)
where K : notnull where K : notnull
{ {
@ -38,5 +44,15 @@ namespace Wabbajack
dict[val.Key] = val.Value; dict[val.Key] = val.Value;
} }
} }
/// <summary>
/// Clears the dictionary and adds the given values
/// </summary>
public static void SetTo<K, V>(this IDictionary<K, V> dict, IEnumerable<KeyValuePair<K, V>> vals)
where K : notnull
{
dict.Clear();
dict.Set(vals);
}
} }
} }

View File

@ -33,5 +33,32 @@ namespace Wabbajack
yield return next; yield return next;
foreach (var itm in coll) yield return itm; foreach (var itm in coll) yield return itm;
} }
/// <summary>
/// Converts and filters a nullable enumerable to a non-nullable enumerable
/// </summary>
public static IEnumerable<T> NotNull<T>(this IEnumerable<T?> e)
where T : class
{
// Filter out nulls
return e.Where(e => e != null)
// Cast to non nullable type
.Select(e => e!);
}
/// <summary>
/// Selects items that are castable to the desired type
/// </summary>
/// <typeparam name="T">Type of the original enumerable to cast from</typeparam>
/// <typeparam name="R">Type to attempt casting to</typeparam>
/// <param name="e">Enumerable to process</param>
/// <returns>Enumerable with only objects that were castable</returns>
public static IEnumerable<R> WhereCastable<T, R>(this IEnumerable<T> e)
where T : class
where R : T
{
return e.Where(e => e is R)
.Select(e => (R)e);
}
} }
} }

View File

@ -0,0 +1,15 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace Wabbajack.Common
{
public static class ListExt
{
public static void SetTo<T>(this List<T> list, IEnumerable<T> rhs)
{
list.Clear();
list.AddRange(rhs);
}
}
}

View File

@ -43,9 +43,9 @@ namespace Wabbajack.Lib
public bool IgnoreMissingFiles { get; set; } public bool IgnoreMissingFiles { get; set; }
public ICollection<Archive> SelectedArchives = new List<Archive>(); public readonly List<Archive> SelectedArchives = new List<Archive>();
public List<Directive> InstallDirectives = new List<Directive>(); public readonly List<Directive> InstallDirectives = new List<Directive>();
public List<RawSourceFile> AllFiles = new List<RawSourceFile>(); public readonly List<RawSourceFile> AllFiles = new List<RawSourceFile>();
public ModList ModList = new ModList(); public ModList ModList = new ModList();
public List<IndexedArchive> IndexedArchives = new List<IndexedArchive>(); public List<IndexedArchive> IndexedArchives = new List<IndexedArchive>();
@ -222,7 +222,7 @@ namespace Wabbajack.Lib
.GroupBy(f => f.File.Hash) .GroupBy(f => f.File.Hash)
.ToDictionary(f => f.Key, f => f.First()); .ToDictionary(f => f.Key, f => f.First());
SelectedArchives = await hashes.PMap(Queue, hash => ResolveArchive(hash, archives)); SelectedArchives.SetTo(await hashes.PMap(Queue, hash => ResolveArchive(hash, archives)));
} }
public async Task<Archive> ResolveArchive(Hash hash, IDictionary<Hash, IndexedArchive> archives) public async Task<Archive> ResolveArchive(Hash hash, IDictionary<Hash, IndexedArchive> archives)

View File

@ -20,12 +20,12 @@ using File = Alphaleonis.Win32.Filesystem.File;
using FileInfo = Alphaleonis.Win32.Filesystem.FileInfo; using FileInfo = Alphaleonis.Win32.Filesystem.FileInfo;
using Game = Wabbajack.Common.Game; using Game = Wabbajack.Common.Game;
using Path = Alphaleonis.Win32.Filesystem.Path; using Path = Alphaleonis.Win32.Filesystem.Path;
#nullable enable
namespace Wabbajack.Lib namespace Wabbajack.Lib
{ {
public class MO2Compiler : ACompiler public class MO2Compiler : ACompiler
{ {
private AbsolutePath _mo2DownloadsFolder; private AbsolutePath _mo2DownloadsFolder;
public AbsolutePath MO2Folder; public AbsolutePath MO2Folder;
@ -38,7 +38,7 @@ namespace Wabbajack.Lib
public override AbsolutePath GamePath { get; } public override AbsolutePath GamePath { get; }
public GameMetaData CompilingGame { get; set; } public GameMetaData CompilingGame { get; }
public override AbsolutePath ModListOutputFolder => ((RelativePath)"output_folder").RelativeToEntryPoint(); public override AbsolutePath ModListOutputFolder => ((RelativePath)"output_folder").RelativeToEntryPoint();
@ -48,6 +48,17 @@ namespace Wabbajack.Lib
Consts.LocalAppDataPath.Combine( Consts.LocalAppDataPath.Combine(
$"vfs_compile_cache-{Path.Combine((string)MO2Folder ?? "Unknown", "ModOrganizer.exe").StringSha256Hex()}.bin"); $"vfs_compile_cache-{Path.Combine((string)MO2Folder ?? "Unknown", "ModOrganizer.exe").StringSha256Hex()}.bin");
public dynamic MO2Ini { get; }
public static AbsolutePath GetTypicalDownloadsFolder(AbsolutePath mo2Folder) => mo2Folder.Combine("downloads");
public AbsolutePath MO2ProfileDir => MO2Folder.Combine("profiles", MO2Profile);
public ConcurrentBag<Directive> ExtraFiles { get; } = new ConcurrentBag<Directive>();
public Dictionary<AbsolutePath, dynamic> ModInis { get; } = new Dictionary<AbsolutePath, dynamic>();
public HashSet<string> SelectedProfiles { get; set; } = new HashSet<string>();
public MO2Compiler(AbsolutePath mo2Folder, string mo2Profile, AbsolutePath outputFile) public MO2Compiler(AbsolutePath mo2Folder, string mo2Profile, AbsolutePath outputFile)
: base(steps: 20) : base(steps: 20)
{ {
@ -60,8 +71,6 @@ namespace Wabbajack.Lib
ModListOutputFile = outputFile; ModListOutputFile = outputFile;
} }
public dynamic MO2Ini { get; }
public AbsolutePath MO2DownloadsFolder public AbsolutePath MO2DownloadsFolder
{ {
get get
@ -76,16 +85,6 @@ namespace Wabbajack.Lib
set => _mo2DownloadsFolder = value; set => _mo2DownloadsFolder = value;
} }
public static AbsolutePath GetTypicalDownloadsFolder(AbsolutePath mo2Folder) => mo2Folder.Combine("downloads");
public AbsolutePath MO2ProfileDir => MO2Folder.Combine("profiles", MO2Profile);
internal UserStatus User { get; private set; }
public ConcurrentBag<Directive> ExtraFiles { get; private set; }
public Dictionary<AbsolutePath, dynamic> ModInis { get; private set; }
public HashSet<string> SelectedProfiles { get; set; } = new HashSet<string>();
protected override async Task<bool> _Begin(CancellationToken cancel) protected override async Task<bool> _Begin(CancellationToken cancel)
{ {
if (cancel.IsCancellationRequested) return false; if (cancel.IsCancellationRequested) return false;
@ -127,6 +126,11 @@ namespace Wabbajack.Lib
if (lootPath.Exists) if (lootPath.Exists)
{ {
if (CompilingGame.MO2Name == null)
{
throw new ArgumentException("Compiling game had no MO2 name specified.");
}
var lootGameDirs = new [] var lootGameDirs = new []
{ {
CompilingGame.MO2Name, // most of the games use the MO2 name CompilingGame.MO2Name, // most of the games use the MO2 name
@ -217,10 +221,9 @@ namespace Wabbajack.Lib
.GroupBy(f => f.Hash) .GroupBy(f => f.Hash)
.ToDictionary(f => f.Key, f => f.AsEnumerable()); .ToDictionary(f => f.Key, f => f.AsEnumerable());
AllFiles = mo2Files.Concat(gameFiles) AllFiles.SetTo(mo2Files.Concat(gameFiles)
.Concat(lootFiles) .Concat(lootFiles)
.DistinctBy(f => f.Path) .DistinctBy(f => f.Path));
.ToList();
Info($"Found {AllFiles.Count} files to build into mod list"); Info($"Found {AllFiles.Count} files to build into mod list");
@ -240,13 +243,10 @@ namespace Wabbajack.Lib
Error($"Found {dups.Count} duplicates, exiting"); Error($"Found {dups.Count} duplicates, exiting");
} }
ExtraFiles = new ConcurrentBag<Directive>();
if (cancel.IsCancellationRequested) return false; if (cancel.IsCancellationRequested) return false;
UpdateTracker.NextStep("Loading INIs"); UpdateTracker.NextStep("Loading INIs");
ModInis = MO2Folder.Combine(Consts.MO2ModFolderName) ModInis.SetTo(MO2Folder.Combine(Consts.MO2ModFolderName)
.EnumerateDirectories() .EnumerateDirectories()
.Select(f => .Select(f =>
{ {
@ -255,7 +255,7 @@ namespace Wabbajack.Lib
return metaPath.Exists ? (mod_name: f, metaPath.LoadIniFile()) : default; return metaPath.Exists ? (mod_name: f, metaPath.LoadIniFile()) : default;
}) })
.Where(f => f.Item1 != default) .Where(f => f.Item1 != default)
.ToDictionary(f => f.Item1, f => f.Item2); .Select(f => new KeyValuePair<AbsolutePath, dynamic>(f.Item1, f.Item2)));
if (cancel.IsCancellationRequested) return false; if (cancel.IsCancellationRequested) return false;
var stack = MakeStack(); var stack = MakeStack();
@ -271,7 +271,7 @@ namespace Wabbajack.Lib
PrintNoMatches(noMatch); PrintNoMatches(noMatch);
if (CheckForNoMatchExit(noMatch)) return false; if (CheckForNoMatchExit(noMatch)) return false;
InstallDirectives = results.Where(i => !(i is IgnoredDirectly)).ToList(); InstallDirectives.SetTo(results.Where(i => !(i is IgnoredDirectly)));
Info("Getting Nexus api_key, please click authorize if a browser window appears"); Info("Getting Nexus api_key, please click authorize if a browser window appears");
@ -338,7 +338,7 @@ namespace Wabbajack.Lib
{ {
return a; return a;
} }
})).Where(a => a != null).ToHashSet(); })).NotNull().ToHashSet();
if (remove.Count == 0) if (remove.Count == 0)
return; return;
@ -390,7 +390,6 @@ namespace Wabbajack.Lib
}); });
} }
private async Task IncludeArchiveMetadata() private async Task IncludeArchiveMetadata()
{ {
Utils.Log($"Including {SelectedArchives.Count} .meta files for downloads"); Utils.Log($"Including {SelectedArchives.Count} .meta files for downloads");
@ -412,13 +411,12 @@ namespace Wabbajack.Lib
/// </summary> /// </summary>
private void ResetMembers() private void ResetMembers()
{ {
AllFiles = null; AllFiles.Clear();
InstallDirectives = null; InstallDirectives.Clear();
SelectedArchives = null; SelectedArchives.Clear();
ExtraFiles = null; ExtraFiles.Clear();
} }
/// <summary> /// <summary>
/// Fills in the Patch fields in files that require them /// Fills in the Patch fields in files that require them
/// </summary> /// </summary>
@ -488,8 +486,7 @@ namespace Wabbajack.Lib
return returnStream; return returnStream;
} }
Error($"Couldn't load data for {to}"); throw new ArgumentException($"Couldn't load data for {to}");
return null;
} }
public override IEnumerable<ICompilationStep> GetStack() public override IEnumerable<ICompilationStep> GetStack()
@ -568,12 +565,5 @@ namespace Wabbajack.Lib
new DropAll(this) new DropAll(this)
}; };
} }
public class IndexedFileMatch
{
public IndexedArchive Archive;
public IndexedArchiveEntry Entry;
public DateTime LastModified;
}
} }
} }