干掉了浩方查马甲代码 #1
@ -52,18 +52,6 @@ namespace AnotherReplayReader
 | 
			
		||||
                    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)
 | 
			
		||||
 | 
			
		||||
@ -34,10 +34,10 @@
 | 
			
		||||
    <PackageReference Include="OxyPlot.Wpf" Version="2.1.0" />
 | 
			
		||||
    <PackageReference Include="Pfim" Version="0.10.1" />
 | 
			
		||||
    <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" />
 | 
			
		||||
  </ItemGroup>
 | 
			
		||||
  <ItemGroup>
 | 
			
		||||
    <ProjectReference Include="..\AnotherReplayReader.PluginSystem\AnotherReplayReader.PluginSystem.csproj" />
 | 
			
		||||
  </ItemGroup>
 | 
			
		||||
  <ItemGroup>
 | 
			
		||||
    <Compile Update="Properties\Settings.Designer.cs">
 | 
			
		||||
@ -54,14 +54,10 @@
 | 
			
		||||
  </ItemGroup>
 | 
			
		||||
  <Target Name="CustomAfterBuild" AfterTargets="Build">
 | 
			
		||||
    <ItemGroup>
 | 
			
		||||
      <_FilesToMove Include="$(OutputPath)*.dll"/>
 | 
			
		||||
      <_FilesToMove Include="$(OutputPath)*.dll" />
 | 
			
		||||
    </ItemGroup>
 | 
			
		||||
    <Message Text="_FilesToMove: @(_FilesToMove->'%(Filename)%(Extension)')" Importance="high"/>
 | 
			
		||||
    <Message Text="DestFiles:
 | 
			
		||||
        @(_FilesToMove->'$(OutputPath)$(ProjectName)Data\%(Filename)%(Extension)')"
 | 
			
		||||
             Importance="high"/>
 | 
			
		||||
    <Move SourceFiles="@(_FilesToMove)"
 | 
			
		||||
          DestinationFiles=
 | 
			
		||||
          "@(_FilesToMove->'$(OutputPath)$(ProjectName)Data\%(Filename)%(Extension)')"/>
 | 
			
		||||
    <Message Text="_FilesToMove: @(_FilesToMove->'%(Filename)%(Extension)')" Importance="high" />
 | 
			
		||||
    <Message Text="DestFiles:
        @(_FilesToMove->'$(OutputPath)$(ProjectName)Data\%(Filename)%(Extension)')" Importance="high" />
 | 
			
		||||
    <Move SourceFiles="@(_FilesToMove)" DestinationFiles="@(_FilesToMove->'$(OutputPath)$(ProjectName)Data\%(Filename)%(Extension)')" />
 | 
			
		||||
  </Target>
 | 
			
		||||
</Project>
 | 
			
		||||
@ -1,14 +1,10 @@
 | 
			
		||||
 | 
			
		||||
Microsoft Visual Studio Solution File, Format Version 12.00
 | 
			
		||||
# Visual Studio Version 16
 | 
			
		||||
VisualStudioVersion = 16.0.30907.101
 | 
			
		||||
# Visual Studio Version 17
 | 
			
		||||
VisualStudioVersion = 17.10.34928.147
 | 
			
		||||
MinimumVisualStudioVersion = 10.0.40219.1
 | 
			
		||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AnotherReplayReader", "AnotherReplayReader.csproj", "{A54AEAB3-D99C-4E29-8C47-3DFD5B1A0FDE}"
 | 
			
		||||
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
 | 
			
		||||
	GlobalSection(SolutionConfigurationPlatforms) = preSolution
 | 
			
		||||
		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}.Release|Any CPU.ActiveCfg = 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
 | 
			
		||||
	GlobalSection(SolutionProperties) = preSolution
 | 
			
		||||
		HideSolutionNode = FALSE
 | 
			
		||||
 | 
			
		||||
@ -108,7 +108,7 @@ namespace AnotherReplayReader.Apm
 | 
			
		||||
                            playerLifes[playerIndex] = estimatedTime;
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                    else if (!IsUnknown(commandId) && !IsAuto(commandId))
 | 
			
		||||
                    else if (!IsUnknown(commandId) && !IsAuto(commandId) && playerIndex >= 0)
 | 
			
		||||
                    {
 | 
			
		||||
                        if (stricterLifes[playerIndex] < estimatedTime)
 | 
			
		||||
                        {
 | 
			
		||||
@ -138,7 +138,7 @@ namespace AnotherReplayReader.Apm
 | 
			
		||||
                }
 | 
			
		||||
                foreach (var command in commands)
 | 
			
		||||
                {
 | 
			
		||||
                    if (options.ShouldSkip(command.CommandId))
 | 
			
		||||
                    if (options.ShouldSkip(command.CommandId) || command.PlayerIndex < 0)
 | 
			
		||||
                    {
 | 
			
		||||
                        continue;
 | 
			
		||||
                    }
 | 
			
		||||
@ -164,7 +164,7 @@ namespace AnotherReplayReader.Apm
 | 
			
		||||
            {
 | 
			
		||||
                foreach (var command in commands)
 | 
			
		||||
                {
 | 
			
		||||
                    if (options.ShouldSkip(command.CommandId))
 | 
			
		||||
                    if (options.ShouldSkip(command.CommandId) || command.PlayerIndex < 0)
 | 
			
		||||
                    {
 | 
			
		||||
                        continue;
 | 
			
		||||
                    }
 | 
			
		||||
@ -209,9 +209,12 @@ namespace AnotherReplayReader.Apm
 | 
			
		||||
                    {
 | 
			
		||||
                        commandCount = playerCommands[command.CommandId] = new int[Players.Length];
 | 
			
		||||
                    }
 | 
			
		||||
                    if (command.PlayerIndex >= 0 && command.PlayerIndex < Players.Length)
 | 
			
		||||
                    {
 | 
			
		||||
                        commandCount[command.PlayerIndex] = commandCount[command.PlayerIndex] + 1;
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            return playerCommands;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -37,17 +37,14 @@ namespace AnotherReplayReader.Apm
 | 
			
		||||
 | 
			
		||||
        public static List<DataRow> GetList(ApmPlotter plotter,
 | 
			
		||||
                                            ApmPlotterFilterOptions options,
 | 
			
		||||
                                            PlayerIdentity identity,
 | 
			
		||||
                                            TimeSpan begin,
 | 
			
		||||
                                            TimeSpan end,
 | 
			
		||||
                                            out int apmRowIndex)
 | 
			
		||||
        {
 | 
			
		||||
            // these commands should appear in this order by default
 | 
			
		||||
            var orderedCommands = new List<byte>();
 | 
			
		||||
            orderedCommands.AddRange(RA3Commands.UnknownCommands);
 | 
			
		||||
            orderedCommands.AddRange(RA3Commands.AutoCommands);
 | 
			
		||||
            orderedCommands.AddRange(new byte[]
 | 
			
		||||
            {
 | 
			
		||||
            orderedCommands.AddRange(
 | 
			
		||||
            [
 | 
			
		||||
                0xF5,
 | 
			
		||||
                0xF8,
 | 
			
		||||
                0x2A,
 | 
			
		||||
@ -83,7 +80,9 @@ namespace AnotherReplayReader.Apm
 | 
			
		||||
                0x02,
 | 
			
		||||
                0x0C,
 | 
			
		||||
                0x10,
 | 
			
		||||
            });
 | 
			
		||||
            ]);
 | 
			
		||||
            orderedCommands.AddRange(RA3Commands.AutoCommands);
 | 
			
		||||
            orderedCommands.AddRange(RA3Commands.UnknownCommands);
 | 
			
		||||
 | 
			
		||||
            var dataList = new List<DataRow>
 | 
			
		||||
            {
 | 
			
		||||
@ -108,13 +107,6 @@ namespace AnotherReplayReader.Apm
 | 
			
		||||
                }
 | 
			
		||||
                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;
 | 
			
		||||
            // kill-death ratio
 | 
			
		||||
            if (plotter.Replay.Footer?.TryGetKillDeathRatios() is { } kdRatios)
 | 
			
		||||
 | 
			
		||||
@ -68,13 +68,6 @@
 | 
			
		||||
        <DockPanel Grid.Row="1"
 | 
			
		||||
                   Margin="16,8,16,16">
 | 
			
		||||
            <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"
 | 
			
		||||
                       Grid.Column="1"
 | 
			
		||||
                       Margin="0"
 | 
			
		||||
 | 
			
		||||
@ -23,7 +23,6 @@ namespace AnotherReplayReader
 | 
			
		||||
    {
 | 
			
		||||
        public static readonly TimeSpan DefaultResolution = TimeSpan.FromSeconds(15);
 | 
			
		||||
        private readonly Regex _resolutionRegex = new(@"[^0-9]+");
 | 
			
		||||
        private readonly PlayerIdentity _identity;
 | 
			
		||||
        private readonly Replay _replay;
 | 
			
		||||
        private readonly Task<ApmPlotter> _plotter;
 | 
			
		||||
        private readonly ApmWindowPlotController _plotController;
 | 
			
		||||
@ -34,9 +33,8 @@ namespace AnotherReplayReader
 | 
			
		||||
        public bool SkipAutos { get; private set; } = true;
 | 
			
		||||
        public bool SkipClicks { get; private set; } = false;
 | 
			
		||||
 | 
			
		||||
        public ApmWindow(Replay replay, PlayerIdentity identity)
 | 
			
		||||
        public ApmWindow(Replay replay)
 | 
			
		||||
        {
 | 
			
		||||
            _identity = identity;
 | 
			
		||||
            _replay = replay;
 | 
			
		||||
            _plotter = Task.Run(() => new ApmPlotter(replay));
 | 
			
		||||
            _plotController = new(this);
 | 
			
		||||
@ -60,17 +58,6 @@ namespace AnotherReplayReader
 | 
			
		||||
 | 
			
		||||
        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);
 | 
			
		||||
            _label.Content = "选择表格之后按 Ctrl+C 可以复制内容";
 | 
			
		||||
        }
 | 
			
		||||
@ -94,7 +81,7 @@ namespace AnotherReplayReader
 | 
			
		||||
            var (dataList, plotData, replayLength, isPartial) = await Task.Run(async () =>
 | 
			
		||||
            {
 | 
			
		||||
                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
 | 
			
		||||
                var avg = plotter.CalculateAverageApm(options);
 | 
			
		||||
                // data for plotting and partial apm
 | 
			
		||||
@ -190,12 +177,6 @@ namespace AnotherReplayReader
 | 
			
		||||
            PlotModel.Model = model;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private void OnSetPlayerButtonClick(object sender, RoutedEventArgs e)
 | 
			
		||||
        {
 | 
			
		||||
            var window1 = new Window1(_identity);
 | 
			
		||||
            window1.ShowDialog();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private void OnTableMouseDoubleClick(object sender, MouseButtonEventArgs e)
 | 
			
		||||
        {
 | 
			
		||||
            _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 MainWindowProperties _properties = new();
 | 
			
		||||
        private readonly Cache _cache = new();
 | 
			
		||||
        private readonly PlayerIdentity _playerIdentity;
 | 
			
		||||
        private readonly BigMinimapCache _minimapCache;
 | 
			
		||||
        private readonly MinimapReader _minimapReader;
 | 
			
		||||
        private readonly CancelManager _cancelLoadReplays = new();
 | 
			
		||||
@ -116,10 +115,9 @@ namespace AnotherReplayReader
 | 
			
		||||
        public MainWindow()
 | 
			
		||||
        {
 | 
			
		||||
            _taskQueue = new(Dispatcher);
 | 
			
		||||
            _playerIdentity = new PlayerIdentity(_cache);
 | 
			
		||||
            _minimapCache = new BigMinimapCache(_properties.RA3Directory);
 | 
			
		||||
            _minimapReader = new MinimapReader(_minimapCache, _properties.CustomMapsDirectory, _properties.ModsDirectory);
 | 
			
		||||
            _replayList = new(_playerIdentity);
 | 
			
		||||
            _replayList = new();
 | 
			
		||||
 | 
			
		||||
            DataContext = _properties;
 | 
			
		||||
            InitializeComponent();
 | 
			
		||||
@ -138,19 +136,8 @@ namespace AnotherReplayReader
 | 
			
		||||
            var token = _cancelLoadReplays.ResetAndGetToken(CancellationToken.None);
 | 
			
		||||
            _ = _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";
 | 
			
		||||
            if (!wantUsePlugin && _cache.GetOrDefault(permissionKey, false) is not true)
 | 
			
		||||
            if (_cache.GetOrDefault(permissionKey, false) is not true)
 | 
			
		||||
            {
 | 
			
		||||
                _cache.Set(permissionKey, true);
 | 
			
		||||
                var sb = new StringWriter();
 | 
			
		||||
@ -194,7 +181,7 @@ namespace AnotherReplayReader
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            cancelToken.ThrowIfCancellationRequested();
 | 
			
		||||
            _replayList = new(_playerIdentity);
 | 
			
		||||
            _replayList = new();
 | 
			
		||||
 | 
			
		||||
            var path = _properties.ReplayFolderPath;
 | 
			
		||||
            if (!Directory.Exists(path))
 | 
			
		||||
@ -228,7 +215,7 @@ namespace AnotherReplayReader
 | 
			
		||||
                        clock.Restart();
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                return new ReplayPinyinList(list.ToImmutableArray(), _playerIdentity);
 | 
			
		||||
                return new ReplayPinyinList(list.ToImmutableArray());
 | 
			
		||||
            }, cancelToken);
 | 
			
		||||
            cancelToken.ThrowIfCancellationRequested();
 | 
			
		||||
            _replayList = result;
 | 
			
		||||
@ -323,7 +310,7 @@ namespace AnotherReplayReader
 | 
			
		||||
                minimapTask.Forget();
 | 
			
		||||
                minimapTask = _minimapReader.TryReadTargaAsync(replay);
 | 
			
		||||
            }
 | 
			
		||||
            var newDetails = replay.GetDetails(_playerIdentity);
 | 
			
		||||
            var newDetails = replay.GetDetails();
 | 
			
		||||
            if (_properties.ReplayDetails != newDetails)
 | 
			
		||||
            {
 | 
			
		||||
                if (_replayList.Replays.FindIndex(r => r.PathEquals(replay)) is int index)
 | 
			
		||||
@ -365,7 +352,6 @@ namespace AnotherReplayReader
 | 
			
		||||
                    _replayDetailsBox.Text += $"{jsonMagic}\r\n";
 | 
			
		||||
                    break;
 | 
			
		||||
                case assemblyMagic:
 | 
			
		||||
                    Plugin.Sign();
 | 
			
		||||
                    break;
 | 
			
		||||
                case jsonMagic:
 | 
			
		||||
                    UpdateChecker.Sign();
 | 
			
		||||
@ -381,7 +367,7 @@ namespace AnotherReplayReader
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            var token = _cancelDisplayReplays.ResetAndGetToken(_cancelFilterReplays.Token);
 | 
			
		||||
            _properties.ReplayDetails = replay.GetDetails(_playerIdentity);
 | 
			
		||||
            _properties.ReplayDetails = replay.GetDetails();
 | 
			
		||||
            await _taskQueue.Enqueue(() => DisplayReplayDetail(replay, _properties.ReplayDetails, token), token);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@ -420,7 +406,7 @@ namespace AnotherReplayReader
 | 
			
		||||
 | 
			
		||||
        private void OnDetailsButtonClick(object sender, RoutedEventArgs e)
 | 
			
		||||
        {
 | 
			
		||||
            var detailsWindow = new ApmWindow(_properties.CurrentReplay!, _playerIdentity)
 | 
			
		||||
            var detailsWindow = new ApmWindow(_properties.CurrentReplay!)
 | 
			
		||||
            {
 | 
			
		||||
                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]);
 | 
			
		||||
                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();
 | 
			
		||||
                    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(string path) => Path.Equals(path, StringComparison.OrdinalIgnoreCase);
 | 
			
		||||
 | 
			
		||||
        public string GetDetails(PlayerIdentity playerIdentity)
 | 
			
		||||
        public string GetDetails()
 | 
			
		||||
        {
 | 
			
		||||
            static string GetSizeString(double size)
 | 
			
		||||
            {
 | 
			
		||||
@ -273,8 +273,7 @@ namespace AnotherReplayReader.ReplayFile
 | 
			
		||||
                    break;
 | 
			
		||||
                }
 | 
			
		||||
                var factionName = ModData.GetFaction(Mod, player.FactionId).Name;
 | 
			
		||||
                var realName = Type == ReplayType.Lan ? playerIdentity.FormatRealName(player.PlayerIp) : string.Empty;
 | 
			
		||||
                writer.WriteLine($"{player.PlayerName + realName},{factionName}");
 | 
			
		||||
                writer.WriteLine($"{player.PlayerName},{factionName}");
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return writer.ToString();
 | 
			
		||||
 | 
			
		||||
@ -6,27 +6,23 @@ 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() :
 | 
			
		||||
            this(ImmutableArray<Replay>.Empty)
 | 
			
		||||
        {
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public ReplayPinyinList(ImmutableArray<Replay> replay, PlayerIdentity playerIdentity) :
 | 
			
		||||
        public ReplayPinyinList(ImmutableArray<Replay> replay) :
 | 
			
		||||
            this(replay,
 | 
			
		||||
                 replay.Select(replay => new ReplayPinyinData(replay, playerIdentity)).ToImmutableArray(),
 | 
			
		||||
                 playerIdentity)
 | 
			
		||||
                 replay.Select(replay => new ReplayPinyinData(replay)).ToImmutableArray())
 | 
			
		||||
        {
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private ReplayPinyinList(ImmutableArray<Replay> replay,
 | 
			
		||||
                                 ImmutableArray<ReplayPinyinData> pinyins,
 | 
			
		||||
                                 PlayerIdentity playerIdentity)
 | 
			
		||||
                                 ImmutableArray<ReplayPinyinData> pinyins)
 | 
			
		||||
        {
 | 
			
		||||
            _playerIdentity = playerIdentity;
 | 
			
		||||
            Replays = replay;
 | 
			
		||||
            Pinyins = pinyins;
 | 
			
		||||
        }
 | 
			
		||||
@ -34,8 +30,7 @@ namespace AnotherReplayReader.Utils
 | 
			
		||||
        public ReplayPinyinList SetItem(int index, Replay replay)
 | 
			
		||||
        {
 | 
			
		||||
            return new(Replays.SetItem(index, replay),
 | 
			
		||||
                       Pinyins.SetItem(index, new(replay, _playerIdentity)),
 | 
			
		||||
                       _playerIdentity);
 | 
			
		||||
                       Pinyins.SetItem(index, new(replay)));
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -45,10 +40,10 @@ namespace AnotherReplayReader.Utils
 | 
			
		||||
        public string? PinyinDetails { get; }
 | 
			
		||||
        public string? PinyinMod { get; }
 | 
			
		||||
 | 
			
		||||
        public ReplayPinyinData(Replay replay, PlayerIdentity playerIdentity)
 | 
			
		||||
        public ReplayPinyinData(Replay replay)
 | 
			
		||||
        {
 | 
			
		||||
            Replay = replay;
 | 
			
		||||
            PinyinDetails = replay.GetDetails(playerIdentity).ToPinyin();
 | 
			
		||||
            PinyinDetails = replay.GetDetails().ToPinyin();
 | 
			
		||||
            PinyinMod = replay.Mod.ModName.ToPinyin();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -13,12 +13,10 @@ namespace AnotherReplayReader
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    internal partial class Window1 : Window
 | 
			
		||||
    {
 | 
			
		||||
        private readonly PlayerIdentity _identity;
 | 
			
		||||
 | 
			
		||||
        public Window1(PlayerIdentity identity)
 | 
			
		||||
        public Window1()
 | 
			
		||||
        {
 | 
			
		||||
            InitializeComponent();
 | 
			
		||||
            _identity = identity;
 | 
			
		||||
            Refresh(true);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@ -34,7 +32,6 @@ namespace AnotherReplayReader
 | 
			
		||||
                    await Display();
 | 
			
		||||
                    _dataGrid.ItemsSource = loading.Concat(_dataGrid.ItemsSource.Cast<IpAndPlayer>());
 | 
			
		||||
                }
 | 
			
		||||
                await _identity.Fetch();
 | 
			
		||||
                await Display();
 | 
			
		||||
            }
 | 
			
		||||
            catch (Exception e)
 | 
			
		||||
@ -49,26 +46,6 @@ namespace AnotherReplayReader
 | 
			
		||||
 | 
			
		||||
        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)
 | 
			
		||||
@ -114,7 +91,7 @@ namespace AnotherReplayReader
 | 
			
		||||
        {
 | 
			
		||||
            var bytes = ip.GetAddressBytes();
 | 
			
		||||
            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)
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user