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

@ -121,4 +121,67 @@ csharp_preserve_single_line_blocks = true
# CS4014: Task not awaited
dotnet_diagnostic.CS4014.severity = error
# 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,13 +9,8 @@ namespace Wabbajack.Common
{
public class AsyncBlockingCollection<T> : IDisposable
{
private readonly ConcurrentQueue<T> _collection;
private readonly ConcurrentQueue<T> _collection = new ConcurrentQueue<T>();
private bool isDisposed = false;
public AsyncBlockingCollection()
{
_collection = new ConcurrentQueue<T>();
}
public void Add(T val)
{

View File

@ -10,10 +10,10 @@ namespace Wabbajack.Common
public static bool NoSettings { get; set; }
[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")]
public static string InstallPath { get; set; }
public static string? InstallPath { get; set; }
[CLIOptions("help", ShortOption = 'h', HelpText = "Display this message")]
public static bool Help { get; set; }
@ -94,7 +94,7 @@ namespace Wabbajack.Common
// -shortOption, short name of the option. Eg: -o
public char ShortOption;
// text to be displayed when --help is called
public string HelpText;
public string HelpText = string.Empty;
public CLIOptions(string option)
{

View File

@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Dynamic;
using System.Text;
using System.Text.RegularExpressions;
@ -35,7 +36,7 @@ namespace Wabbajack.Common
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)
{
@ -114,7 +115,7 @@ namespace Wabbajack.Common
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)
{

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 bool Succeeded { get; }
public Exception Exception { get; }
public Exception? Exception { get; }
private readonly string _reason;
public bool Failed => !Succeeded;
@ -32,15 +32,15 @@ namespace Wabbajack
}
bool IErrorResponse.Succeeded => Succeeded;
Exception IErrorResponse.Exception => Exception;
Exception? IErrorResponse.Exception => Exception;
private ErrorResponse(
bool succeeded,
string reason = null,
Exception ex = null)
string? reason = null,
Exception? ex = null)
{
Succeeded = succeeded;
_reason = reason;
_reason = reason ?? string.Empty;
Exception = ex;
}
@ -60,7 +60,7 @@ namespace Wabbajack
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);
}
@ -75,7 +75,7 @@ namespace Wabbajack
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);
}
@ -91,7 +91,7 @@ namespace Wabbajack
public interface IErrorResponse
{
bool Succeeded { get; }
Exception Exception { get; }
Exception? Exception { get; }
string Reason { get; }
}
}

View File

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

View File

@ -6,17 +6,37 @@ namespace Wabbajack
public static class DictionaryExt
{
public static V TryCreate<K, V>(this IDictionary<K, V> dict, K key)
where K : notnull
where V : new()
{
return dict.TryCreate(key, () => new V());
}
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;
var ret = create();
dict[key] = 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");
}
var attributes = (DescriptionAttribute[])val.GetType().GetField(val.ToString()).GetCustomAttributes(typeof(DescriptionAttribute), false);
return attributes.Length > 0 ? attributes[0].Description : val.ToString();
var attributes = (DescriptionAttribute[])val.GetType().GetField(val.ToString()!)!.GetCustomAttributes(typeof(DescriptionAttribute), false);
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)
{
if (source == null) throw new ArgumentNullException("source");
if (rng == null) throw new ArgumentNullException("rng");
return source.ShuffleIterator(rng);
}
@ -30,5 +27,11 @@ namespace Wabbajack
}
}
#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>
/// <param name="source"></param>
/// <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
{
return source.Where(u => u != null);
return source
.Where(u => u != null)
.Select(u => u!);
}
/// <summary>
@ -88,9 +90,9 @@ namespace Wabbajack
/// Inspiration:
/// 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
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 =>
{
var hasValue = false;
@ -218,8 +220,8 @@ namespace Wabbajack
.StartWith(false));
}
public static IObservable<T> DisposeOld<T>(this IObservable<T> source)
where T : IDisposable
public static IObservable<T?> DisposeOld<T>(this IObservable<T?> source)
where T : class, IDisposable
{
return source
.StartWith(default(T))

View File

@ -5,7 +5,7 @@ namespace Wabbajack
{
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
{

View File

@ -58,45 +58,46 @@ namespace Wabbajack.Common
public class GameMetaData
{
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 string NexusName { get; internal set; }
public string? NexusName { get; internal set; }
// Nexus DB id for the game, used in some specific situations
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 GameLocationRegistryKey { get; internal set; }
public string? GameLocationRegistryKey { get; internal set; }
// 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
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
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
public List<string> RequiredFiles { get; internal set; }
public List<string>? RequiredFiles { get; internal set; }
public bool Disabled { get; internal set; }
// 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
{
get
{
if (GameLocation() == null)
AbsolutePath? gameLoc = GameLocation();
if (gameLoc == null)
throw new GameNotInstalledException(this);
if (MainExecutable == null)
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 string MainExecutable { get; internal set; }
public string? MainExecutable { get; internal set; }
public AbsolutePath? GameLocation()
{
@ -114,14 +115,14 @@ namespace Wabbajack.Common
public static class EnumExtensions
{
public static string GetDescription<T>(this T enumerationValue)
where T : struct
where T : Enum
{
var type = enumerationValue.GetType();
if(!type.IsEnum)
{
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)
{
var attrs = memberInfo[0].GetCustomAttributes(typeof(DescriptionAttribute), false);
@ -131,7 +132,7 @@ namespace Wabbajack.Common
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,
/// <param nambe="someName"></param>
/// <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);
if (result != null) return result;

View File

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

View File

@ -52,7 +52,7 @@ namespace Wabbajack.Common
new JsonSerializerSettings
{
TypeNameHandling = handling, TypeNameAssemblyFormatHandling = format, Converters = Converters
});
})!;
}
public static T FromJSONString<T>(this string data,
@ -63,7 +63,7 @@ namespace Wabbajack.Common
new JsonSerializerSettings
{
TypeNameHandling = handling, TypeNameAssemblyFormatHandling = format, Converters = Converters
});
})!;
}
public static T FromJSON<T>(this Stream data)
@ -72,7 +72,7 @@ namespace Wabbajack.Common
try
{
return JsonConvert.DeserializeObject<T>(s,
new JsonSerializerSettings {TypeNameHandling = TypeNameHandling.Auto, Converters = Converters});
new JsonSerializerSettings {TypeNameHandling = TypeNameHandling.Auto, Converters = Converters})!;
}
catch (JsonSerializationException)
{
@ -95,7 +95,7 @@ namespace Wabbajack.Common
public override Hash ReadJson(JsonReader reader, Type objectType, Hash existingValue, bool hasExistingValue,
JsonSerializer serializer)
{
return Hash.FromBase64((string)reader.Value);
return Hash.FromBase64((string)reader.Value!);
}
}
@ -110,7 +110,7 @@ namespace Wabbajack.Common
bool hasExistingValue,
JsonSerializer serializer)
{
return (RelativePath)(string)reader.Value;
return (RelativePath)(string)reader.Value!;
}
}
@ -125,7 +125,7 @@ namespace Wabbajack.Common
bool hasExistingValue,
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)
{
double d = (double)reader.Value;
double d = (double)reader.Value!;
return Percent.FactoryPutInRange(d);
}
@ -162,13 +162,13 @@ namespace Wabbajack.Common
throw new JsonException("Invalid JSON state while reading Hash Relative Path");
reader.Read();
var hash = Hash.FromBase64((string)reader.Value);
var hash = Hash.FromBase64((string)reader.Value!);
var paths = new List<RelativePath>();
reader.Read();
while (reader.TokenType != JsonToken.EndArray)
{
paths.Add((RelativePath)(string)reader.Value);
paths.Add((RelativePath)(string)reader.Value!);
reader.Read();
}
@ -195,13 +195,13 @@ namespace Wabbajack.Common
throw new JsonException("Invalid JSON state while reading Hash Relative Path");
reader.Read();
var abs = (AbsolutePath)(string)reader.Value;
var abs = (AbsolutePath)(string)reader.Value!;
var paths = new List<RelativePath>();
reader.Read();
while (reader.TokenType != JsonToken.EndArray)
{
paths.Add((RelativePath)(string)reader.Value);
paths.Add((RelativePath)(string)reader.Value!);
reader.Read();
}
@ -228,7 +228,13 @@ namespace Wabbajack.Common
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.Runtime.InteropServices;
using System.Security.Principal;
#nullable disable
namespace Wabbajack.Common.IO
{

View File

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

View File

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

View File

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

View File

@ -39,7 +39,7 @@ namespace Wabbajack.Common
var tempKey = progIDKey?.OpenSubKey("shell\\open\\command");
if (progIDKey == null || tempKey == null) return true;
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()

View File

@ -33,7 +33,7 @@ namespace Wabbajack.Common
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);
}
@ -42,7 +42,7 @@ namespace Wabbajack.Common
public override int GetHashCode()
{
return _path?.GetHashCode(StringComparison.InvariantCultureIgnoreCase) ?? 0;
return _path.GetHashCode(StringComparison.InvariantCultureIgnoreCase);
}
public override string ToString()
@ -50,11 +50,12 @@ namespace Wabbajack.Common
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)
{
_path = path.Replace("/", "\\").TrimEnd('\\');
_nullable_path = path.Replace("/", "\\").TrimEnd('\\');
if (!skipValidation)
{
ValidateAbsolutePath();
@ -63,7 +64,7 @@ namespace Wabbajack.Common
public AbsolutePath(AbsolutePath path)
{
_path = path._path;
_nullable_path = path._path;
}
private void ValidateAbsolutePath()
@ -150,7 +151,8 @@ namespace Wabbajack.Common
var location = Assembly.GetEntryAssembly()?.Location ?? null;
if (location == null)
location = Assembly.GetExecutingAssembly().Location ?? null;
if (location == null)
throw new ArgumentException("Could not find entry point.");
return ((AbsolutePath)location).Parent;
}
}
@ -264,7 +266,7 @@ namespace Wabbajack.Common
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)
@ -371,23 +373,24 @@ namespace Wabbajack.Common
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)
{
if (string.IsNullOrWhiteSpace(path))
{
_path = null;
_nullable_path = null;
return;
}
var trimmed = path.Replace("/", "\\").Trim('\\');
if (string.IsNullOrEmpty(trimmed))
{
_path = null;
_nullable_path = null;
return;
}
_path = trimmed;
_nullable_path = trimmed;
Validate();
}
@ -400,7 +403,7 @@ namespace Wabbajack.Common
public override int GetHashCode()
{
return _path?.GetHashCode(StringComparison.InvariantCultureIgnoreCase) ?? 0;
return _path.GetHashCode(StringComparison.InvariantCultureIgnoreCase);
}
public static RelativePath RandomFileName()
@ -418,7 +421,7 @@ namespace Wabbajack.Common
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()
@ -470,7 +473,7 @@ namespace Wabbajack.Common
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);
}
@ -586,7 +589,7 @@ namespace Wabbajack.Common
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);
}
@ -603,23 +606,24 @@ namespace Wabbajack.Common
#endregion
private readonly string _extension;
private readonly string? _nullable_extension;
private string _extension => _nullable_extension ?? string.Empty;
public Extension(string extension)
{
if (string.IsNullOrWhiteSpace(extension))
{
_extension = None._extension;
_nullable_extension = None._extension;
return;
}
_extension = string.Intern(extension);
_nullable_extension = string.Intern(extension);
Validate();
}
private Extension(string extension, bool validate)
{
_extension = string.Intern(extension);
_nullable_extension = string.Intern(extension);
if (validate)
{
Validate();
@ -628,7 +632,7 @@ namespace Wabbajack.Common
public Extension(Extension other)
{
_extension = other._extension;
_nullable_extension = other._extension;
}
private void Validate()
@ -652,16 +656,6 @@ namespace Wabbajack.Common
public static bool operator ==(Extension a, Extension b)
{
// 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);
}
@ -726,7 +720,7 @@ namespace Wabbajack.Common
return this == other;
}
public override bool Equals(object obj)
public override bool Equals(object? obj)
{
return obj is HashRelativePath other && Equals(other);
}
@ -772,7 +766,6 @@ namespace Wabbajack.Common
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)
{
return false;
@ -799,7 +792,7 @@ namespace Wabbajack.Common
return this == other;
}
public override bool Equals(object obj)
public override bool Equals(object? obj)
{
return obj is FullPath other && Equals(other);
}

View File

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

View File

@ -68,4 +68,4 @@ namespace Wabbajack.Common
}
}
}
}
}

View File

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

View File

@ -8,19 +8,19 @@ namespace Wabbajack.Common.StatusFeed.Errors
{
public class GenericException : IException
{
public string ExtraMessage { get; }
public string ExtraMessage { get; } = string.Empty;
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 GenericException(Exception exception, string extraMessage = null)
public GenericException(Exception exception, string? extraMessage = null)
{
ExtraMessage = extraMessage;
ExtraMessage = extraMessage ?? string.Empty;
Exception = exception;
}
}

View File

@ -12,10 +12,10 @@ namespace Wabbajack.Common.StatusFeed
private readonly string _extendedDescription;
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;
_extendedDescription = long_description;
_extendedDescription = long_description ?? string.Empty;
}
public override string ToString()

View File

@ -6,9 +6,9 @@ namespace Wabbajack.Common
{
private string _message;
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;
_inner = fs;

View File

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

View File

@ -11,33 +11,29 @@ namespace Wabbajack.Common.StoreHandlers
public class SteamGame : AStoreGame
{
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 string Universe;
public string Universe = string.Empty;
public List<SteamWorkshopItem> WorkshopItems;
public int WorkshopItemsSize;
public readonly List<SteamWorkshopItem> WorkshopItems = new List<SteamWorkshopItem>();
public int WorkshopItemsSizeOnDisk;
}
public class SteamWorkshopItem
{
public SteamGame Game;
public SteamGame? Game;
public int ItemID;
public int Size;
}
public class SteamHandler : AStoreHandler
{
public override List<AStoreGame> Games { get; set; }
public override StoreType Type { get; internal set; } = StoreType.STEAM;
private const string SteamRegKey = @"Software\Valve\Steam";
public string SteamPath { get; set; }
private List<string> SteamUniverses { get; set; }
public string SteamPath { get; set; } = string.Empty;
private List<string>? SteamUniverses { get; set; }
private string SteamConfig => Path.Combine(SteamPath, "config", "config.vdf");
@ -54,7 +50,7 @@ namespace Wabbajack.Common.StoreHandlers
return false;
}
SteamPath = steamPathKey.ToString();
SteamPath = steamPathKey.ToString() ?? string.Empty;
if (string.IsNullOrWhiteSpace(SteamPath))
{
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;
}
private void LoadUniverses()
private List<string> LoadUniverses()
{
SteamUniverses = new List<string>();
var ret = new List<string>();
File.ReadAllLines(SteamConfig).Do(l =>
{
@ -100,22 +96,24 @@ namespace Wabbajack.Common.StoreHandlers
Utils.Log($"Directory {s} does not exist, skipping");
return;
}
SteamUniverses.Add(s);
ret.Add(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
if(Directory.Exists(Path.Combine(SteamPath, "steamapps")))
SteamUniverses.Add(Path.Combine(SteamPath, "steamapps"));
if (Directory.Exists(Path.Combine(SteamPath, "steamapps")))
ret.Add(Path.Combine(SteamPath, "steamapps"));
return ret;
}
public override bool LoadAllGames()
{
if(SteamUniverses == null)
LoadUniverses();
if (SteamUniverses == null)
SteamUniverses = LoadUniverses();
if (SteamUniverses.Count == 0)
{
@ -123,9 +121,6 @@ namespace Wabbajack.Common.StoreHandlers
return false;
}
if(Games == null)
Games = new List<AStoreGame>();
SteamUniverses.Do(u =>
{
Utils.Log($"Searching for Steam Games in {u}");
@ -188,9 +183,6 @@ namespace Wabbajack.Common.StoreHandlers
private static void LoadWorkshopItems(SteamGame game)
{
if(game.WorkshopItems == null)
game.WorkshopItems = new List<SteamWorkshopItem>();
var workshop = Path.Combine(game.Universe, "workshop");
if (!Directory.Exists(workshop))
return;
@ -210,13 +202,13 @@ namespace Wabbajack.Common.StoreHandlers
var bracketStart = 0;
var bracketEnd = 0;
var currentItem = new SteamWorkshopItem();
SteamWorkshopItem? currentItem = new SteamWorkshopItem();
lines.Do(l =>
{
if (end)
return;
if(currentItem == null)
if (currentItem == null)
currentItem = new SteamWorkshopItem();
var currentLine = lines.IndexOf(l);
@ -239,7 +231,7 @@ namespace Wabbajack.Common.StoreHandlers
if (!int.TryParse(GetVdfValue(l), out var sizeOnDisk))
return;
game.WorkshopItemsSize += sizeOnDisk;
game.WorkshopItemsSizeOnDisk += sizeOnDisk;
}
if (l.ContainsCaseInsensitive("\"WorkshopItemsInstalled\""))

View File

@ -61,15 +61,15 @@ namespace Wabbajack.Common.StoreHandlers
public abstract class AStoreGame
{
public abstract Game Game { get; internal set; }
public abstract string Name { get; internal set; }
public abstract AbsolutePath Path { get; internal set; }
public abstract int ID { get; internal set; }
public virtual string Name { get; internal set; } = string.Empty;
public virtual AbsolutePath Path { get; internal set; }
public virtual int ID { get; internal set; }
public abstract StoreType Type { get; internal set; }
}
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; }

View File

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

View File

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

View File

@ -142,7 +142,7 @@ namespace Wabbajack.Common
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;
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)
{

View File

@ -5,7 +5,6 @@ using System.Linq;
using System.Text;
using System.Threading.Tasks;
using AlphaPath = Alphaleonis.Win32.Filesystem.Path;
namespace Wabbajack.Common
{

View File

@ -3,6 +3,7 @@ using System.Collections;
using System.Collections.Generic;
using System.Data.HashFunction.xxHash;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Linq;
using System.Net.Http;
@ -132,12 +133,12 @@ namespace Wabbajack.Common
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));
}
public static void ErrorThrow(Exception ex, string extraMessage = null)
public static void ErrorThrow(Exception ex, string? extraMessage = null)
{
Error(ex, extraMessage);
throw ex;
@ -231,29 +232,21 @@ namespace Wabbajack.Common
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();
hash.HashSizeInBits = 64;
hash.Seed = 0x42;
using (var fs = new MemoryStream(data))
var config = new xxHashConfig();
config.HashSizeInBits = 64;
using (var f = new StatusFileStream(fs, $"Hashing memory stream"))
{
var config = new xxHashConfig();
config.HashSizeInBits = 64;
using (var f = new StatusFileStream(fs, $"Hashing memory stream"))
{
var value = xxHashFactory.Instance.Create(config).ComputeHash(f);
return value.AsBase64String();
}
var value = xxHashFactory.Instance.Create(config).ComputeHash(f);
return value.AsBase64String();
}
}
catch (IOException ex)
{
if (nullOnIOError) return null;
throw ex;
}
}
/// <summary>
@ -673,7 +666,9 @@ namespace Wabbajack.Common
return a[a.Length - 1];
}
[return: MaybeNull]
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;
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");
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)
{
var lst = coll.ToList();
@ -1097,7 +1081,7 @@ namespace Wabbajack.Common
public class NexusErrorResponse
{
public int code;
public string message;
public string message = string.Empty;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]

View File

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

View File

@ -25,7 +25,7 @@ namespace Wabbajack.Common
public static bool WorkerThread => AsyncLocalCurrentQueue.Value != null;
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>();
public IObservable<CPUStatus> Status => _Status;
@ -194,7 +194,7 @@ namespace Wabbajack.Common
public class CPUStatus
{
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 bool IsWorking { get; internal set; }
}