mirror of
https://github.com/wabbajack-tools/wabbajack.git
synced 2024-08-30 18:42:17 +00:00
MO2Compiler nullability enabled
This commit is contained in:
@ -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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
15
Wabbajack.Common/Extensions/ListExt.cs
Normal file
15
Wabbajack.Common/Extensions/ListExt.cs
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -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)
|
||||||
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user