diff --git a/Debug.xaml b/Debug.xaml
index 39c5266..c45e0ee 100644
--- a/Debug.xaml
+++ b/Debug.xaml
@@ -6,8 +6,20 @@
xmlns:local="clr-namespace:AnotherReplayReader"
mc:Ignorable="d"
Title="Debug" Height="450" Width="800">
-
-
-
-
+
+
+
+
+
+
+
diff --git a/Debug.xaml.cs b/Debug.xaml.cs
index 91126ae..99792b4 100644
--- a/Debug.xaml.cs
+++ b/Debug.xaml.cs
@@ -1,37 +1,31 @@
-using System;
-using System.Collections.Generic;
-using System.ComponentModel;
+using Microsoft.Win32;
+using System;
using System.IO;
-using System.Linq;
-using System.Text;
-using System.Threading;
-using System.Threading.Tasks;
using System.Windows;
-using System.Windows.Controls;
-using System.Windows.Data;
-using System.Windows.Documents;
-using System.Windows.Input;
-using System.Windows.Media;
-using System.Windows.Media.Imaging;
-using System.Windows.Shapes;
-using Microsoft.Win32;
namespace AnotherReplayReader
{
- public sealed class DebugMessageWrapper : INotifyPropertyChanged
+ public sealed class DebugMessageWrapper
{
- public event PropertyChangedEventHandler PropertyChanged;
- public string DebugMessage
+ public readonly struct Proxy
{
- get => _debugMessage;
- set
+ public readonly string Payload;
+ public Proxy(string text)
{
- _debugMessage = value;
- PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("DebugMessage"));
+ Payload = text;
+ }
+ public static Proxy operator +(Proxy p, string text)
+ {
+ return string.IsNullOrEmpty(p.Payload) ? new Proxy(text) : new Proxy(p.Payload + text);
}
}
- private string _debugMessage;
+ public event Action NewText;
+ public Proxy DebugMessage
+ {
+ get => new Proxy();
+ set => NewText?.Invoke(value.Payload);
+ }
}
///
/// Debug.xaml 的交互逻辑
@@ -42,8 +36,8 @@ namespace AnotherReplayReader
public Debug()
{
- DataContext = Instance;
InitializeComponent();
+ Instance.NewText += t => Dispatcher.Invoke(() => _textBox.AppendText(t));
}
private void OnExport_Click(object sender, RoutedEventArgs e)
@@ -64,5 +58,10 @@ namespace AnotherReplayReader
}
}
}
+
+ private void OnClear_Click(object sender, RoutedEventArgs e)
+ {
+ _textBox.Clear();
+ }
}
}
diff --git a/MainWindow.xaml b/MainWindow.xaml
index a1107de..66367fc 100644
--- a/MainWindow.xaml
+++ b/MainWindow.xaml
@@ -35,7 +35,35 @@
-
+
+
+
+
+
+
+
+
+
diff --git a/MainWindow.xaml.cs b/MainWindow.xaml.cs
index 7dec800..2e8e066 100644
--- a/MainWindow.xaml.cs
+++ b/MainWindow.xaml.cs
@@ -9,6 +9,7 @@ using System.Runtime.CompilerServices;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
+using System.Windows.Threading;
namespace AnotherReplayReader
{
@@ -18,12 +19,19 @@ namespace AnotherReplayReader
{
string userDataLeafName = null;
string replayFolderName = null;
- using (var view32 = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Registry32))
- using (var ra3Key = view32.OpenSubKey(@"Software\Electronic Arts\Electronic Arts\Red Alert 3", false))
+ try
{
- RA3Directory = ra3Key?.GetValue("Install Dir") as string;
- userDataLeafName = ra3Key?.GetValue("UserDataLeafName") as string;
- replayFolderName = ra3Key?.GetValue("ReplayFolderName") as string;
+ using (var view32 = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Registry32))
+ using (var ra3Key = view32.OpenSubKey(@"Software\Electronic Arts\Electronic Arts\Red Alert 3", false))
+ {
+ RA3Directory = ra3Key?.GetValue("Install Dir") as string;
+ userDataLeafName = ra3Key?.GetValue("UserDataLeafName") as string;
+ replayFolderName = ra3Key?.GetValue("ReplayFolderName") as string;
+ }
+ }
+ catch (Exception e)
+ {
+ Debug.Instance.DebugMessage += $"获取注册表项时出现错误:{e}\r\n";
}
if (string.IsNullOrWhiteSpace(userDataLeafName))
@@ -64,12 +72,6 @@ namespace AnotherReplayReader
set { _replayFolderPath = value; NotifyPropertyChanged(_replayFolderPath); }
}
- public string ReplayFilterString
- {
- get { return _replayFilterString; }
- set { _replayFilterString = value; NotifyPropertyChanged(_replayFilterString); }
- }
-
public string ReplayDetails
{
get { return _replayDetails; }
@@ -84,22 +86,21 @@ namespace AnotherReplayReader
public Replay CurrentReplay
{
- get { return _currentReplay; }
+ get => _currentReplay;
set
{
_currentReplay = value;
NotifyPropertyChanged(_currentReplay);
ReplayDetails = "";
- PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("ReplayPlayable"));
- PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("ReplaySelected"));
- PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("ReplayDamaged"));
+ PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(ReplayPlayable)));
+ PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(ReplaySelected)));
+ PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(ReplayDamaged)));
}
}
- private volatile string _replayFolderPath;
- private volatile string _replayDetails;
- private volatile string _replayFilterString;
- private volatile Replay _currentReplay;
+ private string _replayFolderPath;
+ private string _replayDetails;
+ private Replay _currentReplay;
}
///
@@ -107,12 +108,12 @@ namespace AnotherReplayReader
///
public partial class MainWindow : Window
{
- private MainWindowProperties _properties = new MainWindowProperties();
- private volatile List _replayList;
- private Cache _cache = new Cache();
- private PlayerIdentity _playerIdentity;
- private BigMinimapCache _minimapCache;
- private MinimapReader _minimapReader;
+ private readonly MainWindowProperties _properties = new MainWindowProperties();
+ private readonly List _replayList = new List();
+ private readonly Cache _cache = new Cache();
+ private readonly PlayerIdentity _playerIdentity;
+ private readonly BigMinimapCache _minimapCache;
+ private readonly MinimapReader _minimapReader;
private CancellationTokenSource _loadReplaysToken;
public MainWindow()
@@ -124,7 +125,7 @@ namespace AnotherReplayReader
var handling = new bool[1] { false };
Application.Current.Dispatcher.UnhandledException += (sender, eventArgs) =>
{
- if (handling == null || handling[0] == true)
+ if (handling == null || handling[0])
{
return;
}
@@ -132,17 +133,17 @@ namespace AnotherReplayReader
Dispatcher.Invoke(() => MessageBox.Show($"错误:\r\n{eventArgs.Exception}"));
};
- Closing += ((sender, eventArgs) => _cache.Save());
+ Closing += (sender, eventArgs) => _cache.Save();
_playerIdentity = new PlayerIdentity(_cache);
_minimapCache = new BigMinimapCache(_cache, _properties.RA3Directory);
_minimapReader = new MinimapReader(_minimapCache, _properties.RA3Directory, _properties.CustomMapsDirectory, _properties.ModsDirectory);
LoadReplays();
- _ = AutoSaveReplays();
+ Task.Run(() => AutoSaveReplays(Dispatcher, _properties.RA3ReplayFolderPath));
}
- private async Task AutoSaveReplays()
+ private static async Task AutoSaveReplays(Dispatcher dispatcher, string replayFolderPath)
{
const string ourPrefix = "自动保存";
var errorMessageCount = 0;
@@ -155,7 +156,7 @@ namespace AnotherReplayReader
{
try
{
- var changed = (from fileName in Directory.GetFiles(_properties.RA3ReplayFolderPath, "*.RA3Replay")
+ var changed = (from fileName in Directory.GetFiles(replayFolderPath, "*.RA3Replay")
let info = new FileInfo(fileName)
where !info.Name.StartsWith(ourPrefix)
where !previousFiles.ContainsKey(info.FullName) || previousFiles[info.FullName] != info.LastWriteTimeUtc
@@ -238,7 +239,7 @@ namespace AnotherReplayReader
}
var dateString = $"{date.Year}{date.Month:D2}{date.Day:D2}_{date.Hour:D2}{date.Minute:D2}{date.Second:D2}";
- var destinationPath = Path.Combine(_properties.RA3ReplayFolderPath, $"{ourPrefix}-{playerString}{dateString}.RA3Replay");
+ var destinationPath = Path.Combine(replayFolderPath, $"{ourPrefix}-{playerString}{dateString}.RA3Replay");
try
{
File.Copy(replay.Path, destinationPath, true);
@@ -253,20 +254,20 @@ namespace AnotherReplayReader
{
var errorString = $"自动保存录像时出现错误:\r\n{e}\r\n";
Debug.Instance.DebugMessage += errorString;
- _ = Dispatcher.InvokeAsync(() =>
+ if (Interlocked.Increment(ref errorMessageCount) == 1)
{
- try
+ _ = dispatcher.InvokeAsync(() =>
{
- if (Interlocked.Increment(ref errorMessageCount) == 1)
+ try
{
MessageBox.Show(errorString);
}
- }
- finally
- {
- Interlocked.Decrement(ref errorMessageCount);
- }
- });
+ finally
+ {
+ Interlocked.Decrement(ref errorMessageCount);
+ }
+ });
+ }
}
await Task.Delay(10 * 1000);
@@ -275,66 +276,59 @@ namespace AnotherReplayReader
private async void LoadReplays(string nextSelected = null)
{
- const string loadingString = "正在加载录像列表,请稍候";
-
- Dispatcher.Invoke(() =>
- {
- if (_image != null)
- {
- _image.Source = null;
- }
-
- if (_dataGrid != null)
- {
- _dataGrid.Items.Clear();
- }
- });
-
- _loadReplaysToken?.Cancel();
- _loadReplaysToken = new CancellationTokenSource();
-
- var cancelToken = _loadReplaysToken.Token;
- var path = _properties.ReplayFolderPath;
- var task = Task.Run(async () =>
- {
- var messages = "";
- var replayList = new List();
-
- if (!Directory.Exists(path))
- {
- messages = "这个文件夹并不存在。";
- }
- else
- {
- var replays = Directory.EnumerateFiles(path, "*.RA3Replay");
- foreach (var replayPath in replays)
- {
- try
- {
- var replay = await Task.Run(() => new Replay(replayPath));
- replayList.Add(replay);
- _properties.ReplayDetails = loadingString + $"\n已加载 {replayList.Count} 个录像";
- }
- catch (Exception exception)
- {
- Debug.Instance.DebugMessage += $"Uncaught exception when loading replay list: \r\n{exception}\r\n";
- }
-
- cancelToken.ThrowIfCancellationRequested();
- }
- }
-
- return new { Replays = replayList, Messages = messages };
- });
+ const string loadingString = "正在加载录像列表,请稍候… 已加载 {0} 个录像";
try
{
- var result = await task;
- _replayList = result.Replays;
- cancelToken.ThrowIfCancellationRequested();
- DisplayReplays(result.Messages, nextSelected);
+ _loadReplaysToken?.Cancel();
}
- catch (OperationCanceledException) { }
+ catch (AggregateException e)
+ {
+ Debug.Instance.DebugMessage += $"Cancellation failed: {e}";
+ }
+ _loadReplaysToken?.Dispose();
+ _loadReplaysToken = new CancellationTokenSource();
+ var cancelToken = _loadReplaysToken.Token;
+ var path = _properties.ReplayFolderPath;
+
+ if (_image != null)
+ {
+ _image.Source = null;
+ }
+ _dataGrid?.Items.Clear();
+ _replayList.Clear();
+
+ if (!Directory.Exists(path))
+ {
+ DisplayReplays("这个文件夹并不存在。", nextSelected);
+ return;
+ }
+
+ var newList = await Task.Run(() =>
+ {
+ var list = new List();
+ foreach (var replayPath in Directory.EnumerateFiles(path, "*.RA3Replay"))
+ {
+ if (cancelToken.IsCancellationRequested)
+ {
+ break;
+ }
+ try
+ {
+ list.Add(new Replay(replayPath));
+ }
+ catch (Exception exception)
+ {
+ Debug.Instance.DebugMessage += $"Uncaught exception when loading replay list: \r\n{exception}\r\n";
+ continue;
+ }
+ _ = Dispatcher.Invoke(() => _properties.ReplayDetails = string.Format(loadingString, _replayList.Count));
+ }
+ return list;
+ });
+
+ _replayList.AddRange(newList);
+ DisplayReplays(string.Empty, nextSelected);
}
private void DisplayReplays(string message = null, string nextSelected = null)
@@ -351,13 +345,7 @@ namespace AnotherReplayReader
{
for (var i = 0; i < _dataGrid.Items.Count; ++i)
{
- var replay = _dataGrid.Items[i] as Replay;
- if (replay == null)
- {
- continue;
- }
-
- if (replay.Path.Equals(nextSelected, StringComparison.OrdinalIgnoreCase))
+ if (_dataGrid.Items[i] is Replay replay && replay.Path.Equals(nextSelected, StringComparison.OrdinalIgnoreCase))
{
_dataGrid.SelectedIndex = i;
OnReplaySelectionChanged(null, null);
@@ -533,5 +521,10 @@ namespace AnotherReplayReader
LoadReplays();
}
+
+ private void ReplayFilterBox_TextChanged(object sender, System.Windows.Controls.TextChangedEventArgs e)
+ {
+
+ }
}
}
diff --git a/autosave.txt b/autosave.txt
deleted file mode 100644
index dbd35e1..0000000
--- a/autosave.txt
+++ /dev/null
@@ -1,95 +0,0 @@
-private async Task AutoSaveReplays()
- {
- const string ourPrefix = "自动保存";
-
- // filename and last write time
- Dictionary previousFiles = new Dictionary(StringComparer.OrdinalIgnoreCase);
- // filename and file size
- Dictionary lastReplays = new Dictionary(StringComparer.OrdinalIgnoreCase);
-
- while(true)
- {
- try
- {
- var changed = from fileName in Directory.GetFiles(_properties.ReplayFolderPath, "*.RA3Replay")
- let info = new FileInfo(fileName)
- where !info.Name.StartsWith(ourPrefix)
- where !previousFiles.ContainsKey(info.FullName) || previousFiles[info.FullName] != info.LastWriteTimeUtc
- select info;
-
- foreach (var info in changed)
- {
- previousFiles[info.FullName] = info.LastWriteTimeUtc;
- }
-
- var replays = changed.Select(info =>
- {
- try
- {
- return new Replay(info.FullName);
- }
- catch (Exception)
- {
- return null;
- }
- }).Where(replay => replay != null);
-
- var newLastReplays = from replay in replays
- let threshold = Math.Abs((DateTime.UtcNow - replay.Date).TotalSeconds)
- where threshold < 20
- select replay;
-
- var toBeChecked = newLastReplays.ToDictionary(replay => replay.FileName, StringComparer.OrdinalIgnoreCase);
- foreach (var savedLastReplay in lastReplays)
- {
- if (!toBeChecked.ContainsKey(savedLastReplay.Key))
- {
- try
- {
- toBeChecked.Add(savedLastReplay.Key, new Replay(savedLastReplay.Key));
- }
- catch(Exception)
- {
-
- }
- }
- }
-
- foreach (var kv in toBeChecked)
- {
- var replay = kv.Value;
- if (lastReplays.TryGetValue(kv.Key, out var fileSize))
- {
- if (fileSize == replay.Size)
- {
- // skip if size is not changed
- continue;
- }
- }
- lastReplays[kv.Key] = replay.Size;
-
- var date = replay.Date;
- var numberOfPlayers = replay.NumberOfPlayingPlayers;
- var playerString = $"{numberOfPlayers}名玩家";
- if (replay.NumberOfPlayingPlayers <= 2)
- {
- var playingPlayers = from player in replay.Players
- let faction = ModData.GetFaction(replay.Mod, player.FactionID)
- where faction.Kind != FactionKind.Observer
- select $"{player}({faction.Name})";
- playerString = playingPlayers.Aggregate((x, y) => x + y);
- }
-
- var dateString = $"{date.Year}-{date.Month}-{date.Day}_{date.Hour}:{date.Minute}";
-
- File.Copy(replay.FileName, $"{_properties.ReplayFolderPath}/{ourPrefix}-{playerString}{dateString}.RA3Replay");
- }
- }
- catch(Exception e)
- {
- _ = Dispatcher.InvokeAsync(() => MessageBox.Show($"自动保存录像时出现错误:\r\n{e}"));
- }
-
- await Task.Delay(10 * 1000);
- }
- }
\ No newline at end of file