ui, and controller, ecc, ecc
This commit is contained in:
		
							parent
							
								
									1d706b1adc
								
							
						
					
					
						commit
						97067a68a5
					
				
							
								
								
									
										162
									
								
								App.xaml
									
									
									
									
									
								
							
							
						
						
									
										162
									
								
								App.xaml
									
									
									
									
									
								
							@ -1,9 +1,167 @@
 | 
				
			|||||||
<Application x:Class="HashCalculator.GUI.App"
 | 
					<Application x:Class="HashCalculator.GUI.App"
 | 
				
			||||||
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
 | 
					             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
 | 
				
			||||||
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
 | 
					             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
 | 
				
			||||||
             xmlns:local="clr-namespace:HashCalculator.GUI"
 | 
					             xmlns:l="clr-namespace:HashCalculator.GUI"
 | 
				
			||||||
 | 
					             xmlns:c="clr-namespace:HashCalculator.GUI.Converters"
 | 
				
			||||||
             StartupUri="MainWindow.xaml">
 | 
					             StartupUri="MainWindow.xaml">
 | 
				
			||||||
    <Application.Resources>
 | 
					    <Application.Resources>
 | 
				
			||||||
         
 | 
					        <c:ValueConverterAggregate x:Key="ValidInputEntryToVisibilityConverter">
 | 
				
			||||||
 | 
					            <c:ValidInputEntryTypeToBooleanConverter />
 | 
				
			||||||
 | 
					            <BooleanToVisibilityConverter />
 | 
				
			||||||
 | 
					        </c:ValueConverterAggregate>
 | 
				
			||||||
 | 
					        <c:ValueConverterAggregate x:Key="InvalidInputEntryToVisibilityConverter">
 | 
				
			||||||
 | 
					            <c:ValidInputEntryTypeToBooleanConverter />
 | 
				
			||||||
 | 
					            <c:BooleanInvertConverter />
 | 
				
			||||||
 | 
					            <BooleanToVisibilityConverter />
 | 
				
			||||||
 | 
					        </c:ValueConverterAggregate>
 | 
				
			||||||
 | 
					        <c:ValueConverterAggregate x:Key="NullToVisibilityConverter">
 | 
				
			||||||
 | 
					            <c:NullToBooleanConverter />
 | 
				
			||||||
 | 
					            <c:BooleanInvertConverter />
 | 
				
			||||||
 | 
					            <BooleanToVisibilityConverter />
 | 
				
			||||||
 | 
					        </c:ValueConverterAggregate>
 | 
				
			||||||
 | 
					        <c:ValueConverterAggregate x:Key="NotNullToVisibilityConverter">
 | 
				
			||||||
 | 
					            <c:NullToBooleanConverter />
 | 
				
			||||||
 | 
					            <BooleanToVisibilityConverter />
 | 
				
			||||||
 | 
					        </c:ValueConverterAggregate>
 | 
				
			||||||
 | 
					        <c:ValueConverterAggregate x:Key="NonZeroToVisibilityConverter">
 | 
				
			||||||
 | 
					            <c:IsZeroToBooleanConverter />
 | 
				
			||||||
 | 
					            <BooleanToVisibilityConverter />
 | 
				
			||||||
 | 
					        </c:ValueConverterAggregate>
 | 
				
			||||||
 | 
					        <c:MultiValueEqualityConverter x:Key="MultiValueEqualityConverter" />
 | 
				
			||||||
 | 
					        <Style x:Key="CommonStyle" TargetType="Control">
 | 
				
			||||||
 | 
					            <Setter 
 | 
				
			||||||
 | 
					                Property="Foreground"
 | 
				
			||||||
 | 
					                Value="LightGray"
 | 
				
			||||||
 | 
					            />
 | 
				
			||||||
 | 
					            <Setter 
 | 
				
			||||||
 | 
					                Property="Background"
 | 
				
			||||||
 | 
					                Value="#202020"
 | 
				
			||||||
 | 
					            />
 | 
				
			||||||
 | 
					        </Style>
 | 
				
			||||||
 | 
					        <Style
 | 
				
			||||||
 | 
					            BasedOn="{StaticResource CommonStyle}"
 | 
				
			||||||
 | 
					            TargetType="l:MainWindow"
 | 
				
			||||||
 | 
					        />
 | 
				
			||||||
 | 
					        <Style
 | 
				
			||||||
 | 
					            TargetType="l:ShellLink"
 | 
				
			||||||
 | 
					        >
 | 
				
			||||||
 | 
					            <Setter 
 | 
				
			||||||
 | 
					                Property="Foreground"
 | 
				
			||||||
 | 
					                Value="#30B0FF"
 | 
				
			||||||
 | 
					            />
 | 
				
			||||||
 | 
					            <Style.Triggers>
 | 
				
			||||||
 | 
					                <Trigger Property="IsMouseOver" Value="True">
 | 
				
			||||||
 | 
					                    <Setter Property="Foreground" Value="Red" />
 | 
				
			||||||
 | 
					                </Trigger>
 | 
				
			||||||
 | 
					            </Style.Triggers>
 | 
				
			||||||
 | 
					        </Style>
 | 
				
			||||||
 | 
					        <Style 
 | 
				
			||||||
 | 
					            BasedOn="{StaticResource CommonStyle}" 
 | 
				
			||||||
 | 
					            TargetType="{x:Type TextBox}"
 | 
				
			||||||
 | 
					        >
 | 
				
			||||||
 | 
					            <Setter Property="Background" Value="#00000000"/>
 | 
				
			||||||
 | 
					        </Style>
 | 
				
			||||||
 | 
					        <Style 
 | 
				
			||||||
 | 
					            BasedOn="{StaticResource CommonStyle}" 
 | 
				
			||||||
 | 
					            TargetType="{x:Type ComboBox}"
 | 
				
			||||||
 | 
					        >
 | 
				
			||||||
 | 
					            <Setter Property="Background" Value="#00000000"/>
 | 
				
			||||||
 | 
					        </Style>
 | 
				
			||||||
 | 
					        <Style 
 | 
				
			||||||
 | 
					            BasedOn="{StaticResource CommonStyle}" 
 | 
				
			||||||
 | 
					            TargetType="{x:Type Button}"
 | 
				
			||||||
 | 
					            x:Key="ButtonStyle"
 | 
				
			||||||
 | 
					        >
 | 
				
			||||||
 | 
					            <Setter Property="Background" Value="#20808080"/>
 | 
				
			||||||
 | 
					            <Setter Property="Template">
 | 
				
			||||||
 | 
					                <Setter.Value>
 | 
				
			||||||
 | 
					                    <ControlTemplate TargetType="{x:Type Button}">
 | 
				
			||||||
 | 
					                        <Border 
 | 
				
			||||||
 | 
					                            x:Name="ButtonBorderTemplate" 
 | 
				
			||||||
 | 
					                            BorderBrush="{TemplateBinding BorderBrush}" 
 | 
				
			||||||
 | 
					                            BorderThickness="{TemplateBinding BorderThickness}" 
 | 
				
			||||||
 | 
					                            Background="{TemplateBinding Background}" 
 | 
				
			||||||
 | 
					                            SnapsToDevicePixels="True"
 | 
				
			||||||
 | 
					                        >
 | 
				
			||||||
 | 
					                            <ContentPresenter 
 | 
				
			||||||
 | 
					                                x:Name="ButtonContentPresenterTemplate" 
 | 
				
			||||||
 | 
					                                ContentTemplate="{TemplateBinding ContentTemplate}" 
 | 
				
			||||||
 | 
					                                Content="{TemplateBinding Content}" 
 | 
				
			||||||
 | 
					                                ContentStringFormat="{TemplateBinding ContentStringFormat}" 
 | 
				
			||||||
 | 
					                                Focusable="False" 
 | 
				
			||||||
 | 
					                                HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" 
 | 
				
			||||||
 | 
					                                Margin="{TemplateBinding Padding}" 
 | 
				
			||||||
 | 
					                                RecognizesAccessKey="True" 
 | 
				
			||||||
 | 
					                                SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" 
 | 
				
			||||||
 | 
					                                VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
 | 
				
			||||||
 | 
					                            />
 | 
				
			||||||
 | 
					                        </Border>
 | 
				
			||||||
 | 
					                        <ControlTemplate.Triggers>
 | 
				
			||||||
 | 
					                            <Trigger Property="Button.IsDefaulted" Value="True">
 | 
				
			||||||
 | 
					                                <Setter Property="BorderBrush" TargetName="ButtonBorderTemplate" Value="{DynamicResource {x:Static SystemColors.HighlightBrushKey}}"/>
 | 
				
			||||||
 | 
					                            </Trigger>
 | 
				
			||||||
 | 
					                            <Trigger Property="IsMouseOver" Value="True">
 | 
				
			||||||
 | 
					                                <Setter Property="Background" TargetName="ButtonBorderTemplate" Value="#80BEE6FD"/>
 | 
				
			||||||
 | 
					                                <Setter Property="BorderBrush" TargetName="ButtonBorderTemplate" Value="#FF3C7FB1"/>
 | 
				
			||||||
 | 
					                            </Trigger>
 | 
				
			||||||
 | 
					                            <Trigger Property="IsPressed" Value="True">
 | 
				
			||||||
 | 
					                                <Setter Property="Background" TargetName="ButtonBorderTemplate" Value="#80C4E5F6"/>
 | 
				
			||||||
 | 
					                                <Setter Property="BorderBrush" TargetName="ButtonBorderTemplate" Value="#FF2C628B"/>
 | 
				
			||||||
 | 
					                            </Trigger>
 | 
				
			||||||
 | 
					                            <Trigger Property="ToggleButton.IsChecked" Value="True">
 | 
				
			||||||
 | 
					                                <Setter Property="Background" TargetName="ButtonBorderTemplate" Value="#FFBCDDEE"/>
 | 
				
			||||||
 | 
					                                <Setter Property="BorderBrush" TargetName="ButtonBorderTemplate" Value="#FF245A83"/>
 | 
				
			||||||
 | 
					                            </Trigger>
 | 
				
			||||||
 | 
					                            <Trigger Property="IsEnabled" Value="False">
 | 
				
			||||||
 | 
					                                <Setter Property="Background" TargetName="ButtonBorderTemplate" Value="#00000000"/>
 | 
				
			||||||
 | 
					                                <Setter Property="BorderBrush" TargetName="ButtonBorderTemplate" Value="#FFADB2B5"/>
 | 
				
			||||||
 | 
					                                <Setter Property="Foreground" Value="#808080"/>
 | 
				
			||||||
 | 
					                            </Trigger>
 | 
				
			||||||
 | 
					                        </ControlTemplate.Triggers>
 | 
				
			||||||
 | 
					                    </ControlTemplate>
 | 
				
			||||||
 | 
					                </Setter.Value>
 | 
				
			||||||
 | 
					            </Setter>
 | 
				
			||||||
 | 
					        </Style>
 | 
				
			||||||
 | 
					        <Style
 | 
				
			||||||
 | 
					            BasedOn="{StaticResource ButtonStyle}"
 | 
				
			||||||
 | 
					            TargetType="Button"
 | 
				
			||||||
 | 
					        />
 | 
				
			||||||
 | 
					        <Style
 | 
				
			||||||
 | 
					            BasedOn="{StaticResource CommonStyle}"
 | 
				
			||||||
 | 
					            TargetType="CheckBox"
 | 
				
			||||||
 | 
					        />
 | 
				
			||||||
 | 
					        <Style 
 | 
				
			||||||
 | 
					            BasedOn="{StaticResource CommonStyle}" 
 | 
				
			||||||
 | 
					            TargetType="{x:Type DataGrid}"
 | 
				
			||||||
 | 
					        />
 | 
				
			||||||
 | 
					        <Style 
 | 
				
			||||||
 | 
					            BasedOn="{StaticResource CommonStyle}" 
 | 
				
			||||||
 | 
					            TargetType="{x:Type DataGridColumnHeader}"
 | 
				
			||||||
 | 
					        >
 | 
				
			||||||
 | 
					            <Setter Property="Padding" Value="10,5" />
 | 
				
			||||||
 | 
					            <Setter Property="SnapsToDevicePixels" Value="True" />
 | 
				
			||||||
 | 
					            <Setter Property="Cursor" Value="Hand" />
 | 
				
			||||||
 | 
					            <Setter Property="Background" Value="#343434"/>
 | 
				
			||||||
 | 
					            <Setter Property="Foreground" Value="White"/>
 | 
				
			||||||
 | 
					            <Setter Property="BorderThickness" Value="0, 0, 1, 0"/>
 | 
				
			||||||
 | 
					            <Setter Property="BorderBrush" Value="Gray" />
 | 
				
			||||||
 | 
					        </Style>
 | 
				
			||||||
 | 
					        <Style 
 | 
				
			||||||
 | 
					            BasedOn="{StaticResource CommonStyle}"
 | 
				
			||||||
 | 
					            TargetType="{x:Type DataGridRowHeader}"
 | 
				
			||||||
 | 
					        />
 | 
				
			||||||
 | 
					        <Style 
 | 
				
			||||||
 | 
					            BasedOn="{StaticResource CommonStyle}"
 | 
				
			||||||
 | 
					            TargetType="{x:Type DataGridRow}"
 | 
				
			||||||
 | 
					        />
 | 
				
			||||||
 | 
					        <Style TargetType="{x:Type Button}" x:Key="{ComponentResourceKey ResourceId=DataGridSelectAllButtonStyle, TypeInTargetAssembly={x:Type DataGrid}}">
 | 
				
			||||||
 | 
					            <Setter Property="Background" Value="Black" />
 | 
				
			||||||
 | 
					        </Style>
 | 
				
			||||||
 | 
					        <Style 
 | 
				
			||||||
 | 
					            BasedOn="{StaticResource CommonStyle}" 
 | 
				
			||||||
 | 
					            TargetType="{x:Type ListBox}"
 | 
				
			||||||
 | 
					        >
 | 
				
			||||||
 | 
					            <Setter Property="Background" Value="#181818"/>
 | 
				
			||||||
 | 
					        </Style>
 | 
				
			||||||
    </Application.Resources>
 | 
					    </Application.Resources>
 | 
				
			||||||
</Application>
 | 
					</Application>
 | 
				
			||||||
 | 
				
			|||||||
@ -5,6 +5,7 @@ using System.Data;
 | 
				
			|||||||
using System.Linq;
 | 
					using System.Linq;
 | 
				
			||||||
using System.Threading.Tasks;
 | 
					using System.Threading.Tasks;
 | 
				
			||||||
using System.Windows;
 | 
					using System.Windows;
 | 
				
			||||||
 | 
					using TechnologyAssembler;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace HashCalculator.GUI
 | 
					namespace HashCalculator.GUI
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
@ -13,5 +14,10 @@ namespace HashCalculator.GUI
 | 
				
			|||||||
    /// </summary>
 | 
					    /// </summary>
 | 
				
			||||||
    public partial class App : Application
 | 
					    public partial class App : Application
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
 | 
					        public App()
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            var core = new TechnologyAssemblerCoreModule();
 | 
				
			||||||
 | 
					            core.Initialize();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -1,7 +1,9 @@
 | 
				
			|||||||
using System;
 | 
					using System;
 | 
				
			||||||
using System.Collections.Generic;
 | 
					using System.Collections.Generic;
 | 
				
			||||||
 | 
					using System.IO;
 | 
				
			||||||
using System.Linq;
 | 
					using System.Linq;
 | 
				
			||||||
using System.Xml.Linq;
 | 
					using System.Xml.Linq;
 | 
				
			||||||
 | 
					using TechnologyAssembler.Core.Assets;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace HashCalculator.GUI
 | 
					namespace HashCalculator.GUI
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
@ -10,10 +12,10 @@ namespace HashCalculator.GUI
 | 
				
			|||||||
        public string Type { get; }
 | 
					        public string Type { get; }
 | 
				
			||||||
        public string Name { get; }
 | 
					        public string Name { get; }
 | 
				
			||||||
        public IEnumerable<string> DisplayLabels { get; }
 | 
					        public IEnumerable<string> DisplayLabels { get; }
 | 
				
			||||||
        public uint Hash => SageHash.CalculateLowercaseHash(Name);
 | 
					        public uint InstanceId => SageHash.CalculateLowercaseHash(Name);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public string IdString => $"{Type}:{Name}";
 | 
					        public string NameString => $"{Type}:{Name}";
 | 
				
			||||||
        public string HashString => $"{Hash:0X} ({Hash})";
 | 
					        public string InstanceIdString => $"{InstanceId:X8} ({InstanceId})";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public AssetEntry(XElement element)
 | 
					        public AssetEntry(XElement element)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
@ -38,6 +40,21 @@ namespace HashCalculator.GUI
 | 
				
			|||||||
            DisplayLabels = labels.Concat(transformLabels).ToArray();
 | 
					            DisplayLabels = labels.Concat(transformLabels).ToArray();
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public AssetEntry(Asset asset)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            if(asset is null)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                throw new ArgumentNullException($"{nameof(asset)} is null");
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            Type = asset.TypeName;
 | 
				
			||||||
 | 
					            Name = asset.InstanceName;
 | 
				
			||||||
 | 
					            DisplayLabels = Enumerable.Empty<string>();
 | 
				
			||||||
 | 
					            if(InstanceId != asset.InstanceId || SageHash.CalculateBinaryHash(Type) != asset.TypeId)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                throw new InvalidDataException();
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public bool Equals(AssetEntry entry)
 | 
					        public bool Equals(AssetEntry entry)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            return this == entry;
 | 
					            return this == entry;
 | 
				
			||||||
@ -73,7 +90,7 @@ namespace HashCalculator.GUI
 | 
				
			|||||||
                return true;
 | 
					                return true;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            return a.Type == b.Type && a.Hash == b.Hash;
 | 
					            return a.Type == b.Type && a.InstanceId == b.InstanceId;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public static bool operator !=(AssetEntry a, AssetEntry b)
 | 
					        public static bool operator !=(AssetEntry a, AssetEntry b)
 | 
				
			||||||
@ -84,7 +101,34 @@ namespace HashCalculator.GUI
 | 
				
			|||||||
        // override object.GetHashCode
 | 
					        // override object.GetHashCode
 | 
				
			||||||
        public override int GetHashCode()
 | 
					        public override int GetHashCode()
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            return HashCode.Combine(Type, Hash);
 | 
					            return HashCode.Combine(Type, InstanceId);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    internal class DisplayAssetEntry : IComparable<DisplayAssetEntry>
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        public string Name { get; }
 | 
				
			||||||
 | 
					        public string InstanceId { get; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public DisplayAssetEntry(AssetEntry entry)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            Name = entry.NameString;
 | 
				
			||||||
 | 
					            InstanceId = entry.InstanceIdString;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public int CompareTo(DisplayAssetEntry other)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            return string.CompareOrdinal(Name, other.Name);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    internal class LocalizedDisplayAssetEntry : DisplayAssetEntry
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        public string LocalizedNames { get; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public LocalizedDisplayAssetEntry(AssetEntry entry) : base(entry)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            LocalizedNames = entry.DisplayLabels.Aggregate((x, y) => $"{x} {y}");
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										227
									
								
								Controller.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										227
									
								
								Controller.cs
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,227 @@
 | 
				
			|||||||
 | 
					using System;
 | 
				
			||||||
 | 
					using System.Collections.Generic;
 | 
				
			||||||
 | 
					using System.Diagnostics.CodeAnalysis;
 | 
				
			||||||
 | 
					using System.IO;
 | 
				
			||||||
 | 
					using System.Linq;
 | 
				
			||||||
 | 
					using System.Text;
 | 
				
			||||||
 | 
					using System.Threading;
 | 
				
			||||||
 | 
					using System.Threading.Tasks;
 | 
				
			||||||
 | 
					using TechnologyAssembler.Core.IO;
 | 
				
			||||||
 | 
					using TechnologyAssembler.Core.Assets;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace HashCalculator.GUI
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    [SuppressMessage("Design", "CA1001:具有可释放字段的类型应该是可释放的", Justification = "<挂起>")]
 | 
				
			||||||
 | 
					    internal class Controller
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        public const string SelectedFileSystemRootPath = "/selected";
 | 
				
			||||||
 | 
					        public const string ManifestExtension = ".manifest";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        private ViewModel ViewModel { get; }
 | 
				
			||||||
 | 
					        private BigFileSystemProvider? _currentBig = null;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public Controller(ViewModel viewModel)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            ViewModel = viewModel;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        [SuppressMessage("Design", "CA1031:不捕获常规异常类型", Justification = "<挂起>")]
 | 
				
			||||||
 | 
					        public async Task OnMainInputDecided(InputEntry selected)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            _currentBig?.Dispose();
 | 
				
			||||||
 | 
					            _currentBig = null;
 | 
				
			||||||
 | 
					            ViewModel.Entries.Clear();
 | 
				
			||||||
 | 
					            ViewModel.TraceText = string.Empty;
 | 
				
			||||||
 | 
					            ViewModel.BigEntryInput.AllManifests = null;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            try
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                switch (selected.Type)
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    case InputEntryType.BinaryFile:
 | 
				
			||||||
 | 
					                        ViewModel.StatusText = "请留意一下弹出的窗口(";
 | 
				
			||||||
 | 
					                        CopyableBox.ShowDialog(token => CalculateBinaryHash(selected.Value, token));
 | 
				
			||||||
 | 
					                        ViewModel.StatusText = ViewModel.SuggestionString(string.Empty);
 | 
				
			||||||
 | 
					                        return;
 | 
				
			||||||
 | 
					                    case InputEntryType.BigFile:
 | 
				
			||||||
 | 
					                        await LoadBig(selected.Value).ConfigureAwait(true);
 | 
				
			||||||
 | 
					                        return;
 | 
				
			||||||
 | 
					                    case InputEntryType.ManifestFile:
 | 
				
			||||||
 | 
					                        await LoadManifestFromFile(selected.Value).ConfigureAwait(true);
 | 
				
			||||||
 | 
					                        return;
 | 
				
			||||||
 | 
					                    case InputEntryType.XmlFile:
 | 
				
			||||||
 | 
					                        throw new NotImplementedException();
 | 
				
			||||||
 | 
					                    default:
 | 
				
			||||||
 | 
					                        throw new NotSupportedException();
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            catch (Exception error)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                ViewModel.StatusText = "失败…";
 | 
				
			||||||
 | 
					                CopyableBox.ShowDialog(_ =>
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    return Task.FromResult($"在尝试加载 {selected.Type} `{selected.Value}` 时发生错误:\r\n{error}");
 | 
				
			||||||
 | 
					                });
 | 
				
			||||||
 | 
					                ViewModel.StatusText = string.Empty;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        [SuppressMessage("Design", "CA1031:不捕获常规异常类型", Justification = "<挂起>")]
 | 
				
			||||||
 | 
					        private static Task<string> CalculateBinaryHash(string filePath, CancellationToken cancel)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            return Task.Run(() =>
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                try
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    using var file = File.OpenRead(filePath);
 | 
				
			||||||
 | 
					                    var array = new byte[file.Length];
 | 
				
			||||||
 | 
					                    cancel.ThrowIfCancellationRequested();
 | 
				
			||||||
 | 
					                    var totalRead = 0;
 | 
				
			||||||
 | 
					                    const int ReadSize = 32 * 1024 * 1024;
 | 
				
			||||||
 | 
					                    while (totalRead < array.Length)
 | 
				
			||||||
 | 
					                    {
 | 
				
			||||||
 | 
					                        totalRead += file.Read(array, totalRead, Math.Min(array.Length - totalRead, ReadSize));
 | 
				
			||||||
 | 
					                        cancel.ThrowIfCancellationRequested();
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    var hashes = new Func<byte[], uint>[]
 | 
				
			||||||
 | 
					                    {
 | 
				
			||||||
 | 
					                        SageHash.CalculateBinaryHash,
 | 
				
			||||||
 | 
					                        SageHash.CalculateLauncherBinaryHash
 | 
				
			||||||
 | 
					                    }.AsParallel().Select(fn => fn(array)).ToArray();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    return $"使用 SAGE FastHash 计算出的哈希值:{hashes[0]:X8} (十进制 {hashes[0]})\r\n"
 | 
				
			||||||
 | 
					                        + "注意这是以大小写敏感模式计算出的哈希值,与素材ID的哈希值(转换成小写后计算的哈希)一般是不一样的\r\n\r\n"
 | 
				
			||||||
 | 
					                        + "使用 SAGE Launcher FastHash(比如说 RA3.exe 用来校验更新补丁文件的哈希)计算出的哈希值:"
 | 
				
			||||||
 | 
					                        + $"{hashes[1]:X8} (十进制 {hashes[1]})";
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                catch (Exception exception)
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    return exception.ToString();
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        private async Task LoadBig(string path)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            ViewModel.StatusText = "正在尝试加载 big 文件…";
 | 
				
			||||||
 | 
					            var bigViewModel = ViewModel.BigEntryInput;
 | 
				
			||||||
 | 
					            bigViewModel.Text = string.Empty;
 | 
				
			||||||
 | 
					            bigViewModel.AllManifests = await Task.Run(() =>
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                using var big = new BigFile(path);
 | 
				
			||||||
 | 
					                _currentBig = new BigFileSystemProvider(SelectedFileSystemRootPath, big, null);
 | 
				
			||||||
 | 
					                return FindManifests();
 | 
				
			||||||
 | 
					            }).ConfigureAwait(true);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            var first = bigViewModel.AllManifests.FirstOrDefault();
 | 
				
			||||||
 | 
					            if (first != null)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                var name = VirtualFileSystem.GetFileNameWithoutExtension(first.Value);
 | 
				
			||||||
 | 
					                if (name.StartsWith("mod", StringComparison.OrdinalIgnoreCase)
 | 
				
			||||||
 | 
					                    || name.StartsWith("mapmetadata", StringComparison.OrdinalIgnoreCase))
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    bigViewModel.SelectedItem = first;
 | 
				
			||||||
 | 
					                    await bigViewModel.SelectCommand.ExecuteTask(ViewModel).ConfigureAwait(true);
 | 
				
			||||||
 | 
					                    ViewModel.StatusText = "big 已加载完毕,并自动选了一个看起来比较合适的 manifest 文件(";
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            else
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                ViewModel.StatusText = "big 文件已加载完毕,请选择 big 文件里的 manifest 文件";
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        private async Task LoadManifestFromFile(string path)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            using var provider = new FileSystemProvider(SelectedFileSystemRootPath, null);
 | 
				
			||||||
 | 
					            await ProcessManifest(path).ConfigureAwait(true);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public async Task ProcessManifest(string path)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            ViewModel.Entries.Clear();
 | 
				
			||||||
 | 
					            ViewModel.TraceText = string.Empty;
 | 
				
			||||||
 | 
					            ViewModel.StatusText = "正在加载 manifest 文件…";
 | 
				
			||||||
 | 
					            var entries = await Task.Run(() =>
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                if (path.EndsWith(ManifestExtension, StringComparison.OrdinalIgnoreCase))
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    path = path.Remove(path.Length - ManifestExtension.Length);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                using var manifest = Manifest.Load(path);
 | 
				
			||||||
 | 
					                var assets = from asset in manifest.Assets.Values
 | 
				
			||||||
 | 
					                             select new DisplayAssetEntry(new AssetEntry(asset));
 | 
				
			||||||
 | 
					                return assets.ToArray();
 | 
				
			||||||
 | 
					            }).ConfigureAwait(true);
 | 
				
			||||||
 | 
					            await AddEntries(entries).ConfigureAwait(true);
 | 
				
			||||||
 | 
					            ViewModel.StatusText = $"总共加载了{ViewModel.Entries.Count}个素材";
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public async Task AddEntries(IEnumerable<DisplayAssetEntry> entries)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            var target = ViewModel.Entries;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            int BinarySearch(DisplayAssetEntry entry, int? hint = null)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                var begin = hint.GetValueOrDefault(0);
 | 
				
			||||||
 | 
					                var end = target.Count;
 | 
				
			||||||
 | 
					                while (begin < end)
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    var middle = begin + (end - begin) / 2;
 | 
				
			||||||
 | 
					                    var result = entry.CompareTo(target[middle]);
 | 
				
			||||||
 | 
					                    if (result == 0)
 | 
				
			||||||
 | 
					                    {
 | 
				
			||||||
 | 
					                        return middle;
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                    else if (result < 0)
 | 
				
			||||||
 | 
					                    {
 | 
				
			||||||
 | 
					                        end = middle;
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                    else if (result > 0)
 | 
				
			||||||
 | 
					                    {
 | 
				
			||||||
 | 
					                        begin = middle + 1;
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                return end;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            var ordered = await Task.Run(() =>
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                var array = entries.ToArray();
 | 
				
			||||||
 | 
					                Array.Sort(array);
 | 
				
			||||||
 | 
					                return array;
 | 
				
			||||||
 | 
					            }).ConfigureAwait(true);
 | 
				
			||||||
 | 
					            ViewModel.StatusText = $"正在处理刚刚读取出来的 {ordered.Length} 个素材";
 | 
				
			||||||
 | 
					            var hint = 0;
 | 
				
			||||||
 | 
					            foreach (var entry in ordered)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                var index = hint = BinarySearch(entry, hint);
 | 
				
			||||||
 | 
					                target.Insert(index, entry);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public static IEnumerable<InputEntry> FindManifests()
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            var manifests = VirtualFileSystem.ListFiles(SelectedFileSystemRootPath, "*.manifest", VirtualSearchOptionType.AllDirectories);
 | 
				
			||||||
 | 
					            var modManifests = from manifest in manifests
 | 
				
			||||||
 | 
					                               where FileNameStartsWith(manifest, "mod")
 | 
				
			||||||
 | 
					                               select manifest;
 | 
				
			||||||
 | 
					            var globalDataManifests = from manifest in manifests
 | 
				
			||||||
 | 
					                                      where FileNameStartsWith(manifest, "mapmetadata")
 | 
				
			||||||
 | 
					                                      select manifest;
 | 
				
			||||||
 | 
					            var firstManifests = modManifests.Concat(globalDataManifests);
 | 
				
			||||||
 | 
					            var otherManifests = from manifest in manifests
 | 
				
			||||||
 | 
					                                 where !firstManifests.Contains(manifest)
 | 
				
			||||||
 | 
					                                 select manifest;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            return from manifest in firstManifests.Concat(otherManifests)
 | 
				
			||||||
 | 
					                   select new InputEntry(InputEntryType.ManifestFile, manifest, manifest);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        private static bool FileNameStartsWith(string path, string what)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            return VirtualFileSystem.GetFileName(path).StartsWith(what, StringComparison.OrdinalIgnoreCase);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -1,9 +1,11 @@
 | 
				
			|||||||
using System;
 | 
					using System;
 | 
				
			||||||
 | 
					using System.Diagnostics.CodeAnalysis;
 | 
				
			||||||
using System.Globalization;
 | 
					using System.Globalization;
 | 
				
			||||||
using System.Windows.Data;
 | 
					using System.Windows.Data;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace HashCalculator.GUI.Converters
 | 
					namespace HashCalculator.GUI.Converters
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
 | 
					    [SuppressMessage("Microsoft.Performance", "CA1812")]
 | 
				
			||||||
    [ValueConversion(typeof(bool), typeof(bool))]
 | 
					    [ValueConversion(typeof(bool), typeof(bool))]
 | 
				
			||||||
    internal class BooleanInvertConverter : IValueConverter
 | 
					    internal class BooleanInvertConverter : IValueConverter
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										20
									
								
								Converters/IsZeroToBooleanConverter.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								Converters/IsZeroToBooleanConverter.cs
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,20 @@
 | 
				
			|||||||
 | 
					using System;
 | 
				
			||||||
 | 
					using System.Globalization;
 | 
				
			||||||
 | 
					using System.Windows.Data;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace HashCalculator.GUI.Converters
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    [ValueConversion(typeof(int), typeof(bool))]
 | 
				
			||||||
 | 
					    public class IsZeroToBooleanConverter : IValueConverter
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            return (int)value == 0 ? false : true;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            throw new NotImplementedException();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										22
									
								
								Converters/MultiValueEqualityConverter.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								Converters/MultiValueEqualityConverter.cs
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,22 @@
 | 
				
			|||||||
 | 
					using System;
 | 
				
			||||||
 | 
					using System.Diagnostics.CodeAnalysis;
 | 
				
			||||||
 | 
					using System.Globalization;
 | 
				
			||||||
 | 
					using System.Linq;
 | 
				
			||||||
 | 
					using System.Windows.Data;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace HashCalculator.GUI.Converters
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    [SuppressMessage("Microsoft.Performance", "CA1812")]
 | 
				
			||||||
 | 
					    internal class MultiValueEqualityConverter : IMultiValueConverter
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            return values.All(x => Equals(x, values.First()));
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            throw new NotImplementedException();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -1,9 +1,11 @@
 | 
				
			|||||||
using System;
 | 
					using System;
 | 
				
			||||||
 | 
					using System.Diagnostics.CodeAnalysis;
 | 
				
			||||||
using System.Globalization;
 | 
					using System.Globalization;
 | 
				
			||||||
using System.Windows.Data;
 | 
					using System.Windows.Data;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace HashCalculator.GUI.Converters
 | 
					namespace HashCalculator.GUI.Converters
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
 | 
					    [SuppressMessage("Microsoft.Performance", "CA1812")]
 | 
				
			||||||
    [ValueConversion(typeof(InputEntry), typeof(bool))]
 | 
					    [ValueConversion(typeof(InputEntry), typeof(bool))]
 | 
				
			||||||
    internal class ValidInputEntryTypeToBooleanConverter : IValueConverter
 | 
					    internal class ValidInputEntryTypeToBooleanConverter : IValueConverter
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
 | 
				
			|||||||
@ -1,11 +1,13 @@
 | 
				
			|||||||
using System;
 | 
					using System;
 | 
				
			||||||
using System.Collections.Generic;
 | 
					using System.Collections.Generic;
 | 
				
			||||||
 | 
					using System.Diagnostics.CodeAnalysis;
 | 
				
			||||||
using System.Globalization;
 | 
					using System.Globalization;
 | 
				
			||||||
using System.Linq;
 | 
					using System.Linq;
 | 
				
			||||||
using System.Windows.Data;
 | 
					using System.Windows.Data;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace HashCalculator.GUI.Converters
 | 
					namespace HashCalculator.GUI.Converters
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
 | 
					    [SuppressMessage("Microsoft.Performance", "CA1812")]
 | 
				
			||||||
    internal class ValueConverterAggregate : List<IValueConverter>, IValueConverter
 | 
					    internal class ValueConverterAggregate : List<IValueConverter>, IValueConverter
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
 | 
					        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
 | 
				
			||||||
 | 
				
			|||||||
@ -128,32 +128,22 @@ namespace HashCalculator.GUI
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        private async Task InitializationTask(Func<CancellationToken, Task<string>> action, Dispatcher dispatcher)
 | 
					        private async Task InitializationTask(Func<CancellationToken, Task<string>> action, Dispatcher dispatcher)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            var utcBegin = DateTimeOffset.UtcNow;
 | 
					 | 
				
			||||||
            var timer = new DispatcherTimer(TimeSpan.FromMilliseconds(200), DispatcherPriority.Normal, (s, e) =>
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                var timeElapsed = DateTimeOffset.UtcNow - utcBegin;
 | 
					 | 
				
			||||||
                if (((DispatcherTimer)s).IsEnabled && timeElapsed.TotalSeconds > 1)
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    Text = Text = $"{InitialMessage}\r\n目前耗时{timeElapsed},稍微再等一下吧233";
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            }, dispatcher);
 | 
					 | 
				
			||||||
            timer.Start();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            var token = _cancellationTokenSource.Token;
 | 
					            var token = _cancellationTokenSource.Token;
 | 
				
			||||||
            async Task<string> Action()
 | 
					            async Task<string> Action()
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                try
 | 
					                try
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
 | 
					                    using var timer = new DisposableDispatcherTimer(timer =>
 | 
				
			||||||
 | 
					                    {
 | 
				
			||||||
 | 
					                        Text = $"{InitialMessage}\r\n目前耗时{timer.TimeSinceCreation},稍微再等一下吧233";
 | 
				
			||||||
 | 
					                    }, dispatcher, TimeSpan.FromMilliseconds(200), TimeSpan.FromSeconds(1));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    return await action(token).ConfigureAwait(false);
 | 
					                    return await action(token).ConfigureAwait(false);
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                catch (OperationCanceledException)
 | 
					                catch (OperationCanceledException)
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    return "操作已被取消";
 | 
					                    return "操作已被取消";
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                finally
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    timer.Stop();
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            Text = await dispatcher.Invoke(Action).ConfigureAwait(true);
 | 
					            Text = await dispatcher.Invoke(Action).ConfigureAwait(true);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										38
									
								
								DisposableDispatcherTimer.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								DisposableDispatcherTimer.cs
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,38 @@
 | 
				
			|||||||
 | 
					using System;
 | 
				
			||||||
 | 
					using System.Collections.Generic;
 | 
				
			||||||
 | 
					using System.Linq;
 | 
				
			||||||
 | 
					using System.Text;
 | 
				
			||||||
 | 
					using System.Threading.Tasks;
 | 
				
			||||||
 | 
					using System.Windows.Threading;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace HashCalculator.GUI
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    internal class DisposableDispatcherTimer : IDisposable
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        public DispatcherTimer Timer { get; }
 | 
				
			||||||
 | 
					        public DateTimeOffset CreationTime { get; } = DateTimeOffset.UtcNow;
 | 
				
			||||||
 | 
					        public TimeSpan TimeSinceCreation => DateTimeOffset.UtcNow - CreationTime;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public DisposableDispatcherTimer(Action<DisposableDispatcherTimer> action, Dispatcher dispatcher, TimeSpan interval) : 
 | 
				
			||||||
 | 
					            this(action, dispatcher, interval, TimeSpan.Zero)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public DisposableDispatcherTimer(Action<DisposableDispatcherTimer> action, Dispatcher dispatcher, TimeSpan interval, TimeSpan wait)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            Timer = new DispatcherTimer(interval, DispatcherPriority.Normal, (s, e) =>
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                if (Timer.IsEnabled && (TimeSinceCreation > wait))
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    action(this);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }, dispatcher);
 | 
				
			||||||
 | 
					            Timer.Start();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public void Dispose()
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            Timer.Stop();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -44,15 +44,16 @@ namespace HashCalculator.GUI
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
            var currentFiles = new List<InputEntry>();
 | 
					            var currentFiles = new List<InputEntry>();
 | 
				
			||||||
            string? fileName;
 | 
					            string? fileName;
 | 
				
			||||||
            string? currentFullPath;
 | 
					            string? currentFullPath = null;
 | 
				
			||||||
            try
 | 
					            try
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                currentFullPath = Path.GetFullPath(path);
 | 
					                var currentFile = new FileInfo(path);
 | 
				
			||||||
                fileName = Path.GetFileName(path);
 | 
					                fileName = currentFile.Name;
 | 
				
			||||||
                if (File.Exists(currentFullPath))
 | 
					                if (currentFile.Exists)
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    var type = CheckExtension(currentFullPath);
 | 
					                    currentFullPath = currentFile.FullName;
 | 
				
			||||||
                    if(type is InputEntryType entryType)
 | 
					                    var type = CheckExtension(currentFile);
 | 
				
			||||||
 | 
					                    if (type is InputEntryType entryType)
 | 
				
			||||||
                    {
 | 
					                    {
 | 
				
			||||||
                        currentFiles.Add(new InputEntry(entryType, path, currentFullPath));
 | 
					                        currentFiles.Add(new InputEntry(entryType, path, currentFullPath));
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
@ -77,7 +78,7 @@ namespace HashCalculator.GUI
 | 
				
			|||||||
                                 select file;
 | 
					                                 select file;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                var supportedFiles = from file in otherFiles
 | 
					                var supportedFiles = from file in otherFiles
 | 
				
			||||||
                                     let type = CheckExtension(file.Extension)
 | 
					                                     let type = CheckExtension(file)
 | 
				
			||||||
                                     where type.HasValue
 | 
					                                     where type.HasValue
 | 
				
			||||||
                                     select new InputEntry(type.Value, _search.GetInputStyleName(file), file.FullName);
 | 
					                                     select new InputEntry(type.Value, _search.GetInputStyleName(file), file.FullName);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -91,9 +92,9 @@ namespace HashCalculator.GUI
 | 
				
			|||||||
            return currentFiles.Concat(alternatives);
 | 
					            return currentFiles.Concat(alternatives);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private static InputEntryType? CheckExtension(string path)
 | 
					        private static InputEntryType? CheckExtension(FileInfo info)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            if (Mapping.TryGetValue(path, out var type))
 | 
					            if (Mapping.TryGetValue(info.Extension, out var type))
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                return type;
 | 
					                return type;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
				
			|||||||
@ -20,11 +20,11 @@
 | 
				
			|||||||
    <PackageReference Include="Mvp.Xml" Version="2.3.0" />
 | 
					    <PackageReference Include="Mvp.Xml" Version="2.3.0" />
 | 
				
			||||||
  </ItemGroup>
 | 
					  </ItemGroup>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  <ItemGroup>
 | 
				
			||||||
 | 
					    <ProjectReference Include="..\TechnologyAssembler.Core\TechnologyAssembler.Core.csproj" />
 | 
				
			||||||
 | 
					  </ItemGroup>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  <ItemGroup>
 | 
					  <ItemGroup>
 | 
				
			||||||
    <Reference Include="System.Windows.Presentation" />
 | 
					    <Reference Include="System.Windows.Presentation" />
 | 
				
			||||||
    <Reference Include="TechnologyAssembler.Core">
 | 
					 | 
				
			||||||
      <HintPath>TechnologyAssembler.Core.dll</HintPath>
 | 
					 | 
				
			||||||
      <Private>true</Private>
 | 
					 | 
				
			||||||
    </Reference>
 | 
					 | 
				
			||||||
  </ItemGroup>
 | 
					  </ItemGroup>
 | 
				
			||||||
</Project>
 | 
					</Project>
 | 
				
			||||||
@ -8,7 +8,7 @@
 | 
				
			|||||||
    xmlns:c="clr-namespace:HashCalculator.GUI.Converters"
 | 
					    xmlns:c="clr-namespace:HashCalculator.GUI.Converters"
 | 
				
			||||||
    mc:Ignorable="d" 
 | 
					    mc:Ignorable="d" 
 | 
				
			||||||
    x:ClassModifier="internal"
 | 
					    x:ClassModifier="internal"
 | 
				
			||||||
    x:Name="_this"
 | 
					    x:Name="Self"
 | 
				
			||||||
    d:DesignHeight="100" d:DesignWidth="800"
 | 
					    d:DesignHeight="100" d:DesignWidth="800"
 | 
				
			||||||
>
 | 
					>
 | 
				
			||||||
    <UserControl.Resources>
 | 
					    <UserControl.Resources>
 | 
				
			||||||
@ -69,12 +69,17 @@
 | 
				
			|||||||
    <Grid>
 | 
					    <Grid>
 | 
				
			||||||
        <!--TextChanged="OnTextChanged"-->
 | 
					        <!--TextChanged="OnTextChanged"-->
 | 
				
			||||||
        <Grid>
 | 
					        <Grid>
 | 
				
			||||||
 | 
					            <Grid.ColumnDefinitions>
 | 
				
			||||||
 | 
					                <ColumnDefinition/>
 | 
				
			||||||
 | 
					                <ColumnDefinition Width="24"/>
 | 
				
			||||||
 | 
					            </Grid.ColumnDefinitions>
 | 
				
			||||||
            <TextBox 
 | 
					            <TextBox 
 | 
				
			||||||
                x:Name="TextBox"
 | 
					                x:Name="TextBox"
 | 
				
			||||||
                Text="{Binding Path=Text, 
 | 
					                Text="{Binding Path=Text, 
 | 
				
			||||||
                               RelativeSource={RelativeSource AncestorType=local:InputBar},
 | 
					                               RelativeSource={RelativeSource AncestorType=local:InputBar},
 | 
				
			||||||
                               UpdateSourceTrigger=PropertyChanged}"
 | 
					                               UpdateSourceTrigger=PropertyChanged}"
 | 
				
			||||||
                PreviewKeyDown="OnPreviewKeyDown"
 | 
					                PreviewKeyDown="OnPreviewKeyDown"
 | 
				
			||||||
 | 
					                Grid.Column="0"
 | 
				
			||||||
                Padding="2, 0"
 | 
					                Padding="2, 0"
 | 
				
			||||||
                VerticalContentAlignment="Center"
 | 
					                VerticalContentAlignment="Center"
 | 
				
			||||||
                LostFocus="OnLostFocus"
 | 
					                LostFocus="OnLostFocus"
 | 
				
			||||||
@ -84,9 +89,20 @@
 | 
				
			|||||||
                               RelativeSource={RelativeSource AncestorType=local:InputBar}}"
 | 
					                               RelativeSource={RelativeSource AncestorType=local:InputBar}}"
 | 
				
			||||||
                IsHitTestVisible="False"
 | 
					                IsHitTestVisible="False"
 | 
				
			||||||
                Visibility="{Binding ElementName=TextBox, Path=Text, Converter={StaticResource NullToVisibilityConverter}}"
 | 
					                Visibility="{Binding ElementName=TextBox, Path=Text, Converter={StaticResource NullToVisibilityConverter}}"
 | 
				
			||||||
 | 
					                Grid.Column="0"
 | 
				
			||||||
                Padding="5, 0"
 | 
					                Padding="5, 0"
 | 
				
			||||||
                VerticalAlignment="Center"
 | 
					                VerticalAlignment="Center"
 | 
				
			||||||
            />
 | 
					            />
 | 
				
			||||||
 | 
					            <Button 
 | 
				
			||||||
 | 
					                Click="OnToggleDropDownButtonClick"
 | 
				
			||||||
 | 
					                Grid.Column="1"
 | 
				
			||||||
 | 
					                Background="Transparent"
 | 
				
			||||||
 | 
					            >
 | 
				
			||||||
 | 
					                <Path 
 | 
				
			||||||
 | 
					                    Fill="{Binding Path=Foreground, RelativeSource={RelativeSource AncestorType={x:Type Button}}}"
 | 
				
			||||||
 | 
					                    Data="M 0 2 L 1 0 L 6 4 L 11 0 L 12 2 L 6 7 Z"
 | 
				
			||||||
 | 
					                />
 | 
				
			||||||
 | 
					            </Button>
 | 
				
			||||||
        </Grid>
 | 
					        </Grid>
 | 
				
			||||||
        <Popup
 | 
					        <Popup
 | 
				
			||||||
            x:Name="DropDown"
 | 
					            x:Name="DropDown"
 | 
				
			||||||
@ -101,7 +117,6 @@
 | 
				
			|||||||
                ItemsSource="{Binding Path=Collection, 
 | 
					                ItemsSource="{Binding Path=Collection, 
 | 
				
			||||||
                                      RelativeSource={RelativeSource AncestorType=local:InputBar}}"
 | 
					                                      RelativeSource={RelativeSource AncestorType=local:InputBar}}"
 | 
				
			||||||
                SelectionMode="Single"
 | 
					                SelectionMode="Single"
 | 
				
			||||||
                SelectionChanged="OnSelectionChanged"
 | 
					 | 
				
			||||||
                SelectedItem="{Binding Path=SelectedItem,
 | 
					                SelectedItem="{Binding Path=SelectedItem,
 | 
				
			||||||
                                       RelativeSource={RelativeSource AncestorType=local:InputBar}}"
 | 
					                                       RelativeSource={RelativeSource AncestorType=local:InputBar}}"
 | 
				
			||||||
                SelectedIndex="{Binding Path=SelectedIndex,
 | 
					                SelectedIndex="{Binding Path=SelectedIndex,
 | 
				
			||||||
 | 
				
			|||||||
@ -7,13 +7,9 @@ using System.Threading.Tasks;
 | 
				
			|||||||
using System.Windows;
 | 
					using System.Windows;
 | 
				
			||||||
using System.Windows.Controls;
 | 
					using System.Windows.Controls;
 | 
				
			||||||
using System.Windows.Data;
 | 
					using System.Windows.Data;
 | 
				
			||||||
using System.Windows.Documents;
 | 
					 | 
				
			||||||
using System.Windows.Input;
 | 
					using System.Windows.Input;
 | 
				
			||||||
using System.Windows.Media;
 | 
					using System.Windows.Media;
 | 
				
			||||||
using System.Windows.Media.Imaging;
 | 
					using System.Globalization;
 | 
				
			||||||
using System.Windows.Navigation;
 | 
					 | 
				
			||||||
using System.Windows.Shapes;
 | 
					 | 
				
			||||||
using System.Diagnostics;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace HashCalculator.GUI
 | 
					namespace HashCalculator.GUI
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
@ -74,7 +70,12 @@ namespace HashCalculator.GUI
 | 
				
			|||||||
            DependencyProperty.Register(nameof(SelectedItem), typeof(InputEntry), typeof(InputBar), new FrameworkPropertyMetadata
 | 
					            DependencyProperty.Register(nameof(SelectedItem), typeof(InputEntry), typeof(InputBar), new FrameworkPropertyMetadata
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                BindsTwoWayByDefault = true,
 | 
					                BindsTwoWayByDefault = true,
 | 
				
			||||||
                DefaultUpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged
 | 
					                DefaultUpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged,
 | 
				
			||||||
 | 
					                PropertyChangedCallback = (d, e) =>
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    var self = (InputBar)d;
 | 
				
			||||||
 | 
					                    self.OnSelectionChanged();
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
            });
 | 
					            });
 | 
				
			||||||
        public InputEntry? SelectedItem
 | 
					        public InputEntry? SelectedItem
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
@ -127,7 +128,7 @@ namespace HashCalculator.GUI
 | 
				
			|||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private void OnSelectionChanged(object sender, SelectionChangedEventArgs e)
 | 
					        private void OnSelectionChanged()
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            if (SelectedItem != null)
 | 
					            if (SelectedItem != null)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
@ -151,5 +152,14 @@ namespace HashCalculator.GUI
 | 
				
			|||||||
            return Math.Min(Math.Max(rawIndex, -1), Collection.Count() - 1);
 | 
					            return Math.Min(Math.Max(rawIndex, -1), Collection.Count() - 1);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        private void OnToggleDropDownButtonClick(object sender, RoutedEventArgs e)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            TextBox.Focus();
 | 
				
			||||||
 | 
					            TextBox.Select(Text?.Length ?? 0, 0);
 | 
				
			||||||
 | 
					            if (!DropDown.IsOpen)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                DropDown.IsOpen = true;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										243
									
								
								MainWindow.xaml
									
									
									
									
									
								
							
							
						
						
									
										243
									
								
								MainWindow.xaml
									
									
									
									
									
								
							@ -8,106 +8,18 @@
 | 
				
			|||||||
    xmlns:l="clr-namespace:HashCalculator.GUI"
 | 
					    xmlns:l="clr-namespace:HashCalculator.GUI"
 | 
				
			||||||
    xmlns:c="clr-namespace:HashCalculator.GUI.Converters"
 | 
					    xmlns:c="clr-namespace:HashCalculator.GUI.Converters"
 | 
				
			||||||
    mc:Ignorable="d"
 | 
					    mc:Ignorable="d"
 | 
				
			||||||
    Title="Sage FastHash 哈希计算器" Height="600" Width="600"
 | 
					    Title="SAGE FastHash 哈希计算器" 
 | 
				
			||||||
    Background="#202020"
 | 
					    Height="600" 
 | 
				
			||||||
    Foreground="LightGray"
 | 
					    Width="600" 
 | 
				
			||||||
 | 
					    Style="{StaticResource CommonStyle}"
 | 
				
			||||||
>
 | 
					>
 | 
				
			||||||
    <Window.Resources>
 | 
					 | 
				
			||||||
        <c:ValueConverterAggregate x:Key="ValidInputEntryToVisibilityConverter">
 | 
					 | 
				
			||||||
            <c:ValidInputEntryTypeToBooleanConverter />
 | 
					 | 
				
			||||||
            <BooleanToVisibilityConverter />
 | 
					 | 
				
			||||||
        </c:ValueConverterAggregate>
 | 
					 | 
				
			||||||
        <c:ValueConverterAggregate x:Key="InvalidInputEntryToVisibilityConverter">
 | 
					 | 
				
			||||||
            <c:ValidInputEntryTypeToBooleanConverter />
 | 
					 | 
				
			||||||
            <c:BooleanInvertConverter />
 | 
					 | 
				
			||||||
            <BooleanToVisibilityConverter />
 | 
					 | 
				
			||||||
        </c:ValueConverterAggregate>
 | 
					 | 
				
			||||||
        <Style x:Key="CommonStyle" TargetType="Control">
 | 
					 | 
				
			||||||
            <Setter 
 | 
					 | 
				
			||||||
                Property="Foreground"
 | 
					 | 
				
			||||||
                Value="{Binding Path=(TextElement.Foreground),
 | 
					 | 
				
			||||||
                                RelativeSource={RelativeSource AncestorType={x:Type FrameworkElement}}}"
 | 
					 | 
				
			||||||
            />
 | 
					 | 
				
			||||||
            <Setter 
 | 
					 | 
				
			||||||
                Property="Background"
 | 
					 | 
				
			||||||
                Value="{Binding Path=(TextElement.Background),
 | 
					 | 
				
			||||||
                                RelativeSource={RelativeSource AncestorType={x:Type FrameworkElement}}}"
 | 
					 | 
				
			||||||
            />
 | 
					 | 
				
			||||||
        </Style>
 | 
					 | 
				
			||||||
        <Style 
 | 
					 | 
				
			||||||
            BasedOn="{StaticResource CommonStyle}" 
 | 
					 | 
				
			||||||
            TargetType="{x:Type TextBox}"
 | 
					 | 
				
			||||||
        >
 | 
					 | 
				
			||||||
            <Setter Property="Background" Value="#00000000"/>
 | 
					 | 
				
			||||||
        </Style>
 | 
					 | 
				
			||||||
        <Style 
 | 
					 | 
				
			||||||
            BasedOn="{StaticResource CommonStyle}" 
 | 
					 | 
				
			||||||
            TargetType="{x:Type Button}"
 | 
					 | 
				
			||||||
        >
 | 
					 | 
				
			||||||
            <Setter Property="Background" Value="#20808080"/>
 | 
					 | 
				
			||||||
            <Setter Property="Template">
 | 
					 | 
				
			||||||
                <Setter.Value>
 | 
					 | 
				
			||||||
                    <ControlTemplate TargetType="{x:Type Button}">
 | 
					 | 
				
			||||||
                        <Border x:Name="border" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" SnapsToDevicePixels="True">
 | 
					 | 
				
			||||||
                            <ContentPresenter x:Name="contentPresenter" ContentTemplate="{TemplateBinding ContentTemplate}" Content="{TemplateBinding Content}" ContentStringFormat="{TemplateBinding ContentStringFormat}" Focusable="False" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" Margin="{TemplateBinding Padding}" RecognizesAccessKey="True" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
 | 
					 | 
				
			||||||
                        </Border>
 | 
					 | 
				
			||||||
                        <ControlTemplate.Triggers>
 | 
					 | 
				
			||||||
                            <Trigger Property="Button.IsDefaulted" Value="True">
 | 
					 | 
				
			||||||
                                <Setter Property="BorderBrush" TargetName="border" Value="{DynamicResource {x:Static SystemColors.HighlightBrushKey}}"/>
 | 
					 | 
				
			||||||
                            </Trigger>
 | 
					 | 
				
			||||||
                            <Trigger Property="IsMouseOver" Value="True">
 | 
					 | 
				
			||||||
                                <Setter Property="Background" TargetName="border" Value="#80BEE6FD"/>
 | 
					 | 
				
			||||||
                                <Setter Property="BorderBrush" TargetName="border" Value="#FF3C7FB1"/>
 | 
					 | 
				
			||||||
                            </Trigger>
 | 
					 | 
				
			||||||
                            <Trigger Property="IsPressed" Value="True">
 | 
					 | 
				
			||||||
                                <Setter Property="Background" TargetName="border" Value="#80C4E5F6"/>
 | 
					 | 
				
			||||||
                                <Setter Property="BorderBrush" TargetName="border" Value="#FF2C628B"/>
 | 
					 | 
				
			||||||
                            </Trigger>
 | 
					 | 
				
			||||||
                            <Trigger Property="ToggleButton.IsChecked" Value="True">
 | 
					 | 
				
			||||||
                                <Setter Property="Background" TargetName="border" Value="#FFBCDDEE"/>
 | 
					 | 
				
			||||||
                                <Setter Property="BorderBrush" TargetName="border" Value="#FF245A83"/>
 | 
					 | 
				
			||||||
                            </Trigger>
 | 
					 | 
				
			||||||
                            <Trigger Property="IsEnabled" Value="False">
 | 
					 | 
				
			||||||
                                <Setter Property="Background" TargetName="border" Value="#00000000"/>
 | 
					 | 
				
			||||||
                                <Setter Property="BorderBrush" TargetName="border" Value="#FFADB2B5"/>
 | 
					 | 
				
			||||||
                                <Setter Property="Foreground" Value="#808080"/>
 | 
					 | 
				
			||||||
                            </Trigger>
 | 
					 | 
				
			||||||
                        </ControlTemplate.Triggers>
 | 
					 | 
				
			||||||
                    </ControlTemplate>
 | 
					 | 
				
			||||||
                </Setter.Value>
 | 
					 | 
				
			||||||
            </Setter>
 | 
					 | 
				
			||||||
        </Style>
 | 
					 | 
				
			||||||
        <Style 
 | 
					 | 
				
			||||||
            BasedOn="{StaticResource CommonStyle}" 
 | 
					 | 
				
			||||||
            TargetType="{x:Type DataGrid}"
 | 
					 | 
				
			||||||
        />
 | 
					 | 
				
			||||||
        <Style 
 | 
					 | 
				
			||||||
            BasedOn="{StaticResource CommonStyle}" 
 | 
					 | 
				
			||||||
            TargetType="{x:Type DataGridColumnHeader}"
 | 
					 | 
				
			||||||
        >
 | 
					 | 
				
			||||||
            <Setter Property="Padding" Value="10,5" />
 | 
					 | 
				
			||||||
            <Setter Property="MinWidth" Value="0" />
 | 
					 | 
				
			||||||
            <Setter Property="SnapsToDevicePixels" Value="True" />
 | 
					 | 
				
			||||||
            <Setter Property="Cursor" Value="Hand" />
 | 
					 | 
				
			||||||
            <Setter Property="Background" Value="#343434"/>
 | 
					 | 
				
			||||||
            <Setter Property="Foreground" Value="White"/>
 | 
					 | 
				
			||||||
            <Setter Property="BorderThickness" Value="0, 0, 1, 0"/>
 | 
					 | 
				
			||||||
            <Setter Property="BorderBrush" Value="Gray" />
 | 
					 | 
				
			||||||
        </Style>
 | 
					 | 
				
			||||||
        <Style 
 | 
					 | 
				
			||||||
            BasedOn="{StaticResource CommonStyle}" 
 | 
					 | 
				
			||||||
            TargetType="{x:Type ListBox}"
 | 
					 | 
				
			||||||
        >
 | 
					 | 
				
			||||||
            <Setter Property="Background" Value="#181818"/>
 | 
					 | 
				
			||||||
        </Style>
 | 
					 | 
				
			||||||
    </Window.Resources>
 | 
					 | 
				
			||||||
    <Window.DataContext>
 | 
					    <Window.DataContext>
 | 
				
			||||||
        <l:ViewModel/>
 | 
					        <l:ViewModel />
 | 
				
			||||||
    </Window.DataContext>
 | 
					    </Window.DataContext>
 | 
				
			||||||
    <Grid Margin="10,20,10,10">
 | 
					    <Grid Margin="10,20,10,10">
 | 
				
			||||||
        <Grid.RowDefinitions>
 | 
					        <Grid.RowDefinitions>
 | 
				
			||||||
            <RowDefinition/>
 | 
					            <RowDefinition/>
 | 
				
			||||||
            <RowDefinition Height="130" />
 | 
					            <RowDefinition Height="150" />
 | 
				
			||||||
        </Grid.RowDefinitions>
 | 
					        </Grid.RowDefinitions>
 | 
				
			||||||
        <DockPanel Grid.Row="0">
 | 
					        <DockPanel Grid.Row="0">
 | 
				
			||||||
            <Grid 
 | 
					            <Grid 
 | 
				
			||||||
@ -150,79 +62,134 @@
 | 
				
			|||||||
                DockPanel.Dock="Top"
 | 
					                DockPanel.Dock="Top"
 | 
				
			||||||
                Height="25"
 | 
					                Height="25"
 | 
				
			||||||
                Margin="0,10,0,0"
 | 
					                Margin="0,10,0,0"
 | 
				
			||||||
                Visibility="Visible"
 | 
					                Visibility="{Binding BigEntryInput.AllManifests, Converter={StaticResource NotNullToVisibilityConverter}}"
 | 
				
			||||||
            >
 | 
					            >
 | 
				
			||||||
                <Grid.ColumnDefinitions>
 | 
					                <Grid.ColumnDefinitions>
 | 
				
			||||||
                    <ColumnDefinition />
 | 
					                    <ColumnDefinition />
 | 
				
			||||||
                    <ColumnDefinition Width="92"/>
 | 
					                    <ColumnDefinition Width="92" />
 | 
				
			||||||
                </Grid.ColumnDefinitions>
 | 
					                </Grid.ColumnDefinitions>
 | 
				
			||||||
                <l:InputBar
 | 
					                <l:InputBar
 | 
				
			||||||
                    HintText="输入 big 文件里包含的 manifest 文件的路径"
 | 
					                    HintText="输入 big 文件里包含的 manifest 文件的路径"
 | 
				
			||||||
                    Collection="{Binding Items}"
 | 
					                    Text="{Binding BigEntryInput.Text}"
 | 
				
			||||||
                    SelectedItem="{Binding SelectedItem}"
 | 
					                    Collection="{Binding BigEntryInput.Items}"
 | 
				
			||||||
 | 
					                    SelectedItem="{Binding BigEntryInput.SelectedItem}"
 | 
				
			||||||
 | 
					                    SelectedIndex="{Binding BigEntryInput.SelectedIndex}"
 | 
				
			||||||
                    Grid.Column="0"
 | 
					                    Grid.Column="0"
 | 
				
			||||||
                    Margin="0,0,10,0"
 | 
					                    Margin="0,0,10,0"
 | 
				
			||||||
                />
 | 
					                />
 | 
				
			||||||
                <Button 
 | 
					                <Button 
 | 
				
			||||||
                    Content="确认" 
 | 
					                    Content="确认" 
 | 
				
			||||||
 | 
					                    Command="{Binding BigEntryInput.SelectCommand}"
 | 
				
			||||||
 | 
					                    CommandParameter="{Binding}"
 | 
				
			||||||
                    Grid.Column="1" 
 | 
					                    Grid.Column="1" 
 | 
				
			||||||
                    Margin="0"
 | 
					                    Margin="0"
 | 
				
			||||||
                    Foreground="ForestGreen"
 | 
					                >
 | 
				
			||||||
                    BorderBrush="ForestGreen" Template="{DynamicResource ButtonBaseControlTemplate1}"
 | 
					                    <Button.Style>
 | 
				
			||||||
 | 
					                        <Style 
 | 
				
			||||||
 | 
					                            BasedOn="{StaticResource ButtonStyle}"
 | 
				
			||||||
 | 
					                            TargetType="Button"
 | 
				
			||||||
 | 
					                        >
 | 
				
			||||||
 | 
					                            <Style.Triggers>
 | 
				
			||||||
 | 
					                                <MultiDataTrigger>
 | 
				
			||||||
 | 
					                                    <MultiDataTrigger.Conditions>
 | 
				
			||||||
 | 
					                                        <Condition Value="False">
 | 
				
			||||||
 | 
					                                            <Condition.Binding>
 | 
				
			||||||
 | 
					                                                <MultiBinding Converter="{StaticResource MultiValueEqualityConverter}">
 | 
				
			||||||
 | 
					                                                    <Binding Path="BigEntryInput.LastProcessedManifest"></Binding>
 | 
				
			||||||
 | 
					                                                    <Binding Path="BigEntryInput.SelectedItem"></Binding>
 | 
				
			||||||
 | 
					                                                </MultiBinding>
 | 
				
			||||||
 | 
					                                            </Condition.Binding>
 | 
				
			||||||
 | 
					                                        </Condition>
 | 
				
			||||||
 | 
					                                        <Condition 
 | 
				
			||||||
 | 
					                                            Binding="{Binding RelativeSource={RelativeSource Mode=Self}, 
 | 
				
			||||||
 | 
					                                                              Path=IsEnabled}"
 | 
				
			||||||
 | 
					                                            Value="True"
 | 
				
			||||||
                                        />
 | 
					                                        />
 | 
				
			||||||
 | 
					                                    </MultiDataTrigger.Conditions>
 | 
				
			||||||
 | 
					                                    <Setter Property="Foreground" Value="#20FF30" />
 | 
				
			||||||
 | 
					                                    <Setter Property="BorderBrush" Value="#20FF30" />
 | 
				
			||||||
 | 
					                                </MultiDataTrigger>
 | 
				
			||||||
 | 
					                            </Style.Triggers>
 | 
				
			||||||
 | 
					                        </Style>
 | 
				
			||||||
 | 
					                    </Button.Style>
 | 
				
			||||||
 | 
					                </Button>
 | 
				
			||||||
            </Grid>
 | 
					            </Grid>
 | 
				
			||||||
            <DockPanel
 | 
					            <Grid
 | 
				
			||||||
                DockPanel.Dock="Top"
 | 
					                DockPanel.Dock="Top"
 | 
				
			||||||
                Height="25"
 | 
					                Height="50"
 | 
				
			||||||
                Margin="0,10"
 | 
					                Margin="0,10"
 | 
				
			||||||
            >
 | 
					            >
 | 
				
			||||||
                <Button 
 | 
					                <Grid.RowDefinitions>
 | 
				
			||||||
                    DockPanel.Dock="Right"
 | 
					                    <RowDefinition />
 | 
				
			||||||
                    Content="取消加载" 
 | 
					                    <RowDefinition />
 | 
				
			||||||
                    Grid.Column="1" 
 | 
					                </Grid.RowDefinitions>
 | 
				
			||||||
                    Width="92"
 | 
					                <TextBlock
 | 
				
			||||||
                />
 | 
					                    Text="{Binding StatusText}"
 | 
				
			||||||
                <Button 
 | 
					                    Grid.Row="0"
 | 
				
			||||||
                    DockPanel.Dock="Right"
 | 
					 | 
				
			||||||
                    Content="加载 csf / mod.str" 
 | 
					 | 
				
			||||||
                    Grid.Column="1" 
 | 
					 | 
				
			||||||
                    Width="140"
 | 
					 | 
				
			||||||
                    Margin="10,0"
 | 
					                    Margin="10,0"
 | 
				
			||||||
 | 
					                    VerticalAlignment="Center"
 | 
				
			||||||
                />
 | 
					                />
 | 
				
			||||||
                <TextBlock
 | 
					                <TextBlock
 | 
				
			||||||
                    Text="正在加载……"
 | 
					                    Text="{Binding Entries.Count, StringFormat=目前加载了{0}个素材}"
 | 
				
			||||||
 | 
					                    Grid.Row="1"
 | 
				
			||||||
 | 
					                    Margin="10,5,0,5"
 | 
				
			||||||
 | 
					                    Width="160"
 | 
				
			||||||
 | 
					                    HorizontalAlignment="Left"
 | 
				
			||||||
                    VerticalAlignment="Center"  
 | 
					                    VerticalAlignment="Center"  
 | 
				
			||||||
                    Grid.Column="0"
 | 
					                    Visibility="{Binding Entries.Count, Converter={StaticResource NonZeroToVisibilityConverter}}"
 | 
				
			||||||
                />
 | 
					                />
 | 
				
			||||||
            </DockPanel>
 | 
					
 | 
				
			||||||
            <l:InputBar
 | 
					                <CheckBox 
 | 
				
			||||||
                HintText="过滤Asset ID(可选)"
 | 
					                    Content="只显示 GameObject"
 | 
				
			||||||
                Collection="{Binding Items}"
 | 
					                    IsChecked="True"
 | 
				
			||||||
                SelectedItem="{Binding SelectedItem}"
 | 
					                    Grid.Row="1"
 | 
				
			||||||
 | 
					                    Margin="0,5,242,5"  
 | 
				
			||||||
 | 
					                    Width="155"
 | 
				
			||||||
 | 
					                    HorizontalAlignment="Right"
 | 
				
			||||||
 | 
					                    VerticalAlignment="Center"  
 | 
				
			||||||
 | 
					                />
 | 
				
			||||||
 | 
					                <Button 
 | 
				
			||||||
 | 
					                    Content="加载 csf/mod.str"
 | 
				
			||||||
 | 
					                    Grid.Row="1"
 | 
				
			||||||
 | 
					                    Width="140"
 | 
				
			||||||
 | 
					                    Margin="0,0,97,0" 
 | 
				
			||||||
 | 
					                    HorizontalAlignment="Right"
 | 
				
			||||||
 | 
					                />
 | 
				
			||||||
 | 
					                <Button 
 | 
				
			||||||
 | 
					                    Content="取消加载" 
 | 
				
			||||||
 | 
					                    Grid.Row="1"
 | 
				
			||||||
 | 
					                    Width="92"
 | 
				
			||||||
 | 
					                    HorizontalAlignment="Right"
 | 
				
			||||||
 | 
					                />
 | 
				
			||||||
 | 
					            </Grid>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            <Grid
 | 
				
			||||||
                DockPanel.Dock="Top"
 | 
					                DockPanel.Dock="Top"
 | 
				
			||||||
                Height="25" 
 | 
					                Height="25" 
 | 
				
			||||||
 | 
					            >
 | 
				
			||||||
 | 
					                <TextBox></TextBox>
 | 
				
			||||||
 | 
					                <TextBlock
 | 
				
			||||||
 | 
					                    Text="过滤Asset ID(可选)"
 | 
				
			||||||
 | 
					                    Padding="5,0"
 | 
				
			||||||
 | 
					                    VerticalAlignment="Center"
 | 
				
			||||||
                />
 | 
					                />
 | 
				
			||||||
 | 
					            </Grid>
 | 
				
			||||||
            <DataGrid 
 | 
					            <DataGrid 
 | 
				
			||||||
                x:Name="DataGrid"
 | 
					                ItemsSource="{Binding Entries}"
 | 
				
			||||||
                AutoGenerateColumns="False" 
 | 
					 | 
				
			||||||
                ScrollViewer.CanContentScroll="True"
 | 
					                ScrollViewer.CanContentScroll="True"
 | 
				
			||||||
                VerticalScrollBarVisibility="Auto"
 | 
					                VerticalScrollBarVisibility="Auto"
 | 
				
			||||||
                EnableRowVirtualization="True"
 | 
					                EnableRowVirtualization="True"
 | 
				
			||||||
                VirtualizingPanel.VirtualizationMode="Recycling"
 | 
					                VirtualizingPanel.VirtualizationMode="Recycling"
 | 
				
			||||||
                DockPanel.Dock="Top"
 | 
					            />
 | 
				
			||||||
            >
 | 
					 | 
				
			||||||
                <DataGrid.Columns>
 | 
					 | 
				
			||||||
                    <DataGridTextColumn Header="Asset ID" Width="200" Binding="{Binding AssetId}"/>
 | 
					 | 
				
			||||||
                    <DataGridTextColumn Header="哈希" Width="*" Binding="{Binding Hash}"/>
 | 
					 | 
				
			||||||
                </DataGrid.Columns>
 | 
					 | 
				
			||||||
            </DataGrid>
 | 
					 | 
				
			||||||
        </DockPanel>
 | 
					        </DockPanel>
 | 
				
			||||||
        <TextBlock 
 | 
					        <StackPanel
 | 
				
			||||||
            x:Name="textBlock" 
 | 
					 | 
				
			||||||
            Grid.Row="1"
 | 
					            Grid.Row="1"
 | 
				
			||||||
            Margin="0,10,0,0"
 | 
					            Margin="0,10,0,0"
 | 
				
			||||||
            TextWrapping="Wrap"
 | 
					            ScrollViewer.CanContentScroll="True"
 | 
				
			||||||
 | 
					            ScrollViewer.HorizontalScrollBarVisibility="Disabled"
 | 
				
			||||||
 | 
					            ScrollViewer.VerticalScrollBarVisibility="Auto"
 | 
				
			||||||
        >
 | 
					        >
 | 
				
			||||||
 | 
					            <TextBlock TextWrapping="Wrap">
 | 
				
			||||||
                本工具基于 <l:ShellLink NavigateUri="https://github.com/Qibbi">Qibbi</l:ShellLink> 提供的 TechnologyAssembler 制作<LineBreak />
 | 
					                本工具基于 <l:ShellLink NavigateUri="https://github.com/Qibbi">Qibbi</l:ShellLink> 提供的 TechnologyAssembler 制作<LineBreak />
 | 
				
			||||||
                此外使用了 <l:ShellLink NavigateUri="https://github.com/bgrainger">Bradley Grainger</l:ShellLink> 的
 | 
					                此外使用了 <l:ShellLink NavigateUri="https://github.com/bgrainger">Bradley Grainger</l:ShellLink> 的
 | 
				
			||||||
                <l:ShellLink NavigateUri="https://github.com/bgrainger/IndexRange">IndexRange</l:ShellLink>
 | 
					                <l:ShellLink NavigateUri="https://github.com/bgrainger/IndexRange">IndexRange</l:ShellLink>
 | 
				
			||||||
@ -230,5 +197,17 @@
 | 
				
			|||||||
                <LineBreak />
 | 
					                <LineBreak />
 | 
				
			||||||
                假如你对本工具有任何疑问或者建议的话,可以来到<l:ShellLink NavigateUri="https://tieba.baidu.com/ra3">红警3吧</l:ShellLink>发帖寻找岚依(
 | 
					                假如你对本工具有任何疑问或者建议的话,可以来到<l:ShellLink NavigateUri="https://tieba.baidu.com/ra3">红警3吧</l:ShellLink>发帖寻找岚依(
 | 
				
			||||||
            </TextBlock>
 | 
					            </TextBlock>
 | 
				
			||||||
 | 
					            <TextBlock />
 | 
				
			||||||
 | 
					            <StackPanel 
 | 
				
			||||||
 | 
					                Height="25"
 | 
				
			||||||
 | 
					                HorizontalAlignment="Right"
 | 
				
			||||||
 | 
					            >
 | 
				
			||||||
 | 
					                <Button
 | 
				
			||||||
 | 
					                    Content="清除输出" 
 | 
				
			||||||
 | 
					                    Width="92"
 | 
				
			||||||
 | 
					                    Grid.Row="1"
 | 
				
			||||||
 | 
					                />
 | 
				
			||||||
 | 
					            </StackPanel>
 | 
				
			||||||
 | 
					        </StackPanel>
 | 
				
			||||||
    </Grid>
 | 
					    </Grid>
 | 
				
			||||||
</Window>
 | 
					</Window>
 | 
				
			||||||
 | 
				
			|||||||
@ -21,65 +21,37 @@ namespace HashCalculator.GUI
 | 
				
			|||||||
    public partial class MainWindow : Window
 | 
					    public partial class MainWindow : Window
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        internal ViewModel ViewModel => (ViewModel)DataContext;
 | 
					        internal ViewModel ViewModel => (ViewModel)DataContext;
 | 
				
			||||||
 | 
					        private bool _autoscroll = true;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public MainWindow()
 | 
					        public MainWindow()
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            InitializeComponent();
 | 
					            InitializeComponent();
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private void OnMainInputTextChanged(object sender, TextChangedEventArgs e)
 | 
					        private void OnButtomScrollViewerScrollChanged(object sender, ScrollChangedEventArgs e)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            CopyableBox.ShowDialog(async token =>
 | 
					            var scrollViewer = (ScrollViewer)e.Source;
 | 
				
			||||||
            {
 | 
					            // User scroll event : set or unset auto-scroll mode
 | 
				
			||||||
                var init = DateTimeOffset.UtcNow;
 | 
					            if (e.ExtentHeightChange == 0)
 | 
				
			||||||
                var i = 0;
 | 
					            {   // Content unchanged : user scroll event
 | 
				
			||||||
                while (i < 100)
 | 
					                if (scrollViewer.VerticalOffset == scrollViewer.ScrollableHeight)
 | 
				
			||||||
                {
 | 
					                {   // Scroll bar is in bottom
 | 
				
			||||||
                    token.ThrowIfCancellationRequested();
 | 
					                    // Set auto-scroll mode
 | 
				
			||||||
                    await Task.Delay(100).ConfigureAwait(false);
 | 
					                    _autoscroll = true;
 | 
				
			||||||
                    ++i;
 | 
					 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                MessageBox.Show("Completed!");
 | 
					                else
 | 
				
			||||||
                return $"Completed after exactly {DateTimeOffset.UtcNow - init}";
 | 
					                {   // Scroll bar isn't in bottom
 | 
				
			||||||
            });
 | 
					                    // Unset auto-scroll mode
 | 
				
			||||||
            /*var mainInput = _viewModel.MainInput;
 | 
					                    _autoscroll = false;
 | 
				
			||||||
            if (sender is InputBar)
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                var inputValue = mainInput.Text;
 | 
					 | 
				
			||||||
                if(inputValue != null)
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    mainInput.Items = _suggestions.ProvideFileSystemSuggestions(inputValue)
 | 
					 | 
				
			||||||
                        .Prepend(new InputEntry(InputEntryType.Text, inputValue, inputValue));
 | 
					 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            }*/
 | 
					 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private void OnBigInputTextChanged(object sender, TextChangedEventArgs e)
 | 
					            // Content scroll event : auto-scroll eventually
 | 
				
			||||||
        {
 | 
					            if (_autoscroll && e.ExtentHeightChange != 0)
 | 
				
			||||||
            /*var mainInput = _viewModel.MainInput;
 | 
					            {   // Content changed and auto-scroll mode set
 | 
				
			||||||
            if (sender is InputBar)
 | 
					                // Autoscroll
 | 
				
			||||||
            {
 | 
					                scrollViewer.ScrollToVerticalOffset(scrollViewer.ExtentHeight);
 | 
				
			||||||
                var inputValue = mainInput.Text;
 | 
					 | 
				
			||||||
                if (inputValue != null)
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    mainInput.Items = _suggestions.ProvideFileSystemSuggestions(inputValue)
 | 
					 | 
				
			||||||
                        .Prepend(new InputEntry(InputEntryType.Text, inputValue, inputValue));
 | 
					 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            }*/
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        private void OnAssetInputTextChanged(object sender, TextChangedEventArgs e)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            /*var mainInput = _viewModel.MainInput;
 | 
					 | 
				
			||||||
            if (sender is InputBar)
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                var inputValue = mainInput.Text;
 | 
					 | 
				
			||||||
                if (inputValue != null)
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    mainInput.Items = _suggestions.ProvideFileSystemSuggestions(inputValue)
 | 
					 | 
				
			||||||
                        .Prepend(new InputEntry(InputEntryType.Text, inputValue, inputValue));
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            }*/
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -1,19 +0,0 @@
 | 
				
			|||||||
using System;
 | 
					 | 
				
			||||||
using System.Collections.Generic;
 | 
					 | 
				
			||||||
using System.Linq;
 | 
					 | 
				
			||||||
using System.Text;
 | 
					 | 
				
			||||||
using System.Threading.Tasks;
 | 
					 | 
				
			||||||
using TechnologyAssembler.Core.Assets;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
namespace HashCalculator.GUI
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
    public class ModManifest
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
        IReadOnlyCollection<AssetEntry> Entries;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        public ModManifest()
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            var x = Manifest.Load("");
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@ -18,6 +18,15 @@ namespace HashCalculator.GUI
 | 
				
			|||||||
            return FastHash.GetHashCode(content);
 | 
					            return FastHash.GetHashCode(content);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public static uint CalculateLauncherBinaryHash(byte[] content)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            if (content == null)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                throw new ArgumentNullException($"{nameof(content)} is null");
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            return FastHash.GetHashCodeLauncher(0, content);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public static uint CalculateBinaryHash(string content)
 | 
					        public static uint CalculateBinaryHash(string content)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            if (content == null)
 | 
					            if (content == null)
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										262
									
								
								ViewModel.cs
									
									
									
									
									
								
							
							
						
						
									
										262
									
								
								ViewModel.cs
									
									
									
									
									
								
							@ -1,5 +1,6 @@
 | 
				
			|||||||
using System;
 | 
					using System;
 | 
				
			||||||
using System.Collections.Generic;
 | 
					using System.Collections.Generic;
 | 
				
			||||||
 | 
					using System.Collections.ObjectModel;
 | 
				
			||||||
using System.ComponentModel;
 | 
					using System.ComponentModel;
 | 
				
			||||||
using System.Diagnostics.CodeAnalysis;
 | 
					using System.Diagnostics.CodeAnalysis;
 | 
				
			||||||
using System.IO;
 | 
					using System.IO;
 | 
				
			||||||
@ -46,74 +47,52 @@ namespace HashCalculator.GUI
 | 
				
			|||||||
    [SuppressMessage("Microsoft.Performance", "CA1812")]
 | 
					    [SuppressMessage("Microsoft.Performance", "CA1812")]
 | 
				
			||||||
    internal class ViewModel : NotifyPropertyChanged
 | 
					    internal class ViewModel : NotifyPropertyChanged
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        public MainInputViewModel MainInput { get; } = new MainInputViewModel();
 | 
					        private static readonly Random _random = new Random();
 | 
				
			||||||
        public BigInputViewModel BigEntryInput { get; } = new BigInputViewModel();
 | 
					 | 
				
			||||||
        public InputBarViewModel AssetIdInput { get; } = new InputBarViewModel();
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private Visibility _bigInputVisibility = Visibility.Collapsed;
 | 
					        public MainInputViewModel MainInput { get; }
 | 
				
			||||||
        public Visibility BigInputVisibility
 | 
					        public BigInputViewModel BigEntryInput { get; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        private ObservableCollection<DisplayAssetEntry> _entries = new ObservableCollection<DisplayAssetEntry>();
 | 
				
			||||||
 | 
					        public ObservableCollection<DisplayAssetEntry> Entries
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            get => _bigInputVisibility;
 | 
					            get => _entries;
 | 
				
			||||||
            set => SetField(ref _bigInputVisibility, value);
 | 
					            set => SetField(ref _entries, value);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private InputEntry? _mainInputResult; // if not null
 | 
					        private string _statusText = SuggestionString("不知道该显示些什么呢……");
 | 
				
			||||||
        private string? _bigManifest;
 | 
					        public string StatusText
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            get => _statusText;
 | 
				
			||||||
 | 
					            set => SetField(ref _statusText, value);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        private string _traceText = string.Empty;
 | 
				
			||||||
 | 
					        public string TraceText
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            get => _traceText;
 | 
				
			||||||
 | 
					            set => SetField(ref _traceText, value);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public ViewModel()
 | 
					        public ViewModel()
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
 | 
					            var controller = new Controller(this);
 | 
				
			||||||
 | 
					            MainInput = new MainInputViewModel(controller);
 | 
				
			||||||
 | 
					            BigEntryInput = new BigInputViewModel(controller);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        [SuppressMessage("Design", "CA1031:不捕获常规异常类型", Justification = "<挂起>")]
 | 
					        public static string SuggestionString(string original)
 | 
				
			||||||
        public async Task OnMainInputDecided(InputEntry selected)
 | 
					 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            BigInputVisibility = Visibility.Collapsed;
 | 
					            var generated = $"{_random.NextDouble()}";
 | 
				
			||||||
            switch (selected.Type)
 | 
					            System.Diagnostics.Debug.WriteLine(generated);
 | 
				
			||||||
 | 
					            if (generated.Contains("38") || generated.Contains("16"))
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                case InputEntryType.BinaryFile:
 | 
					                return "你们都是喂鱼的马甲!(";
 | 
				
			||||||
                    CopyableBox.ShowDialog(async cancel =>
 | 
					 | 
				
			||||||
                    {
 | 
					 | 
				
			||||||
                        try
 | 
					 | 
				
			||||||
                        {
 | 
					 | 
				
			||||||
                            var hash = await Task.Run(() =>
 | 
					 | 
				
			||||||
                            {
 | 
					 | 
				
			||||||
                                return SageHash.CalculateBinaryHash(File.ReadAllBytes(selected.Value));
 | 
					 | 
				
			||||||
                            }).ConfigureAwait(false);
 | 
					 | 
				
			||||||
                            return $"使用Sage Hash计算出的哈希值:{hash:X8} (十进制 {hash})\r\n"
 | 
					 | 
				
			||||||
                                + "注意这是以大小写敏感模式计算出的哈希值,与素材ID(大小写不敏感)的哈希其实并不完全一样";
 | 
					 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
                        catch (Exception exception)
 | 
					            if (generated.IndexOf('2') == 2)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                            return exception.ToString();
 | 
					                return "本来以为两小时就能写完这个小工具,没想到写了两个星期,开始怀疑自己的智商orz";
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
                    });
 | 
					            return original;
 | 
				
			||||||
                    break;
 | 
					 | 
				
			||||||
                case InputEntryType.BigFile:
 | 
					 | 
				
			||||||
                    BigInputVisibility = Visibility.Visible;
 | 
					 | 
				
			||||||
                    throw new NotImplementedException();
 | 
					 | 
				
			||||||
                case InputEntryType.ManifestFile:
 | 
					 | 
				
			||||||
                    await ProcessManifest(selected).ConfigureAwait(true);
 | 
					 | 
				
			||||||
                    break;
 | 
					 | 
				
			||||||
                case InputEntryType.XmlFile:
 | 
					 | 
				
			||||||
                    throw new NotImplementedException();
 | 
					 | 
				
			||||||
                default:
 | 
					 | 
				
			||||||
                    throw new NotSupportedException();
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        public async Task ProcessManifest(InputEntry entry)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            var content = await Task.Run(() =>
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                using var stream = File.OpenRead(entry.Value);
 | 
					 | 
				
			||||||
                return new ManifestContent(entry.Text, stream);
 | 
					 | 
				
			||||||
            }).ConfigureAwait(true);
 | 
					 | 
				
			||||||
            await ProcessManifest(content).ConfigureAwait(true);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        public async Task ProcessManifest(ManifestContent content)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            throw new NotImplementedException();
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -133,6 +112,13 @@ namespace HashCalculator.GUI
 | 
				
			|||||||
            set => SetField(ref _selectedItem, value);
 | 
					            set => SetField(ref _selectedItem, value);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        private int _selectedIndex;
 | 
				
			||||||
 | 
					        public virtual int SelectedIndex
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            get => _selectedIndex;
 | 
				
			||||||
 | 
					            set => SetField(ref _selectedIndex, value);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private string? _text;
 | 
					        private string? _text;
 | 
				
			||||||
        public virtual string? Text
 | 
					        public virtual string? Text
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
@ -145,10 +131,9 @@ namespace HashCalculator.GUI
 | 
				
			|||||||
    {
 | 
					    {
 | 
				
			||||||
        private readonly FileSystemSuggestions _suggestions = new FileSystemSuggestions();
 | 
					        private readonly FileSystemSuggestions _suggestions = new FileSystemSuggestions();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private int _selectedIndex;
 | 
					        public override int SelectedIndex
 | 
				
			||||||
        public int SelectedIndex
 | 
					 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            get => _selectedIndex;
 | 
					            get => base.SelectedIndex;
 | 
				
			||||||
            set
 | 
					            set
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                if (value == 0)
 | 
					                if (value == 0)
 | 
				
			||||||
@ -159,10 +144,10 @@ namespace HashCalculator.GUI
 | 
				
			|||||||
                    }
 | 
					                    }
 | 
				
			||||||
                    else
 | 
					                    else
 | 
				
			||||||
                    {
 | 
					                    {
 | 
				
			||||||
                        value = _selectedIndex < value ? 1 : -1;
 | 
					                        value = base.SelectedIndex < value ? 1 : -1;
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                SetField(ref _selectedIndex, value);
 | 
					                base.SelectedIndex = value;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -182,7 +167,7 @@ namespace HashCalculator.GUI
 | 
				
			|||||||
        public Command<MainWindow> BrowseCommand { get; }
 | 
					        public Command<MainWindow> BrowseCommand { get; }
 | 
				
			||||||
        public Command<ViewModel> SelectCommand { get; }
 | 
					        public Command<ViewModel> SelectCommand { get; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public MainInputViewModel()
 | 
					        public MainInputViewModel(Controller controller)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            Items = Enumerable.Empty<InputEntry>();
 | 
					            Items = Enumerable.Empty<InputEntry>();
 | 
				
			||||||
            BrowseCommand = new Command<MainWindow>(window =>
 | 
					            BrowseCommand = new Command<MainWindow>(window =>
 | 
				
			||||||
@ -200,7 +185,7 @@ namespace HashCalculator.GUI
 | 
				
			|||||||
            });
 | 
					            });
 | 
				
			||||||
            SelectCommand = new Command<ViewModel>(async viewModel =>
 | 
					            SelectCommand = new Command<ViewModel>(async viewModel =>
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                if(SelectedItem?.IsValid != true)
 | 
					                if (SelectedItem?.IsValid != true)
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    return;
 | 
					                    return;
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
@ -209,7 +194,7 @@ namespace HashCalculator.GUI
 | 
				
			|||||||
                {
 | 
					                {
 | 
				
			||||||
                    BrowseCommand.CanExecuteValue = false;
 | 
					                    BrowseCommand.CanExecuteValue = false;
 | 
				
			||||||
                    SelectCommand.CanExecuteValue = false;
 | 
					                    SelectCommand.CanExecuteValue = false;
 | 
				
			||||||
                    await viewModel.OnMainInputDecided(SelectedItem).ConfigureAwait(true);
 | 
					                    await controller.OnMainInputDecided(SelectedItem).ConfigureAwait(true);
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                finally
 | 
					                finally
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
@ -246,102 +231,105 @@ namespace HashCalculator.GUI
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    [SuppressMessage("Design", "CA1001:具有可释放字段的类型应该是可释放的", Justification = "<挂起>")]
 | 
					    internal class BigInputViewModel : InputBarViewModel
 | 
				
			||||||
    internal class BigInputViewModel : NotifyPropertyChanged
 | 
					 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        private CancellationTokenSource? _currentCancellator = null;
 | 
					        public override IEnumerable<InputEntry>? Items
 | 
				
			||||||
 | 
					 | 
				
			||||||
        private IEnumerable<ManifestContent>? _manifests;
 | 
					 | 
				
			||||||
        public IEnumerable<ManifestContent>? Manifests
 | 
					 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            get => _manifests;
 | 
					            get => base.Items;
 | 
				
			||||||
            set => SetField(ref _manifests, value);
 | 
					            set => base.Items = value;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private ManifestContent? _selectedManifest;
 | 
					        public override InputEntry? SelectedItem
 | 
				
			||||||
        public ManifestContent? SelectedManifest
 | 
					 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            get => _selectedManifest;
 | 
					            get => base.SelectedItem;
 | 
				
			||||||
            set
 | 
					            set
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                SetField(ref _selectedManifest, value);
 | 
					                base.SelectedItem = value;
 | 
				
			||||||
                SelectCommand.CanExecuteValue = value != null;
 | 
					                SelectCommand.CanExecuteValue = value != null;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public override string? Text
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            get => base.Text;
 | 
				
			||||||
 | 
					            set
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                base.Text = value;
 | 
				
			||||||
 | 
					                if (value != SelectedItem?.ToString())
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    UpdateList(value);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        private InputEntry? _lastProcessedManifest;
 | 
				
			||||||
 | 
					        public InputEntry? LastProcessedManifest
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            get => _lastProcessedManifest;
 | 
				
			||||||
 | 
					            set => SetField(ref _lastProcessedManifest, value);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        private IEnumerable<InputEntry>? _allManifests;
 | 
				
			||||||
 | 
					        public IEnumerable<InputEntry>? AllManifests
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            get => _allManifests;
 | 
				
			||||||
 | 
					            set
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                SetField(ref _allManifests, value);
 | 
				
			||||||
 | 
					                Items = value;
 | 
				
			||||||
 | 
					                LastProcessedManifest = null;
 | 
				
			||||||
 | 
					                Text = null;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public Command<ViewModel> SelectCommand { get; }
 | 
					        public Command<ViewModel> SelectCommand { get; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public BigInputViewModel()
 | 
					        [SuppressMessage("Design", "CA1031:不捕获常规异常类型", Justification = "<挂起>")]
 | 
				
			||||||
 | 
					        public BigInputViewModel(Controller controller)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            SelectCommand = new Command<ViewModel>(viewModel => viewModel.ProcessManifest(SelectedManifest!));
 | 
					            SelectCommand = new Command<ViewModel>(async viewModel =>
 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        public async Task LoadBig(string path)
 | 
					 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
            using var cancellator = _currentCancellator = new CancellationTokenSource();
 | 
					                var mainInput = viewModel.MainInput;
 | 
				
			||||||
            var saved = Interlocked.Exchange(ref _currentCancellator, cancellator);
 | 
					 | 
				
			||||||
            saved?.Cancel();
 | 
					 | 
				
			||||||
            
 | 
					 | 
				
			||||||
            var token = cancellator.Token;
 | 
					 | 
				
			||||||
                try
 | 
					                try
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                using var big = await Task.Run(() => new BigFile(path), token).ConfigureAwait(true);
 | 
					                    SelectCommand.CanExecuteValue = false;
 | 
				
			||||||
                var manifests = await Task.Run(() =>
 | 
					                    mainInput.BrowseCommand.CanExecuteValue = false;
 | 
				
			||||||
 | 
					                    mainInput.SelectCommand.CanExecuteValue = false;
 | 
				
			||||||
 | 
					                    LastProcessedManifest = SelectedItem;
 | 
				
			||||||
 | 
					                    await controller.ProcessManifest(SelectedItem!.Value).ConfigureAwait(true);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                catch (Exception exception)
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    var manifests = big.GetFiles(string.Empty, "*.manifest", VirtualSearchOptionType.AllDirectories);
 | 
					                    CopyableBox.ShowDialog(_ =>
 | 
				
			||||||
                    var modManifests = from manifest in manifests
 | 
					                    {
 | 
				
			||||||
                                       where FileNameStartsWith(manifest, "mod")
 | 
					                        return Task.FromResult($"在加载 manifest 时发生错误:{exception}");
 | 
				
			||||||
 | 
					                    });
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                finally
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    SelectCommand.CanExecuteValue = true;
 | 
				
			||||||
 | 
					                    mainInput.BrowseCommand.CanExecuteValue = true;
 | 
				
			||||||
 | 
					                    mainInput.SelectCommand.CanExecuteValue = true;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            })
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                CanExecuteValue = false
 | 
				
			||||||
 | 
					            };
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        private void UpdateList(string? input)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            input ??= string.Empty;
 | 
				
			||||||
 | 
					            input = input.Replace(VirtualFileSystem.AltDirectorySeparatorChar, VirtualFileSystem.DirectorySeparatorChar);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            var filtered = from manifest in AllManifests
 | 
				
			||||||
 | 
					                           where manifest.Value.IndexOf(input, StringComparison.OrdinalIgnoreCase) != -1
 | 
				
			||||||
                           select manifest;
 | 
					                           select manifest;
 | 
				
			||||||
                    var globalDataManifests = from manifest in manifests
 | 
					            Items = filtered;
 | 
				
			||||||
                                              where FileNameStartsWith(manifest, "mapmetadata")
 | 
					            if (Items.FirstOrDefault()?.Value.Equals(input, StringComparison.OrdinalIgnoreCase) == true)
 | 
				
			||||||
                                              select manifest;
 | 
					 | 
				
			||||||
                    var firstManifests = modManifests.Concat(globalDataManifests);
 | 
					 | 
				
			||||||
                    var otherManifests = from manifest in manifests
 | 
					 | 
				
			||||||
                                         where !firstManifests.Contains(manifest)
 | 
					 | 
				
			||||||
                                         select manifest;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                    var list = new List<ManifestContent>();
 | 
					 | 
				
			||||||
                    foreach (var path in firstManifests.Concat(otherManifests))
 | 
					 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                        token.ThrowIfCancellationRequested();
 | 
					                SelectedIndex = 0;
 | 
				
			||||||
                        using var stream = big.OpenStream(path);
 | 
					 | 
				
			||||||
                        list.Add(new ManifestContent(path, stream));
 | 
					 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
                    return list;
 | 
					 | 
				
			||||||
                }, token).ConfigureAwait(true);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                if (!token.IsCancellationRequested)
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    Manifests = manifests;
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            catch (OperationCanceledException) { }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            Interlocked.CompareExchange(ref _currentCancellator, null, cancellator);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        private static bool FileNameStartsWith(string path, string what)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            return Path.GetFileName(path).StartsWith(what, StringComparison.OrdinalIgnoreCase);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    internal class ManifestContent
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
        public string Name { get; }
 | 
					 | 
				
			||||||
        public byte[] Data { get; }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        public ManifestContent(string name, Stream stream)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            Name = name;
 | 
					 | 
				
			||||||
            using var memoryStream = new MemoryStream();
 | 
					 | 
				
			||||||
            stream.CopyTo(memoryStream);
 | 
					 | 
				
			||||||
            Data = memoryStream.ToArray();
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        public override string ToString()
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            return Name;
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user