改了一亿个东西,修了一亿个 BUG

This commit is contained in:
2021-10-19 17:42:18 +02:00
parent 5b907309e0
commit 888ddce4ef
40 changed files with 1870 additions and 1715 deletions

48
Utils/CancelManager.cs Normal file
View File

@@ -0,0 +1,48 @@
using System;
using System.Threading;
namespace AnotherReplayReader.Utils
{
internal class CancelManager : IDisposable
{
private CancellationToken _linkedToken;
private CancellationTokenSource? _source;
public CancellationToken Token => Materialize().Token;
public void Reset(CancellationToken linked)
{
_linkedToken = linked;
if (_source is { } source)
{
_source = null;
try
{
source.Cancel();
}
catch (AggregateException e)
{
Debug.Instance.DebugMessage += $"Cancellation failed: {e}";
}
source.Dispose();
}
}
public CancellationToken ResetAndGetToken(CancellationToken linked)
{
Reset(linked);
return Token;
}
public void Dispose() => Reset(default);
private CancellationTokenSource Materialize()
{
if (_source is null)
{
_source = CancellationTokenSource.CreateLinkedTokenSource(_linkedToken);
}
return _source;
}
}
}

View File

@@ -0,0 +1,23 @@
using System;
using System.Threading.Tasks;
namespace AnotherReplayReader.Utils
{
internal static class CancellableTaskExtensions
{
public static async Task IgnoreCancel(this Task task)
{
try
{
await task.ConfigureAwait(false);
}
catch (OperationCanceledException) { }
}
public static void Forget(this Task task)
{
const TaskContinuationOptions flags = TaskContinuationOptions.NotOnRanToCompletion | TaskContinuationOptions.ExecuteSynchronously;
task.ContinueWith(t => t.Exception?.Handle(_ => true), flags);
}
}
}

View File

@@ -0,0 +1,20 @@
using System;
using System.Collections.Immutable;
namespace AnotherReplayReader.Utils
{
static class ImmutableArrayExtensions
{
public static int? FindIndex<T>(this in ImmutableArray<T> a, Predicate<T> p)
{
for (var i = 0; i < a.Length; ++i)
{
if (p(a[i]))
{
return i;
}
}
return null;
}
}
}

32
Utils/Lock.cs Normal file
View File

@@ -0,0 +1,32 @@
using System;
using System.Threading;
namespace AnotherReplayReader.Utils
{
internal class Lock : IDisposable
{
public readonly object LockObject;
private bool _disposed = false;
public Lock(object lockObject)
{
LockObject = lockObject;
Monitor.Enter(LockObject);
}
public void Dispose()
{
if (!_disposed)
{
Monitor.Exit(LockObject);
_disposed = true;
}
}
public static T Run<T>(object @lock, Func<T> action)
{
using var locker = new Lock(@lock);
return action();
}
}
}

27
Utils/PinyinExtensions.cs Normal file
View File

@@ -0,0 +1,27 @@
using NPinyin;
using System;
namespace AnotherReplayReader.Utils
{
static class PinyinExtensions
{
public static bool ContainsIgnoreCase(this string self, string? s)
{
return s != null && self.IndexOf(s, StringComparison.CurrentCultureIgnoreCase) != -1;
}
public static string? ToPinyin(this string self)
{
string pinyin;
try
{
pinyin = Pinyin.GetPinyin(self);
}
catch
{
return null;
}
return pinyin.Replace(" ", "");
}
}
}

28
Utils/RegistryUtils.cs Normal file
View File

@@ -0,0 +1,28 @@
using Microsoft.Win32;
using System;
namespace AnotherReplayReader.Utils
{
internal static class RegistryUtils
{
public static string? Retrieve(RegistryHive hive, string path, string value)
{
try
{
using var view32 = RegistryKey.OpenBaseKey(hive, RegistryView.Registry32);
using var ra3Key = view32.OpenSubKey(path, false);
return ra3Key?.GetValue(value) as string;
}
catch (Exception e)
{
Debug.Instance.DebugMessage += $"Failed to retrieve registy {hive}:{path}:{value}: {e}";
return null;
}
}
public static string? RetrieveInRa3(RegistryHive hive, string value)
{
return Retrieve(hive, @"Software\Electronic Arts\Electronic Arts\Red Alert 3", value);
}
}
}

61
Utils/ReplayPinyinList.cs Normal file
View File

@@ -0,0 +1,61 @@
using AnotherReplayReader.ReplayFile;
using System.Collections.Immutable;
using System.Linq;
namespace AnotherReplayReader.Utils
{
internal class ReplayPinyinList
{
private readonly PlayerIdentity _playerIdentity;
public ImmutableArray<Replay> Replays { get; } = ImmutableArray<Replay>.Empty;
public ImmutableArray<ReplayPinyinData> Pinyins { get; } = ImmutableArray<ReplayPinyinData>.Empty;
public ReplayPinyinList(PlayerIdentity playerIdentity) :
this(ImmutableArray<Replay>.Empty, playerIdentity)
{
}
public ReplayPinyinList(ImmutableArray<Replay> replay, PlayerIdentity playerIdentity) :
this(replay,
replay.Select(replay => new ReplayPinyinData(replay, playerIdentity)).ToImmutableArray(),
playerIdentity)
{
}
private ReplayPinyinList(ImmutableArray<Replay> replay,
ImmutableArray<ReplayPinyinData> pinyins,
PlayerIdentity playerIdentity)
{
_playerIdentity = playerIdentity;
Replays = replay;
Pinyins = pinyins;
}
public ReplayPinyinList SetItem(int index, Replay replay)
{
return new(Replays.SetItem(index, replay),
Pinyins.SetItem(index, new(replay, _playerIdentity)),
_playerIdentity);
}
}
class ReplayPinyinData
{
public Replay Replay { get; }
public string? PinyinDetails { get; }
public string? PinyinMod { get; }
public ReplayPinyinData(Replay replay, PlayerIdentity playerIdentity)
{
Replay = replay;
PinyinDetails = replay.GetDetails(playerIdentity).ToPinyin();
PinyinMod = replay.Mod.ModName.ToPinyin();
}
public bool MatchPinyin(string? pinyin)
{
return PinyinDetails?.ContainsIgnoreCase(pinyin) is true
|| PinyinMod?.ContainsIgnoreCase(pinyin) is true;
}
}
}

28
Utils/ShortTimeSpan.cs Normal file
View File

@@ -0,0 +1,28 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace AnotherReplayReader.Utils
{
public readonly struct ShortTimeSpan : IEquatable<ShortTimeSpan>, IComparable<ShortTimeSpan>, IComparable
{
public readonly TimeSpan Value;
public ShortTimeSpan(TimeSpan value) => Value = value;
public static implicit operator TimeSpan(ShortTimeSpan span) => span.Value;
public static implicit operator ShortTimeSpan(TimeSpan value) => new(value);
public override string ToString() => $"{(int)Value.TotalMinutes:00}:{Value.Seconds:00}";
public int CompareTo(ShortTimeSpan other) => Value.CompareTo(other.Value);
public int CompareTo(object obj) => obj is ShortTimeSpan span ? CompareTo(span) : 1;
public override bool Equals(object? obj) => obj is ShortTimeSpan span && Equals(span);
public bool Equals(ShortTimeSpan other) => Value.Equals(other.Value);
public override int GetHashCode() => Value.GetHashCode();
public static bool operator ==(ShortTimeSpan left, ShortTimeSpan right) => left.Equals(right);
public static bool operator !=(ShortTimeSpan left, ShortTimeSpan right) => !(left == right);
public static bool operator <(ShortTimeSpan left, ShortTimeSpan right) => left.CompareTo(right) < 0;
public static bool operator >(ShortTimeSpan left, ShortTimeSpan right) => left.CompareTo(right) > 0;
}
}

30
Utils/TaskQueue.cs Normal file
View File

@@ -0,0 +1,30 @@
using System;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Threading;
namespace AnotherReplayReader.Utils
{
internal class TaskQueue
{
private readonly object _lock = new();
private readonly Dispatcher _dispatcher;
private Task _current = Task.CompletedTask;
public TaskQueue(Dispatcher dispatcher)
{
_dispatcher = dispatcher;
}
public Task Enqueue(Func<Task> getTask, CancellationToken cancelToken)
{
using var locker = new Lock(_lock);
_current = _current.ContinueWith(async t =>
{
await _dispatcher.InvokeAsync(getTask, DispatcherPriority.Background, cancelToken);
}, cancelToken);
return _current.IgnoreCancel();
}
}
}