Merge pull request #674 from Noggog/Nullability-Flavor-Bootcamp

Nullability flavor bootcamp
This commit is contained in:
Timothy Baldridge 2020-04-03 20:58:33 -06:00 committed by GitHub
commit 1b7cb772d2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
39 changed files with 273 additions and 360 deletions

View File

@ -122,3 +122,66 @@ csharp_preserve_single_line_blocks = true
dotnet_diagnostic.CS4014.severity = error dotnet_diagnostic.CS4014.severity = error
# CS1998: Async function does not contain await # CS1998: Async function does not contain await
dotnet_diagnostic.CS1998.severity = silent dotnet_diagnostic.CS1998.severity = silent
###############################
# C# Nullability #
###############################
# CS8602: Dereference of a possibly null reference.
dotnet_diagnostic.CS8602.severity = error
# CS8600: Converting null literal or possible null value to non-nullable type.
dotnet_diagnostic.CS8600.severity = error
# CS8619: Nullability of reference types in value doesn't match target type.
dotnet_diagnostic.CS8619.severity = error
# CS8603: Possible null reference return.
dotnet_diagnostic.CS8603.severity = error
# CS8625: Cannot convert null literal to non-nullable reference type.
dotnet_diagnostic.CS8625.severity = error
# CS8653: A default expression introduces a null value for a type parameter.
dotnet_diagnostic.CS8653.severity = silent
# CS8601: Possible null reference assignment.
dotnet_diagnostic.CS8601.severity = error
# CS8604: Possible null reference argument.
dotnet_diagnostic.CS8604.severity = error
# CS8622: Nullability of reference types in type of parameter doesn't match the target delegate.
dotnet_diagnostic.CS8622.severity = error
# CS8610: Nullability of reference types in type of parameter doesn't match overridden member.
dotnet_diagnostic.CS8610.severity = error
# CS8618: Non-nullable field is uninitialized. Consider declaring as nullable.
dotnet_diagnostic.CS8618.severity = error
# CS8629: Nullable value type may be null.
dotnet_diagnostic.CS8629.severity = error
# CS8620: Argument cannot be used for parameter due to differences in the nullability of reference types.
dotnet_diagnostic.CS8620.severity = error
# CS8614: Nullability of reference types in type of parameter doesn't match implicitly implemented member.
dotnet_diagnostic.CS8614.severity = error
# CS8617: Nullability of reference types in type of parameter doesn't match implemented member.
dotnet_diagnostic.CS8617.severity = error
# CS8611: Nullability of reference types in type of parameter doesn't match partial method declaration.
dotnet_diagnostic.CS8611.severity = error
# CS8597: Thrown value may be null.
dotnet_diagnostic.CS8597.severity = error
# CS8609: Nullability of reference types in return type doesn't match overridden member.
dotnet_diagnostic.CS8609.severity = error
# CS8714: The type cannot be used as type parameter in the generic type or method. Nullability of type argument doesn't match 'notnull' constraint.
dotnet_diagnostic.CS8714.severity = error
# CS8605: Unboxing a possibly null value.
dotnet_diagnostic.CS8605.severity = error

View File

@ -9,14 +9,9 @@ namespace Wabbajack.Common
{ {
public class AsyncBlockingCollection<T> : IDisposable public class AsyncBlockingCollection<T> : IDisposable
{ {
private readonly ConcurrentQueue<T> _collection; private readonly ConcurrentQueue<T> _collection = new ConcurrentQueue<T>();
private bool isDisposed = false; private bool isDisposed = false;
public AsyncBlockingCollection()
{
_collection = new ConcurrentQueue<T>();
}
public void Add(T val) public void Add(T val)
{ {
_collection.Enqueue(val); _collection.Enqueue(val);

View File

@ -10,10 +10,10 @@ namespace Wabbajack.Common
public static bool NoSettings { get; set; } public static bool NoSettings { get; set; }
[CLIOptions("apikey", HelpText = "Manually input an Nexus api key")] [CLIOptions("apikey", HelpText = "Manually input an Nexus api key")]
public static string ApiKey { get; set; } public static string? ApiKey { get; set; }
[CLIOptions("install", ShortOption = 'i', HelpText = "Install a ModList via CLI")] [CLIOptions("install", ShortOption = 'i', HelpText = "Install a ModList via CLI")]
public static string InstallPath { get; set; } public static string? InstallPath { get; set; }
[CLIOptions("help", ShortOption = 'h', HelpText = "Display this message")] [CLIOptions("help", ShortOption = 'h', HelpText = "Display this message")]
public static bool Help { get; set; } public static bool Help { get; set; }
@ -94,7 +94,7 @@ namespace Wabbajack.Common
// -shortOption, short name of the option. Eg: -o // -shortOption, short name of the option. Eg: -o
public char ShortOption; public char ShortOption;
// text to be displayed when --help is called // text to be displayed when --help is called
public string HelpText; public string HelpText = string.Empty;
public CLIOptions(string option) public CLIOptions(string option)
{ {

View File

@ -1,5 +1,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Dynamic; using System.Dynamic;
using System.Text; using System.Text;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
@ -35,7 +36,7 @@ namespace Wabbajack.Common
return true; return true;
} }
public override bool TryGetIndex(GetIndexBinder binder, object[] indexes, out object result) public override bool TryGetIndex(GetIndexBinder binder, object[] indexes, [MaybeNullWhen(false)] out object result)
{ {
if (indexes.Length > 1) if (indexes.Length > 1)
{ {
@ -114,7 +115,7 @@ namespace Wabbajack.Common
return Encoding.UTF8.GetString(acc.ToArray()); return Encoding.UTF8.GetString(acc.ToArray());
} }
public override bool TryGetIndex(GetIndexBinder binder, object[] indexes, out object result) public override bool TryGetIndex(GetIndexBinder binder, object[] indexes, [MaybeNullWhen(false)] out object result)
{ {
if (indexes.Length > 1) if (indexes.Length > 1)
{ {

View File

@ -1,14 +0,0 @@
using System.Collections.Generic;
namespace Wabbajack.Common
{
public static partial class Utils
{
public static IEnumerable<T> Cons<T>(this IEnumerable<T> coll, T next)
{
yield return next;
foreach (var itm in coll) yield return itm;
}
}
}

View File

@ -8,7 +8,7 @@ namespace Wabbajack
public readonly static ErrorResponse Failure = new ErrorResponse(); public readonly static ErrorResponse Failure = new ErrorResponse();
public bool Succeeded { get; } public bool Succeeded { get; }
public Exception Exception { get; } public Exception? Exception { get; }
private readonly string _reason; private readonly string _reason;
public bool Failed => !Succeeded; public bool Failed => !Succeeded;
@ -32,15 +32,15 @@ namespace Wabbajack
} }
bool IErrorResponse.Succeeded => Succeeded; bool IErrorResponse.Succeeded => Succeeded;
Exception IErrorResponse.Exception => Exception; Exception? IErrorResponse.Exception => Exception;
private ErrorResponse( private ErrorResponse(
bool succeeded, bool succeeded,
string reason = null, string? reason = null,
Exception ex = null) Exception? ex = null)
{ {
Succeeded = succeeded; Succeeded = succeeded;
_reason = reason; _reason = reason ?? string.Empty;
Exception = ex; Exception = ex;
} }
@ -60,7 +60,7 @@ namespace Wabbajack
return new ErrorResponse(true, reason); return new ErrorResponse(true, reason);
} }
public static ErrorResponse Fail(string reason, Exception ex = null) public static ErrorResponse Fail(string reason, Exception? ex = null)
{ {
return new ErrorResponse(false, reason: reason, ex: ex); return new ErrorResponse(false, reason: reason, ex: ex);
} }
@ -75,7 +75,7 @@ namespace Wabbajack
return new ErrorResponse(false); return new ErrorResponse(false);
} }
public static ErrorResponse Create(bool successful, string reason = null) public static ErrorResponse Create(bool successful, string? reason = null)
{ {
return new ErrorResponse(successful, reason); return new ErrorResponse(successful, reason);
} }
@ -91,7 +91,7 @@ namespace Wabbajack
public interface IErrorResponse public interface IErrorResponse
{ {
bool Succeeded { get; } bool Succeeded { get; }
Exception Exception { get; } Exception? Exception { get; }
string Reason { get; } string Reason { get; }
} }
} }

View File

@ -8,7 +8,7 @@ namespace Wabbajack
public readonly T Value; public readonly T Value;
public readonly bool Succeeded; public readonly bool Succeeded;
public readonly Exception Exception; public readonly Exception? Exception;
private readonly string _reason; private readonly string _reason;
public bool Failed => !Succeeded; public bool Failed => !Succeeded;
@ -25,17 +25,17 @@ namespace Wabbajack
} }
bool IErrorResponse.Succeeded => Succeeded; bool IErrorResponse.Succeeded => Succeeded;
Exception IErrorResponse.Exception => Exception; Exception? IErrorResponse.Exception => Exception;
private GetResponse( private GetResponse(
bool succeeded, bool succeeded,
T val = default(T), T val = default,
string reason = null, string? reason = null,
Exception ex = null) Exception? ex = null)
{ {
Value = val; Value = val;
Succeeded = succeeded; Succeeded = succeeded;
_reason = reason; _reason = reason ?? string.Empty;
Exception = ex; Exception = ex;
} }
@ -45,7 +45,7 @@ namespace Wabbajack
&& Equals(Value, other.Value); && Equals(Value, other.Value);
} }
public override bool Equals(object obj) public override bool Equals(object? obj)
{ {
if (!(obj is GetResponse<T> rhs)) return false; if (!(obj is GetResponse<T> rhs)) return false;
return Equals(rhs); return Equals(rhs);
@ -53,8 +53,10 @@ namespace Wabbajack
public override int GetHashCode() public override int GetHashCode()
{ {
return HashHelper.GetHashCode(Value) System.HashCode hash = new HashCode();
.CombineHashCode(Succeeded.GetHashCode()); hash.Add(Value);
hash.Add(Succeeded);
return hash.ToHashCode();
} }
public override string ToString() public override string ToString()
@ -124,7 +126,7 @@ namespace Wabbajack
return new GetResponse<T>(false, val); return new GetResponse<T>(false, val);
} }
public static GetResponse<T> Create(bool successful, T val = default(T), string reason = null) public static GetResponse<T> Create(bool successful, T val = default(T), string? reason = null)
{ {
return new GetResponse<T>(successful, val, reason); return new GetResponse<T>(successful, val, reason);
} }

View File

@ -6,17 +6,37 @@ namespace Wabbajack
public static class DictionaryExt public static class DictionaryExt
{ {
public static V TryCreate<K, V>(this IDictionary<K, V> dict, K key) public static V TryCreate<K, V>(this IDictionary<K, V> dict, K key)
where K : notnull
where V : new() where V : new()
{ {
return dict.TryCreate(key, () => new V()); return dict.TryCreate(key, () => new V());
} }
public static V TryCreate<K, V>(this IDictionary<K, V> dict, K key, Func<V> create) public static V TryCreate<K, V>(this IDictionary<K, V> dict, K key, Func<V> create)
where K : notnull
{ {
if (dict.TryGetValue(key, out var val)) return val; if (dict.TryGetValue(key, out var val)) return val;
var ret = create(); var ret = create();
dict[key] = ret; dict[key] = ret;
return ret; return ret;
} }
public static void Add<K, V>(this IDictionary<K, V> dict, IEnumerable<KeyValuePair<K, V>> vals)
where K : notnull
{
foreach (var val in vals)
{
dict.Add(val);
}
}
public static void Set<K, V>(this IDictionary<K, V> dict, IEnumerable<KeyValuePair<K, V>> vals)
where K : notnull
{
foreach (var val in vals)
{
dict[val.Key] = val.Value;
}
}
} }
} }

View File

@ -21,8 +21,8 @@ namespace Wabbajack.Common
throw new ArgumentException("T must be an Enum"); throw new ArgumentException("T must be an Enum");
} }
var attributes = (DescriptionAttribute[])val.GetType().GetField(val.ToString()).GetCustomAttributes(typeof(DescriptionAttribute), false); var attributes = (DescriptionAttribute[])val.GetType().GetField(val.ToString()!)!.GetCustomAttributes(typeof(DescriptionAttribute), false);
return attributes.Length > 0 ? attributes[0].Description : val.ToString(); return attributes.Length > 0 ? attributes[0].Description : val.ToString()!;
} }
} }
} }

View File

@ -11,9 +11,6 @@ namespace Wabbajack
public static IEnumerable<T> Shuffle<T>(this IEnumerable<T> source, Random rng) public static IEnumerable<T> Shuffle<T>(this IEnumerable<T> source, Random rng)
{ {
if (source == null) throw new ArgumentNullException("source");
if (rng == null) throw new ArgumentNullException("rng");
return source.ShuffleIterator(rng); return source.ShuffleIterator(rng);
} }
@ -30,5 +27,11 @@ namespace Wabbajack
} }
} }
#endregion #endregion
public static IEnumerable<T> Cons<T>(this IEnumerable<T> coll, T next)
{
yield return next;
foreach (var itm in coll) yield return itm;
}
} }
} }

View File

@ -1,133 +0,0 @@
using System;
using System.Collections.Generic;
/*
* Taken from: http://stackoverflow.com/questions/263400/what-is-the-best-algorithm-for-an-overridden-system-object-gethashcode
*/
namespace Wabbajack
{
public static class HashHelper
{
public static int GetHashCode<T1, T2>(T1 arg1, T2 arg2)
{
unchecked
{
return 31 * (arg1 == null ? 0 : arg1.GetHashCode())
+ (arg2 == null ? 0 : arg2.GetHashCode());
}
}
public static int GetHashCode<T1, T2, T3>(T1 arg1, T2 arg2, T3 arg3)
{
unchecked
{
int hash = (arg1 == null ? 0 : arg1.GetHashCode());
hash = 31 * hash + (arg2 == null ? 0 : arg2.GetHashCode());
return 31 * hash + (arg3 == null ? 0 : arg3.GetHashCode());
}
}
public static int GetHashCode<T1, T2, T3, T4>(T1 arg1, T2 arg2, T3 arg3, T4 arg4)
{
unchecked
{
int hash = (arg1 == null ? 0 : arg1.GetHashCode());
hash = 31 * hash + (arg2 == null ? 0 : arg2.GetHashCode());
hash = 31 * hash + (arg3 == null ? 0 : arg3.GetHashCode());
return 31 * hash + (arg4 == null ? 0 : arg4.GetHashCode());
}
}
public static int GetHashCode<T>(params T[] list)
{
unchecked
{
int hash = 0;
if (list == null) return hash;
for (int i = 0; i < list.Length; i++)
{
hash = 31 * hash + GetHashCode(list[i]);
}
return hash;
}
}
public static int GetHashCode<T>(ReadOnlySpan<T> span)
{
unchecked
{
int hash = 0;
if (span == null) return hash;
for (int i = 0; i < span.Length; i++)
{
hash = 31 * hash + GetHashCode(span[i]);
}
return hash;
}
}
public static int GetHashCode<T>(T t)
{
unchecked
{
return (t == null ? 0 : t.GetHashCode());
}
}
public static int GetHashCode<T>(IEnumerable<T> list)
{
unchecked
{
int hash = 0;
foreach (var item in list)
{
hash = 31 * hash + (item == null ? 0 : item.GetHashCode());
}
return hash;
}
}
/// <summary>
/// Gets a hashcode for a collection for that the order of items
/// does not matter.
/// So {1, 2, 3} and {3, 2, 1} will get same hash code.
/// </summary>
public static int GetHashCode_OrderBlind<T>(
IEnumerable<T> list)
{
unchecked
{
int hash = 0;
int count = 0;
foreach (var item in list)
{
hash += (item == null ? 0 : item.GetHashCode());
count++;
}
return 31 * hash + count.GetHashCode();
}
}
/// <summary>
/// Alternative way to get a hashcode is to use a fluent
/// interface like this:<br />
/// return 0.CombineHashCode(field1).CombineHashCode(field2).
/// CombineHashCode(field3);
/// </summary>
public static int CombineHashCode<T>(this int hashCode, T arg)
{
unchecked
{
return CombineHashCode(hashCode, (arg == null ? 0 : arg.GetHashCode()));
}
}
public static int CombineHashCode(this int hashCode, int rhsHash)
{
unchecked
{
return 31 * hashCode + rhsHash;
}
}
}
}

View File

@ -16,10 +16,12 @@ namespace Wabbajack
/// <typeparam name="T"></typeparam> /// <typeparam name="T"></typeparam>
/// <param name="source"></param> /// <param name="source"></param>
/// <returns>Source events that are not null</returns> /// <returns>Source events that are not null</returns>
public static IObservable<T> NotNull<T>(this IObservable<T> source) public static IObservable<T> NotNull<T>(this IObservable<T?> source)
where T : class where T : class
{ {
return source.Where(u => u != null); return source
.Where(u => u != null)
.Select(u => u!);
} }
/// <summary> /// <summary>
@ -88,9 +90,9 @@ namespace Wabbajack
/// Inspiration: /// Inspiration:
/// http://reactivex.io/documentation/operators/debounce.html /// http://reactivex.io/documentation/operators/debounce.html
/// https://stackoverflow.com/questions/20034476/how-can-i-use-reactive-extensions-to-throttle-events-using-a-max-window-size /// https://stackoverflow.com/questions/20034476/how-can-i-use-reactive-extensions-to-throttle-events-using-a-max-window-size
public static IObservable<T> Debounce<T>(this IObservable<T> source, TimeSpan interval, IScheduler scheduler = null) public static IObservable<T> Debounce<T>(this IObservable<T> source, TimeSpan interval, IScheduler? scheduler = null)
{ {
scheduler = scheduler ?? Scheduler.Default; scheduler ??= Scheduler.Default;
return Observable.Create<T>(o => return Observable.Create<T>(o =>
{ {
var hasValue = false; var hasValue = false;
@ -218,8 +220,8 @@ namespace Wabbajack
.StartWith(false)); .StartWith(false));
} }
public static IObservable<T> DisposeOld<T>(this IObservable<T> source) public static IObservable<T?> DisposeOld<T>(this IObservable<T?> source)
where T : IDisposable where T : class, IDisposable
{ {
return source return source
.StartWith(default(T)) .StartWith(default(T))

View File

@ -5,7 +5,7 @@ namespace Wabbajack
{ {
public static class TaskExt public static class TaskExt
{ {
public static async void FireAndForget(this Task task, Action<Exception> onException = null) public static async void FireAndForget(this Task task, Action<Exception>? onException = null)
{ {
try try
{ {

View File

@ -58,45 +58,46 @@ namespace Wabbajack.Common
public class GameMetaData public class GameMetaData
{ {
public ModManager SupportedModManager { get; internal set; } public ModManager SupportedModManager { get; internal set; }
public string MO2ArchiveName { get; internal set; } public string? MO2ArchiveName { get; internal set; }
public Game Game { get; internal set; } public Game Game { get; internal set; }
public string NexusName { get; internal set; } public string? NexusName { get; internal set; }
// Nexus DB id for the game, used in some specific situations // Nexus DB id for the game, used in some specific situations
public long NexusGameId { get; internal set; } public long NexusGameId { get; internal set; }
public string MO2Name { get; internal set; } public string? MO2Name { get; internal set; }
public string HumanFriendlyGameName => Game.GetDescription(); public string HumanFriendlyGameName => Game.GetDescription();
public string GameLocationRegistryKey { get; internal set; } public string? GameLocationRegistryKey { get; internal set; }
// to get steam ids: https://steamdb.info // to get steam ids: https://steamdb.info
public List<int> SteamIDs { get; internal set; } public List<int>? SteamIDs { get; internal set; }
// to get gog ids: https://www.gogdb.org // to get gog ids: https://www.gogdb.org
public List<int> GOGIDs { get; internal set; } public List<int>? GOGIDs { get; internal set; }
// these are additional folders when a game installs mods outside the game folder // these are additional folders when a game installs mods outside the game folder
public List<string> AdditionalFolders { get; internal set; } public List<string>? AdditionalFolders { get; internal set; }
// file to check if the game is present, useful when steamIds and gogIds dont help // file to check if the game is present, useful when steamIds and gogIds dont help
public List<string> RequiredFiles { get; internal set; } public List<string>? RequiredFiles { get; internal set; }
public bool Disabled { get; internal set; } public bool Disabled { get; internal set; }
// Games that this game are commonly confused with, for example Skyrim SE vs Skyrim LE // Games that this game are commonly confused with, for example Skyrim SE vs Skyrim LE
public Game[] CommonlyConfusedWith { get; set; } public Game[] CommonlyConfusedWith { get; set; } = Array.Empty<Game>();
public string InstalledVersion public string InstalledVersion
{ {
get get
{ {
if (GameLocation() == null) AbsolutePath? gameLoc = GameLocation();
if (gameLoc == null)
throw new GameNotInstalledException(this); throw new GameNotInstalledException(this);
if (MainExecutable == null) if (MainExecutable == null)
throw new NotImplementedException(); throw new NotImplementedException();
return FileVersionInfo.GetVersionInfo((string)GameLocation()?.Combine(MainExecutable)).ProductVersion; return FileVersionInfo.GetVersionInfo((string)gameLoc.Value.Combine(MainExecutable)).ProductVersion;
} }
} }
public bool IsInstalled => GameLocation() != null; public bool IsInstalled => GameLocation() != null;
public string MainExecutable { get; internal set; } public string? MainExecutable { get; internal set; }
public AbsolutePath? GameLocation() public AbsolutePath? GameLocation()
{ {
@ -114,14 +115,14 @@ namespace Wabbajack.Common
public static class EnumExtensions public static class EnumExtensions
{ {
public static string GetDescription<T>(this T enumerationValue) public static string GetDescription<T>(this T enumerationValue)
where T : struct where T : Enum
{ {
var type = enumerationValue.GetType(); var type = enumerationValue.GetType();
if(!type.IsEnum) if(!type.IsEnum)
{ {
throw new ArgumentException($"{nameof(enumerationValue)} must be of Enum type", nameof(enumerationValue)); throw new ArgumentException($"{nameof(enumerationValue)} must be of Enum type", nameof(enumerationValue));
} }
var memberInfo = type.GetMember(enumerationValue.ToString()); var memberInfo = type.GetMember(enumerationValue.ToString()!);
if(memberInfo.Length > 0) if(memberInfo.Length > 0)
{ {
var attrs = memberInfo[0].GetCustomAttributes(typeof(DescriptionAttribute), false); var attrs = memberInfo[0].GetCustomAttributes(typeof(DescriptionAttribute), false);
@ -131,7 +132,7 @@ namespace Wabbajack.Common
return ((DescriptionAttribute)attrs[0]).Description; return ((DescriptionAttribute)attrs[0]).Description;
} }
} }
return enumerationValue.ToString(); return enumerationValue.ToString()!;
} }
} }
@ -158,12 +159,11 @@ namespace Wabbajack.Common
/// Tries to parse game data from an arbitrary string. Tries first via parsing as a game Enum, then by Nexus name, /// Tries to parse game data from an arbitrary string. Tries first via parsing as a game Enum, then by Nexus name,
/// <param nambe="someName"></param> /// <param nambe="someName"></param>
/// <returns></returns> /// <returns></returns>
public static GameMetaData GetByFuzzyName(string someName) public static GameMetaData? GetByFuzzyName(string someName)
{ {
if (Enum.TryParse(typeof(Game), someName, true, out var metadata)) return ((Game)metadata!).MetaData();
if (Enum.TryParse(typeof(Game), someName, true, out var metadata)) return ((Game)metadata).MetaData(); GameMetaData? result = null;
GameMetaData result = null;
result = GetByNexusName(someName); result = GetByNexusName(someName);
if (result != null) return result; if (result != null) return result;

View File

@ -1,5 +1,4 @@
#nullable enable using System;
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Data.HashFunction.xxHash; using System.Data.HashFunction.xxHash;
using System.IO; using System.IO;

View File

@ -52,7 +52,7 @@ namespace Wabbajack.Common
new JsonSerializerSettings new JsonSerializerSettings
{ {
TypeNameHandling = handling, TypeNameAssemblyFormatHandling = format, Converters = Converters TypeNameHandling = handling, TypeNameAssemblyFormatHandling = format, Converters = Converters
}); })!;
} }
public static T FromJSONString<T>(this string data, public static T FromJSONString<T>(this string data,
@ -63,7 +63,7 @@ namespace Wabbajack.Common
new JsonSerializerSettings new JsonSerializerSettings
{ {
TypeNameHandling = handling, TypeNameAssemblyFormatHandling = format, Converters = Converters TypeNameHandling = handling, TypeNameAssemblyFormatHandling = format, Converters = Converters
}); })!;
} }
public static T FromJSON<T>(this Stream data) public static T FromJSON<T>(this Stream data)
@ -72,7 +72,7 @@ namespace Wabbajack.Common
try try
{ {
return JsonConvert.DeserializeObject<T>(s, return JsonConvert.DeserializeObject<T>(s,
new JsonSerializerSettings {TypeNameHandling = TypeNameHandling.Auto, Converters = Converters}); new JsonSerializerSettings {TypeNameHandling = TypeNameHandling.Auto, Converters = Converters})!;
} }
catch (JsonSerializationException) catch (JsonSerializationException)
{ {
@ -95,7 +95,7 @@ namespace Wabbajack.Common
public override Hash ReadJson(JsonReader reader, Type objectType, Hash existingValue, bool hasExistingValue, public override Hash ReadJson(JsonReader reader, Type objectType, Hash existingValue, bool hasExistingValue,
JsonSerializer serializer) JsonSerializer serializer)
{ {
return Hash.FromBase64((string)reader.Value); return Hash.FromBase64((string)reader.Value!);
} }
} }
@ -110,7 +110,7 @@ namespace Wabbajack.Common
bool hasExistingValue, bool hasExistingValue,
JsonSerializer serializer) JsonSerializer serializer)
{ {
return (RelativePath)(string)reader.Value; return (RelativePath)(string)reader.Value!;
} }
} }
@ -125,7 +125,7 @@ namespace Wabbajack.Common
bool hasExistingValue, bool hasExistingValue,
JsonSerializer serializer) JsonSerializer serializer)
{ {
return (AbsolutePath)(string)reader.Value; return (AbsolutePath)(string)reader.Value!;
} }
} }
@ -133,7 +133,7 @@ namespace Wabbajack.Common
{ {
public override Percent ReadJson(JsonReader reader, Type objectType, Percent existingValue, bool hasExistingValue, JsonSerializer serializer) public override Percent ReadJson(JsonReader reader, Type objectType, Percent existingValue, bool hasExistingValue, JsonSerializer serializer)
{ {
double d = (double)reader.Value; double d = (double)reader.Value!;
return Percent.FactoryPutInRange(d); return Percent.FactoryPutInRange(d);
} }
@ -162,13 +162,13 @@ namespace Wabbajack.Common
throw new JsonException("Invalid JSON state while reading Hash Relative Path"); throw new JsonException("Invalid JSON state while reading Hash Relative Path");
reader.Read(); reader.Read();
var hash = Hash.FromBase64((string)reader.Value); var hash = Hash.FromBase64((string)reader.Value!);
var paths = new List<RelativePath>(); var paths = new List<RelativePath>();
reader.Read(); reader.Read();
while (reader.TokenType != JsonToken.EndArray) while (reader.TokenType != JsonToken.EndArray)
{ {
paths.Add((RelativePath)(string)reader.Value); paths.Add((RelativePath)(string)reader.Value!);
reader.Read(); reader.Read();
} }
@ -195,13 +195,13 @@ namespace Wabbajack.Common
throw new JsonException("Invalid JSON state while reading Hash Relative Path"); throw new JsonException("Invalid JSON state while reading Hash Relative Path");
reader.Read(); reader.Read();
var abs = (AbsolutePath)(string)reader.Value; var abs = (AbsolutePath)(string)reader.Value!;
var paths = new List<RelativePath>(); var paths = new List<RelativePath>();
reader.Read(); reader.Read();
while (reader.TokenType != JsonToken.EndArray) while (reader.TokenType != JsonToken.EndArray)
{ {
paths.Add((RelativePath)(string)reader.Value); paths.Add((RelativePath)(string)reader.Value!);
reader.Read(); reader.Read();
} }
@ -228,7 +228,13 @@ namespace Wabbajack.Common
return (Game)i; return (Game)i;
} }
return GameRegistry.GetByFuzzyName(str).Game; GameMetaData? game = GameRegistry.GetByFuzzyName(str);
if (game == null)
{
throw new ArgumentException($"Could not convert {str} to a Game type.");
}
return game.Game;
} }
} }
} }

View File

@ -1,6 +1,7 @@
using System; using System;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Security.Principal; using System.Security.Principal;
#nullable disable
namespace Wabbajack.Common.IO namespace Wabbajack.Common.IO
{ {

View File

@ -1,5 +1,6 @@
using System; using System;
using System.Reflection; using System.Reflection;
#nullable disable
namespace Wabbajack.Common.IO namespace Wabbajack.Common.IO
{ {

View File

@ -1,5 +1,6 @@
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Collections.Generic; using System.Collections.Generic;
#nullable disable
namespace Wabbajack.Common.IO namespace Wabbajack.Common.IO
{ {

View File

@ -11,8 +11,8 @@ namespace Wabbajack.Common
{ {
public partial class Utils public partial class Utils
{ {
private static MessagePackSerializerOptions _messagePackOptions; private static MessagePackSerializerOptions? _messagePackOptions;
private static IFormatterResolver _resolver; private static IFormatterResolver? _resolver;
private static void MessagePackInit() private static void MessagePackInit()
{ {

View File

@ -39,7 +39,7 @@ namespace Wabbajack.Common
var tempKey = progIDKey?.OpenSubKey("shell\\open\\command"); var tempKey = progIDKey?.OpenSubKey("shell\\open\\command");
if (progIDKey == null || tempKey == null) return true; if (progIDKey == null || tempKey == null) return true;
var value = tempKey.GetValue(""); var value = tempKey.GetValue("");
return value == null || !value.ToString().Equals($"\"{execPath}\" -i=\"%1\""); return value == null || !string.Equals(value.ToString(), $"\"{execPath}\" -i=\"%1\"");
} }
public static bool IsAssociated() public static bool IsAssociated()

View File

@ -33,7 +33,7 @@ namespace Wabbajack.Common
return string.Equals(_path, other._path, StringComparison.InvariantCultureIgnoreCase); return string.Equals(_path, other._path, StringComparison.InvariantCultureIgnoreCase);
} }
public override bool Equals(object obj) public override bool Equals(object? obj)
{ {
return obj is AbsolutePath other && Equals(other); return obj is AbsolutePath other && Equals(other);
} }
@ -42,7 +42,7 @@ namespace Wabbajack.Common
public override int GetHashCode() public override int GetHashCode()
{ {
return _path?.GetHashCode(StringComparison.InvariantCultureIgnoreCase) ?? 0; return _path.GetHashCode(StringComparison.InvariantCultureIgnoreCase);
} }
public override string ToString() public override string ToString()
@ -50,11 +50,12 @@ namespace Wabbajack.Common
return _path; return _path;
} }
private readonly string _path; private readonly string _nullable_path;
private string _path => _nullable_path ?? string.Empty;
public AbsolutePath(string path, bool skipValidation = false) public AbsolutePath(string path, bool skipValidation = false)
{ {
_path = path.Replace("/", "\\").TrimEnd('\\'); _nullable_path = path.Replace("/", "\\").TrimEnd('\\');
if (!skipValidation) if (!skipValidation)
{ {
ValidateAbsolutePath(); ValidateAbsolutePath();
@ -63,7 +64,7 @@ namespace Wabbajack.Common
public AbsolutePath(AbsolutePath path) public AbsolutePath(AbsolutePath path)
{ {
_path = path._path; _nullable_path = path._path;
} }
private void ValidateAbsolutePath() private void ValidateAbsolutePath()
@ -150,7 +151,8 @@ namespace Wabbajack.Common
var location = Assembly.GetEntryAssembly()?.Location ?? null; var location = Assembly.GetEntryAssembly()?.Location ?? null;
if (location == null) if (location == null)
location = Assembly.GetExecutingAssembly().Location ?? null; location = Assembly.GetExecutingAssembly().Location ?? null;
if (location == null)
throw new ArgumentException("Could not find entry point.");
return ((AbsolutePath)location).Parent; return ((AbsolutePath)location).Parent;
} }
} }
@ -264,7 +266,7 @@ namespace Wabbajack.Common
public AbsolutePath Combine(params RelativePath[] paths) public AbsolutePath Combine(params RelativePath[] paths)
{ {
return new AbsolutePath(Path.Combine(paths.Select(s => (string)s).Where(s => s != null).Cons(_path).ToArray())); return new AbsolutePath(Path.Combine(paths.Select(s => (string)s).Cons(_path).ToArray()));
} }
public AbsolutePath Combine(params string[] paths) public AbsolutePath Combine(params string[] paths)
@ -371,23 +373,24 @@ namespace Wabbajack.Common
public struct RelativePath : IPath, IEquatable<RelativePath>, IComparable<RelativePath> public struct RelativePath : IPath, IEquatable<RelativePath>, IComparable<RelativePath>
{ {
private readonly string _path; private readonly string? _nullable_path;
private string _path => _nullable_path ?? string.Empty;
public RelativePath(string path) public RelativePath(string path)
{ {
if (string.IsNullOrWhiteSpace(path)) if (string.IsNullOrWhiteSpace(path))
{ {
_path = null; _nullable_path = null;
return; return;
} }
var trimmed = path.Replace("/", "\\").Trim('\\'); var trimmed = path.Replace("/", "\\").Trim('\\');
if (string.IsNullOrEmpty(trimmed)) if (string.IsNullOrEmpty(trimmed))
{ {
_path = null; _nullable_path = null;
return; return;
} }
_path = trimmed; _nullable_path = trimmed;
Validate(); Validate();
} }
@ -400,7 +403,7 @@ namespace Wabbajack.Common
public override int GetHashCode() public override int GetHashCode()
{ {
return _path?.GetHashCode(StringComparison.InvariantCultureIgnoreCase) ?? 0; return _path.GetHashCode(StringComparison.InvariantCultureIgnoreCase);
} }
public static RelativePath RandomFileName() public static RelativePath RandomFileName()
@ -418,7 +421,7 @@ namespace Wabbajack.Common
public AbsolutePath RelativeTo(AbsolutePath abs) public AbsolutePath RelativeTo(AbsolutePath abs)
{ {
return _path == null ? abs : new AbsolutePath(Path.Combine((string)abs, _path)); return new AbsolutePath(Path.Combine((string)abs, _path));
} }
public AbsolutePath RelativeToEntryPoint() public AbsolutePath RelativeToEntryPoint()
@ -470,7 +473,7 @@ namespace Wabbajack.Common
return string.Equals(_path, other._path, StringComparison.InvariantCultureIgnoreCase); return string.Equals(_path, other._path, StringComparison.InvariantCultureIgnoreCase);
} }
public override bool Equals(object obj) public override bool Equals(object? obj)
{ {
return obj is RelativePath other && Equals(other); return obj is RelativePath other && Equals(other);
} }
@ -586,7 +589,7 @@ namespace Wabbajack.Common
return string.Equals(_extension, other._extension, StringComparison.InvariantCultureIgnoreCase); return string.Equals(_extension, other._extension, StringComparison.InvariantCultureIgnoreCase);
} }
public override bool Equals(object obj) public override bool Equals(object? obj)
{ {
return obj is Extension other && Equals(other); return obj is Extension other && Equals(other);
} }
@ -603,23 +606,24 @@ namespace Wabbajack.Common
#endregion #endregion
private readonly string _extension; private readonly string? _nullable_extension;
private string _extension => _nullable_extension ?? string.Empty;
public Extension(string extension) public Extension(string extension)
{ {
if (string.IsNullOrWhiteSpace(extension)) if (string.IsNullOrWhiteSpace(extension))
{ {
_extension = None._extension; _nullable_extension = None._extension;
return; return;
} }
_extension = string.Intern(extension); _nullable_extension = string.Intern(extension);
Validate(); Validate();
} }
private Extension(string extension, bool validate) private Extension(string extension, bool validate)
{ {
_extension = string.Intern(extension); _nullable_extension = string.Intern(extension);
if (validate) if (validate)
{ {
Validate(); Validate();
@ -628,7 +632,7 @@ namespace Wabbajack.Common
public Extension(Extension other) public Extension(Extension other)
{ {
_extension = other._extension; _nullable_extension = other._extension;
} }
private void Validate() private void Validate()
@ -652,16 +656,6 @@ namespace Wabbajack.Common
public static bool operator ==(Extension a, Extension b) public static bool operator ==(Extension a, Extension b)
{ {
// Super fast comparison because extensions are interned // Super fast comparison because extensions are interned
if ((object)a == null && (object)b == null)
{
return true;
}
if ((object)a == null || (object)b == null)
{
return false;
}
return ReferenceEquals(a._extension, b._extension); return ReferenceEquals(a._extension, b._extension);
} }
@ -726,7 +720,7 @@ namespace Wabbajack.Common
return this == other; return this == other;
} }
public override bool Equals(object obj) public override bool Equals(object? obj)
{ {
return obj is HashRelativePath other && Equals(other); return obj is HashRelativePath other && Equals(other);
} }
@ -772,7 +766,6 @@ namespace Wabbajack.Common
public static bool operator ==(FullPath a, FullPath b) public static bool operator ==(FullPath a, FullPath b)
{ {
if (a.Paths == null || b.Paths == null) return false;
if (a.Base != b.Base || a.Paths.Length != b.Paths.Length) if (a.Base != b.Base || a.Paths.Length != b.Paths.Length)
{ {
return false; return false;
@ -799,7 +792,7 @@ namespace Wabbajack.Common
return this == other; return this == other;
} }
public override bool Equals(object obj) public override bool Equals(object? obj)
{ {
return obj is FullPath other && Equals(other); return obj is FullPath other && Equals(other);
} }

View File

@ -16,8 +16,8 @@ namespace Wabbajack.Common
Error, Error,
} }
public string Path { get; set; } public string Path { get; set; } = string.Empty;
public IEnumerable<object> Arguments { get; set; } public IEnumerable<object> Arguments { get; set; } = Enumerable.Empty<object>();
public bool LogError { get; set; } = true; public bool LogError { get; set; } = true;

View File

@ -10,8 +10,8 @@ namespace Wabbajack.Common.StatusFeed.Errors
{ {
private string _filename; private string _filename;
private string _destination; private string _destination;
public override string ShortDescription { get; } public override string ShortDescription { get; } = string.Empty;
public override string ExtendedDescription { get; } public override string ExtendedDescription { get; } = string.Empty;
public FileExtractionError(string filename, string destination) public FileExtractionError(string filename, string destination)
{ {

View File

@ -8,19 +8,19 @@ namespace Wabbajack.Common.StatusFeed.Errors
{ {
public class GenericException : IException public class GenericException : IException
{ {
public string ExtraMessage { get; } public string ExtraMessage { get; } = string.Empty;
public DateTime Timestamp { get; } = DateTime.Now; public DateTime Timestamp { get; } = DateTime.Now;
public string ShortDescription => ExtraMessage == null ? Exception?.Message : $"{ExtraMessage} - {Exception?.Message}"; public string ShortDescription => string.IsNullOrWhiteSpace(ExtraMessage) ? Exception?.Message ?? string.Empty : $"{ExtraMessage} - {Exception?.Message}";
public string ExtendedDescription => ExtraMessage == null ? Exception?.ToString() : $"{ExtraMessage} - {Exception?.ToString()}"; public string ExtendedDescription => string.IsNullOrWhiteSpace(ExtraMessage) ? Exception?.ToString() ?? string.Empty : $"{ExtraMessage} - {Exception?.ToString()}";
public Exception Exception { get; } public Exception Exception { get; }
public GenericException(Exception exception, string extraMessage = null) public GenericException(Exception exception, string? extraMessage = null)
{ {
ExtraMessage = extraMessage; ExtraMessage = extraMessage ?? string.Empty;
Exception = exception; Exception = exception;
} }
} }

View File

@ -12,10 +12,10 @@ namespace Wabbajack.Common.StatusFeed
private readonly string _extendedDescription; private readonly string _extendedDescription;
public override string ExtendedDescription => _extendedDescription ?? ShortDescription; public override string ExtendedDescription => _extendedDescription ?? ShortDescription;
public GenericInfo(string short_description, string long_description = null) public GenericInfo(string short_description, string? long_description = null)
{ {
ShortDescription = short_description; ShortDescription = short_description;
_extendedDescription = long_description; _extendedDescription = long_description ?? string.Empty;
} }
public override string ToString() public override string ToString()

View File

@ -6,9 +6,9 @@ namespace Wabbajack.Common
{ {
private string _message; private string _message;
private Stream _inner; private Stream _inner;
private WorkQueue _queue; private WorkQueue? _queue;
public StatusFileStream(Stream fs, string message, WorkQueue queue = null) public StatusFileStream(Stream fs, string message, WorkQueue? queue = null)
{ {
_queue = queue; _queue = queue;
_inner = fs; _inner = fs;

View File

@ -10,21 +10,17 @@ namespace Wabbajack.Common.StoreHandlers
public class GOGGame : AStoreGame public class GOGGame : AStoreGame
{ {
public override Game Game { get; internal set; } public override Game Game { get; internal set; }
public override string Name { get; internal set; }
public override AbsolutePath Path { get; internal set; }
public override int ID { get; internal set; }
public override StoreType Type { get; internal set; } = StoreType.GOG; public override StoreType Type { get; internal set; } = StoreType.GOG;
} }
public class GOGHandler : AStoreHandler public class GOGHandler : AStoreHandler
{ {
public override List<AStoreGame> Games { get; set; }
public override StoreType Type { get; internal set; } public override StoreType Type { get; internal set; }
private const string GOGRegKey = @"Software\GOG.com\Games"; private const string GOGRegKey = @"Software\GOG.com\Games";
private const string GOG64RegKey = @"Software\WOW6432Node\GOG.com\Games"; private const string GOG64RegKey = @"Software\WOW6432Node\GOG.com\Games";
private RegistryKey GOGKey { get; set; } private RegistryKey? GOGKey { get; set; }
public override bool Init() public override bool Init()
{ {
@ -56,9 +52,11 @@ namespace Wabbajack.Common.StoreHandlers
public override bool LoadAllGames() public override bool LoadAllGames()
{ {
if(Games == null) if (GOGKey == null)
Games = new List<AStoreGame>(); {
Utils.Error("GOGHandler could not read from registry!");
return false;
}
try try
{ {
string[] keys = GOGKey.GetSubKeyNames(); string[] keys = GOGKey.GetSubKeyNames();
@ -86,7 +84,7 @@ namespace Wabbajack.Common.StoreHandlers
return; return;
} }
var gameName = gameNameValue.ToString(); var gameName = gameNameValue.ToString() ?? string.Empty;
var pathValue = subKey.GetValue("PATH"); var pathValue = subKey.GetValue("PATH");
if (pathValue == null) if (pathValue == null)
@ -95,7 +93,7 @@ namespace Wabbajack.Common.StoreHandlers
return; return;
} }
var path = pathValue.ToString(); var path = pathValue.ToString() ?? string.Empty;
var game = new GOGGame var game = new GOGGame
{ {

View File

@ -11,33 +11,29 @@ namespace Wabbajack.Common.StoreHandlers
public class SteamGame : AStoreGame public class SteamGame : AStoreGame
{ {
public override Game Game { get; internal set; } public override Game Game { get; internal set; }
public override string Name { get; internal set; }
public override AbsolutePath Path { get; internal set; }
public override int ID { get; internal set; }
public override StoreType Type { get; internal set; } = StoreType.STEAM; public override StoreType Type { get; internal set; } = StoreType.STEAM;
public string Universe; public string Universe = string.Empty;
public List<SteamWorkshopItem> WorkshopItems; public readonly List<SteamWorkshopItem> WorkshopItems = new List<SteamWorkshopItem>();
public int WorkshopItemsSize; public int WorkshopItemsSizeOnDisk;
} }
public class SteamWorkshopItem public class SteamWorkshopItem
{ {
public SteamGame Game; public SteamGame? Game;
public int ItemID; public int ItemID;
public int Size; public int Size;
} }
public class SteamHandler : AStoreHandler public class SteamHandler : AStoreHandler
{ {
public override List<AStoreGame> Games { get; set; }
public override StoreType Type { get; internal set; } = StoreType.STEAM; public override StoreType Type { get; internal set; } = StoreType.STEAM;
private const string SteamRegKey = @"Software\Valve\Steam"; private const string SteamRegKey = @"Software\Valve\Steam";
public string SteamPath { get; set; } public string SteamPath { get; set; } = string.Empty;
private List<string> SteamUniverses { get; set; } private List<string>? SteamUniverses { get; set; }
private string SteamConfig => Path.Combine(SteamPath, "config", "config.vdf"); private string SteamConfig => Path.Combine(SteamPath, "config", "config.vdf");
@ -54,7 +50,7 @@ namespace Wabbajack.Common.StoreHandlers
return false; return false;
} }
SteamPath = steamPathKey.ToString(); SteamPath = steamPathKey.ToString() ?? string.Empty;
if (string.IsNullOrWhiteSpace(SteamPath)) if (string.IsNullOrWhiteSpace(SteamPath))
{ {
Utils.Error(new StoreException("Path to the Steam Directory from registry is Null or Empty!")); Utils.Error(new StoreException("Path to the Steam Directory from registry is Null or Empty!"));
@ -86,9 +82,9 @@ namespace Wabbajack.Common.StoreHandlers
return false; return false;
} }
private void LoadUniverses() private List<string> LoadUniverses()
{ {
SteamUniverses = new List<string>(); var ret = new List<string>();
File.ReadAllLines(SteamConfig).Do(l => File.ReadAllLines(SteamConfig).Do(l =>
{ {
@ -101,21 +97,23 @@ namespace Wabbajack.Common.StoreHandlers
return; return;
} }
SteamUniverses.Add(s); ret.Add(s);
Utils.Log($"Steam Library found at {s}"); Utils.Log($"Steam Library found at {s}");
}); });
Utils.Log($"Total number of Steam Libraries found: {SteamUniverses.Count}"); Utils.Log($"Total number of Steam Libraries found: {ret.Count}");
// Default path in the Steam folder isn't in the configs // Default path in the Steam folder isn't in the configs
if(Directory.Exists(Path.Combine(SteamPath, "steamapps"))) if (Directory.Exists(Path.Combine(SteamPath, "steamapps")))
SteamUniverses.Add(Path.Combine(SteamPath, "steamapps")); ret.Add(Path.Combine(SteamPath, "steamapps"));
return ret;
} }
public override bool LoadAllGames() public override bool LoadAllGames()
{ {
if(SteamUniverses == null) if (SteamUniverses == null)
LoadUniverses(); SteamUniverses = LoadUniverses();
if (SteamUniverses.Count == 0) if (SteamUniverses.Count == 0)
{ {
@ -123,9 +121,6 @@ namespace Wabbajack.Common.StoreHandlers
return false; return false;
} }
if(Games == null)
Games = new List<AStoreGame>();
SteamUniverses.Do(u => SteamUniverses.Do(u =>
{ {
Utils.Log($"Searching for Steam Games in {u}"); Utils.Log($"Searching for Steam Games in {u}");
@ -188,9 +183,6 @@ namespace Wabbajack.Common.StoreHandlers
private static void LoadWorkshopItems(SteamGame game) private static void LoadWorkshopItems(SteamGame game)
{ {
if(game.WorkshopItems == null)
game.WorkshopItems = new List<SteamWorkshopItem>();
var workshop = Path.Combine(game.Universe, "workshop"); var workshop = Path.Combine(game.Universe, "workshop");
if (!Directory.Exists(workshop)) if (!Directory.Exists(workshop))
return; return;
@ -210,13 +202,13 @@ namespace Wabbajack.Common.StoreHandlers
var bracketStart = 0; var bracketStart = 0;
var bracketEnd = 0; var bracketEnd = 0;
var currentItem = new SteamWorkshopItem(); SteamWorkshopItem? currentItem = new SteamWorkshopItem();
lines.Do(l => lines.Do(l =>
{ {
if (end) if (end)
return; return;
if(currentItem == null) if (currentItem == null)
currentItem = new SteamWorkshopItem(); currentItem = new SteamWorkshopItem();
var currentLine = lines.IndexOf(l); var currentLine = lines.IndexOf(l);
@ -239,7 +231,7 @@ namespace Wabbajack.Common.StoreHandlers
if (!int.TryParse(GetVdfValue(l), out var sizeOnDisk)) if (!int.TryParse(GetVdfValue(l), out var sizeOnDisk))
return; return;
game.WorkshopItemsSize += sizeOnDisk; game.WorkshopItemsSizeOnDisk += sizeOnDisk;
} }
if (l.ContainsCaseInsensitive("\"WorkshopItemsInstalled\"")) if (l.ContainsCaseInsensitive("\"WorkshopItemsInstalled\""))

View File

@ -61,15 +61,15 @@ namespace Wabbajack.Common.StoreHandlers
public abstract class AStoreGame public abstract class AStoreGame
{ {
public abstract Game Game { get; internal set; } public abstract Game Game { get; internal set; }
public abstract string Name { get; internal set; } public virtual string Name { get; internal set; } = string.Empty;
public abstract AbsolutePath Path { get; internal set; } public virtual AbsolutePath Path { get; internal set; }
public abstract int ID { get; internal set; } public virtual int ID { get; internal set; }
public abstract StoreType Type { get; internal set; } public abstract StoreType Type { get; internal set; }
} }
public abstract class AStoreHandler public abstract class AStoreHandler
{ {
public abstract List<AStoreGame> Games { get; set; } public List<AStoreGame> Games { get; } = new List<AStoreGame>();
public abstract StoreType Type { get; internal set; } public abstract StoreType Type { get; internal set; }

View File

@ -10,7 +10,7 @@ namespace Wabbajack.Common
{ {
public class AsyncLock public class AsyncLock
{ {
private SemaphoreSlim _lock = new SemaphoreSlim(1, 1); private readonly SemaphoreSlim _lock = new SemaphoreSlim(1, 1);
public async Task<IDisposable> Wait() public async Task<IDisposable> Wait()
{ {

View File

@ -43,9 +43,9 @@ namespace Wabbajack.Common
public void Dispose() public void Dispose()
{ {
_allocated.Do(s => s.Dispose()); _allocated.Do(s => s.Dispose());
_mmap?.Dispose(); _mmap.Dispose();
_fileStream?.Dispose(); _fileStream.Dispose();
_file?.Dispose(); _file.Dispose();
} }
} }
} }

View File

@ -142,7 +142,7 @@ namespace Wabbajack.Common
return new Percent(percent, check: false); return new Percent(percent, check: false);
} }
public override bool Equals(object obj) public override bool Equals(object? obj)
{ {
if (!(obj is Percent rhs)) return false; if (!(obj is Percent rhs)) return false;
return Equals(rhs); return Equals(rhs);
@ -191,7 +191,7 @@ namespace Wabbajack.Common
} }
} }
public int CompareTo(object obj) public int CompareTo(object? obj)
{ {
if (obj is Percent rhs) if (obj is Percent rhs)
{ {

View File

@ -6,7 +6,6 @@ using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using AlphaPath = Alphaleonis.Win32.Filesystem.Path; using AlphaPath = Alphaleonis.Win32.Filesystem.Path;
namespace Wabbajack.Common namespace Wabbajack.Common
{ {
public class TempFile : IDisposable public class TempFile : IDisposable

View File

@ -3,6 +3,7 @@ using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using System.Data.HashFunction.xxHash; using System.Data.HashFunction.xxHash;
using System.Diagnostics; using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Net.Http; using System.Net.Http;
@ -132,12 +133,12 @@ namespace Wabbajack.Common
Log(errMessage); Log(errMessage);
} }
public static void Error(Exception ex, string extraMessage = null) public static void Error(Exception ex, string? extraMessage = null)
{ {
Log(new GenericException(ex, extraMessage)); Log(new GenericException(ex, extraMessage));
} }
public static void ErrorThrow(Exception ex, string extraMessage = null) public static void ErrorThrow(Exception ex, string? extraMessage = null)
{ {
Error(ex, extraMessage); Error(ex, extraMessage);
throw ex; throw ex;
@ -231,29 +232,21 @@ namespace Wabbajack.Common
Status(status, Percent.FactoryPutInRange(totalRead, maxSize)); Status(status, Percent.FactoryPutInRange(totalRead, maxSize));
} }
} }
public static string xxHash(this byte[] data, bool nullOnIOError = false) public static string xxHash(this byte[] data)
{ {
try var hash = new xxHashConfig();
hash.HashSizeInBits = 64;
hash.Seed = 0x42;
using (var fs = new MemoryStream(data))
{ {
var hash = new xxHashConfig(); var config = new xxHashConfig();
hash.HashSizeInBits = 64; config.HashSizeInBits = 64;
hash.Seed = 0x42; using (var f = new StatusFileStream(fs, $"Hashing memory stream"))
using (var fs = new MemoryStream(data))
{ {
var config = new xxHashConfig(); var value = xxHashFactory.Instance.Create(config).ComputeHash(f);
config.HashSizeInBits = 64; return value.AsBase64String();
using (var f = new StatusFileStream(fs, $"Hashing memory stream"))
{
var value = xxHashFactory.Instance.Create(config).ComputeHash(f);
return value.AsBase64String();
}
} }
} }
catch (IOException ex)
{
if (nullOnIOError) return null;
throw ex;
}
} }
/// <summary> /// <summary>
@ -673,7 +666,9 @@ namespace Wabbajack.Common
return a[a.Length - 1]; return a[a.Length - 1];
} }
[return: MaybeNull]
public static V GetOrDefault<K, V>(this IDictionary<K, V> dict, K key) public static V GetOrDefault<K, V>(this IDictionary<K, V> dict, K key)
where K : notnull
{ {
if (dict.TryGetValue(key, out var v)) return v; if (dict.TryGetValue(key, out var v)) return v;
return default; return default;
@ -776,7 +771,7 @@ namespace Wabbajack.Common
} }
} }
public static bool TryGetPatch(Hash foundHash, Hash fileHash, out byte[] ePatch) public static bool TryGetPatch(Hash foundHash, Hash fileHash, [MaybeNullWhen(false)] out byte[] ePatch)
{ {
var patchName = Consts.PatchCacheFolder.Combine($"{foundHash.ToHex()}_{fileHash.ToHex()}.patch"); var patchName = Consts.PatchCacheFolder.Combine($"{foundHash.ToHex()}_{fileHash.ToHex()}.patch");
if (patchName.Exists) if (patchName.Exists)
@ -809,17 +804,6 @@ namespace Wabbajack.Common
} }
/*
public static void Warning(string s)
{
Log($"WARNING: {s}");
}*/
public static TV GetOrDefault<TK, TV>(this Dictionary<TK, TV> dict, TK key)
{
return dict.TryGetValue(key, out var result) ? result : default;
}
public static IEnumerable<T> ButLast<T>(this IEnumerable<T> coll) public static IEnumerable<T> ButLast<T>(this IEnumerable<T> coll)
{ {
var lst = coll.ToList(); var lst = coll.ToList();
@ -1097,7 +1081,7 @@ namespace Wabbajack.Common
public class NexusErrorResponse public class NexusErrorResponse
{ {
public int code; public int code;
public string message; public string message = string.Empty;
} }
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)] [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]

View File

@ -4,7 +4,7 @@
<TargetFramework>netcoreapp3.1</TargetFramework> <TargetFramework>netcoreapp3.1</TargetFramework>
<Platforms>x64</Platforms> <Platforms>x64</Platforms>
<RuntimeIdentifier>win10-x64</RuntimeIdentifier> <RuntimeIdentifier>win10-x64</RuntimeIdentifier>
<Nullable>enable</Nullable>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<None Remove="7z.dll.gz" /> <None Remove="7z.dll.gz" />

View File

@ -25,7 +25,7 @@ namespace Wabbajack.Common
public static bool WorkerThread => AsyncLocalCurrentQueue.Value != null; public static bool WorkerThread => AsyncLocalCurrentQueue.Value != null;
public bool IsWorkerThread => WorkerThread; public bool IsWorkerThread => WorkerThread;
internal static readonly AsyncLocal<WorkQueue> AsyncLocalCurrentQueue = new AsyncLocal<WorkQueue>(); internal static readonly AsyncLocal<WorkQueue?> AsyncLocalCurrentQueue = new AsyncLocal<WorkQueue?>();
private readonly Subject<CPUStatus> _Status = new Subject<CPUStatus>(); private readonly Subject<CPUStatus> _Status = new Subject<CPUStatus>();
public IObservable<CPUStatus> Status => _Status; public IObservable<CPUStatus> Status => _Status;
@ -194,7 +194,7 @@ namespace Wabbajack.Common
public class CPUStatus public class CPUStatus
{ {
public Percent ProgressPercent { get; internal set; } public Percent ProgressPercent { get; internal set; }
public string Msg { get; internal set; } public string Msg { get; internal set; } = string.Empty;
public int ID { get; internal set; } public int ID { get; internal set; }
public bool IsWorking { get; internal set; } public bool IsWorking { get; internal set; }
} }