干掉了浩方查马甲代码 #1
@ -52,18 +52,6 @@ namespace AnotherReplayReader
|
|||||||
return h;
|
return h;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
_ = Auth.Id.ContinueWith(t => Dispatcher.Invoke(() =>
|
|
||||||
{
|
|
||||||
if (t.Result is { } id)
|
|
||||||
{
|
|
||||||
_idBox.Text = id;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_bottom.Visibility = Visibility.Collapsed;
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnHyperlinkRequestNavigate(object sender, System.Windows.Navigation.RequestNavigateEventArgs e)
|
private void OnHyperlinkRequestNavigate(object sender, System.Windows.Navigation.RequestNavigateEventArgs e)
|
||||||
|
@ -34,10 +34,10 @@
|
|||||||
<PackageReference Include="OxyPlot.Wpf" Version="2.1.0" />
|
<PackageReference Include="OxyPlot.Wpf" Version="2.1.0" />
|
||||||
<PackageReference Include="Pfim" Version="0.10.1" />
|
<PackageReference Include="Pfim" Version="0.10.1" />
|
||||||
<PackageReference Include="System.Collections.Immutable" Version="5.0.0" />
|
<PackageReference Include="System.Collections.Immutable" Version="5.0.0" />
|
||||||
|
<PackageReference Include="System.Text.Encoding.CodePages" Version="9.0.0-preview.7.24405.7" />
|
||||||
<PackageReference Include="System.Text.Json" Version="5.0.2" />
|
<PackageReference Include="System.Text.Json" Version="5.0.2" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\AnotherReplayReader.PluginSystem\AnotherReplayReader.PluginSystem.csproj" />
|
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Compile Update="Properties\Settings.Designer.cs">
|
<Compile Update="Properties\Settings.Designer.cs">
|
||||||
@ -54,14 +54,10 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<Target Name="CustomAfterBuild" AfterTargets="Build">
|
<Target Name="CustomAfterBuild" AfterTargets="Build">
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<_FilesToMove Include="$(OutputPath)*.dll"/>
|
<_FilesToMove Include="$(OutputPath)*.dll" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<Message Text="_FilesToMove: @(_FilesToMove->'%(Filename)%(Extension)')" Importance="high"/>
|
<Message Text="_FilesToMove: @(_FilesToMove->'%(Filename)%(Extension)')" Importance="high" />
|
||||||
<Message Text="DestFiles:
|
<Message Text="DestFiles:
 @(_FilesToMove->'$(OutputPath)$(ProjectName)Data\%(Filename)%(Extension)')" Importance="high" />
|
||||||
@(_FilesToMove->'$(OutputPath)$(ProjectName)Data\%(Filename)%(Extension)')"
|
<Move SourceFiles="@(_FilesToMove)" DestinationFiles="@(_FilesToMove->'$(OutputPath)$(ProjectName)Data\%(Filename)%(Extension)')" />
|
||||||
Importance="high"/>
|
|
||||||
<Move SourceFiles="@(_FilesToMove)"
|
|
||||||
DestinationFiles=
|
|
||||||
"@(_FilesToMove->'$(OutputPath)$(ProjectName)Data\%(Filename)%(Extension)')"/>
|
|
||||||
</Target>
|
</Target>
|
||||||
</Project>
|
</Project>
|
@ -1,14 +1,10 @@
|
|||||||
|
|
||||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||||
# Visual Studio Version 16
|
# Visual Studio Version 17
|
||||||
VisualStudioVersion = 16.0.30907.101
|
VisualStudioVersion = 17.10.34928.147
|
||||||
MinimumVisualStudioVersion = 10.0.40219.1
|
MinimumVisualStudioVersion = 10.0.40219.1
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AnotherReplayReader", "AnotherReplayReader.csproj", "{A54AEAB3-D99C-4E29-8C47-3DFD5B1A0FDE}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AnotherReplayReader", "AnotherReplayReader.csproj", "{A54AEAB3-D99C-4E29-8C47-3DFD5B1A0FDE}"
|
||||||
EndProject
|
EndProject
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AnotherReplayReader.PlayerIdentity", "..\AnotherReplayReader.PlayerIdentity\AnotherReplayReader.PlayerIdentity.csproj", "{78678E57-CBA2-46E4-B764-4CB6E66EF156}"
|
|
||||||
EndProject
|
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AnotherReplayReader.PluginSystem", "..\AnotherReplayReader.PluginSystem\AnotherReplayReader.PluginSystem.csproj", "{0730D7D0-64A3-4F3F-8D8C-46E7676C659F}"
|
|
||||||
EndProject
|
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
Debug|Any CPU = Debug|Any CPU
|
Debug|Any CPU = Debug|Any CPU
|
||||||
@ -19,14 +15,6 @@ Global
|
|||||||
{A54AEAB3-D99C-4E29-8C47-3DFD5B1A0FDE}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{A54AEAB3-D99C-4E29-8C47-3DFD5B1A0FDE}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{A54AEAB3-D99C-4E29-8C47-3DFD5B1A0FDE}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
{A54AEAB3-D99C-4E29-8C47-3DFD5B1A0FDE}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
{A54AEAB3-D99C-4E29-8C47-3DFD5B1A0FDE}.Release|Any CPU.Build.0 = Release|Any CPU
|
{A54AEAB3-D99C-4E29-8C47-3DFD5B1A0FDE}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
{78678E57-CBA2-46E4-B764-4CB6E66EF156}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
|
||||||
{78678E57-CBA2-46E4-B764-4CB6E66EF156}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
|
||||||
{78678E57-CBA2-46E4-B764-4CB6E66EF156}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
|
||||||
{78678E57-CBA2-46E4-B764-4CB6E66EF156}.Release|Any CPU.Build.0 = Release|Any CPU
|
|
||||||
{0730D7D0-64A3-4F3F-8D8C-46E7676C659F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
|
||||||
{0730D7D0-64A3-4F3F-8D8C-46E7676C659F}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
|
||||||
{0730D7D0-64A3-4F3F-8D8C-46E7676C659F}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
|
||||||
{0730D7D0-64A3-4F3F-8D8C-46E7676C659F}.Release|Any CPU.Build.0 = Release|Any CPU
|
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
HideSolutionNode = FALSE
|
HideSolutionNode = FALSE
|
||||||
|
@ -108,7 +108,7 @@ namespace AnotherReplayReader.Apm
|
|||||||
playerLifes[playerIndex] = estimatedTime;
|
playerLifes[playerIndex] = estimatedTime;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (!IsUnknown(commandId) && !IsAuto(commandId))
|
else if (!IsUnknown(commandId) && !IsAuto(commandId) && playerIndex >= 0)
|
||||||
{
|
{
|
||||||
if (stricterLifes[playerIndex] < estimatedTime)
|
if (stricterLifes[playerIndex] < estimatedTime)
|
||||||
{
|
{
|
||||||
@ -138,7 +138,7 @@ namespace AnotherReplayReader.Apm
|
|||||||
}
|
}
|
||||||
foreach (var command in commands)
|
foreach (var command in commands)
|
||||||
{
|
{
|
||||||
if (options.ShouldSkip(command.CommandId))
|
if (options.ShouldSkip(command.CommandId) || command.PlayerIndex < 0)
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -164,7 +164,7 @@ namespace AnotherReplayReader.Apm
|
|||||||
{
|
{
|
||||||
foreach (var command in commands)
|
foreach (var command in commands)
|
||||||
{
|
{
|
||||||
if (options.ShouldSkip(command.CommandId))
|
if (options.ShouldSkip(command.CommandId) || command.PlayerIndex < 0)
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -209,7 +209,10 @@ namespace AnotherReplayReader.Apm
|
|||||||
{
|
{
|
||||||
commandCount = playerCommands[command.CommandId] = new int[Players.Length];
|
commandCount = playerCommands[command.CommandId] = new int[Players.Length];
|
||||||
}
|
}
|
||||||
commandCount[command.PlayerIndex] = commandCount[command.PlayerIndex] + 1;
|
if (command.PlayerIndex >= 0 && command.PlayerIndex < Players.Length)
|
||||||
|
{
|
||||||
|
commandCount[command.PlayerIndex] = commandCount[command.PlayerIndex] + 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return playerCommands;
|
return playerCommands;
|
||||||
|
@ -37,17 +37,14 @@ namespace AnotherReplayReader.Apm
|
|||||||
|
|
||||||
public static List<DataRow> GetList(ApmPlotter plotter,
|
public static List<DataRow> GetList(ApmPlotter plotter,
|
||||||
ApmPlotterFilterOptions options,
|
ApmPlotterFilterOptions options,
|
||||||
PlayerIdentity identity,
|
|
||||||
TimeSpan begin,
|
TimeSpan begin,
|
||||||
TimeSpan end,
|
TimeSpan end,
|
||||||
out int apmRowIndex)
|
out int apmRowIndex)
|
||||||
{
|
{
|
||||||
// these commands should appear in this order by default
|
// these commands should appear in this order by default
|
||||||
var orderedCommands = new List<byte>();
|
var orderedCommands = new List<byte>();
|
||||||
orderedCommands.AddRange(RA3Commands.UnknownCommands);
|
orderedCommands.AddRange(
|
||||||
orderedCommands.AddRange(RA3Commands.AutoCommands);
|
[
|
||||||
orderedCommands.AddRange(new byte[]
|
|
||||||
{
|
|
||||||
0xF5,
|
0xF5,
|
||||||
0xF8,
|
0xF8,
|
||||||
0x2A,
|
0x2A,
|
||||||
@ -83,7 +80,9 @@ namespace AnotherReplayReader.Apm
|
|||||||
0x02,
|
0x02,
|
||||||
0x0C,
|
0x0C,
|
||||||
0x10,
|
0x10,
|
||||||
});
|
]);
|
||||||
|
orderedCommands.AddRange(RA3Commands.AutoCommands);
|
||||||
|
orderedCommands.AddRange(RA3Commands.UnknownCommands);
|
||||||
|
|
||||||
var dataList = new List<DataRow>
|
var dataList = new List<DataRow>
|
||||||
{
|
{
|
||||||
@ -108,13 +107,6 @@ namespace AnotherReplayReader.Apm
|
|||||||
}
|
}
|
||||||
dataList.Add(new("存活状态(推测)", plotter.Players.Select(GetStatus)));
|
dataList.Add(new("存活状态(推测)", plotter.Players.Select(GetStatus)));
|
||||||
}
|
}
|
||||||
if (plotter.Replay.Type is ReplayType.Lan && identity.IsUsable)
|
|
||||||
{
|
|
||||||
string GetIpAndName(Player player) => player.IsComputer
|
|
||||||
? "这是 AI"
|
|
||||||
: identity.QueryRealNameAndIP(player.PlayerIp) ?? string.Empty;
|
|
||||||
dataList.Add(new("局域网 IP", plotter.Players.Select(GetIpAndName)));
|
|
||||||
}
|
|
||||||
apmRowIndex = dataList.Count;
|
apmRowIndex = dataList.Count;
|
||||||
// kill-death ratio
|
// kill-death ratio
|
||||||
if (plotter.Replay.Footer?.TryGetKillDeathRatios() is { } kdRatios)
|
if (plotter.Replay.Footer?.TryGetKillDeathRatios() is { } kdRatios)
|
||||||
|
@ -68,13 +68,6 @@
|
|||||||
<DockPanel Grid.Row="1"
|
<DockPanel Grid.Row="1"
|
||||||
Margin="16,8,16,16">
|
Margin="16,8,16,16">
|
||||||
<DockPanel DockPanel.Dock="Top">
|
<DockPanel DockPanel.Dock="Top">
|
||||||
<Button x:Name="_setPlayerButton"
|
|
||||||
DockPanel.Dock="Right"
|
|
||||||
Content="设置玩家信息..."
|
|
||||||
VerticalAlignment="Center"
|
|
||||||
Padding="8,4"
|
|
||||||
Visibility="Visible"
|
|
||||||
Click="OnSetPlayerButtonClick" />
|
|
||||||
<Label x:Name="_label"
|
<Label x:Name="_label"
|
||||||
Grid.Column="1"
|
Grid.Column="1"
|
||||||
Margin="0"
|
Margin="0"
|
||||||
|
@ -23,7 +23,6 @@ namespace AnotherReplayReader
|
|||||||
{
|
{
|
||||||
public static readonly TimeSpan DefaultResolution = TimeSpan.FromSeconds(15);
|
public static readonly TimeSpan DefaultResolution = TimeSpan.FromSeconds(15);
|
||||||
private readonly Regex _resolutionRegex = new(@"[^0-9]+");
|
private readonly Regex _resolutionRegex = new(@"[^0-9]+");
|
||||||
private readonly PlayerIdentity _identity;
|
|
||||||
private readonly Replay _replay;
|
private readonly Replay _replay;
|
||||||
private readonly Task<ApmPlotter> _plotter;
|
private readonly Task<ApmPlotter> _plotter;
|
||||||
private readonly ApmWindowPlotController _plotController;
|
private readonly ApmWindowPlotController _plotController;
|
||||||
@ -34,9 +33,8 @@ namespace AnotherReplayReader
|
|||||||
public bool SkipAutos { get; private set; } = true;
|
public bool SkipAutos { get; private set; } = true;
|
||||||
public bool SkipClicks { get; private set; } = false;
|
public bool SkipClicks { get; private set; } = false;
|
||||||
|
|
||||||
public ApmWindow(Replay replay, PlayerIdentity identity)
|
public ApmWindow(Replay replay)
|
||||||
{
|
{
|
||||||
_identity = identity;
|
|
||||||
_replay = replay;
|
_replay = replay;
|
||||||
_plotter = Task.Run(() => new ApmPlotter(replay));
|
_plotter = Task.Run(() => new ApmPlotter(replay));
|
||||||
_plotController = new(this);
|
_plotController = new(this);
|
||||||
@ -60,17 +58,6 @@ namespace AnotherReplayReader
|
|||||||
|
|
||||||
private async Task InitializeApmWindowData()
|
private async Task InitializeApmWindowData()
|
||||||
{
|
{
|
||||||
if (_identity.IsUsable)
|
|
||||||
{
|
|
||||||
_setPlayerButton.IsEnabled = true;
|
|
||||||
_setPlayerButton.Visibility = Visibility.Visible;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_setPlayerButton.IsEnabled = false;
|
|
||||||
_setPlayerButton.Visibility = Visibility.Hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
await FilterDataAsync(TimeSpan.MinValue, TimeSpan.MaxValue, true);
|
await FilterDataAsync(TimeSpan.MinValue, TimeSpan.MaxValue, true);
|
||||||
_label.Content = "选择表格之后按 Ctrl+C 可以复制内容";
|
_label.Content = "选择表格之后按 Ctrl+C 可以复制内容";
|
||||||
}
|
}
|
||||||
@ -94,7 +81,7 @@ namespace AnotherReplayReader
|
|||||||
var (dataList, plotData, replayLength, isPartial) = await Task.Run(async () =>
|
var (dataList, plotData, replayLength, isPartial) = await Task.Run(async () =>
|
||||||
{
|
{
|
||||||
var plotter = await _plotter.ConfigureAwait(false);
|
var plotter = await _plotter.ConfigureAwait(false);
|
||||||
var list = DataRow.GetList(plotter, options, _identity, begin, end, out var apmIndex);
|
var list = DataRow.GetList(plotter, options, begin, end, out var apmIndex);
|
||||||
// avg apm
|
// avg apm
|
||||||
var avg = plotter.CalculateAverageApm(options);
|
var avg = plotter.CalculateAverageApm(options);
|
||||||
// data for plotting and partial apm
|
// data for plotting and partial apm
|
||||||
@ -190,12 +177,6 @@ namespace AnotherReplayReader
|
|||||||
PlotModel.Model = model;
|
PlotModel.Model = model;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnSetPlayerButtonClick(object sender, RoutedEventArgs e)
|
|
||||||
{
|
|
||||||
var window1 = new Window1(_identity);
|
|
||||||
window1.ShowDialog();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnTableMouseDoubleClick(object sender, MouseButtonEventArgs e)
|
private void OnTableMouseDoubleClick(object sender, MouseButtonEventArgs e)
|
||||||
{
|
{
|
||||||
_table.SelectAll();
|
_table.SelectAll();
|
||||||
|
34
Auth.cs
34
Auth.cs
@ -1,34 +0,0 @@
|
|||||||
using AnotherReplayReader.PluginSystem;
|
|
||||||
using AnotherReplayReader.Utils;
|
|
||||||
using System;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace AnotherReplayReader
|
|
||||||
{
|
|
||||||
internal static class Auth
|
|
||||||
{
|
|
||||||
private static readonly TaskCompletionSource<Task<string?>> _source = new();
|
|
||||||
public static Task<string?> Id { get; } = _source.Task.Unwrap();
|
|
||||||
|
|
||||||
public static void LoadPlugin(IPlugin plugin)
|
|
||||||
{
|
|
||||||
var authService = plugin.CreateAuthService(RegistryUtils.RetrieveInHklm64, Cache.CacheDirectory);
|
|
||||||
authService.Id.ContinueWith(_source.TrySetResult);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static async Task<byte[]?> IdAsKey()
|
|
||||||
{
|
|
||||||
var id = await Id.ConfigureAwait(false);
|
|
||||||
if (string.IsNullOrEmpty(id))
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
var bytes = Encoding.UTF8.GetBytes(id);
|
|
||||||
var destination = Enumerable.Repeat<byte>(0xEA, 24).ToArray();
|
|
||||||
Array.Copy(bytes, destination, Math.Min(bytes.Length, destination.Length));
|
|
||||||
return destination;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
179
ChineseEncoding.cs
Normal file
179
ChineseEncoding.cs
Normal file
@ -0,0 +1,179 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Numerics;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace Ra3.BattleNet.Database.Utils;
|
||||||
|
|
||||||
|
public static class ChineseEncoding
|
||||||
|
{
|
||||||
|
private const int EncodedChineseTotalLength = 120;
|
||||||
|
private const int EncodedChineseChecksumLength = 3;
|
||||||
|
private const int EncodedChineseContentLength = EncodedChineseTotalLength - EncodedChineseChecksumLength;
|
||||||
|
private const int ChineseCodeSize = 13;
|
||||||
|
private const int ChineseCodeMask = 0b1111111111111;
|
||||||
|
private const int AsciiSectionSize = 0x80;
|
||||||
|
private const int Gb2312EucOffset = 0xA0;
|
||||||
|
private const int Gb2312RowWidth = 94;
|
||||||
|
private const int Gb2312LastRow = 87;
|
||||||
|
private const int Gb2312FirstUnassignedSectionBegin = 10;
|
||||||
|
private const int Gb2312FirstUnassignedSectionSize = 6;
|
||||||
|
private const int Gb2312SecondUnassignedSectionBegin = 88;
|
||||||
|
private const int Base64CodeSize = 6;
|
||||||
|
private const int Base64CodeMask = 0b111111;
|
||||||
|
private const string Base64Table = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
|
||||||
|
|
||||||
|
public static string DecodeChineseFromBase64(string original)
|
||||||
|
{
|
||||||
|
BigInteger bits = 0;
|
||||||
|
int index = 0;
|
||||||
|
foreach (var c in original)
|
||||||
|
{
|
||||||
|
BigInteger value = Base64Table.IndexOf(c);
|
||||||
|
if (value == -1)
|
||||||
|
{
|
||||||
|
throw new Exception("Invalid base64 string");
|
||||||
|
}
|
||||||
|
bits = bits | (value << index);
|
||||||
|
index += Base64CodeSize;
|
||||||
|
}
|
||||||
|
BigInteger checksum = 0;
|
||||||
|
var result = new List<byte>();
|
||||||
|
for (var bitIndex = 0; bitIndex < EncodedChineseContentLength; bitIndex += ChineseCodeSize)
|
||||||
|
{
|
||||||
|
int value = (int)((bits >> bitIndex) & ChineseCodeMask);
|
||||||
|
checksum += value;
|
||||||
|
if (value == 0)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (value < AsciiSectionSize)
|
||||||
|
{
|
||||||
|
if (value < 0x20 || value == 0x7F)
|
||||||
|
{
|
||||||
|
throw new Exception("Invalid ASCII");
|
||||||
|
}
|
||||||
|
result.Add((byte)value);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
value -= AsciiSectionSize;
|
||||||
|
var index1 = value / Gb2312RowWidth + 1;
|
||||||
|
var index2 = value % Gb2312RowWidth + 1;
|
||||||
|
if (index1 >= Gb2312FirstUnassignedSectionBegin)
|
||||||
|
{
|
||||||
|
index1 += Gb2312FirstUnassignedSectionSize;
|
||||||
|
}
|
||||||
|
if (index1 >= Gb2312SecondUnassignedSectionBegin)
|
||||||
|
{
|
||||||
|
throw new Exception("Invalid first byte");
|
||||||
|
}
|
||||||
|
if (index2 > Gb2312RowWidth)
|
||||||
|
{
|
||||||
|
throw new Exception("Invalid second byte");
|
||||||
|
}
|
||||||
|
index1 += Gb2312EucOffset;
|
||||||
|
index2 += Gb2312EucOffset;
|
||||||
|
result.Add((byte)index1);
|
||||||
|
result.Add((byte)index2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ((bits >> EncodedChineseContentLength) != checksum % 8)
|
||||||
|
{
|
||||||
|
throw new Exception("Invalid checksum");
|
||||||
|
}
|
||||||
|
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
|
||||||
|
return Encoding.GetEncoding(936).GetString(result.ToArray());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string EncodeChineseToBase64(string original)
|
||||||
|
{
|
||||||
|
BigInteger bits = 0;
|
||||||
|
int index = 0;
|
||||||
|
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
|
||||||
|
var bytes = Encoding.GetEncoding(936).GetBytes(original);
|
||||||
|
BigInteger checksum = 0;
|
||||||
|
for (var i = 0; i < bytes.Length;)
|
||||||
|
{
|
||||||
|
BigInteger c = bytes[i];
|
||||||
|
if (c < AsciiSectionSize)
|
||||||
|
{
|
||||||
|
if (c < 0x20 || c == 0x7F)
|
||||||
|
{
|
||||||
|
throw new Exception("Invalid ASCII");
|
||||||
|
}
|
||||||
|
bits = bits | (c << index);
|
||||||
|
index += ChineseCodeSize;
|
||||||
|
checksum += c;
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (c <= Gb2312EucOffset || c > (Gb2312EucOffset + Gb2312LastRow))
|
||||||
|
{
|
||||||
|
throw new Exception("Invalid first byte");
|
||||||
|
}
|
||||||
|
BigInteger c2 = bytes[i + 1];
|
||||||
|
if (c2 <= Gb2312EucOffset || c2 > (Gb2312EucOffset + Gb2312RowWidth))
|
||||||
|
{
|
||||||
|
throw new Exception("Invalid second byte");
|
||||||
|
}
|
||||||
|
var index1 = c - Gb2312EucOffset;
|
||||||
|
if (index1 >= Gb2312FirstUnassignedSectionBegin)
|
||||||
|
{
|
||||||
|
var offset = index1 - Gb2312FirstUnassignedSectionBegin;
|
||||||
|
if (offset < Gb2312FirstUnassignedSectionSize)
|
||||||
|
{
|
||||||
|
throw new Exception("AA-AF user defined zone not supported");
|
||||||
|
}
|
||||||
|
index1 -= Gb2312FirstUnassignedSectionSize;
|
||||||
|
}
|
||||||
|
var index2 = c2 - Gb2312EucOffset;
|
||||||
|
if (index2 > Gb2312RowWidth)
|
||||||
|
{
|
||||||
|
throw new Exception("Invalid second byte");
|
||||||
|
}
|
||||||
|
var value = (index1 - 1) * Gb2312RowWidth + (index2 - 1);
|
||||||
|
value = AsciiSectionSize + value;
|
||||||
|
bits = bits | (value << index);
|
||||||
|
index += ChineseCodeSize;
|
||||||
|
checksum += value;
|
||||||
|
i += 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bits = bits | ((checksum % 8) << EncodedChineseContentLength);
|
||||||
|
var result = "";
|
||||||
|
var bitsIndex = 0;
|
||||||
|
while (bitsIndex < EncodedChineseTotalLength)
|
||||||
|
{
|
||||||
|
int v = (int)((bits >> bitsIndex) & Base64CodeMask);
|
||||||
|
result += Base64Table[v];
|
||||||
|
bitsIndex += Base64CodeSize;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string GetPrettyName(string name)
|
||||||
|
{
|
||||||
|
if (name.Length != 20)
|
||||||
|
{
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
foreach (var c in name)
|
||||||
|
{
|
||||||
|
if (!Base64Table.Contains(c))
|
||||||
|
{
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return DecodeChineseFromBase64(name);
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -103,7 +103,6 @@ namespace AnotherReplayReader
|
|||||||
private readonly TaskQueue _taskQueue;
|
private readonly TaskQueue _taskQueue;
|
||||||
private readonly MainWindowProperties _properties = new();
|
private readonly MainWindowProperties _properties = new();
|
||||||
private readonly Cache _cache = new();
|
private readonly Cache _cache = new();
|
||||||
private readonly PlayerIdentity _playerIdentity;
|
|
||||||
private readonly BigMinimapCache _minimapCache;
|
private readonly BigMinimapCache _minimapCache;
|
||||||
private readonly MinimapReader _minimapReader;
|
private readonly MinimapReader _minimapReader;
|
||||||
private readonly CancelManager _cancelLoadReplays = new();
|
private readonly CancelManager _cancelLoadReplays = new();
|
||||||
@ -116,10 +115,9 @@ namespace AnotherReplayReader
|
|||||||
public MainWindow()
|
public MainWindow()
|
||||||
{
|
{
|
||||||
_taskQueue = new(Dispatcher);
|
_taskQueue = new(Dispatcher);
|
||||||
_playerIdentity = new PlayerIdentity(_cache);
|
|
||||||
_minimapCache = new BigMinimapCache(_properties.RA3Directory);
|
_minimapCache = new BigMinimapCache(_properties.RA3Directory);
|
||||||
_minimapReader = new MinimapReader(_minimapCache, _properties.CustomMapsDirectory, _properties.ModsDirectory);
|
_minimapReader = new MinimapReader(_minimapCache, _properties.CustomMapsDirectory, _properties.ModsDirectory);
|
||||||
_replayList = new(_playerIdentity);
|
_replayList = new();
|
||||||
|
|
||||||
DataContext = _properties;
|
DataContext = _properties;
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
@ -137,20 +135,9 @@ namespace AnotherReplayReader
|
|||||||
ReplayAutoSaver.SpawnAutoSaveReplaysTask(_properties.RA3ReplayFolderPath);
|
ReplayAutoSaver.SpawnAutoSaveReplaysTask(_properties.RA3ReplayFolderPath);
|
||||||
var token = _cancelLoadReplays.ResetAndGetToken(CancellationToken.None);
|
var token = _cancelLoadReplays.ResetAndGetToken(CancellationToken.None);
|
||||||
_ = _taskQueue.Enqueue(() => LoadReplays(null, token), token);
|
_ = _taskQueue.Enqueue(() => LoadReplays(null, token), token);
|
||||||
|
|
||||||
var wantUsePlugin = true;
|
|
||||||
try
|
|
||||||
{
|
|
||||||
wantUsePlugin = await Plugin.LoadPlayerIdentityPlugin(_playerIdentity);
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
Debug.Instance.DebugMessage += $"Failed to load plugin: {e}";
|
|
||||||
MessageBox.Show(this, $"插件加载失败:{e.Message}");
|
|
||||||
}
|
|
||||||
|
|
||||||
const string permissionKey = "questionAsked";
|
const string permissionKey = "questionAsked";
|
||||||
if (!wantUsePlugin && _cache.GetOrDefault(permissionKey, false) is not true)
|
if (_cache.GetOrDefault(permissionKey, false) is not true)
|
||||||
{
|
{
|
||||||
_cache.Set(permissionKey, true);
|
_cache.Set(permissionKey, true);
|
||||||
var sb = new StringWriter();
|
var sb = new StringWriter();
|
||||||
@ -194,7 +181,7 @@ namespace AnotherReplayReader
|
|||||||
}
|
}
|
||||||
|
|
||||||
cancelToken.ThrowIfCancellationRequested();
|
cancelToken.ThrowIfCancellationRequested();
|
||||||
_replayList = new(_playerIdentity);
|
_replayList = new();
|
||||||
|
|
||||||
var path = _properties.ReplayFolderPath;
|
var path = _properties.ReplayFolderPath;
|
||||||
if (!Directory.Exists(path))
|
if (!Directory.Exists(path))
|
||||||
@ -228,7 +215,7 @@ namespace AnotherReplayReader
|
|||||||
clock.Restart();
|
clock.Restart();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return new ReplayPinyinList(list.ToImmutableArray(), _playerIdentity);
|
return new ReplayPinyinList(list.ToImmutableArray());
|
||||||
}, cancelToken);
|
}, cancelToken);
|
||||||
cancelToken.ThrowIfCancellationRequested();
|
cancelToken.ThrowIfCancellationRequested();
|
||||||
_replayList = result;
|
_replayList = result;
|
||||||
@ -323,7 +310,7 @@ namespace AnotherReplayReader
|
|||||||
minimapTask.Forget();
|
minimapTask.Forget();
|
||||||
minimapTask = _minimapReader.TryReadTargaAsync(replay);
|
minimapTask = _minimapReader.TryReadTargaAsync(replay);
|
||||||
}
|
}
|
||||||
var newDetails = replay.GetDetails(_playerIdentity);
|
var newDetails = replay.GetDetails();
|
||||||
if (_properties.ReplayDetails != newDetails)
|
if (_properties.ReplayDetails != newDetails)
|
||||||
{
|
{
|
||||||
if (_replayList.Replays.FindIndex(r => r.PathEquals(replay)) is int index)
|
if (_replayList.Replays.FindIndex(r => r.PathEquals(replay)) is int index)
|
||||||
@ -365,7 +352,6 @@ namespace AnotherReplayReader
|
|||||||
_replayDetailsBox.Text += $"{jsonMagic}\r\n";
|
_replayDetailsBox.Text += $"{jsonMagic}\r\n";
|
||||||
break;
|
break;
|
||||||
case assemblyMagic:
|
case assemblyMagic:
|
||||||
Plugin.Sign();
|
|
||||||
break;
|
break;
|
||||||
case jsonMagic:
|
case jsonMagic:
|
||||||
UpdateChecker.Sign();
|
UpdateChecker.Sign();
|
||||||
@ -381,7 +367,7 @@ namespace AnotherReplayReader
|
|||||||
}
|
}
|
||||||
|
|
||||||
var token = _cancelDisplayReplays.ResetAndGetToken(_cancelFilterReplays.Token);
|
var token = _cancelDisplayReplays.ResetAndGetToken(_cancelFilterReplays.Token);
|
||||||
_properties.ReplayDetails = replay.GetDetails(_playerIdentity);
|
_properties.ReplayDetails = replay.GetDetails();
|
||||||
await _taskQueue.Enqueue(() => DisplayReplayDetail(replay, _properties.ReplayDetails, token), token);
|
await _taskQueue.Enqueue(() => DisplayReplayDetail(replay, _properties.ReplayDetails, token), token);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -420,7 +406,7 @@ namespace AnotherReplayReader
|
|||||||
|
|
||||||
private void OnDetailsButtonClick(object sender, RoutedEventArgs e)
|
private void OnDetailsButtonClick(object sender, RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
var detailsWindow = new ApmWindow(_properties.CurrentReplay!, _playerIdentity)
|
var detailsWindow = new ApmWindow(_properties.CurrentReplay!)
|
||||||
{
|
{
|
||||||
Owner = this
|
Owner = this
|
||||||
};
|
};
|
||||||
|
@ -1,184 +0,0 @@
|
|||||||
using AnotherReplayReader.PluginSystem;
|
|
||||||
using AnotherReplayReader.Utils;
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.IO;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Security.Cryptography;
|
|
||||||
using System.Text.Json;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using static System.Text.Json.JsonSerializer;
|
|
||||||
|
|
||||||
namespace AnotherReplayReader
|
|
||||||
{
|
|
||||||
internal class PlayerIdentity
|
|
||||||
{
|
|
||||||
private class NetworkSerializer : IGenericCallable
|
|
||||||
{
|
|
||||||
public TResult Invoke<TArg, TResult>(TArg arg) => throw new NotImplementedException();
|
|
||||||
|
|
||||||
public Task<TResult?> InvokeAsync<TArg, TResult>(TArg arg)
|
|
||||||
{
|
|
||||||
var url = arg as string ?? throw new NotImplementedException();
|
|
||||||
return Network.HttpGetJson<TResult>(url);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private const string StoredKey = "pt";
|
|
||||||
private const string IvKey = "jw";
|
|
||||||
private static readonly JsonSerializerOptions _jsonOptions = new()
|
|
||||||
{
|
|
||||||
PropertyNameCaseInsensitive = true
|
|
||||||
};
|
|
||||||
private readonly object _lock = new();
|
|
||||||
private readonly Cache _cache;
|
|
||||||
private readonly TaskCompletionSource<IPlayerIdentityService> _serviceSource = new();
|
|
||||||
private IReadOnlyDictionary<uint, string>? _list;
|
|
||||||
|
|
||||||
public bool IsUsable => Lock.Run(_lock, () => _list is not null);
|
|
||||||
|
|
||||||
public PlayerIdentity(Cache cache)
|
|
||||||
{
|
|
||||||
_cache = cache;
|
|
||||||
Task.Run(FetchLocal);
|
|
||||||
Fetch();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void LoadPlugin(IPlugin plugin)
|
|
||||||
{
|
|
||||||
var service = plugin.CreatePlayerIdentityService(Network.UrlEncode, new NetworkSerializer());
|
|
||||||
_serviceSource.TrySetResult(service);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Task Fetch()
|
|
||||||
{
|
|
||||||
return Task.Run(async () =>
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (await Auth.Id is null)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var service = await _serviceSource.Task.ConfigureAwait(false);
|
|
||||||
var list = await service.Fetch<IpAndPlayer>().ConfigureAwait(false);
|
|
||||||
var converted = list?.ToDictionary(x => x.Ip, x => x.Id);
|
|
||||||
lock (_lock)
|
|
||||||
{
|
|
||||||
_list = converted;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (list is null || await Auth.IdAsKey() is not { } encryptKey)
|
|
||||||
{
|
|
||||||
_cache.Set<string?>(StoredKey, null);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
using var memory = new MemoryStream();
|
|
||||||
using var aes = Aes.Create();
|
|
||||||
using (var encryptor = aes.CreateEncryptor(encryptKey, aes.IV))
|
|
||||||
using (var decryptorStream = new CryptoStream(memory, encryptor, CryptoStreamMode.Write))
|
|
||||||
{
|
|
||||||
await SerializeAsync(decryptorStream, list, _jsonOptions).ConfigureAwait(false);
|
|
||||||
}
|
|
||||||
memory.Flush();
|
|
||||||
_cache.SetValues((StoredKey, Convert.ToBase64String(memory.ToArray())), (IvKey, Convert.ToBase64String(aes.IV)));
|
|
||||||
}
|
|
||||||
catch { }
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<bool> UpdateIpTable(uint ip, string idText)
|
|
||||||
{
|
|
||||||
var service = await _serviceSource.Task.ConfigureAwait(false);
|
|
||||||
return await service.UpdateIpTable(ip, idText).ConfigureAwait(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<IpAndPlayer> AsSortedList()
|
|
||||||
{
|
|
||||||
using var locker = new Lock(_lock);
|
|
||||||
|
|
||||||
return _list?
|
|
||||||
.Select((kv) => new IpAndPlayer { Ip = kv.Key, Id = kv.Value })
|
|
||||||
.OrderBy(x => x.Ip)
|
|
||||||
.ToList() ?? new(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
public string? GetRealName(uint ip)
|
|
||||||
{
|
|
||||||
using var locker = new Lock(_lock);
|
|
||||||
if (_list is null)
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ip is 0 || !_list.TryGetValue(ip, out var name))
|
|
||||||
{
|
|
||||||
return string.Empty;
|
|
||||||
}
|
|
||||||
|
|
||||||
return name;
|
|
||||||
}
|
|
||||||
|
|
||||||
public string FormatRealName(uint ip)
|
|
||||||
{
|
|
||||||
var name = GetRealName(ip);
|
|
||||||
if (name is null)
|
|
||||||
{
|
|
||||||
return string.Empty;
|
|
||||||
}
|
|
||||||
if (string.IsNullOrEmpty(name))
|
|
||||||
{
|
|
||||||
name = IpAndPlayer.SimpleIPToString(ip);
|
|
||||||
}
|
|
||||||
return $"({name})";
|
|
||||||
}
|
|
||||||
|
|
||||||
public string? QueryRealNameAndIP(uint ip)
|
|
||||||
{
|
|
||||||
var name = GetRealName(ip);
|
|
||||||
if (name is null)
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
var ipText = IpAndPlayer.SimpleIPToString(ip);
|
|
||||||
if (string.IsNullOrEmpty(name))
|
|
||||||
{
|
|
||||||
return ipText;
|
|
||||||
}
|
|
||||||
return $"{name},{ipText}";
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task FetchLocal()
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
await _cache.Initialization;
|
|
||||||
var stored = _cache.GetOrDefault(StoredKey, string.Empty);
|
|
||||||
var iv = Convert.FromBase64String(_cache.GetOrDefault(IvKey, string.Empty));
|
|
||||||
if (string.IsNullOrWhiteSpace(stored) || await Auth.IdAsKey() is not { } key)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
using var aes = Aes.Create();
|
|
||||||
using var decryptor = aes.CreateDecryptor(key, iv);
|
|
||||||
using var memory = new MemoryStream(Convert.FromBase64String(stored));
|
|
||||||
using var decryptorStream = new CryptoStream(memory, decryptor, CryptoStreamMode.Read);
|
|
||||||
var cachedTable = await DeserializeAsync<List<IpAndPlayer>>(decryptorStream, _jsonOptions).ConfigureAwait(false);
|
|
||||||
if (cachedTable is null)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
var converted = cachedTable.ToDictionary(x => x.Ip, x => x.Id);
|
|
||||||
converted[0] = "【没有网络连接,正在使用上次保存的数据】";
|
|
||||||
lock (_lock)
|
|
||||||
{
|
|
||||||
_list ??= converted;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch { }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
78
Plugin.cs
78
Plugin.cs
@ -1,78 +0,0 @@
|
|||||||
using AnotherReplayReader.PluginSystem;
|
|
||||||
using AnotherReplayReader.Utils;
|
|
||||||
using Microsoft.Win32;
|
|
||||||
using System;
|
|
||||||
using System.IO;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Reflection;
|
|
||||||
using System.Security.Cryptography;
|
|
||||||
using System.Text.Json;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace AnotherReplayReader
|
|
||||||
{
|
|
||||||
|
|
||||||
internal static class Plugin
|
|
||||||
{
|
|
||||||
public static Task<bool> LoadPlayerIdentityPlugin(PlayerIdentity playerIdentity)
|
|
||||||
{
|
|
||||||
return Task.Run(async () =>
|
|
||||||
{
|
|
||||||
var name = $"{nameof(AnotherReplayReader)}.{nameof(PlayerIdentity)}";
|
|
||||||
var fileName = Path.Combine(App.LibsFolder, $"{name}.dll");
|
|
||||||
if (File.Exists(fileName))
|
|
||||||
{
|
|
||||||
var assembly = await Load(fileName);
|
|
||||||
var pluginType = assembly.GetType($"{name}.Plugin", true);
|
|
||||||
var plugin = (IPlugin)Activator.CreateInstance(pluginType);
|
|
||||||
Auth.LoadPlugin(plugin);
|
|
||||||
playerIdentity.LoadPlugin(plugin);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public static async Task<Assembly> Load(string path)
|
|
||||||
{
|
|
||||||
using var file = File.OpenRead(path);
|
|
||||||
using var metaFile = File.OpenRead($"{path}.meta");
|
|
||||||
var metaTask = JsonSerializer.DeserializeAsync<VerifierPayload>(metaFile, Network.CommonJsonOptions);
|
|
||||||
using var memory = new MemoryStream();
|
|
||||||
await file.CopyToAsync(memory);
|
|
||||||
var array = memory.ToArray();
|
|
||||||
using var sha256 = SHA256.Create();
|
|
||||||
var hash = sha256.ComputeHash(array);
|
|
||||||
var meta = await metaTask ?? throw new InvalidDataException("Failed to load meta file");
|
|
||||||
if (!Enumerable.SequenceEqual(hash, meta.ByteData.Value))
|
|
||||||
{
|
|
||||||
throw new InvalidDataException($"Hash does not match, might be corrupt {Convert.ToBase64String(hash)} vs {meta.Data}");
|
|
||||||
}
|
|
||||||
if (!Verifier.Verify(meta))
|
|
||||||
{
|
|
||||||
throw new InvalidDataException("Invalid metadata, might be corrupt");
|
|
||||||
}
|
|
||||||
return Assembly.Load(array);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void Sign()
|
|
||||||
{
|
|
||||||
var openFileDialog = new OpenFileDialog
|
|
||||||
{
|
|
||||||
Filter = "dll (*.dll)|*.dll|所有文件 (*.*)|*.*",
|
|
||||||
InitialDirectory = AppContext.BaseDirectory,
|
|
||||||
};
|
|
||||||
|
|
||||||
var result = openFileDialog.ShowDialog();
|
|
||||||
if (result is true)
|
|
||||||
{
|
|
||||||
using var file = openFileDialog.OpenFile();
|
|
||||||
using var memory = new MemoryStream();
|
|
||||||
file.CopyTo(memory);
|
|
||||||
using var sha256 = SHA256.Create();
|
|
||||||
var hash = sha256.ComputeHash(memory.ToArray());
|
|
||||||
Verifier.Sign(hash);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -37,6 +37,11 @@ namespace AnotherReplayReader.ReplayFile
|
|||||||
FactionId = int.Parse(playerEntry[5]);
|
FactionId = int.Parse(playerEntry[5]);
|
||||||
Team = int.Parse(playerEntry[7]);
|
Team = int.Parse(playerEntry[7]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (PlayerName.Length == 20)
|
||||||
|
{
|
||||||
|
PlayerName = Ra3.BattleNet.Database.Utils.ChineseEncoding.DecodeChineseFromBase64(PlayerName);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -45,7 +45,7 @@ namespace AnotherReplayReader.ReplayFile
|
|||||||
var lastByte = current.ReadBytes(size - 2).Last();
|
var lastByte = current.ReadBytes(size - 2).Last();
|
||||||
if (lastByte != 0xFF)
|
if (lastByte != 0xFF)
|
||||||
{
|
{
|
||||||
throw new InvalidDataException($"Failed to parse command {command:X}, last byte is {lastByte:X}");
|
// throw new InvalidDataException($"Failed to parse command {command:X}, last byte is {lastByte:X}");
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -239,7 +239,7 @@ namespace AnotherReplayReader.ReplayFile
|
|||||||
public bool PathEquals(Replay replay) => PathEquals(replay.Path);
|
public bool PathEquals(Replay replay) => PathEquals(replay.Path);
|
||||||
public bool PathEquals(string path) => Path.Equals(path, StringComparison.OrdinalIgnoreCase);
|
public bool PathEquals(string path) => Path.Equals(path, StringComparison.OrdinalIgnoreCase);
|
||||||
|
|
||||||
public string GetDetails(PlayerIdentity playerIdentity)
|
public string GetDetails()
|
||||||
{
|
{
|
||||||
static string GetSizeString(double size)
|
static string GetSizeString(double size)
|
||||||
{
|
{
|
||||||
@ -273,8 +273,7 @@ namespace AnotherReplayReader.ReplayFile
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
var factionName = ModData.GetFaction(Mod, player.FactionId).Name;
|
var factionName = ModData.GetFaction(Mod, player.FactionId).Name;
|
||||||
var realName = Type == ReplayType.Lan ? playerIdentity.FormatRealName(player.PlayerIp) : string.Empty;
|
writer.WriteLine($"{player.PlayerName},{factionName}");
|
||||||
writer.WriteLine($"{player.PlayerName + realName},{factionName}");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return writer.ToString();
|
return writer.ToString();
|
||||||
|
@ -6,27 +6,23 @@ namespace AnotherReplayReader.Utils
|
|||||||
{
|
{
|
||||||
internal class ReplayPinyinList
|
internal class ReplayPinyinList
|
||||||
{
|
{
|
||||||
private readonly PlayerIdentity _playerIdentity;
|
|
||||||
public ImmutableArray<Replay> Replays { get; } = ImmutableArray<Replay>.Empty;
|
public ImmutableArray<Replay> Replays { get; } = ImmutableArray<Replay>.Empty;
|
||||||
public ImmutableArray<ReplayPinyinData> Pinyins { get; } = ImmutableArray<ReplayPinyinData>.Empty;
|
public ImmutableArray<ReplayPinyinData> Pinyins { get; } = ImmutableArray<ReplayPinyinData>.Empty;
|
||||||
|
|
||||||
public ReplayPinyinList(PlayerIdentity playerIdentity) :
|
public ReplayPinyinList() :
|
||||||
this(ImmutableArray<Replay>.Empty, playerIdentity)
|
this(ImmutableArray<Replay>.Empty)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
public ReplayPinyinList(ImmutableArray<Replay> replay, PlayerIdentity playerIdentity) :
|
public ReplayPinyinList(ImmutableArray<Replay> replay) :
|
||||||
this(replay,
|
this(replay,
|
||||||
replay.Select(replay => new ReplayPinyinData(replay, playerIdentity)).ToImmutableArray(),
|
replay.Select(replay => new ReplayPinyinData(replay)).ToImmutableArray())
|
||||||
playerIdentity)
|
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
private ReplayPinyinList(ImmutableArray<Replay> replay,
|
private ReplayPinyinList(ImmutableArray<Replay> replay,
|
||||||
ImmutableArray<ReplayPinyinData> pinyins,
|
ImmutableArray<ReplayPinyinData> pinyins)
|
||||||
PlayerIdentity playerIdentity)
|
|
||||||
{
|
{
|
||||||
_playerIdentity = playerIdentity;
|
|
||||||
Replays = replay;
|
Replays = replay;
|
||||||
Pinyins = pinyins;
|
Pinyins = pinyins;
|
||||||
}
|
}
|
||||||
@ -34,8 +30,7 @@ namespace AnotherReplayReader.Utils
|
|||||||
public ReplayPinyinList SetItem(int index, Replay replay)
|
public ReplayPinyinList SetItem(int index, Replay replay)
|
||||||
{
|
{
|
||||||
return new(Replays.SetItem(index, replay),
|
return new(Replays.SetItem(index, replay),
|
||||||
Pinyins.SetItem(index, new(replay, _playerIdentity)),
|
Pinyins.SetItem(index, new(replay)));
|
||||||
_playerIdentity);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -45,10 +40,10 @@ namespace AnotherReplayReader.Utils
|
|||||||
public string? PinyinDetails { get; }
|
public string? PinyinDetails { get; }
|
||||||
public string? PinyinMod { get; }
|
public string? PinyinMod { get; }
|
||||||
|
|
||||||
public ReplayPinyinData(Replay replay, PlayerIdentity playerIdentity)
|
public ReplayPinyinData(Replay replay)
|
||||||
{
|
{
|
||||||
Replay = replay;
|
Replay = replay;
|
||||||
PinyinDetails = replay.GetDetails(playerIdentity).ToPinyin();
|
PinyinDetails = replay.GetDetails().ToPinyin();
|
||||||
PinyinMod = replay.Mod.ModName.ToPinyin();
|
PinyinMod = replay.Mod.ModName.ToPinyin();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -13,12 +13,10 @@ namespace AnotherReplayReader
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
internal partial class Window1 : Window
|
internal partial class Window1 : Window
|
||||||
{
|
{
|
||||||
private readonly PlayerIdentity _identity;
|
|
||||||
|
|
||||||
public Window1(PlayerIdentity identity)
|
public Window1()
|
||||||
{
|
{
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
_identity = identity;
|
|
||||||
Refresh(true);
|
Refresh(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -34,7 +32,6 @@ namespace AnotherReplayReader
|
|||||||
await Display();
|
await Display();
|
||||||
_dataGrid.ItemsSource = loading.Concat(_dataGrid.ItemsSource.Cast<IpAndPlayer>());
|
_dataGrid.ItemsSource = loading.Concat(_dataGrid.ItemsSource.Cast<IpAndPlayer>());
|
||||||
}
|
}
|
||||||
await _identity.Fetch();
|
|
||||||
await Display();
|
await Display();
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
@ -49,26 +46,6 @@ namespace AnotherReplayReader
|
|||||||
|
|
||||||
private async Task Display(string filter = "", string nameFilter = "")
|
private async Task Display(string filter = "", string nameFilter = "")
|
||||||
{
|
{
|
||||||
var result = await Task.Run(() =>
|
|
||||||
{
|
|
||||||
var pinyin = nameFilter.ToPinyin();
|
|
||||||
var query = _identity
|
|
||||||
.AsSortedList()
|
|
||||||
.Where(x =>
|
|
||||||
{
|
|
||||||
if (!x.IpString.Contains(filter))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (x.PinyinId?.ContainsIgnoreCase(pinyin) is true)
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return x.Id.ContainsIgnoreCase(nameFilter);
|
|
||||||
});
|
|
||||||
return new ObservableCollection<IpAndPlayer>(query);
|
|
||||||
});
|
|
||||||
_dataGrid.ItemsSource = result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async void OnClick(object sender, RoutedEventArgs e)
|
private async void OnClick(object sender, RoutedEventArgs e)
|
||||||
@ -114,7 +91,7 @@ namespace AnotherReplayReader
|
|||||||
{
|
{
|
||||||
var bytes = ip.GetAddressBytes();
|
var bytes = ip.GetAddressBytes();
|
||||||
var ipNum = (uint)bytes[0] * 256 * 256 * 256 + bytes[1] * 256 * 256 + bytes[2] * 256 + bytes[3];
|
var ipNum = (uint)bytes[0] * 256 * 256 * 256 + bytes[1] * 256 * 256 + bytes[2] * 256 + bytes[3];
|
||||||
return await _identity.UpdateIpTable(checked((uint)ipNum), idText).ConfigureAwait(false);
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async void OnIpFieldChanged(object sender, System.Windows.Controls.TextChangedEventArgs e)
|
private async void OnIpFieldChanged(object sender, System.Windows.Controls.TextChangedEventArgs e)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user