From 1499aa4086826ba7f6f06e73fbe03cf5903e2f3b Mon Sep 17 00:00:00 2001 From: Aigio Liu Date: Sat, 14 May 2022 13:38:10 +0800 Subject: [PATCH] Add Steam++.Launcher.exe --- download-guide.md | 19 +- references/Steam++.Launcher.exe | Bin 0 -> 285184 bytes references/Steam++.Launcher.exe.config | 7 + src/FDELauncher/Compat/Contract.cs | 12 + src/FDELauncher/Compat/Enumerable2.cs | 17 + src/FDELauncher/Compat/ValueTuple.cs | 2287 +++++++++++++++++ src/FDELauncher/Program.cs | 49 +- src/FDELauncher/Properties/Resources.cs | 19 + .../LocalizableStrings.Designer.cs | 36 + .../LocalizableStrings.resx | 12 + .../Shared/BundleInfo/BundleType.cs | 3 +- .../WindowsDesktopRuntimeVersion.cs | 45 + .../Shared/Utils/RuntimeInfo.cs | 31 + .../VisualStudioSafeVersionsExtractor.cs | 125 + .../Windows/RegistryQuery.cs | 13 +- .../ST.Client.Android.App.Modern.csproj | 264 +- .../Steam++.csproj | 1 + .../ST.Client.Avalonia.App.MsixPackage.csproj | 3 +- .../ST.Client.Avalonia.App.csproj | 4 +- src/ST.Tools.Publish/Models/PublishDirInfo.cs | 52 +- src/ST.Tools.Publish/Steps/Step_cd.cs | 10 +- src/ST.Tools.Publish/Steps/Step_deb.cs | 2 +- src/ST.Tools.Publish/Steps/Step_rpm.cs | 2 +- src/ST.Tools.Publish/Steps/Step_start.cs | 5 +- src/ST.Tools.Publish/Utils.cs | 30 +- 25 files changed, 2883 insertions(+), 165 deletions(-) create mode 100644 references/Steam++.Launcher.exe create mode 100644 references/Steam++.Launcher.exe.config create mode 100644 src/FDELauncher/Compat/Contract.cs create mode 100644 src/FDELauncher/Compat/Enumerable2.cs create mode 100644 src/FDELauncher/Compat/ValueTuple.cs create mode 100644 src/FDELauncher/dotnet-core-uninstall/Shared/BundleInfo/Versioning/WindowsDesktopRuntimeVersion.cs create mode 100644 src/FDELauncher/dotnet-core-uninstall/Shared/Utils/RuntimeInfo.cs create mode 100644 src/FDELauncher/dotnet-core-uninstall/Shared/VSVersioning/VisualStudioSafeVersionsExtractor.cs diff --git a/download-guide.md b/download-guide.md index 720162b38a6..23424411ca3 100644 --- a/download-guide.md +++ b/download-guide.md @@ -1,13 +1,18 @@ # 下载指南 - Desktop(桌面端) - Windows - - 如果你使用 Intel、AMD 的 x64(x86-64) 芯片的 PC,则下载文件名中带有 **win_x64** 的文件 - - 如果你 **已安装** 了 [ASP.NET Core 运行时 6.0.5](https://dotnet.microsoft.com/zh-cn/download/dotnet/6.0) 则下载文件名中带有 **fde(框架依赖)** 的文件 - - 注意:是 《ASP.NET Core 运行时》 而不是 《.NET 桌面运行时》 - - [在 Windows 上安装 .NET](https://docs.microsoft.com/zh-cn/dotnet/core/install/windows) - - 本程序在 Windows 上不需要 Hosting Bundle 和 IIS support - - [下载安装程序并手动安装(aspnetcore-runtime-6.0.5-win-x64.exe)](https://dotnet.microsoft.com/zh-cn/download/dotnet/thank-you/runtime-aspnetcore-6.0.5-windows-x64-installer) - - [使用 PowerShell 自动化安装](https://docs.microsoft.com/zh-cn/dotnet/core/install/windows?tabs=net60#install-with-powershell-automation) + - 如果你使用 Intel、AMD 的 x64(x86-64/AMD64) 芯片的 PC,则下载文件名中带有 **win_x64** 的文件 + - **框架依赖版(framework-dependent executable / fde)** + - 需要 **《ASP.NET Core 运行时 6.0.x》** 与 **《.NET 运行时 6.0.x》**,如果你 **已安装** 了相关运行时,则下载文件名中带有 **fde** 的文件,可减少本应用占用的磁盘空间 + - 如果你 **未安装相关运行时则应下载文件名中不包含 fde 的文件**,或在 [此下载](https://dotnet.microsoft.com/zh-cn/download/dotnet/6.0) 最新的 6.0.x 运行时 + - 在网页上点击 安装程序 x64 的链接即可下载,通常下载的文件名如下,将其安装后即可,无安装顺序要求 + - dotnet-runtime-6.0.x-win-x64.exe (.NET 运行时 6.0.x) + - aspnetcore-runtime-6.0.x-win-x64.exe (ASP.NET Core 运行时 6.0.x) + - 其他也包含了所需的运行时的下载项,但不推荐仅为运行本应用而安装 + - Hosting Bundle 还包含了本应用不需要的 IIS 运行时支持 (ASP.NET Core Module v2) 与 x86 的多个运行库 + - SDK 包含了较多不需要的内容 + + - macOS - 如果你使用 Intel 的 x64(x86-64) 芯片的 Mac,则下载文件名中带有 **macos_x64** 的文件 - 如果你使用 ARM64(Apple Silicon) 芯片的 Mac,例如 **Apple M1**,则下载文件名中带有 **macos_arm64** 的文件 diff --git a/references/Steam++.Launcher.exe b/references/Steam++.Launcher.exe new file mode 100644 index 0000000000000000000000000000000000000000..e132b45e8f1ff3e3dafb98b54549d23e724b8303 GIT binary patch literal 285184 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%0AyC;rSUy8RB4-iTLAL!- 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 literal 0 HcmV?d00001 diff --git a/references/Steam++.Launcher.exe.config b/references/Steam++.Launcher.exe.config new file mode 100644 index 00000000000..02e633b6f66 --- /dev/null +++ b/references/Steam++.Launcher.exe.config @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/src/FDELauncher/Compat/Contract.cs b/src/FDELauncher/Compat/Contract.cs new file mode 100644 index 00000000000..f1386d27c58 --- /dev/null +++ b/src/FDELauncher/Compat/Contract.cs @@ -0,0 +1,12 @@ +namespace System.Diagnostics.Contracts +{ + public static class Contract + { + [Conditional("DEBUG")] + [Conditional("CONTRACTS_FULL")] + public static void Assert(bool condition, string? userMessage) + { + + } + } +} diff --git a/src/FDELauncher/Compat/Enumerable2.cs b/src/FDELauncher/Compat/Enumerable2.cs new file mode 100644 index 00000000000..bf75c5879fe --- /dev/null +++ b/src/FDELauncher/Compat/Enumerable2.cs @@ -0,0 +1,17 @@ +using System.Collections.Generic; + +namespace System.Linq +{ + public static class Enumerable2 + { + public static IEnumerable Append(this IEnumerable collection, T item) // https://github.com/iamandylamb/IEnumerable.Append/blob/master/src/Extension.cs + { + if (collection == null) + { + throw new ArgumentNullException("Collection should not be null"); + } + + return collection.Concat(Enumerable.Repeat(item, 1)); + } + } +} diff --git a/src/FDELauncher/Compat/ValueTuple.cs b/src/FDELauncher/Compat/ValueTuple.cs new file mode 100644 index 00000000000..56e119a8ae7 --- /dev/null +++ b/src/FDELauncher/Compat/ValueTuple.cs @@ -0,0 +1,2287 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. +// https://github.com/dotnet/roslyn/blob/main/src/Compilers/Test/Resources/Core/NetFX/ValueTuple/ValueTuple.cs + +namespace System.Numerics.Hashing +{ + internal static class HashHelpers + { + public static readonly int RandomSeed = new Random().Next(Int32.MinValue, Int32.MaxValue); + + public static int Combine(int h1, int h2) + { + // RyuJIT optimizes this to use the ROL instruction + // Related GitHub pull request: dotnet/coreclr#1830 + var rol5 = ((uint)h1 << 5) | ((uint)h1 >> 27); + return ((int)rol5 + h1) ^ h2; + } + } +} + +namespace System +{ + using System.Collections; + using System.Collections.Generic; + using System.Diagnostics.Contracts; + using System.Runtime.InteropServices; + using System.Runtime.CompilerServices; + using HashHelpers = Numerics.Hashing.HashHelpers; + + /// + /// Helper so we can call some tuple methods recursively without knowing the underlying types. + /// + internal interface IValueTupleInternal : ITuple + { + int GetHashCode(IEqualityComparer comparer); + string ToStringEnd(); + } + + /// + /// The ValueTuple types (from arity 0 to 8) comprise the runtime implementation that underlies tuples in C# and struct tuples in F#. + /// Aside from created via language syntax, they are most easily created via the ValueTuple.Create factory methods. + /// The System.ValueTuple types differ from the System.Tuple types in that: + /// - they are structs rather than classes, + /// - they are mutable rather than readonly, and + /// - their members (such as Item1, Item2, etc) are fields rather than properties. + /// + [Serializable] + public struct ValueTuple + : IEquatable, IStructuralEquatable, IStructuralComparable, IComparable, IComparable, IValueTupleInternal, ITuple + { + /// + /// Returns a value that indicates whether the current instance is equal to a specified object. + /// + /// The object to compare with this instance. + /// if is a . + public override bool Equals(object obj) + { + return obj is ValueTuple; + } + + /// Returns a value indicating whether this instance is equal to a specified value. + /// An instance to compare to this instance. + /// true if has the same value as this instance; otherwise, false. + public bool Equals(ValueTuple other) + { + return true; + } + + bool IStructuralEquatable.Equals(object? other, IEqualityComparer comparer) + { + return other is ValueTuple; + } + + int IComparable.CompareTo(object? other) + { + if (other == null) return 1; + + if (other is not ValueTuple) + { + throw new ArgumentException(); + } + + return 0; + } + + /// Compares this instance to a specified instance and returns an indication of their relative values. + /// An instance to compare. + /// + /// A signed number indicating the relative values of this instance and . + /// Returns less than zero if this instance is less than , zero if this + /// instance is equal to , and greater than zero if this instance is greater + /// than . + /// + public int CompareTo(ValueTuple other) + { + return 0; + } + + int IStructuralComparable.CompareTo(object? other, IComparer comparer) + { + if (other == null) return 1; + + if (other is not ValueTuple) + { + throw new ArgumentException(); + } + + return 0; + } + + /// Returns the hash code for this instance. + /// A 32-bit signed integer hash code. + public override int GetHashCode() + { + return 0; + } + + int IStructuralEquatable.GetHashCode(IEqualityComparer comparer) + { + return 0; + } + + int IValueTupleInternal.GetHashCode(IEqualityComparer comparer) + { + return 0; + } + + /// + /// Returns a string that represents the value of this instance. + /// + /// The string representation of this instance. + /// + /// The string returned by this method takes the form (). + /// + public override string ToString() + { + return "()"; + } + + string IValueTupleInternal.ToStringEnd() + { + return ")"; + } + + /// + /// The number of positions in this data structure. + /// + int ITuple.Length => 0; + + /// + /// Get the element at position . + /// + object? ITuple.this[int index] => throw new IndexOutOfRangeException(); + + /// Creates a new struct 0-tuple. + /// A 0-tuple. + public static ValueTuple Create() => + new ValueTuple(); + + /// Creates a new struct 1-tuple, or singleton. + /// The type of the first component of the tuple. + /// The value of the first component of the tuple. + /// A 1-tuple (singleton) whose value is (item1). + public static ValueTuple Create(T1 item1) => + new ValueTuple(item1); + + /// Creates a new struct 2-tuple, or pair. + /// The type of the first component of the tuple. + /// The type of the second component of the tuple. + /// The value of the first component of the tuple. + /// The value of the second component of the tuple. + /// A 2-tuple (pair) whose value is (item1, item2). + public static ValueTuple Create(T1 item1, T2 item2) => + new ValueTuple(item1, item2); + + /// Creates a new struct 3-tuple, or triple. + /// The type of the first component of the tuple. + /// The type of the second component of the tuple. + /// The type of the third component of the tuple. + /// The value of the first component of the tuple. + /// The value of the second component of the tuple. + /// The value of the third component of the tuple. + /// A 3-tuple (triple) whose value is (item1, item2, item3). + public static ValueTuple Create(T1 item1, T2 item2, T3 item3) => + new ValueTuple(item1, item2, item3); + + /// Creates a new struct 4-tuple, or quadruple. + /// The type of the first component of the tuple. + /// The type of the second component of the tuple. + /// The type of the third component of the tuple. + /// The type of the fourth component of the tuple. + /// The value of the first component of the tuple. + /// The value of the second component of the tuple. + /// The value of the third component of the tuple. + /// The value of the fourth component of the tuple. + /// A 4-tuple (quadruple) whose value is (item1, item2, item3, item4). + public static ValueTuple Create(T1 item1, T2 item2, T3 item3, T4 item4) => + new ValueTuple(item1, item2, item3, item4); + + /// Creates a new struct 5-tuple, or quintuple. + /// The type of the first component of the tuple. + /// The type of the second component of the tuple. + /// The type of the third component of the tuple. + /// The type of the fourth component of the tuple. + /// The type of the fifth component of the tuple. + /// The value of the first component of the tuple. + /// The value of the second component of the tuple. + /// The value of the third component of the tuple. + /// The value of the fourth component of the tuple. + /// The value of the fifth component of the tuple. + /// A 5-tuple (quintuple) whose value is (item1, item2, item3, item4, item5). + public static ValueTuple Create(T1 item1, T2 item2, T3 item3, T4 item4, T5 item5) => + new ValueTuple(item1, item2, item3, item4, item5); + + /// Creates a new struct 6-tuple, or sextuple. + /// The type of the first component of the tuple. + /// The type of the second component of the tuple. + /// The type of the third component of the tuple. + /// The type of the fourth component of the tuple. + /// The type of the fifth component of the tuple. + /// The type of the sixth component of the tuple. + /// The value of the first component of the tuple. + /// The value of the second component of the tuple. + /// The value of the third component of the tuple. + /// The value of the fourth component of the tuple. + /// The value of the fifth component of the tuple. + /// The value of the sixth component of the tuple. + /// A 6-tuple (sextuple) whose value is (item1, item2, item3, item4, item5, item6). + public static ValueTuple Create(T1 item1, T2 item2, T3 item3, T4 item4, T5 item5, T6 item6) => + new ValueTuple(item1, item2, item3, item4, item5, item6); + + /// Creates a new struct 7-tuple, or septuple. + /// The type of the first component of the tuple. + /// The type of the second component of the tuple. + /// The type of the third component of the tuple. + /// The type of the fourth component of the tuple. + /// The type of the fifth component of the tuple. + /// The type of the sixth component of the tuple. + /// The type of the seventh component of the tuple. + /// The value of the first component of the tuple. + /// The value of the second component of the tuple. + /// The value of the third component of the tuple. + /// The value of the fourth component of the tuple. + /// The value of the fifth component of the tuple. + /// The value of the sixth component of the tuple. + /// The value of the seventh component of the tuple. + /// A 7-tuple (septuple) whose value is (item1, item2, item3, item4, item5, item6, item7). + public static ValueTuple Create(T1 item1, T2 item2, T3 item3, T4 item4, T5 item5, T6 item6, T7 item7) => + new ValueTuple(item1, item2, item3, item4, item5, item6, item7); + + /// Creates a new struct 8-tuple, or octuple. + /// The type of the first component of the tuple. + /// The type of the second component of the tuple. + /// The type of the third component of the tuple. + /// The type of the fourth component of the tuple. + /// The type of the fifth component of the tuple. + /// The type of the sixth component of the tuple. + /// The type of the seventh component of the tuple. + /// The type of the eighth component of the tuple. + /// The value of the first component of the tuple. + /// The value of the second component of the tuple. + /// The value of the third component of the tuple. + /// The value of the fourth component of the tuple. + /// The value of the fifth component of the tuple. + /// The value of the sixth component of the tuple. + /// The value of the seventh component of the tuple. + /// The value of the eighth component of the tuple. + /// An 8-tuple (octuple) whose value is (item1, item2, item3, item4, item5, item6, item7, item8). + public static ValueTuple> Create(T1 item1, T2 item2, T3 item3, T4 item4, T5 item5, T6 item6, T7 item7, T8 item8) => + new ValueTuple>(item1, item2, item3, item4, item5, item6, item7, ValueTuple.Create(item8)); + + internal static int CombineHashCodes(int h1, int h2) + { + return HashHelpers.Combine(HashHelpers.Combine(HashHelpers.RandomSeed, h1), h2); + } + + internal static int CombineHashCodes(int h1, int h2, int h3) + { + return HashHelpers.Combine(CombineHashCodes(h1, h2), h3); + } + + internal static int CombineHashCodes(int h1, int h2, int h3, int h4) + { + return HashHelpers.Combine(CombineHashCodes(h1, h2, h3), h4); + } + + internal static int CombineHashCodes(int h1, int h2, int h3, int h4, int h5) + { + return HashHelpers.Combine(CombineHashCodes(h1, h2, h3, h4), h5); + } + + internal static int CombineHashCodes(int h1, int h2, int h3, int h4, int h5, int h6) + { + return HashHelpers.Combine(CombineHashCodes(h1, h2, h3, h4, h5), h6); + } + + internal static int CombineHashCodes(int h1, int h2, int h3, int h4, int h5, int h6, int h7) + { + return HashHelpers.Combine(CombineHashCodes(h1, h2, h3, h4, h5, h6), h7); + } + + internal static int CombineHashCodes(int h1, int h2, int h3, int h4, int h5, int h6, int h7, int h8) + { + return HashHelpers.Combine(CombineHashCodes(h1, h2, h3, h4, h5, h6, h7), h8); + } + } + + /// Represents a 1-tuple, or singleton, as a value type. + /// The type of the tuple's only component. + [Serializable] + public struct ValueTuple + : IEquatable>, IStructuralEquatable, IStructuralComparable, IComparable, IComparable>, IValueTupleInternal, ITuple + { + /// + /// The current instance's first component. + /// + public T1 Item1; + + /// + /// Initializes a new instance of the value type. + /// + /// The value of the tuple's first component. + public ValueTuple(T1 item1) + { + Item1 = item1; + } + + /// + /// Returns a value that indicates whether the current instance is equal to a specified object. + /// + /// The object to compare with this instance. + /// if the current instance is equal to the specified object; otherwise, . + /// + /// The parameter is considered to be equal to the current instance under the following conditions: + /// + /// It is a value type. + /// Its components are of the same types as those of the current instance. + /// Its components are equal to those of the current instance. Equality is determined by the default object equality comparer for each component. + /// + /// + public override bool Equals(object? obj) + { + return obj is ValueTuple tuple && Equals(tuple); + } + + /// + /// Returns a value that indicates whether the current + /// instance is equal to a specified . + /// + /// The tuple to compare with this instance. + /// if the current instance is equal to the specified tuple; otherwise, . + /// + /// The parameter is considered to be equal to the current instance if each of its field + /// is equal to that of the current instance, using the default comparer for that field's type. + /// + public bool Equals(ValueTuple other) + { + return EqualityComparer.Default.Equals(Item1, other.Item1); + } + + bool IStructuralEquatable.Equals(object? other, IEqualityComparer comparer) + { + if (other == null || other is not ValueTuple) return false; + + var objTuple = (ValueTuple)other; + + return comparer.Equals(Item1, objTuple.Item1); + } + + int IComparable.CompareTo(object? other) + { + if (other == null) return 1; + + if (other is not ValueTuple) + { + throw new ArgumentException(); + } + + var objTuple = (ValueTuple)other; + + return Comparer.Default.Compare(Item1, objTuple.Item1); + } + + /// Compares this instance to a specified instance and returns an indication of their relative values. + /// An instance to compare. + /// + /// A signed number indicating the relative values of this instance and . + /// Returns less than zero if this instance is less than , zero if this + /// instance is equal to , and greater than zero if this instance is greater + /// than . + /// + public int CompareTo(ValueTuple other) + { + return Comparer.Default.Compare(Item1, other.Item1); + } + + int IStructuralComparable.CompareTo(object? other, IComparer comparer) + { + if (other == null) return 1; + + if (other is not ValueTuple) + { + throw new ArgumentException(); + } + + var objTuple = (ValueTuple)other; + + return comparer.Compare(Item1, objTuple.Item1); + } + + /// + /// Returns the hash code for the current instance. + /// + /// A 32-bit signed integer hash code. + public override int GetHashCode() + { + return Item1?.GetHashCode() ?? 0; + } + + int IStructuralEquatable.GetHashCode(IEqualityComparer comparer) + { + return comparer.GetHashCode(Item1); + } + + int IValueTupleInternal.GetHashCode(IEqualityComparer comparer) + { + return comparer.GetHashCode(Item1); + } + + /// + /// Returns a string that represents the value of this instance. + /// + /// The string representation of this instance. + /// + /// The string returned by this method takes the form (Item1), + /// where Item1 represents the value of . If the field is , + /// it is represented as . + /// + public override string ToString() + { + return "(" + Item1?.ToString() + ")"; + } + + string IValueTupleInternal.ToStringEnd() + { + return Item1?.ToString() + ")"; + } + + /// + /// The number of positions in this data structure. + /// + int ITuple.Length => 1; + + /// + /// Get the element at position . + /// + object? ITuple.this[int index] + { + get + { + if (index != 0) + { + throw new IndexOutOfRangeException(); + } + return Item1; + } + } + } + + /// + /// Represents a 2-tuple, or pair, as a value type. + /// + /// The type of the tuple's first component. + /// The type of the tuple's second component. + [Serializable] + [StructLayout(LayoutKind.Auto)] + public struct ValueTuple + : IEquatable>, IStructuralEquatable, IStructuralComparable, IComparable, IComparable>, IValueTupleInternal, ITuple + { + /// + /// The current instance's first component. + /// + public T1 Item1; + + /// + /// The current instance's first component. + /// + public T2 Item2; + + /// + /// Initializes a new instance of the value type. + /// + /// The value of the tuple's first component. + /// The value of the tuple's second component. + public ValueTuple(T1 item1, T2 item2) + { + Item1 = item1; + Item2 = item2; + } + + /// + /// Returns a value that indicates whether the current instance is equal to a specified object. + /// + /// The object to compare with this instance. + /// if the current instance is equal to the specified object; otherwise, . + /// + /// + /// The parameter is considered to be equal to the current instance under the following conditions: + /// + /// It is a value type. + /// Its components are of the same types as those of the current instance. + /// Its components are equal to those of the current instance. Equality is determined by the default object equality comparer for each component. + /// + /// + public override bool Equals(object obj) + { + return obj is ValueTuple && Equals((ValueTuple)obj); + } + + /// + /// Returns a value that indicates whether the current instance is equal to a specified . + /// + /// The tuple to compare with this instance. + /// if the current instance is equal to the specified tuple; otherwise, . + /// + /// The parameter is considered to be equal to the current instance if each of its fields + /// are equal to that of the current instance, using the default comparer for that field's type. + /// + public bool Equals(ValueTuple other) + { + return EqualityComparer.Default.Equals(Item1, other.Item1) + && EqualityComparer.Default.Equals(Item2, other.Item2); + } + + /// + /// Returns a value that indicates whether the current instance is equal to a specified object based on a specified comparison method. + /// + /// The object to compare with this instance. + /// An object that defines the method to use to evaluate whether the two objects are equal. + /// if the current instance is equal to the specified object; otherwise, . + /// + /// + /// This member is an explicit interface member implementation. It can be used only when the + /// instance is cast to an interface. + /// + /// The implementation is called only if other is not , + /// and if it can be successfully cast (in C#) or converted (in Visual Basic) to a + /// whose components are of the same types as those of the current instance. The IStructuralEquatable.Equals(Object, IEqualityComparer) method + /// first passes the values of the objects to be compared to the + /// implementation. If this method call returns , the method is + /// called again and passed the values of the two instances. + /// + bool IStructuralEquatable.Equals(object? other, IEqualityComparer comparer) + { + if (other == null || other is not ValueTuple) return false; + + var objTuple = (ValueTuple)other; + + return comparer.Equals(Item1, objTuple.Item1) + && comparer.Equals(Item2, objTuple.Item2); + } + + int IComparable.CompareTo(object other) + { + if (other == null) return 1; + + if (other is not ValueTuple) + { + throw new ArgumentException(); + } + + return CompareTo((ValueTuple)other); + } + + /// Compares this instance to a specified instance and returns an indication of their relative values. + /// An instance to compare. + /// + /// A signed number indicating the relative values of this instance and . + /// Returns less than zero if this instance is less than , zero if this + /// instance is equal to , and greater than zero if this instance is greater + /// than . + /// + public int CompareTo(ValueTuple other) + { + var c = Comparer.Default.Compare(Item1, other.Item1); + if (c != 0) return c; + + return Comparer.Default.Compare(Item2, other.Item2); + } + + int IStructuralComparable.CompareTo(object? other, IComparer comparer) + { + if (other == null) return 1; + + if (other is not ValueTuple) + { + throw new ArgumentException(); + } + + var objTuple = (ValueTuple)other; + + var c = comparer.Compare(Item1, objTuple.Item1); + if (c != 0) return c; + + return comparer.Compare(Item2, objTuple.Item2); + } + + /// + /// Returns the hash code for the current instance. + /// + /// A 32-bit signed integer hash code. + public override int GetHashCode() + { + return ValueTuple.CombineHashCodes(Item1?.GetHashCode() ?? 0, + Item2?.GetHashCode() ?? 0); + } + + int IStructuralEquatable.GetHashCode(IEqualityComparer comparer) + { + return GetHashCodeCore(comparer); + } + + private int GetHashCodeCore(IEqualityComparer comparer) + { + return ValueTuple.CombineHashCodes(comparer.GetHashCode(Item1), + comparer.GetHashCode(Item2)); + } + + int IValueTupleInternal.GetHashCode(IEqualityComparer comparer) + { + return GetHashCodeCore(comparer); + } + + /// + /// Returns a string that represents the value of this instance. + /// + /// The string representation of this instance. + /// + /// The string returned by this method takes the form (Item1, Item2), + /// where Item1 and Item2 represent the values of the + /// and fields. If either field value is , + /// it is represented as . + /// + public override string ToString() + { + return "(" + Item1?.ToString() + ", " + Item2?.ToString() + ")"; + } + + string IValueTupleInternal.ToStringEnd() + { + return Item1?.ToString() + ", " + Item2?.ToString() + ")"; + } + + /// + /// The number of positions in this data structure. + /// + int ITuple.Length => 2; + + /// + /// Get the element at position . + /// + object? ITuple.this[int index] => index switch + { + 0 => Item1, + 1 => Item2, + _ => throw new IndexOutOfRangeException(), + }; + } + + /// + /// Represents a 3-tuple, or triple, as a value type. + /// + /// The type of the tuple's first component. + /// The type of the tuple's second component. + /// The type of the tuple's third component. + [Serializable] + [StructLayout(LayoutKind.Auto)] + public struct ValueTuple + : IEquatable>, IStructuralEquatable, IStructuralComparable, IComparable, IComparable>, IValueTupleInternal, ITuple + { + /// + /// The current instance's first component. + /// + public T1 Item1; + /// + /// The current instance's second component. + /// + public T2 Item2; + /// + /// The current instance's third component. + /// + public T3 Item3; + + /// + /// Initializes a new instance of the value type. + /// + /// The value of the tuple's first component. + /// The value of the tuple's second component. + /// The value of the tuple's third component. + public ValueTuple(T1 item1, T2 item2, T3 item3) + { + Item1 = item1; + Item2 = item2; + Item3 = item3; + } + + /// + /// Returns a value that indicates whether the current instance is equal to a specified object. + /// + /// The object to compare with this instance. + /// if the current instance is equal to the specified object; otherwise, . + /// + /// The parameter is considered to be equal to the current instance under the following conditions: + /// + /// It is a value type. + /// Its components are of the same types as those of the current instance. + /// Its components are equal to those of the current instance. Equality is determined by the default object equality comparer for each component. + /// + /// + public override bool Equals(object obj) + { + return obj is ValueTuple && Equals((ValueTuple)obj); + } + + /// + /// Returns a value that indicates whether the current + /// instance is equal to a specified . + /// + /// The tuple to compare with this instance. + /// if the current instance is equal to the specified tuple; otherwise, . + /// + /// The parameter is considered to be equal to the current instance if each of its fields + /// are equal to that of the current instance, using the default comparer for that field's type. + /// + public bool Equals(ValueTuple other) + { + return EqualityComparer.Default.Equals(Item1, other.Item1) + && EqualityComparer.Default.Equals(Item2, other.Item2) + && EqualityComparer.Default.Equals(Item3, other.Item3); + } + + bool IStructuralEquatable.Equals(object? other, IEqualityComparer comparer) + { + if (other == null || other is not ValueTuple) return false; + + var objTuple = (ValueTuple)other; + + return comparer.Equals(Item1, objTuple.Item1) + && comparer.Equals(Item2, objTuple.Item2) + && comparer.Equals(Item3, objTuple.Item3); + } + + int IComparable.CompareTo(object other) + { + if (other == null) return 1; + + if (other is not ValueTuple) + { + throw new ArgumentException(); + } + + return CompareTo((ValueTuple)other); + } + + /// Compares this instance to a specified instance and returns an indication of their relative values. + /// An instance to compare. + /// + /// A signed number indicating the relative values of this instance and . + /// Returns less than zero if this instance is less than , zero if this + /// instance is equal to , and greater than zero if this instance is greater + /// than . + /// + public int CompareTo(ValueTuple other) + { + var c = Comparer.Default.Compare(Item1, other.Item1); + if (c != 0) return c; + + c = Comparer.Default.Compare(Item2, other.Item2); + if (c != 0) return c; + + return Comparer.Default.Compare(Item3, other.Item3); + } + + int IStructuralComparable.CompareTo(object? other, IComparer comparer) + { + if (other == null) return 1; + + if (other is not ValueTuple) + { + throw new ArgumentException(); + } + + var objTuple = (ValueTuple)other; + + var c = comparer.Compare(Item1, objTuple.Item1); + if (c != 0) return c; + + c = comparer.Compare(Item2, objTuple.Item2); + if (c != 0) return c; + + return comparer.Compare(Item3, objTuple.Item3); + } + + /// + /// Returns the hash code for the current instance. + /// + /// A 32-bit signed integer hash code. + public override int GetHashCode() + { + return ValueTuple.CombineHashCodes(Item1?.GetHashCode() ?? 0, + Item2?.GetHashCode() ?? 0, + Item3?.GetHashCode() ?? 0); + } + + int IStructuralEquatable.GetHashCode(IEqualityComparer comparer) + { + return GetHashCodeCore(comparer); + } + + private int GetHashCodeCore(IEqualityComparer comparer) + { + return ValueTuple.CombineHashCodes(comparer.GetHashCode(Item1), + comparer.GetHashCode(Item2), + comparer.GetHashCode(Item3)); + } + + int IValueTupleInternal.GetHashCode(IEqualityComparer comparer) + { + return GetHashCodeCore(comparer); + } + + /// + /// Returns a string that represents the value of this instance. + /// + /// The string representation of this instance. + /// + /// The string returned by this method takes the form (Item1, Item2, Item3). + /// If any field value is , it is represented as . + /// + public override string ToString() + { + return "(" + Item1?.ToString() + ", " + Item2?.ToString() + ", " + Item3?.ToString() + ")"; + } + + string IValueTupleInternal.ToStringEnd() + { + return Item1?.ToString() + ", " + Item2?.ToString() + ", " + Item3?.ToString() + ")"; + } + + /// + /// The number of positions in this data structure. + /// + int ITuple.Length => 3; + + /// + /// Get the element at position . + /// + object? ITuple.this[int index] => index switch + { + 0 => Item1, + 1 => Item2, + 2 => Item3, + _ => throw new IndexOutOfRangeException(), + }; + } + + /// + /// Represents a 4-tuple, or quadruple, as a value type. + /// + /// The type of the tuple's first component. + /// The type of the tuple's second component. + /// The type of the tuple's third component. + /// The type of the tuple's fourth component. + [Serializable] + [StructLayout(LayoutKind.Auto)] + public struct ValueTuple + : IEquatable>, IStructuralEquatable, IStructuralComparable, IComparable, IComparable>, IValueTupleInternal, ITuple + { + /// + /// The current instance's first component. + /// + public T1 Item1; + /// + /// The current instance's second component. + /// + public T2 Item2; + /// + /// The current instance's third component. + /// + public T3 Item3; + /// + /// The current instance's fourth component. + /// + public T4 Item4; + + /// + /// Initializes a new instance of the value type. + /// + /// The value of the tuple's first component. + /// The value of the tuple's second component. + /// The value of the tuple's third component. + /// The value of the tuple's fourth component. + public ValueTuple(T1 item1, T2 item2, T3 item3, T4 item4) + { + Item1 = item1; + Item2 = item2; + Item3 = item3; + Item4 = item4; + } + + /// + /// Returns a value that indicates whether the current instance is equal to a specified object. + /// + /// The object to compare with this instance. + /// if the current instance is equal to the specified object; otherwise, . + /// + /// The parameter is considered to be equal to the current instance under the following conditions: + /// + /// It is a value type. + /// Its components are of the same types as those of the current instance. + /// Its components are equal to those of the current instance. Equality is determined by the default object equality comparer for each component. + /// + /// + public override bool Equals(object obj) + { + return obj is ValueTuple && Equals((ValueTuple)obj); + } + + /// + /// Returns a value that indicates whether the current + /// instance is equal to a specified . + /// + /// The tuple to compare with this instance. + /// if the current instance is equal to the specified tuple; otherwise, . + /// + /// The parameter is considered to be equal to the current instance if each of its fields + /// are equal to that of the current instance, using the default comparer for that field's type. + /// + public bool Equals(ValueTuple other) + { + return EqualityComparer.Default.Equals(Item1, other.Item1) + && EqualityComparer.Default.Equals(Item2, other.Item2) + && EqualityComparer.Default.Equals(Item3, other.Item3) + && EqualityComparer.Default.Equals(Item4, other.Item4); + } + + bool IStructuralEquatable.Equals(object? other, IEqualityComparer comparer) + { + if (other == null || other is not ValueTuple) return false; + + var objTuple = (ValueTuple)other; + + return comparer.Equals(Item1, objTuple.Item1) + && comparer.Equals(Item2, objTuple.Item2) + && comparer.Equals(Item3, objTuple.Item3) + && comparer.Equals(Item4, objTuple.Item4); + } + + int IComparable.CompareTo(object other) + { + if (other == null) return 1; + + if (other is not ValueTuple) + { + throw new ArgumentException(); + } + + return CompareTo((ValueTuple)other); + } + + /// Compares this instance to a specified instance and returns an indication of their relative values. + /// An instance to compare. + /// + /// A signed number indicating the relative values of this instance and . + /// Returns less than zero if this instance is less than , zero if this + /// instance is equal to , and greater than zero if this instance is greater + /// than . + /// + public int CompareTo(ValueTuple other) + { + var c = Comparer.Default.Compare(Item1, other.Item1); + if (c != 0) return c; + + c = Comparer.Default.Compare(Item2, other.Item2); + if (c != 0) return c; + + c = Comparer.Default.Compare(Item3, other.Item3); + if (c != 0) return c; + + return Comparer.Default.Compare(Item4, other.Item4); + } + + int IStructuralComparable.CompareTo(object? other, IComparer comparer) + { + if (other == null) return 1; + + if (other is not ValueTuple) + { + throw new ArgumentException(); + } + + var objTuple = (ValueTuple)other; + + var c = comparer.Compare(Item1, objTuple.Item1); + if (c != 0) return c; + + c = comparer.Compare(Item2, objTuple.Item2); + if (c != 0) return c; + + c = comparer.Compare(Item3, objTuple.Item3); + if (c != 0) return c; + + return comparer.Compare(Item4, objTuple.Item4); + } + + /// + /// Returns the hash code for the current instance. + /// + /// A 32-bit signed integer hash code. + public override int GetHashCode() + { + return ValueTuple.CombineHashCodes(Item1?.GetHashCode() ?? 0, + Item2?.GetHashCode() ?? 0, + Item3?.GetHashCode() ?? 0, + Item4?.GetHashCode() ?? 0); + } + + int IStructuralEquatable.GetHashCode(IEqualityComparer comparer) + { + return GetHashCodeCore(comparer); + } + + private int GetHashCodeCore(IEqualityComparer comparer) + { + return ValueTuple.CombineHashCodes(comparer.GetHashCode(Item1), + comparer.GetHashCode(Item2), + comparer.GetHashCode(Item3), + comparer.GetHashCode(Item4)); + } + + int IValueTupleInternal.GetHashCode(IEqualityComparer comparer) + { + return GetHashCodeCore(comparer); + } + + /// + /// Returns a string that represents the value of this instance. + /// + /// The string representation of this instance. + /// + /// The string returned by this method takes the form (Item1, Item2, Item3, Item4). + /// If any field value is , it is represented as . + /// + public override string ToString() + { + return "(" + Item1?.ToString() + ", " + Item2?.ToString() + ", " + Item3?.ToString() + ", " + Item4?.ToString() + ")"; + } + + string IValueTupleInternal.ToStringEnd() + { + return Item1?.ToString() + ", " + Item2?.ToString() + ", " + Item3?.ToString() + ", " + Item4?.ToString() + ")"; + } + + /// + /// The number of positions in this data structure. + /// + int ITuple.Length => 4; + + /// + /// Get the element at position . + /// + object? ITuple.this[int index] => index switch + { + 0 => Item1, + 1 => Item2, + 2 => Item3, + 3 => Item4, + _ => throw new IndexOutOfRangeException(), + }; + } + + /// + /// Represents a 5-tuple, or quintuple, as a value type. + /// + /// The type of the tuple's first component. + /// The type of the tuple's second component. + /// The type of the tuple's third component. + /// The type of the tuple's fourth component. + /// The type of the tuple's fifth component. + [Serializable] + [StructLayout(LayoutKind.Auto)] + public struct ValueTuple + : IEquatable>, IStructuralEquatable, IStructuralComparable, IComparable, IComparable>, IValueTupleInternal, ITuple + { + /// + /// The current instance's first component. + /// + public T1 Item1; + /// + /// The current instance's second component. + /// + public T2 Item2; + /// + /// The current instance's third component. + /// + public T3 Item3; + /// + /// The current instance's fourth component. + /// + public T4 Item4; + /// + /// The current instance's fifth component. + /// + public T5 Item5; + + /// + /// Initializes a new instance of the value type. + /// + /// The value of the tuple's first component. + /// The value of the tuple's second component. + /// The value of the tuple's third component. + /// The value of the tuple's fourth component. + /// The value of the tuple's fifth component. + public ValueTuple(T1 item1, T2 item2, T3 item3, T4 item4, T5 item5) + { + Item1 = item1; + Item2 = item2; + Item3 = item3; + Item4 = item4; + Item5 = item5; + } + + /// + /// Returns a value that indicates whether the current instance is equal to a specified object. + /// + /// The object to compare with this instance. + /// if the current instance is equal to the specified object; otherwise, . + /// + /// The parameter is considered to be equal to the current instance under the following conditions: + /// + /// It is a value type. + /// Its components are of the same types as those of the current instance. + /// Its components are equal to those of the current instance. Equality is determined by the default object equality comparer for each component. + /// + /// + public override bool Equals(object obj) + { + return obj is ValueTuple && Equals((ValueTuple)obj); + } + + /// + /// Returns a value that indicates whether the current + /// instance is equal to a specified . + /// + /// The tuple to compare with this instance. + /// if the current instance is equal to the specified tuple; otherwise, . + /// + /// The parameter is considered to be equal to the current instance if each of its fields + /// are equal to that of the current instance, using the default comparer for that field's type. + /// + public bool Equals(ValueTuple other) + { + return EqualityComparer.Default.Equals(Item1, other.Item1) + && EqualityComparer.Default.Equals(Item2, other.Item2) + && EqualityComparer.Default.Equals(Item3, other.Item3) + && EqualityComparer.Default.Equals(Item4, other.Item4) + && EqualityComparer.Default.Equals(Item5, other.Item5); + } + + bool IStructuralEquatable.Equals(object? other, IEqualityComparer comparer) + { + if (other == null || other is not ValueTuple) return false; + + var objTuple = (ValueTuple)other; + + return comparer.Equals(Item1, objTuple.Item1) + && comparer.Equals(Item2, objTuple.Item2) + && comparer.Equals(Item3, objTuple.Item3) + && comparer.Equals(Item4, objTuple.Item4) + && comparer.Equals(Item5, objTuple.Item5); + } + + int IComparable.CompareTo(object other) + { + if (other == null) return 1; + + if (other is not ValueTuple) + { + throw new ArgumentException(); + } + + return CompareTo((ValueTuple)other); + } + + /// Compares this instance to a specified instance and returns an indication of their relative values. + /// An instance to compare. + /// + /// A signed number indicating the relative values of this instance and . + /// Returns less than zero if this instance is less than , zero if this + /// instance is equal to , and greater than zero if this instance is greater + /// than . + /// + public int CompareTo(ValueTuple other) + { + var c = Comparer.Default.Compare(Item1, other.Item1); + if (c != 0) return c; + + c = Comparer.Default.Compare(Item2, other.Item2); + if (c != 0) return c; + + c = Comparer.Default.Compare(Item3, other.Item3); + if (c != 0) return c; + + c = Comparer.Default.Compare(Item4, other.Item4); + if (c != 0) return c; + + return Comparer.Default.Compare(Item5, other.Item5); + } + + int IStructuralComparable.CompareTo(object? other, IComparer comparer) + { + if (other == null) return 1; + + if (other is not ValueTuple) + { + throw new ArgumentException(); + } + + var objTuple = (ValueTuple)other; + + var c = comparer.Compare(Item1, objTuple.Item1); + if (c != 0) return c; + + c = comparer.Compare(Item2, objTuple.Item2); + if (c != 0) return c; + + c = comparer.Compare(Item3, objTuple.Item3); + if (c != 0) return c; + + c = comparer.Compare(Item4, objTuple.Item4); + if (c != 0) return c; + + return comparer.Compare(Item5, objTuple.Item5); + } + + /// + /// Returns the hash code for the current instance. + /// + /// A 32-bit signed integer hash code. + public override int GetHashCode() + { + return ValueTuple.CombineHashCodes(Item1?.GetHashCode() ?? 0, + Item2?.GetHashCode() ?? 0, + Item3?.GetHashCode() ?? 0, + Item4?.GetHashCode() ?? 0, + Item5?.GetHashCode() ?? 0); + } + + int IStructuralEquatable.GetHashCode(IEqualityComparer comparer) + { + return GetHashCodeCore(comparer); + } + + private int GetHashCodeCore(IEqualityComparer comparer) + { + return ValueTuple.CombineHashCodes(comparer.GetHashCode(Item1), + comparer.GetHashCode(Item2), + comparer.GetHashCode(Item3), + comparer.GetHashCode(Item4), + comparer.GetHashCode(Item5)); + } + + int IValueTupleInternal.GetHashCode(IEqualityComparer comparer) + { + return GetHashCodeCore(comparer); + } + + /// + /// Returns a string that represents the value of this instance. + /// + /// The string representation of this instance. + /// + /// The string returned by this method takes the form (Item1, Item2, Item3, Item4, Item5). + /// If any field value is , it is represented as . + /// + public override string ToString() + { + return "(" + Item1?.ToString() + ", " + Item2?.ToString() + ", " + Item3?.ToString() + ", " + Item4?.ToString() + ", " + Item5?.ToString() + ")"; + } + + string IValueTupleInternal.ToStringEnd() + { + return Item1?.ToString() + ", " + Item2?.ToString() + ", " + Item3?.ToString() + ", " + Item4?.ToString() + ", " + Item5?.ToString() + ")"; + } + + /// + /// The number of positions in this data structure. + /// + int ITuple.Length => 5; + + /// + /// Get the element at position . + /// + object? ITuple.this[int index] => index switch + { + 0 => Item1, + 1 => Item2, + 2 => Item3, + 3 => Item4, + 4 => Item5, + _ => throw new IndexOutOfRangeException(), + }; + } + + /// + /// Represents a 6-tuple, or sixtuple, as a value type. + /// + /// The type of the tuple's first component. + /// The type of the tuple's second component. + /// The type of the tuple's third component. + /// The type of the tuple's fourth component. + /// The type of the tuple's fifth component. + /// The type of the tuple's sixth component. + [Serializable] + [StructLayout(LayoutKind.Auto)] + public struct ValueTuple + : IEquatable>, IStructuralEquatable, IStructuralComparable, IComparable, IComparable>, IValueTupleInternal, ITuple + { + /// + /// The current instance's first component. + /// + public T1 Item1; + /// + /// The current instance's second component. + /// + public T2 Item2; + /// + /// The current instance's third component. + /// + public T3 Item3; + /// + /// The current instance's fourth component. + /// + public T4 Item4; + /// + /// The current instance's fifth component. + /// + public T5 Item5; + /// + /// The current instance's sixth component. + /// + public T6 Item6; + + /// + /// Initializes a new instance of the value type. + /// + /// The value of the tuple's first component. + /// The value of the tuple's second component. + /// The value of the tuple's third component. + /// The value of the tuple's fourth component. + /// The value of the tuple's fifth component. + /// The value of the tuple's sixth component. + public ValueTuple(T1 item1, T2 item2, T3 item3, T4 item4, T5 item5, T6 item6) + { + Item1 = item1; + Item2 = item2; + Item3 = item3; + Item4 = item4; + Item5 = item5; + Item6 = item6; + } + + /// + /// Returns a value that indicates whether the current instance is equal to a specified object. + /// + /// The object to compare with this instance. + /// if the current instance is equal to the specified object; otherwise, . + /// + /// The parameter is considered to be equal to the current instance under the following conditions: + /// + /// It is a value type. + /// Its components are of the same types as those of the current instance. + /// Its components are equal to those of the current instance. Equality is determined by the default object equality comparer for each component. + /// + /// + public override bool Equals(object obj) + { + return obj is ValueTuple && Equals((ValueTuple)obj); + } + + /// + /// Returns a value that indicates whether the current + /// instance is equal to a specified . + /// + /// The tuple to compare with this instance. + /// if the current instance is equal to the specified tuple; otherwise, . + /// + /// The parameter is considered to be equal to the current instance if each of its fields + /// are equal to that of the current instance, using the default comparer for that field's type. + /// + public bool Equals(ValueTuple other) + { + return EqualityComparer.Default.Equals(Item1, other.Item1) + && EqualityComparer.Default.Equals(Item2, other.Item2) + && EqualityComparer.Default.Equals(Item3, other.Item3) + && EqualityComparer.Default.Equals(Item4, other.Item4) + && EqualityComparer.Default.Equals(Item5, other.Item5) + && EqualityComparer.Default.Equals(Item6, other.Item6); + } + + bool IStructuralEquatable.Equals(object? other, IEqualityComparer comparer) + { + if (other == null || other is not ValueTuple) return false; + + var objTuple = (ValueTuple)other; + + return comparer.Equals(Item1, objTuple.Item1) + && comparer.Equals(Item2, objTuple.Item2) + && comparer.Equals(Item3, objTuple.Item3) + && comparer.Equals(Item4, objTuple.Item4) + && comparer.Equals(Item5, objTuple.Item5) + && comparer.Equals(Item6, objTuple.Item6); + } + + int IComparable.CompareTo(object other) + { + if (other == null) return 1; + + if (other is not ValueTuple) + { + throw new ArgumentException(); + } + + return CompareTo((ValueTuple)other); + } + + /// Compares this instance to a specified instance and returns an indication of their relative values. + /// An instance to compare. + /// + /// A signed number indicating the relative values of this instance and . + /// Returns less than zero if this instance is less than , zero if this + /// instance is equal to , and greater than zero if this instance is greater + /// than . + /// + public int CompareTo(ValueTuple other) + { + var c = Comparer.Default.Compare(Item1, other.Item1); + if (c != 0) return c; + + c = Comparer.Default.Compare(Item2, other.Item2); + if (c != 0) return c; + + c = Comparer.Default.Compare(Item3, other.Item3); + if (c != 0) return c; + + c = Comparer.Default.Compare(Item4, other.Item4); + if (c != 0) return c; + + c = Comparer.Default.Compare(Item5, other.Item5); + if (c != 0) return c; + + return Comparer.Default.Compare(Item6, other.Item6); + } + + int IStructuralComparable.CompareTo(object? other, IComparer comparer) + { + if (other == null) return 1; + + if (other is not ValueTuple) + { + throw new ArgumentException(); + } + + var objTuple = (ValueTuple)other; + + var c = comparer.Compare(Item1, objTuple.Item1); + if (c != 0) return c; + + c = comparer.Compare(Item2, objTuple.Item2); + if (c != 0) return c; + + c = comparer.Compare(Item3, objTuple.Item3); + if (c != 0) return c; + + c = comparer.Compare(Item4, objTuple.Item4); + if (c != 0) return c; + + c = comparer.Compare(Item5, objTuple.Item5); + if (c != 0) return c; + + return comparer.Compare(Item6, objTuple.Item6); + } + + /// + /// Returns the hash code for the current instance. + /// + /// A 32-bit signed integer hash code. + public override int GetHashCode() + { + return ValueTuple.CombineHashCodes(Item1?.GetHashCode() ?? 0, + Item2?.GetHashCode() ?? 0, + Item3?.GetHashCode() ?? 0, + Item4?.GetHashCode() ?? 0, + Item5?.GetHashCode() ?? 0, + Item6?.GetHashCode() ?? 0); + } + + int IStructuralEquatable.GetHashCode(IEqualityComparer comparer) + { + return GetHashCodeCore(comparer); + } + + private int GetHashCodeCore(IEqualityComparer comparer) + { + return ValueTuple.CombineHashCodes(comparer.GetHashCode(Item1), + comparer.GetHashCode(Item2), + comparer.GetHashCode(Item3), + comparer.GetHashCode(Item4), + comparer.GetHashCode(Item5), + comparer.GetHashCode(Item6)); + } + + int IValueTupleInternal.GetHashCode(IEqualityComparer comparer) + { + return GetHashCodeCore(comparer); + } + + /// + /// Returns a string that represents the value of this instance. + /// + /// The string representation of this instance. + /// + /// The string returned by this method takes the form (Item1, Item2, Item3, Item4, Item5, Item6). + /// If any field value is , it is represented as . + /// + public override string ToString() + { + return "(" + Item1?.ToString() + ", " + Item2?.ToString() + ", " + Item3?.ToString() + ", " + Item4?.ToString() + ", " + Item5?.ToString() + ", " + Item6?.ToString() + ")"; + } + + string IValueTupleInternal.ToStringEnd() + { + return Item1?.ToString() + ", " + Item2?.ToString() + ", " + Item3?.ToString() + ", " + Item4?.ToString() + ", " + Item5?.ToString() + ", " + Item6?.ToString() + ")"; + } + + /// + /// The number of positions in this data structure. + /// + int ITuple.Length => 6; + + /// + /// Get the element at position . + /// + object? ITuple.this[int index] => index switch + { + 0 => Item1, + 1 => Item2, + 2 => Item3, + 3 => Item4, + 4 => Item5, + 5 => Item6, + _ => throw new IndexOutOfRangeException(), + }; + } + + /// + /// Represents a 7-tuple, or sentuple, as a value type. + /// + /// The type of the tuple's first component. + /// The type of the tuple's second component. + /// The type of the tuple's third component. + /// The type of the tuple's fourth component. + /// The type of the tuple's fifth component. + /// The type of the tuple's sixth component. + /// The type of the tuple's seventh component. + [Serializable] + [StructLayout(LayoutKind.Auto)] + public struct ValueTuple + : IEquatable>, IStructuralEquatable, IStructuralComparable, IComparable, IComparable>, IValueTupleInternal, ITuple + { + /// + /// The current instance's first component. + /// + public T1 Item1; + /// + /// The current instance's second component. + /// + public T2 Item2; + /// + /// The current instance's third component. + /// + public T3 Item3; + /// + /// The current instance's fourth component. + /// + public T4 Item4; + /// + /// The current instance's fifth component. + /// + public T5 Item5; + /// + /// The current instance's sixth component. + /// + public T6 Item6; + /// + /// The current instance's seventh component. + /// + public T7 Item7; + + /// + /// Initializes a new instance of the value type. + /// + /// The value of the tuple's first component. + /// The value of the tuple's second component. + /// The value of the tuple's third component. + /// The value of the tuple's fourth component. + /// The value of the tuple's fifth component. + /// The value of the tuple's sixth component. + /// The value of the tuple's seventh component. + public ValueTuple(T1 item1, T2 item2, T3 item3, T4 item4, T5 item5, T6 item6, T7 item7) + { + Item1 = item1; + Item2 = item2; + Item3 = item3; + Item4 = item4; + Item5 = item5; + Item6 = item6; + Item7 = item7; + } + + /// + /// Returns a value that indicates whether the current instance is equal to a specified object. + /// + /// The object to compare with this instance. + /// if the current instance is equal to the specified object; otherwise, . + /// + /// The parameter is considered to be equal to the current instance under the following conditions: + /// + /// It is a value type. + /// Its components are of the same types as those of the current instance. + /// Its components are equal to those of the current instance. Equality is determined by the default object equality comparer for each component. + /// + /// + public override bool Equals(object obj) + { + return obj is ValueTuple && Equals((ValueTuple)obj); + } + + /// + /// Returns a value that indicates whether the current + /// instance is equal to a specified . + /// + /// The tuple to compare with this instance. + /// if the current instance is equal to the specified tuple; otherwise, . + /// + /// The parameter is considered to be equal to the current instance if each of its fields + /// are equal to that of the current instance, using the default comparer for that field's type. + /// + public bool Equals(ValueTuple other) + { + return EqualityComparer.Default.Equals(Item1, other.Item1) + && EqualityComparer.Default.Equals(Item2, other.Item2) + && EqualityComparer.Default.Equals(Item3, other.Item3) + && EqualityComparer.Default.Equals(Item4, other.Item4) + && EqualityComparer.Default.Equals(Item5, other.Item5) + && EqualityComparer.Default.Equals(Item6, other.Item6) + && EqualityComparer.Default.Equals(Item7, other.Item7); + } + + bool IStructuralEquatable.Equals(object? other, IEqualityComparer comparer) + { + if (other == null || other is not ValueTuple) return false; + + var objTuple = (ValueTuple)other; + + return comparer.Equals(Item1, objTuple.Item1) + && comparer.Equals(Item2, objTuple.Item2) + && comparer.Equals(Item3, objTuple.Item3) + && comparer.Equals(Item4, objTuple.Item4) + && comparer.Equals(Item5, objTuple.Item5) + && comparer.Equals(Item6, objTuple.Item6) + && comparer.Equals(Item7, objTuple.Item7); + } + + int IComparable.CompareTo(object other) + { + if (other == null) return 1; + + if (other is not ValueTuple) + { + throw new ArgumentException(); + } + + return CompareTo((ValueTuple)other); + } + + /// Compares this instance to a specified instance and returns an indication of their relative values. + /// An instance to compare. + /// + /// A signed number indicating the relative values of this instance and . + /// Returns less than zero if this instance is less than , zero if this + /// instance is equal to , and greater than zero if this instance is greater + /// than . + /// + public int CompareTo(ValueTuple other) + { + var c = Comparer.Default.Compare(Item1, other.Item1); + if (c != 0) return c; + + c = Comparer.Default.Compare(Item2, other.Item2); + if (c != 0) return c; + + c = Comparer.Default.Compare(Item3, other.Item3); + if (c != 0) return c; + + c = Comparer.Default.Compare(Item4, other.Item4); + if (c != 0) return c; + + c = Comparer.Default.Compare(Item5, other.Item5); + if (c != 0) return c; + + c = Comparer.Default.Compare(Item6, other.Item6); + if (c != 0) return c; + + return Comparer.Default.Compare(Item7, other.Item7); + } + + int IStructuralComparable.CompareTo(object? other, IComparer comparer) + { + if (other == null) return 1; + + if (other is not ValueTuple) + { + throw new ArgumentException(); + } + + var objTuple = (ValueTuple)other; + + var c = comparer.Compare(Item1, objTuple.Item1); + if (c != 0) return c; + + c = comparer.Compare(Item2, objTuple.Item2); + if (c != 0) return c; + + c = comparer.Compare(Item3, objTuple.Item3); + if (c != 0) return c; + + c = comparer.Compare(Item4, objTuple.Item4); + if (c != 0) return c; + + c = comparer.Compare(Item5, objTuple.Item5); + if (c != 0) return c; + + c = comparer.Compare(Item6, objTuple.Item6); + if (c != 0) return c; + + return comparer.Compare(Item7, objTuple.Item7); + } + + /// + /// Returns the hash code for the current instance. + /// + /// A 32-bit signed integer hash code. + public override int GetHashCode() + { + return ValueTuple.CombineHashCodes(Item1?.GetHashCode() ?? 0, + Item2?.GetHashCode() ?? 0, + Item3?.GetHashCode() ?? 0, + Item4?.GetHashCode() ?? 0, + Item5?.GetHashCode() ?? 0, + Item6?.GetHashCode() ?? 0, + Item7?.GetHashCode() ?? 0); + } + + int IStructuralEquatable.GetHashCode(IEqualityComparer comparer) + { + return GetHashCodeCore(comparer); + } + + private int GetHashCodeCore(IEqualityComparer comparer) + { + return ValueTuple.CombineHashCodes(comparer.GetHashCode(Item1), + comparer.GetHashCode(Item2), + comparer.GetHashCode(Item3), + comparer.GetHashCode(Item4), + comparer.GetHashCode(Item5), + comparer.GetHashCode(Item6), + comparer.GetHashCode(Item7)); + } + + int IValueTupleInternal.GetHashCode(IEqualityComparer comparer) + { + return GetHashCodeCore(comparer); + } + + /// + /// Returns a string that represents the value of this instance. + /// + /// The string representation of this instance. + /// + /// The string returned by this method takes the form (Item1, Item2, Item3, Item4, Item5, Item6, Item7). + /// If any field value is , it is represented as . + /// + public override string ToString() + { + return "(" + Item1?.ToString() + ", " + Item2?.ToString() + ", " + Item3?.ToString() + ", " + Item4?.ToString() + ", " + Item5?.ToString() + ", " + Item6?.ToString() + ", " + Item7?.ToString() + ")"; + } + + string IValueTupleInternal.ToStringEnd() + { + return Item1?.ToString() + ", " + Item2?.ToString() + ", " + Item3?.ToString() + ", " + Item4?.ToString() + ", " + Item5?.ToString() + ", " + Item6?.ToString() + ", " + Item7?.ToString() + ")"; + } + + /// + /// The number of positions in this data structure. + /// + int ITuple.Length => 7; + + /// + /// Get the element at position . + /// + object? ITuple.this[int index] => index switch + { + 0 => Item1, + 1 => Item2, + 2 => Item3, + 3 => Item4, + 4 => Item5, + 5 => Item6, + 6 => Item7, + _ => throw new IndexOutOfRangeException(), + }; + } + + /// + /// Represents an 8-tuple, or octuple, as a value type. + /// + /// The type of the tuple's first component. + /// The type of the tuple's second component. + /// The type of the tuple's third component. + /// The type of the tuple's fourth component. + /// The type of the tuple's fifth component. + /// The type of the tuple's sixth component. + /// The type of the tuple's seventh component. + /// The type of the tuple's eighth component. + [Serializable] + [StructLayout(LayoutKind.Auto)] + public struct ValueTuple + : IEquatable>, IStructuralEquatable, IStructuralComparable, IComparable, IComparable>, IValueTupleInternal, ITuple + where TRest : struct + { + /// + /// The current instance's first component. + /// + public T1 Item1; + /// + /// The current instance's second component. + /// + public T2 Item2; + /// + /// The current instance's third component. + /// + public T3 Item3; + /// + /// The current instance's fourth component. + /// + public T4 Item4; + /// + /// The current instance's fifth component. + /// + public T5 Item5; + /// + /// The current instance's sixth component. + /// + public T6 Item6; + /// + /// The current instance's seventh component. + /// + public T7 Item7; + /// + /// The current instance's eighth component. + /// + public TRest Rest; + + /// + /// Initializes a new instance of the value type. + /// + /// The value of the tuple's first component. + /// The value of the tuple's second component. + /// The value of the tuple's third component. + /// The value of the tuple's fourth component. + /// The value of the tuple's fifth component. + /// The value of the tuple's sixth component. + /// The value of the tuple's seventh component. + /// The value of the tuple's eight component. + public ValueTuple(T1 item1, T2 item2, T3 item3, T4 item4, T5 item5, T6 item6, T7 item7, TRest rest) + { + if (rest is not IValueTupleInternal) + { + throw new ArgumentException(); + } + + Item1 = item1; + Item2 = item2; + Item3 = item3; + Item4 = item4; + Item5 = item5; + Item6 = item6; + Item7 = item7; + Rest = rest; + } + + /// + /// Returns a value that indicates whether the current instance is equal to a specified object. + /// + /// The object to compare with this instance. + /// if the current instance is equal to the specified object; otherwise, . + /// + /// The parameter is considered to be equal to the current instance under the following conditions: + /// + /// It is a value type. + /// Its components are of the same types as those of the current instance. + /// Its components are equal to those of the current instance. Equality is determined by the default object equality comparer for each component. + /// + /// + public override bool Equals(object obj) + { + return obj is ValueTuple && Equals((ValueTuple)obj); + } + + /// + /// Returns a value that indicates whether the current + /// instance is equal to a specified . + /// + /// The tuple to compare with this instance. + /// if the current instance is equal to the specified tuple; otherwise, . + /// + /// The parameter is considered to be equal to the current instance if each of its fields + /// are equal to that of the current instance, using the default comparer for that field's type. + /// + public bool Equals(ValueTuple other) + { + return EqualityComparer.Default.Equals(Item1, other.Item1) + && EqualityComparer.Default.Equals(Item2, other.Item2) + && EqualityComparer.Default.Equals(Item3, other.Item3) + && EqualityComparer.Default.Equals(Item4, other.Item4) + && EqualityComparer.Default.Equals(Item5, other.Item5) + && EqualityComparer.Default.Equals(Item6, other.Item6) + && EqualityComparer.Default.Equals(Item7, other.Item7) + && EqualityComparer.Default.Equals(Rest, other.Rest); + } + + bool IStructuralEquatable.Equals(object? other, IEqualityComparer comparer) + { + if (other == null || other is not ValueTuple) return false; + + var objTuple = (ValueTuple)other; + + return comparer.Equals(Item1, objTuple.Item1) + && comparer.Equals(Item2, objTuple.Item2) + && comparer.Equals(Item3, objTuple.Item3) + && comparer.Equals(Item4, objTuple.Item4) + && comparer.Equals(Item5, objTuple.Item5) + && comparer.Equals(Item6, objTuple.Item6) + && comparer.Equals(Item7, objTuple.Item7) + && comparer.Equals(Rest, objTuple.Rest); + } + + int IComparable.CompareTo(object other) + { + if (other == null) return 1; + + if (other is not ValueTuple) + { + throw new ArgumentException(); + } + + return CompareTo((ValueTuple)other); + } + + /// Compares this instance to a specified instance and returns an indication of their relative values. + /// An instance to compare. + /// + /// A signed number indicating the relative values of this instance and . + /// Returns less than zero if this instance is less than , zero if this + /// instance is equal to , and greater than zero if this instance is greater + /// than . + /// + public int CompareTo(ValueTuple other) + { + var c = Comparer.Default.Compare(Item1, other.Item1); + if (c != 0) return c; + + c = Comparer.Default.Compare(Item2, other.Item2); + if (c != 0) return c; + + c = Comparer.Default.Compare(Item3, other.Item3); + if (c != 0) return c; + + c = Comparer.Default.Compare(Item4, other.Item4); + if (c != 0) return c; + + c = Comparer.Default.Compare(Item5, other.Item5); + if (c != 0) return c; + + c = Comparer.Default.Compare(Item6, other.Item6); + if (c != 0) return c; + + c = Comparer.Default.Compare(Item7, other.Item7); + if (c != 0) return c; + + return Comparer.Default.Compare(Rest, other.Rest); + } + + int IStructuralComparable.CompareTo(object? other, IComparer comparer) + { + if (other == null) return 1; + + if (other is not ValueTuple) + { + throw new ArgumentException(); + } + + var objTuple = (ValueTuple)other; + + var c = comparer.Compare(Item1, objTuple.Item1); + if (c != 0) return c; + + c = comparer.Compare(Item2, objTuple.Item2); + if (c != 0) return c; + + c = comparer.Compare(Item3, objTuple.Item3); + if (c != 0) return c; + + c = comparer.Compare(Item4, objTuple.Item4); + if (c != 0) return c; + + c = comparer.Compare(Item5, objTuple.Item5); + if (c != 0) return c; + + c = comparer.Compare(Item6, objTuple.Item6); + if (c != 0) return c; + + c = comparer.Compare(Item7, objTuple.Item7); + if (c != 0) return c; + + return comparer.Compare(Rest, objTuple.Rest); + } + + /// + /// Returns the hash code for the current instance. + /// + /// A 32-bit signed integer hash code. + public override int GetHashCode() + { + // We want to have a limited hash in this case. We'll use the last 8 elements of the tuple + var rest = Rest as IValueTupleInternal; + if (rest == null) + { + return ValueTuple.CombineHashCodes(Item1?.GetHashCode() ?? 0, + Item2?.GetHashCode() ?? 0, + Item3?.GetHashCode() ?? 0, + Item4?.GetHashCode() ?? 0, + Item5?.GetHashCode() ?? 0, + Item6?.GetHashCode() ?? 0, + Item7?.GetHashCode() ?? 0); + } + + var size = rest.Length; + if (size >= 8) { return rest.GetHashCode(); } + + // In this case, the rest member has less than 8 elements so we need to combine some our elements with the elements in rest + var k = 8 - size; + switch (k) + { + case 1: + return ValueTuple.CombineHashCodes(Item7?.GetHashCode() ?? 0, + rest.GetHashCode()); + case 2: + return ValueTuple.CombineHashCodes(Item6?.GetHashCode() ?? 0, + Item7?.GetHashCode() ?? 0, + rest.GetHashCode()); + case 3: + return ValueTuple.CombineHashCodes(Item5?.GetHashCode() ?? 0, + Item6?.GetHashCode() ?? 0, + Item7?.GetHashCode() ?? 0, + rest.GetHashCode()); + case 4: + return ValueTuple.CombineHashCodes(Item4?.GetHashCode() ?? 0, + Item5?.GetHashCode() ?? 0, + Item6?.GetHashCode() ?? 0, + Item7?.GetHashCode() ?? 0, + rest.GetHashCode()); + case 5: + return ValueTuple.CombineHashCodes(Item3?.GetHashCode() ?? 0, + Item4?.GetHashCode() ?? 0, + Item5?.GetHashCode() ?? 0, + Item6?.GetHashCode() ?? 0, + Item7?.GetHashCode() ?? 0, + rest.GetHashCode()); + case 6: + return ValueTuple.CombineHashCodes(Item2?.GetHashCode() ?? 0, + Item3?.GetHashCode() ?? 0, + Item4?.GetHashCode() ?? 0, + Item5?.GetHashCode() ?? 0, + Item6?.GetHashCode() ?? 0, + Item7?.GetHashCode() ?? 0, + rest.GetHashCode()); + case 7: + case 8: + return ValueTuple.CombineHashCodes(Item1?.GetHashCode() ?? 0, + Item2?.GetHashCode() ?? 0, + Item3?.GetHashCode() ?? 0, + Item4?.GetHashCode() ?? 0, + Item5?.GetHashCode() ?? 0, + Item6?.GetHashCode() ?? 0, + Item7?.GetHashCode() ?? 0, + rest.GetHashCode()); + } + + Contract.Assert(false, "Missed all cases for computing ValueTuple hash code"); + return -1; + } + + int IStructuralEquatable.GetHashCode(IEqualityComparer comparer) + { + return GetHashCodeCore(comparer); + } + + private int GetHashCodeCore(IEqualityComparer comparer) + { + // We want to have a limited hash in this case. We'll use the last 8 elements of the tuple + var rest = Rest as IValueTupleInternal; + if (rest == null) + { + return ValueTuple.CombineHashCodes(comparer.GetHashCode(Item1), comparer.GetHashCode(Item2), comparer.GetHashCode(Item3), + comparer.GetHashCode(Item4), comparer.GetHashCode(Item5), comparer.GetHashCode(Item6), + comparer.GetHashCode(Item7)); + } + + var size = rest.Length; + if (size >= 8) { return rest.GetHashCode(comparer); } + + // In this case, the rest member has less than 8 elements so we need to combine some our elements with the elements in rest + var k = 8 - size; + switch (k) + { + case 1: + return ValueTuple.CombineHashCodes(Item7?.GetHashCode() ?? 0, + rest.GetHashCode()); + case 2: + return ValueTuple.CombineHashCodes(Item6?.GetHashCode() ?? 0, + Item7?.GetHashCode() ?? 0, + rest.GetHashCode()); + case 3: + return ValueTuple.CombineHashCodes(Item5?.GetHashCode() ?? 0, + Item6?.GetHashCode() ?? 0, + Item7?.GetHashCode() ?? 0, + rest.GetHashCode()); + case 4: + return ValueTuple.CombineHashCodes(Item4?.GetHashCode() ?? 0, + Item5?.GetHashCode() ?? 0, + Item6?.GetHashCode() ?? 0, + Item7?.GetHashCode() ?? 0, + rest.GetHashCode()); + case 5: + return ValueTuple.CombineHashCodes(Item3?.GetHashCode() ?? 0, + Item4?.GetHashCode() ?? 0, + Item5?.GetHashCode() ?? 0, + Item6?.GetHashCode() ?? 0, + Item7?.GetHashCode() ?? 0, + rest.GetHashCode()); + case 6: + return ValueTuple.CombineHashCodes(Item2?.GetHashCode() ?? 0, + Item3?.GetHashCode() ?? 0, + Item4?.GetHashCode() ?? 0, + Item5?.GetHashCode() ?? 0, + Item6?.GetHashCode() ?? 0, + Item7?.GetHashCode() ?? 0, + rest.GetHashCode()); + case 7: + case 8: + return ValueTuple.CombineHashCodes(Item1?.GetHashCode() ?? 0, + Item2?.GetHashCode() ?? 0, + Item3?.GetHashCode() ?? 0, + Item4?.GetHashCode() ?? 0, + Item5?.GetHashCode() ?? 0, + Item6?.GetHashCode() ?? 0, + Item7?.GetHashCode() ?? 0, + rest.GetHashCode()); + } + + Contract.Assert(false, "Missed all cases for computing ValueTuple hash code"); + return -1; + } + + int IValueTupleInternal.GetHashCode(IEqualityComparer comparer) + { + return GetHashCodeCore(comparer); + } + + /// + /// Returns a string that represents the value of this instance. + /// + /// The string representation of this instance. + /// + /// The string returned by this method takes the form (Item1, Item2, Item3, Item4, Item5, Item6, Item7, Rest). + /// If any field value is , it is represented as . + /// + public override string ToString() + { + var rest = Rest as IValueTupleInternal; + if (rest == null) + { + return "(" + Item1?.ToString() + ", " + Item2?.ToString() + ", " + Item3?.ToString() + ", " + Item4?.ToString() + ", " + Item5?.ToString() + ", " + Item6?.ToString() + ", " + Item7?.ToString() + ", " + Rest.ToString() + ")"; + } + else + { + return "(" + Item1?.ToString() + ", " + Item2?.ToString() + ", " + Item3?.ToString() + ", " + Item4?.ToString() + ", " + Item5?.ToString() + ", " + Item6?.ToString() + ", " + Item7?.ToString() + ", " + rest.ToStringEnd(); + } + } + + string IValueTupleInternal.ToStringEnd() + { + var rest = Rest as IValueTupleInternal; + if (rest == null) + { + return Item1?.ToString() + ", " + Item2?.ToString() + ", " + Item3?.ToString() + ", " + Item4?.ToString() + ", " + Item5?.ToString() + ", " + Item6?.ToString() + ", " + Item7?.ToString() + ", " + Rest.ToString() + ")"; + } + else + { + return Item1?.ToString() + ", " + Item2?.ToString() + ", " + Item3?.ToString() + ", " + Item4?.ToString() + ", " + Item5?.ToString() + ", " + Item6?.ToString() + ", " + Item7?.ToString() + ", " + rest.ToStringEnd(); + } + } + + /// + /// The number of positions in this data structure. + /// + int ITuple.Length + { + get + { + var rest = Rest as IValueTupleInternal; + return rest == null ? 8 : 7 + rest.Length; + } + } + + /// + /// Get the element at position . + /// + object? ITuple.this[int index] + { + get + { + switch (index) + { + case 0: + return Item1; + case 1: + return Item2; + case 2: + return Item3; + case 3: + return Item4; + case 4: + return Item5; + case 5: + return Item6; + case 6: + return Item7; + } + + var rest = Rest as IValueTupleInternal; + if (rest == null) + { + if (index == 7) + { + return Rest; + } + throw new IndexOutOfRangeException(); + } + return rest[index - 7]; + } + } + } +} \ No newline at end of file diff --git a/src/FDELauncher/Program.cs b/src/FDELauncher/Program.cs index 626207db8df..d6a1b0c4d37 100644 --- a/src/FDELauncher/Program.cs +++ b/src/FDELauncher/Program.cs @@ -30,6 +30,13 @@ static int Main(string[] args) { try { + if (args.Length == 1 && args[0] == "--query") + { + var installed = RegistryQuery.GetAllInstalledBundles().ToArray(); + var text = string.Join(Environment.NewLine, installed.Select(x => $"{x.DisplayName} [{x.Version.Type} {x.Version} {ToString(x.Arch)}]").ToArray()); + MessageBox.Show(text, _ThisAssembly.AssemblyTrademark); + return 0; + } if (!IsSupportedPlatform(out var error)) return ShowError(error); var executivePath = GetExecutivePath(); if (!File.Exists(executivePath)) return ShowError(R.ExecutiveNotExistsFailure); @@ -134,11 +141,21 @@ static BundleArch GetArchByPeHeader(string filePath) /// static void ShowRuntimeMissingFailure() { - var text = String2.TryFormat(R.RuntimeMissingFailureFormat2, + var archStr = ToString(matchArch); + var runtimeVersionStr = runtimeVersion.ToString(); + var _AspNetCoreRuntime = String2.TryFormat( + R.AspNetCoreRuntimeFormat2, + runtimeVersionStr, + archStr); + var _NetRuntime = String2.TryFormat( + R.NetRuntimeFormat2, + runtimeVersionStr, + archStr); + var _Runtime = $"{_AspNetCoreRuntime} {R.And} {_NetRuntime}"; + var text = String2.TryFormat( + R.RuntimeMissingFailureFormat2, _ThisAssembly.AssemblyTrademark, - String2.TryFormat(R.AspNetCoreRuntimeFormat2, - runtimeVersion.ToString(), - ToString(matchArch))); + _Runtime); var result = MessageBox.Show(text, _ThisAssembly.AssemblyTrademark, MessageBoxButtons.YesNo, MessageBoxIcon.Error); if (result == DialogResult.Yes) { @@ -179,13 +196,23 @@ static void OnError(Exception e) /// static bool IsRuntimeInstalled(BundleArch matchArch, SemanticVersion runtimeVersion, SemanticVersion sdkVersion) { - var installed = RegistryQuery.GetAllInstalledBundles(); - var query = from m in installed - where m.Arch.HasFlags(matchArch) && - ((m.Version.Type == BundleType.AspNetRuntime && m.Version.SemVer >= runtimeVersion) || - (m.Version.Type == BundleType.Sdk && m.Version.SemVer >= sdkVersion)) - select m; - return query.Any(); + // ArchiSteamFarm 依赖 ASP.NET Core + // ASP.NET Core 运行时安装程序除 Hosting Bundle 不包含 .NET 运行时 缺少文件 C:\Program Files\dotnet\host\fxr\{version}\hostfxr.dll + // 缺少 hostfxr.dll 会提示 To run this application, you must install .NET Desktop Runtime + // https://github.com/dotnet/runtime/blob/v6.0.5/src/native/corehost/apphost/apphost.windows.cpp#L62-L64 + // https://github.com/dotnet/runtime/blob/v6.0.5/src/native/corehost/apphost/apphost.windows.cpp#L106-L113 + var installed = RegistryQuery.GetAllInstalledBundles().Where(m => m.Arch.HasFlags(matchArch)).ToArray(); + + var query_sdk = installed.Where(m => m.Version.Type == BundleType.Sdk && m.Version.SemVer >= sdkVersion); + if (query_sdk.Any()) return true; + + var query_runtime = installed.Where(m => (m.Version.Type == BundleType.Runtime || m.Version.Type == BundleType.WindowsDesktopRuntime) && m.Version.SemVer >= runtimeVersion); + if (!query_runtime.Any()) return false; + + var query_aspnetruntime = installed.Where(m => m.Version.Type == BundleType.AspNetRuntime && m.Version.SemVer >= runtimeVersion); + if (!query_aspnetruntime.Any()) return false; + + return true; } /// diff --git a/src/FDELauncher/Properties/Resources.cs b/src/FDELauncher/Properties/Resources.cs index d0818cf64e4..caaa3dd0773 100644 --- a/src/FDELauncher/Properties/Resources.cs +++ b/src/FDELauncher/Properties/Resources.cs @@ -48,6 +48,25 @@ public static string GetString(Func getString, CultureInfo? re _ => "ASP.NET Core Runtime {0} ({1})", }); + public static string NetRuntimeFormat2 => GetString(l => l switch + { + Language.ChineseSimplified => ".NET 运行时 {0} ({1})", + Language.ChineseTraditional => ".NET 運行時 {0} ({1})", + Language.Japanese => ".NET ランタイム {0} ({1})", + _ => ".NET Runtime {0} ({1})", + }); + + public static string And => GetString(l => l switch + { + Language.ChineseSimplified or Language.ChineseTraditional => "和", + Language.Spanish => "y", + Language.Italian => "e", + Language.Japanese => "と", + Language.Korean => "및", + Language.Russian => "и", + _ => "and", + }); + public static string ExecutiveNotExistsFailure => GetString(l => l switch { Language.ChineseSimplified => "主程序文件不存在,请重新下载或安装此应用。", diff --git a/src/FDELauncher/dotnet-core-uninstall/LocalizableStrings.Designer.cs b/src/FDELauncher/dotnet-core-uninstall/LocalizableStrings.Designer.cs index a8ea41ee494..72d56c9e5bf 100644 --- a/src/FDELauncher/dotnet-core-uninstall/LocalizableStrings.Designer.cs +++ b/src/FDELauncher/dotnet-core-uninstall/LocalizableStrings.Designer.cs @@ -77,5 +77,41 @@ internal static string InvalidInputVersionExceptionMessageFormat { return ResourceManager.GetString("InvalidInputVersionExceptionMessageFormat", resourceCulture); } } + + /// + /// 查找类似 Used by Visual Studio for Mac or SDKs. Specify individually or use —-force to remove 的本地化字符串。 + /// + internal static string MacRuntimeRequirementExplainationString { + get { + return ResourceManager.GetString("MacRuntimeRequirementExplainationString", resourceCulture); + } + } + + /// + /// 查找类似 Used by Visual Studio for Mac. Specify individually or use —-force to remove 的本地化字符串。 + /// + internal static string MacSDKRequirementExplainationString { + get { + return ResourceManager.GetString("MacSDKRequirementExplainationString", resourceCulture); + } + } + + /// + /// 查找类似 Cannot uninstall version {0} and above 的本地化字符串。 + /// + internal static string UpperLimitRequirement { + get { + return ResourceManager.GetString("UpperLimitRequirement", resourceCulture); + } + } + + /// + /// 查找类似 Used by Visual Studio{0}. Specify individually or use —-force to remove 的本地化字符串。 + /// + internal static string WindowsRequirementExplainationString { + get { + return ResourceManager.GetString("WindowsRequirementExplainationString", resourceCulture); + } + } } } diff --git a/src/FDELauncher/dotnet-core-uninstall/LocalizableStrings.resx b/src/FDELauncher/dotnet-core-uninstall/LocalizableStrings.resx index 225a41d8e4c..dc1d1d4663d 100644 --- a/src/FDELauncher/dotnet-core-uninstall/LocalizableStrings.resx +++ b/src/FDELauncher/dotnet-core-uninstall/LocalizableStrings.resx @@ -123,4 +123,16 @@ The specified version is not valid: "{0}". + + Used by Visual Studio for Mac or SDKs. Specify individually or use —-force to remove + + + Used by Visual Studio for Mac. Specify individually or use —-force to remove + + + Cannot uninstall version {0} and above + + + Used by Visual Studio{0}. Specify individually or use —-force to remove + \ No newline at end of file diff --git a/src/FDELauncher/dotnet-core-uninstall/Shared/BundleInfo/BundleType.cs b/src/FDELauncher/dotnet-core-uninstall/Shared/BundleInfo/BundleType.cs index 401c776f32a..ddd556c6ca7 100644 --- a/src/FDELauncher/dotnet-core-uninstall/Shared/BundleInfo/BundleType.cs +++ b/src/FDELauncher/dotnet-core-uninstall/Shared/BundleInfo/BundleType.cs @@ -12,6 +12,7 @@ internal enum BundleType Sdk = 0x1, Runtime = 0x2, AspNetRuntime = 0x4, - HostingBundle = 0x8 + HostingBundle = 0x8, + WindowsDesktopRuntime = 0x10, } } \ No newline at end of file diff --git a/src/FDELauncher/dotnet-core-uninstall/Shared/BundleInfo/Versioning/WindowsDesktopRuntimeVersion.cs b/src/FDELauncher/dotnet-core-uninstall/Shared/BundleInfo/Versioning/WindowsDesktopRuntimeVersion.cs new file mode 100644 index 00000000000..762ab4cad60 --- /dev/null +++ b/src/FDELauncher/dotnet-core-uninstall/Shared/BundleInfo/Versioning/WindowsDesktopRuntimeVersion.cs @@ -0,0 +1,45 @@ +using System; + +namespace Microsoft.DotNet.Tools.Uninstall.Shared.BundleInfo.Versioning +{ + internal class WindowsDesktopRuntimeVersion : BundleVersion, IComparable, IComparable, IEquatable + { + public override BundleType Type => BundleType.WindowsDesktopRuntime; + public override BeforePatch BeforePatch => new MajorMinorVersion(Major, Minor); + + public WindowsDesktopRuntimeVersion() { } + + public WindowsDesktopRuntimeVersion(string value) : base(value) { } + + public int CompareTo(object obj) + { + return CompareTo(obj as WindowsDesktopRuntimeVersion); + } + + public int CompareTo(WindowsDesktopRuntimeVersion? other) + { + return other == null ? 1 : SemVer.CompareTo(other.SemVer); + } + + public override bool Equals(object obj) + { + return Equals(obj as WindowsDesktopRuntimeVersion); + } + + public bool Equals(WindowsDesktopRuntimeVersion? other) + { + return other != null && + base.Equals(other); + } + + public override int GetHashCode() + { + return HashCode.Combine(base.GetHashCode()); + } + + public override Bundle ToBundle(BundleArch arch, string uninstallCommand, string displayName) + { + return new Bundle(this, arch, uninstallCommand, displayName); + } + } +} \ No newline at end of file diff --git a/src/FDELauncher/dotnet-core-uninstall/Shared/Utils/RuntimeInfo.cs b/src/FDELauncher/dotnet-core-uninstall/Shared/Utils/RuntimeInfo.cs new file mode 100644 index 00000000000..248dfdc615d --- /dev/null +++ b/src/FDELauncher/dotnet-core-uninstall/Shared/Utils/RuntimeInfo.cs @@ -0,0 +1,31 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// https://github.com/dotnet/cli-lab/blob/1.5.255402/src/dotnet-core-uninstall/Shared/Utils/RuntimeInfo.cs + +using System; + +namespace Microsoft.DotNet.Tools.Uninstall.Shared.Utils +{ + internal static class RuntimeInfo + { + static RuntimeInfo() + { + switch (Environment.OSVersion.Platform) + { + case PlatformID.Win32NT: + RunningOnWindows = true; + break; + case PlatformID.Unix: + RunningOnLinux = true; + break; + case PlatformID.MacOSX: + RunningOnOSX = true; + break; + } + } + + public static readonly bool RunningOnWindows; + public static readonly bool RunningOnOSX; + public static readonly bool RunningOnLinux; + } +} \ No newline at end of file diff --git a/src/FDELauncher/dotnet-core-uninstall/Shared/VSVersioning/VisualStudioSafeVersionsExtractor.cs b/src/FDELauncher/dotnet-core-uninstall/Shared/VSVersioning/VisualStudioSafeVersionsExtractor.cs new file mode 100644 index 00000000000..fe902d0bbbf --- /dev/null +++ b/src/FDELauncher/dotnet-core-uninstall/Shared/VSVersioning/VisualStudioSafeVersionsExtractor.cs @@ -0,0 +1,125 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// https://github.com/dotnet/cli-lab/blob/1.5.255402/src/dotnet-core-uninstall/Shared/VSVersioning/VisualStudioSafeVersionsExtractor.cs + +using System.Collections.Generic; +using System.Linq; +using Microsoft.DotNet.Tools.Uninstall.Shared.BundleInfo; +using Microsoft.DotNet.Tools.Uninstall.Shared.BundleInfo.Versioning; +using Microsoft.DotNet.Tools.Uninstall.Shared.Utils; +using NuGet.Versioning; + +namespace Microsoft.DotNet.Tools.Uninstall.Shared.VSVersioning +{ + internal static class VisualStudioSafeVersionsExtractor + { + // The tool should not be used to uninstall any more recent versions of the sdk + public static readonly SemanticVersion UpperLimit = new(7, 0, 0); + + // Must keep one of each of these divisions to ensure Visual Studio works. + // Pairs are [inclusive, exclusive) + private static readonly Dictionary<(SemanticVersion, SemanticVersion), string> WindowsVersionDivisionsToExplaination = new() + { + { (new SemanticVersion(1, 0, 0), new SemanticVersion(2, 0, 0)), string.Format(LocalizableStrings.WindowsRequirementExplainationString, "") }, + { (new SemanticVersion(2, 0, 0), new SemanticVersion(2, 1, 300)), string.Format(LocalizableStrings.WindowsRequirementExplainationString, "") }, + { (new SemanticVersion(2, 1, 300), new SemanticVersion(2, 1, 600)), string.Format(LocalizableStrings.WindowsRequirementExplainationString, " 2017") }, + { (new SemanticVersion(2, 1, 600), new SemanticVersion(2, 1, 900)), string.Format(LocalizableStrings.WindowsRequirementExplainationString, " 2019") }, + { (new SemanticVersion(2, 2, 100), new SemanticVersion(2, 2, 200)), string.Format(LocalizableStrings.WindowsRequirementExplainationString, " 2017") }, + { (new SemanticVersion(2, 2, 200), new SemanticVersion(2, 2, 500)), string.Format(LocalizableStrings.WindowsRequirementExplainationString, " 2019") }, + { (new SemanticVersion(2, 2, 500), UpperLimit), string.Format(LocalizableStrings.WindowsRequirementExplainationString, "") } + }; + + private static (IDictionary, string>, IEnumerable) ApplyWindowsVersionDivisions(IEnumerable bundleList) + { + var dividedBundles = new Dictionary, string>(); + foreach (var item in WindowsVersionDivisionsToExplaination) + { + var division = item.Key; + var explaination = item.Value; + var bundlesInRange = bundleList.Where(bundle => bundle.Version is SdkVersion && + division.Item1 <= bundle.Version.GetVersionWithoutTags() && bundle.Version.GetVersionWithoutTags() < division.Item2); + bundleList = bundleList.Except(bundlesInRange); + if (bundlesInRange.Count() > 0) + { + dividedBundles.Add(bundlesInRange, explaination); + } + } + + return (dividedBundles, bundleList); + } + + private static (IDictionary, string>, IEnumerable) ApplyMacVersionDivisions(IEnumerable bundleList) + { + var bundlesAboveLimit = bundleList.Where(bundle => bundle.Version.SemVer >= UpperLimit); + bundleList = bundleList.Except(bundlesAboveLimit); + + var dividedBundles = bundleList + .Where(bundle => bundle.Version is RuntimeVersion) + .GroupBy(bundle => bundle.Version.MajorMinor) + .Select(pair => (pair as IEnumerable, LocalizableStrings.MacRuntimeRequirementExplainationString)) + .ToDictionary(key => key.Item1, value => value.Item2); + + var sdks = bundleList.Where(bundle => bundle.Version is SdkVersion); + if (sdks != null && sdks.Count() > 0) + { + dividedBundles.Add(sdks, LocalizableStrings.MacSDKRequirementExplainationString); + } + + var remainingBundles = bundleList + .Where(bundle => !(bundle.Version is RuntimeVersion || bundle.Version is SdkVersion)) + .Concat(bundlesAboveLimit); + return (dividedBundles, remainingBundles); + } + + private static (IDictionary, string>, IEnumerable) ApplyVersionDivisions(IEnumerable bundles) + { + if (RuntimeInfo.RunningOnWindows) + { + return ApplyWindowsVersionDivisions(bundles); + } + else + { + return ApplyMacVersionDivisions(bundles); + } + } + + public static IEnumerable GetUninstallableBundles(IEnumerable bundles) + { + var required = new List(); + var (bundlesByDivisions, remainingBundles) = ApplyVersionDivisions(bundles); + + foreach (IEnumerable band in bundlesByDivisions.Keys) + { + required.Add(band.Max()); + } + + required = required.Concat(remainingBundles.Where(bundle => bundle.Version.SemVer >= UpperLimit)).ToList(); + + return bundles.Where(b => !required.Contains(b)); + } + + public static Dictionary GetReasonRequiredStrings(IEnumerable allBundles) + { + var (bundlesByDivisions, remainingBundles) = ApplyVersionDivisions(allBundles); + + var bundlesAboveUpperLimit = remainingBundles.Where(bundle => bundle.Version.SemVer >= UpperLimit); + var requirementStringResults = remainingBundles.Except(bundlesAboveUpperLimit) + .Select(bundle => (bundle, string.Empty)) + .Concat(bundlesAboveUpperLimit + .Select(bundle => (bundle, string.Format(LocalizableStrings.UpperLimitRequirement, UpperLimit)))); + + foreach (var division in bundlesByDivisions) + { + var requiredBundle = division.Key.Max(); + requirementStringResults = requirementStringResults.Append((requiredBundle, division.Value)); + requirementStringResults = requirementStringResults.Concat(division.Key + .Where(bundle => !bundle.Equals(requiredBundle)) + .Select(bundle => (bundle, string.Empty))); + } + + return requirementStringResults + .OrderByDescending(pair => pair.bundle.DisplayName) + .ToDictionary(i => i.bundle, i => i.Item2); + } + } +} \ No newline at end of file diff --git a/src/FDELauncher/dotnet-core-uninstall/Windows/RegistryQuery.cs b/src/FDELauncher/dotnet-core-uninstall/Windows/RegistryQuery.cs index c51237b6c86..4ec5ba67dd7 100644 --- a/src/FDELauncher/dotnet-core-uninstall/Windows/RegistryQuery.cs +++ b/src/FDELauncher/dotnet-core-uninstall/Windows/RegistryQuery.cs @@ -9,12 +9,18 @@ using Microsoft.DotNet.Tools.Uninstall.Shared.BundleInfo; using Microsoft.DotNet.Tools.Uninstall.Shared.BundleInfo.Versioning; using Microsoft.DotNet.Tools.Uninstall.Shared.Utils; +using Microsoft.DotNet.Tools.Uninstall.Shared.VSVersioning; using Microsoft.Win32; namespace Microsoft.DotNet.Tools.Uninstall.Windows { internal static class RegistryQuery { + public static IEnumerable GetInstalledBundles() + { + return VisualStudioSafeVersionsExtractor.GetUninstallableBundles(GetAllInstalledBundles()); + } + public static IEnumerable GetAllInstalledBundles() { var bundles = GetNetCoreBundleKeys(RegistryKey2.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Registry64)); @@ -22,7 +28,8 @@ public static IEnumerable GetAllInstalledBundles() var wrappedBundles = bundles .Select(WrapRegistryKey) - .Where(bundle => bundle != null); + .Where(bundle => bundle != null) + .ToArray(); return wrappedBundles!; } @@ -130,6 +137,10 @@ internal static bool IsNetCoreBundle(string displayName, string displayVersion, { return new RuntimeVersion(versionString); } + else if (displayName.IndexOf("Windows Desktop Runtime", StringComparison.OrdinalIgnoreCase) >= 0) + { + return new WindowsDesktopRuntimeVersion(versionString); + } else { return null; diff --git a/src/ST.Client.Android.App.Modern/ST.Client.Android.App.Modern.csproj b/src/ST.Client.Android.App.Modern/ST.Client.Android.App.Modern.csproj index 40ecf1f9222..d86485a5099 100644 --- a/src/ST.Client.Android.App.Modern/ST.Client.Android.App.Modern.csproj +++ b/src/ST.Client.Android.App.Modern/ST.Client.Android.App.Modern.csproj @@ -495,213 +495,213 @@ Resources\layout\activity_about_not_binding.xml - + Resources\layout\activity_barcode_scanner.xml - + Resources\layout\activity_ca_cert_status.xml Resources\layout\activity_ca_cert_status_content.xml - + Resources\layout\activity_detail_auth.xml - - + + Resources\layout\activity_donate.xml - - + + Resources\layout\activity_encryption_auth.xml - - + + Resources\layout\activity_explorer.xml - - + + Resources\layout\activity_export_auth.xml - - + + Resources\layout\activity_guide_ca_cert.xml - - + + Resources\layout\activity_guide_vpn.xml - - + + Resources\layout\activity_guide_vpn_content.xml - - + + Resources\layout\activity_login_or_register.xml - - + + Resources\layout\activity_main_3.xml - - + + Resources\layout\activity_proxy_settings.xml - + Resources\layout\activity_settings_not_binding.xml - + Resources\layout\activity_steam_auth_data.xml - - + + Resources\layout\activity_steam_auth_trade.xml - - + + Resources\layout\activity_tablayout_viewpager2.xml - - + + Resources\layout\activity_textblock.xml - - + + Resources\layout\activity_toolbar_tablayout_viewpager2.xml - - + + Resources\layout\fragment_about.xml - - + + Resources\layout\fragment_account_binding.xml - - + + Resources\layout\fragment_account_binding_content.xml - - + + Resources\layout\fragment_asf_plus_bot.xml - - + + Resources\layout\fragment_asf_plus_config.xml - - + + Resources\layout\fragment_asf_plus_console.xml - - + + Resources\layout\fragment_basic_profile.xml - - + + Resources\layout\fragment_basic_profile_content.xml - - + + Resources\layout\fragment_bind_phone_number.xml - - + + Resources\layout\fragment_bind_phone_number_content.xml - - + + Resources\layout\fragment_change_bind_phone_number.xml - - + + Resources\layout\fragment_change_bind_phone_number_content.xml - - + + Resources\layout\fragment_community_fix.xml - - + + Resources\layout\fragment_community_fix_content_ready.xml - - + + Resources\layout\fragment_community_fix_content_starting.xml - - + + Resources\layout\fragment_game_list.xml - - + + Resources\layout\fragment_guide_export_ca_cert.xml - - + + Resources\layout\fragment_guide_export_ca_cert_content.xml - - + + Resources\layout\fragment_guide_how_install_ca_cert.xml - - + + Resources\layout\fragment_guide_how_install_ca_cert_content.xml - - + + Resources\layout\fragment_local_auth.xml - - + + Resources\layout\fragment_local_auth_import_other.xml - - + + Resources\layout\fragment_local_auth_import_steam_app.xml - - + + Resources\layout\fragment_local_auth_import_steam_plus_plus.xml - - + + Resources\layout\fragment_local_auth_import_steam_user.xml - - + + Resources\layout\fragment_login_and_register_by_fast.xml - - + + Resources\layout\fragment_login_and_register_by_phone_number.xml - - + + Resources\layout\fragment_login_and_register_by_phone_number_content.xml - - + + Resources\layout\fragment_my.xml - - + + Resources\layout\fragment_my_content.xml - - + + Resources\layout\fragment_settings.xml - - + + Resources\layout\layout_card_accelerate_project.xml - - + + Resources\layout\layout_card_accelerate_project_group.xml - - + + Resources\layout\layout_card_accelerate_project_group_content.xml - - + + Resources\layout\layout_card_gap_authenticator.xml - - + + Resources\layout\layout_card_gap_authenticator_content.xml - - + + Resources\layout\layout_card_steam_auth_trade_confirmation.xml - - + + Resources\layout\layout_card_steam_auth_trade_confirmation_content.xml - - + + Resources\layout\layout_donate_user.xml - - + + Resources\layout\layout_file_or_dir.xml - - + + Resources\layout\layout_login_and_register_by_fast_channel.xml - - + + Resources\layout\layout_preference_large_button.xml - - + + Resources\layout\layout_preference_small_button.xml - - + + Resources\layout\list_combobox_item.xml - - + + Resources\layout\local_auth_appwidget.xml - - + + Resources\layout\shared_divider.xml - - + + Resources\layout\shared_loading.xml - - + + Resources\layout\shared_toolbar.xml - - + + Resources\layout\textbox_password.xml - + Resources\menu\asf_plus_toolbar_menu.xml diff --git a/src/ST.Client.Desktop.Avalonia.App.Bridge/Steam++.csproj b/src/ST.Client.Desktop.Avalonia.App.Bridge/Steam++.csproj index 3e014b2f8cf..f388aaaf439 100644 --- a/src/ST.Client.Desktop.Avalonia.App.Bridge/Steam++.csproj +++ b/src/ST.Client.Desktop.Avalonia.App.Bridge/Steam++.csproj @@ -17,6 +17,7 @@ OutOfProcess 10.0.19041.24 true + false diff --git a/src/ST.Client.Desktop.Avalonia.App.Bridge2/ST.Client.Avalonia.App.MsixPackage.csproj b/src/ST.Client.Desktop.Avalonia.App.Bridge2/ST.Client.Avalonia.App.MsixPackage.csproj index 48ab74d01f1..8ce48d1211e 100644 --- a/src/ST.Client.Desktop.Avalonia.App.Bridge2/ST.Client.Avalonia.App.MsixPackage.csproj +++ b/src/ST.Client.Desktop.Avalonia.App.Bridge2/ST.Client.Avalonia.App.MsixPackage.csproj @@ -12,7 +12,8 @@ ..\ST.Client.Desktop.Avalonia.App\Properties\app.manifest OutOfProcess 10.0.19041.24 - + false + false x86;x64;arm64 win10-x86;win10-x64;win10-arm64 win10-$(Platform).pubxml diff --git a/src/ST.Client.Desktop.Avalonia.App/ST.Client.Avalonia.App.csproj b/src/ST.Client.Desktop.Avalonia.App/ST.Client.Avalonia.App.csproj index da273f28720..159339480fb 100644 --- a/src/ST.Client.Desktop.Avalonia.App/ST.Client.Avalonia.App.csproj +++ b/src/ST.Client.Desktop.Avalonia.App/ST.Client.Avalonia.App.csproj @@ -49,8 +49,8 @@ 10.0.19041.24 win7-x64 - true - + false + false diff --git a/src/ST.Tools.Publish/Models/PublishDirInfo.cs b/src/ST.Tools.Publish/Models/PublishDirInfo.cs index b9fd0d59f60..3e6cdca12de 100644 --- a/src/ST.Tools.Publish/Models/PublishDirInfo.cs +++ b/src/ST.Tools.Publish/Models/PublishDirInfo.cs @@ -1,5 +1,7 @@ using MessagePack; using System.Collections.Generic; +using System.Runtime.InteropServices; +using MPIgnore = MessagePack.IgnoreMemberAttribute; using MPKey = MessagePack.KeyAttribute; using MPObject = MessagePack.MessagePackObjectAttribute; @@ -23,11 +25,59 @@ public PublishDirInfo(string dirName, string dirPath, DeploymentMode deploymentM DeploymentMode = deploymentMode; } + string _Name = string.Empty; /// /// 发布文件夹名 /// [MPKey(0)] - public string Name { get; set; } = string.Empty; + public string Name + { + get => _Name; + set + { + var array = value.Split('-', StringSplitOptions.RemoveEmptyEntries); + if (array.Length >= 1) + { + switch (array[0]?.ToLower()) + { + case "win": + Platform = OSPlatform.Windows; + break; + case "linux": + Platform = OSPlatform.Linux; + break; + case "osx": + Platform = OSPlatform.OSX; + break; + } + } + if (array.Length >= 2) + { + switch (array[1]?.ToLower()) + { + case "x86": + Architecture = Architecture.X86; + break; + case "x64": + Architecture = Architecture.X64; + break; + case "arm": + Architecture = Architecture.Arm; + break; + case "arm64": + Architecture = Architecture.Arm64; + break; + } + } + _Name = value; + } + } + + [MPIgnore] + public OSPlatform Platform { get; private set; } + + [MPIgnore] + public Architecture Architecture { get; private set; } = (Architecture)int.MinValue; /// /// 发布文件夹路径 diff --git a/src/ST.Tools.Publish/Steps/Step_cd.cs b/src/ST.Tools.Publish/Steps/Step_cd.cs index 6eba985151c..62a34f9cf71 100644 --- a/src/ST.Tools.Publish/Steps/Step_cd.cs +++ b/src/ST.Tools.Publish/Steps/Step_cd.cs @@ -186,7 +186,7 @@ static async Task FullHandlerAsync(string token, string[] val, bool dev) if (hasWindows) { - var win_publishDirs = publishDirs.Where(x => x.Name.StartsWith("win-")); + var win_publishDirs = publishDirs.Where(x => x.Platform == OSPlatform.Windows); Console.WriteLine("nsis Step 正在打包 EXE installer..."); @@ -195,7 +195,7 @@ static async Task FullHandlerAsync(string token, string[] val, bool dev) if (hasLinux) { - var linux_publishDirs = publishDirs.Where(x => x.Name.StartsWith("linux-")); + var linux_publishDirs = publishDirs.Where(x => x.Platform == OSPlatform.Linux); Console.WriteLine("rpm Step 正在打包 CentOS/RedHat Linux installer..."); @@ -225,7 +225,7 @@ static async Task FullHandlerAsync(string token, string[] val, bool dev) #endregion - var winX64 = publishDirs.Where(x => x.Name.StartsWith("win-x64")).ToArray(); + var winX64 = publishDirs.Where(x => x.Platform == OSPlatform.Windows && x.Architecture == Architecture.X64).ToArray(); if (winX64.Any()) { Console.WriteLine("wdb Step 正在上传新版本数据中..."); @@ -281,6 +281,8 @@ static async Task FullHandlerAsync(string token, string[] val, bool dev) continue; } + OnScanPathBefore(item); + ScanPath(item.Path, item.Files, ignoreRootDirNames: ignoreDirNames); Console.WriteLine($"{item.Name}, count: {item.Files.Count}"); @@ -303,7 +305,7 @@ static void GenerateCompressedPackage(bool dev, PublishDirInfo item) /// public static void GenerateCompressedPackage(bool dev, PublishDirInfo item, AppDownloadType type) { - var isLinux = item.Name.StartsWith("linux-"); + var isLinux = item.Platform == OSPlatform.Linux; if (isLinux) { type = AppDownloadType.Compressed_Zstd; diff --git a/src/ST.Tools.Publish/Steps/Step_deb.cs b/src/ST.Tools.Publish/Steps/Step_deb.cs index c21f50aa7b9..27c246d7816 100644 --- a/src/ST.Tools.Publish/Steps/Step_deb.cs +++ b/src/ST.Tools.Publish/Steps/Step_deb.cs @@ -32,7 +32,7 @@ static void Handler(bool dev) foreach (var item in dirNames) { - var isLinux = item.Name.StartsWith("linux-"); + var isLinux = item.Platform == OSPlatform.Linux; if (!isLinux) continue; HandlerItem(dev, item); diff --git a/src/ST.Tools.Publish/Steps/Step_rpm.cs b/src/ST.Tools.Publish/Steps/Step_rpm.cs index 882064cb5de..0c75ba98415 100644 --- a/src/ST.Tools.Publish/Steps/Step_rpm.cs +++ b/src/ST.Tools.Publish/Steps/Step_rpm.cs @@ -48,7 +48,7 @@ static void Handler(bool dev) foreach (var item in dirNames) { - var isLinux = item.Name.StartsWith("linux-"); + var isLinux = item.Platform == OSPlatform.Linux; if (!isLinux) continue; HandlerItem(dev, item); diff --git a/src/ST.Tools.Publish/Steps/Step_start.cs b/src/ST.Tools.Publish/Steps/Step_start.cs index 671e9379e77..186f0c6e11a 100644 --- a/src/ST.Tools.Publish/Steps/Step_start.cs +++ b/src/ST.Tools.Publish/Steps/Step_start.cs @@ -5,6 +5,7 @@ using System.CommandLine.NamingConventionBinder; using System.IO; using System.Linq; +using System.Runtime.InteropServices; using System.Security.Cryptography; using static System.Application.Utils; using static System.ProjectPathUtil; @@ -41,7 +42,7 @@ internal static class Step5 foreach (var item in dirNames) { - var isMacOS = item.Name.StartsWith("osx"); + var isMacOS = item.Platform == OSPlatform.OSX; // ASP.NET Core Runtime 6.0.0 缺少 macOS Installers if (isMacOS && d == DeploymentMode.FDE) continue; @@ -55,6 +56,8 @@ internal static class Step5 lazyActions.Add(() => { + OnScanPathBefore(item); + ScanPath(item.Path, item.Files, ignoreRootDirNames: ignoreDirNames); }); diff --git a/src/ST.Tools.Publish/Utils.cs b/src/ST.Tools.Publish/Utils.cs index a0996bf7f2a..69072cc1d08 100644 --- a/src/ST.Tools.Publish/Utils.cs +++ b/src/ST.Tools.Publish/Utils.cs @@ -117,7 +117,33 @@ public static string GetInfoVersion(string assemblyFile) //return attr?.InformationalVersion ?? string.Empty; } - public static void ScanPath(string dirPath, List? list = null, string? relativeTo = null, string[]? ignoreRootDirNames = null) + public static void OnScanPathBefore(PublishDirInfo publish) + { + if (publish.DeploymentMode == DeploymentMode.FDE && publish.Platform == OSPlatform.Windows) + { + const string fileName = "Steam++.Launcher.exe"; + const string config_ex = ".config"; + var sourceFileName = Path.Combine(ProjectPathUtil.projPath, "references", fileName); + if (File.Exists(sourceFileName)) + { + var destFileName = Path.Combine(publish.Path, fileName); + File.Copy(sourceFileName, destFileName); + sourceFileName += config_ex; + if (File.Exists(sourceFileName)) + { + destFileName += config_ex; + File.Copy(sourceFileName, destFileName); + } + } + } + } + + public static void ScanPath(string dirPath, List? list = null, string[]? ignoreRootDirNames = null) + { + ScanPathCore(dirPath, list, ignoreRootDirNames: ignoreRootDirNames); + } + + static void ScanPathCore(string dirPath, List? list = null, string? relativeTo = null, string[]? ignoreRootDirNames = null) { list ??= new List(); relativeTo ??= dirPath; @@ -138,7 +164,7 @@ public static void ScanPath(string dirPath, List? list = null, // 忽略顶级文件夹 continue; } - ScanPath(dir, list, relativeTo); + ScanPathCore(dir, list, relativeTo); } }