mirror of
https://github.com/BeyondDimension/SteamTools.git
synced 2026-10-28 05:56:52 +08:00
Compare commits
106
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
82814649e3 | ||
|
|
978411b677 | ||
|
|
166f8732dc | ||
|
|
8c2a7068b9 | ||
|
|
b832f3c927 | ||
|
|
c367cac992 | ||
|
|
f78271c2f5 | ||
|
|
f2bdc19d74 | ||
|
|
0fde0eccba | ||
|
|
5c55fa97e7 | ||
|
|
323c7eb6c3 | ||
|
|
42dea46260 | ||
|
|
1bf65820c0 | ||
|
|
ba1c5b6c8b | ||
|
|
deb6c4eaa1 | ||
|
|
af337a8fbd | ||
|
|
80d14c6c24 | ||
|
|
07d82be4c7 | ||
|
|
1e261f1125 | ||
|
|
8a1bde0773 | ||
|
|
617b99c39d | ||
|
|
8b4680fcee | ||
|
|
e27ae2b202 | ||
|
|
5419530031 | ||
|
|
415fd30cc5 | ||
|
|
ef627692ef | ||
|
|
6f28ce0993 | ||
|
|
2dbb13db87 | ||
|
|
40d3c0e669 | ||
|
|
6be424dc5f | ||
|
|
6babd615cd | ||
|
|
d1f266465b | ||
|
|
fccd8da9f6 | ||
|
|
df2f84ff99 | ||
|
|
d0bfbdbc77 | ||
|
|
51e19bae95 | ||
|
|
5673bf2150 | ||
|
|
f5c66a353e | ||
|
|
09dd6fe47e | ||
|
|
96471ccf27 | ||
|
|
7b3f40da3b | ||
|
|
e369c6aa10 | ||
|
|
5b4fcc2b8b | ||
|
|
33c6f0d5f9 | ||
|
|
6abdb6dc11 | ||
|
|
a981d31ec4 | ||
|
|
8a0dd49e39 | ||
|
|
dc005a1a47 | ||
|
|
5d69679073 | ||
|
|
1030e15d8e | ||
|
|
48083a90d2 | ||
|
|
aab89668a9 | ||
|
|
a4a6f0e61d | ||
|
|
a103ec141b | ||
|
|
c23dc29e1c | ||
|
|
ddda4c0273 | ||
|
|
cd1c1b91d7 | ||
|
|
ce09385a52 | ||
|
|
8d6e83a42b | ||
|
|
b0a72ab23a | ||
|
|
b5f8910e2e | ||
|
|
881ff3fba9 | ||
|
|
8878d183a7 | ||
|
|
46f95574d1 | ||
|
|
c9c846ccd6 | ||
|
|
72976110d7 | ||
|
|
82736e38d8 | ||
|
|
1ec6199c18 | ||
|
|
e5eaf173b2 | ||
|
|
3227df2afb | ||
|
|
769398fb56 | ||
|
|
7d8cad6f6a | ||
|
|
f82aafba45 | ||
|
|
1a2b0e5fbc | ||
|
|
3afd331ced | ||
|
|
daa43cb0a2 | ||
|
|
e1ff5b17bb | ||
|
|
c55870379e | ||
|
|
48bbc5a530 | ||
|
|
94d26bd874 | ||
|
|
f3d5c251da | ||
|
|
a8ac5b9901 | ||
|
|
5ba8d727ed | ||
|
|
570ca3c623 | ||
|
|
295948816b | ||
|
|
2a196b1ca0 | ||
|
|
81a70492a7 | ||
|
|
5f374557de | ||
|
|
f0a0a77f0a | ||
|
|
c2a502a6aa | ||
|
|
f6d6ba08a5 | ||
|
|
98e4620c0b | ||
|
|
ba6405074e | ||
|
|
06d67e527f | ||
|
|
b9e4c8a410 | ||
|
|
1571259321 | ||
|
|
a9c11ee72f | ||
|
|
0393e82007 | ||
|
|
3358ee78e7 | ||
|
|
b7e5746d7d | ||
|
|
efae85db2c | ||
|
|
4826939668 | ||
|
|
ebe346b3b1 | ||
|
|
0652f49b65 | ||
|
|
b6729e45ae | ||
|
|
a0fe199d20 |
@@ -7,26 +7,45 @@ assignees: ''
|
||||
|
||||
---
|
||||
|
||||
### 🐛 描述(Description)
|
||||
|
||||
<!--
|
||||
发布前请先尝试在 Issuse 内搜索一下你的问题是否已经被提出过,
|
||||
发布前请先尝试在 Issues 内搜索一下你的问题是否已经被提出过,
|
||||
详细地描述 bug,让大家都能理解,请一定确定你所要发布的内容是一个 Bug,如果不确定请发 Question
|
||||
Before posting, please try searching in Issuse to see if your question has already been asked,
|
||||
别忘了填写标题,标题要简短的描述问题
|
||||
Before posting, please try searching in Issues to see if your question has already been asked,
|
||||
Describe the bug in detail so that everyone can understand it
|
||||
Don't forget to fill in the title, which should briefly describe the problem
|
||||
-->
|
||||
|
||||
### 🐛 描述(Description)
|
||||
<!--
|
||||
详细的描述该问题
|
||||
Describe the problem in detail
|
||||
-->
|
||||
|
||||
|
||||
### 📷 复现步骤(Steps to Reproduce)
|
||||
|
||||
<!--
|
||||
清晰描述复现步骤,让别人也能看到问题
|
||||
确保上述步骤尽可能能够在大多数设备上 100% 复现,以便定位问题原因
|
||||
Clearly describe the reproduction steps so that others can see the problem
|
||||
Ensure that the above steps can be reproduced 100% on most devices as far as possible in order to locate the cause of the problem
|
||||
-->
|
||||
1.
|
||||
2.
|
||||
3.
|
||||
|
||||
### 📄 [异常/日志]信息([Exception/Log] Information)
|
||||
### 📄 日志信息(Log Information)
|
||||
<!--
|
||||
在程序内 设置 - 通用设置 - 存储空间 - 打开Logs文件夹 找到当天的日志,将文件上传或将关键部分写在此处,在内容的开始与结束上加上 ``` 单独作为一行,如下所示
|
||||
Set in the program - General Settings - storage space - open the logs folder to find the log of the day, upload the file or write the key part here, and add ``` to the beginning and end of the content as a separate line, as shown below
|
||||
|
||||
```
|
||||
你的日志内容
|
||||
Your log content
|
||||
```
|
||||
|
||||
-->
|
||||
|
||||
### 🚑 基本信息(Basic Information)
|
||||
|
||||
|
||||
@@ -7,6 +7,13 @@ assignees: ''
|
||||
|
||||
---
|
||||
|
||||
<!--
|
||||
发布前请先尝试在 Issuse 内搜索一下你的 Feature 是否已经被提出过,
|
||||
别忘了填写标题,标题要简短的描述 Feature
|
||||
Before publishing, please try to search in issue to see if your feature has been proposed,
|
||||
Don't forget to fill in the title, which should briefly describe the feature
|
||||
-->
|
||||
|
||||
### 🥰 需求描述(Description)
|
||||
|
||||
<!--
|
||||
|
||||
@@ -7,11 +7,18 @@ assignees: ''
|
||||
|
||||
---
|
||||
|
||||
<!--
|
||||
发布前请先尝试在 Issuse 内搜索一下你的 Question 是否已经被提出过,
|
||||
别忘了填写标题,标题要简短的描述 Question
|
||||
Before publishing, please try to search in issue to see if your question has been proposed,
|
||||
Don't forget to fill in the title, which should briefly describe the question
|
||||
-->
|
||||
|
||||
### 🧐 问题描述(Description)
|
||||
|
||||
<!--
|
||||
详细地描述需求,让大家都能理解
|
||||
Describe the requirements in detail so that everyone can understand them
|
||||
详细地描述 Question,让大家都能理解
|
||||
Describe the question in detail so that everyone can understand it
|
||||
-->
|
||||
|
||||
### 🚑 其他信息(Other Information)
|
||||
|
||||
+61
-56
@@ -5,18 +5,18 @@
|
||||
<PackageVersion Include="Microsoft.Extensions.Http" Version="6.0.0" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Logging" Version="6.0.0" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Configuration.Abstractions" Version="6.0.0" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Logging.AzureAppServices" Version="6.0.7" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Logging.AzureAppServices" Version="6.0.8" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Caching.Memory" Version="6.0.1" />
|
||||
<PackageVersion Include="Microsoft.EntityFrameworkCore.Tools" Version="6.0.7" />
|
||||
<PackageVersion Include="Microsoft.EntityFrameworkCore" Version="6.0.7" />
|
||||
<PackageVersion Include="Microsoft.EntityFrameworkCore.Relational" Version="6.0.7" />
|
||||
<PackageVersion Include="Microsoft.EntityFrameworkCore.SqlServer" Version="6.0.7" />
|
||||
<PackageVersion Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="6.0.7" />
|
||||
<PackageVersion Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="6.0.7" />
|
||||
<PackageVersion Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="6.0.7" />
|
||||
<PackageVersion Include="Microsoft.AspNetCore.Authentication.MicrosoftAccount" Version="6.0.7" />
|
||||
<PackageVersion Include="Microsoft.AspNetCore.Authentication.OpenIdConnect" Version="6.0.7" />
|
||||
<PackageVersion Include="Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation" Version="6.0.7" />
|
||||
<PackageVersion Include="Microsoft.EntityFrameworkCore.Tools" Version="6.0.8" />
|
||||
<PackageVersion Include="Microsoft.EntityFrameworkCore" Version="6.0.8" />
|
||||
<PackageVersion Include="Microsoft.EntityFrameworkCore.Relational" Version="6.0.8" />
|
||||
<PackageVersion Include="Microsoft.EntityFrameworkCore.SqlServer" Version="6.0.8" />
|
||||
<PackageVersion Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="6.0.8" />
|
||||
<PackageVersion Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="6.0.8" />
|
||||
<PackageVersion Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="6.0.8" />
|
||||
<PackageVersion Include="Microsoft.AspNetCore.Authentication.MicrosoftAccount" Version="6.0.8" />
|
||||
<PackageVersion Include="Microsoft.AspNetCore.Authentication.OpenIdConnect" Version="6.0.8" />
|
||||
<PackageVersion Include="Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation" Version="6.0.8" />
|
||||
</ItemGroup>
|
||||
<ItemGroup Condition=" $(TargetFramework.StartsWith('netstandard')) Or $(TargetFramework.StartsWith('netcoreapp3.1'))">
|
||||
<!-- Microsoft.Extensions.* & AspNetCore & EntityFrameworkCore(NETCOREAPP31) -->
|
||||
@@ -24,7 +24,7 @@
|
||||
<PackageVersion Include="Microsoft.Extensions.Http" Version="3.1.27" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Logging" Version="3.1.27" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Configuration.Abstractions" Version="3.1.27" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Logging.AzureAppServices" Version="3.1.27" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Logging.AzureAppServices" Version="6.0.8" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Caching.Memory" Version="3.1.27" />
|
||||
<PackageVersion Include="Microsoft.EntityFrameworkCore.Tools" Version="3.1.27" />
|
||||
<PackageVersion Include="Microsoft.EntityFrameworkCore" Version="3.1.27" />
|
||||
@@ -40,12 +40,12 @@
|
||||
<ItemGroup Condition=" $(TargetFramework.StartsWith('net6.0')) ">
|
||||
<!-- Entity Framework Plus -->
|
||||
<!-- A FREE & Open Source library to enhance EF6 and EF Core -->
|
||||
<PackageVersion Include="Z.EntityFramework.Plus.EFCore" Version="6.14.3" />
|
||||
<PackageVersion Include="Z.EntityFramework.Plus.EFCore" Version="6.14.4" />
|
||||
|
||||
<!-- AspNetCore 6 OpenId/OAuth -->
|
||||
<PackageVersion Include="AspNet.Security.OpenId.Steam" Version="6.0.0" />
|
||||
<PackageVersion Include="AspNet.Security.OAuth.QQ" Version="6.0.6" />
|
||||
<PackageVersion Include="AspNet.Security.OAuth.Apple" Version="6.0.6" />
|
||||
<PackageVersion Include="AspNet.Security.OAuth.QQ" Version="6.0.9" />
|
||||
<PackageVersion Include="AspNet.Security.OAuth.Apple" Version="6.0.9" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
</ItemGroup>
|
||||
@@ -74,24 +74,26 @@
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<!-- Microsoft.* -->
|
||||
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.2.0" />
|
||||
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.3.0" />
|
||||
<!--<PackageVersion Include="Microsoft.Windows.CsWinRT" Version="1.4.1" />-->
|
||||
<!--<PackageVersion Include="Microsoft.Windows.SDK.NET.Ref" Version="10.0.19041.22" />-->
|
||||
<!--<PackageVersion Include="Microsoft.Windows.SDK.Contracts" Version="10.0.19041.1" />-->
|
||||
<PackageVersion Include="Microsoft.Windows.SDK.BuildTools" Version="10.0.22621.1" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Primitives" Version="6.0.0" />
|
||||
<PackageVersion Include="Microsoft.Net.Http.Headers" Version="2.2.255.255" />
|
||||
<PackageVersion Include="Microsoft.Extensions.DependencyInjection" Version="6.0.0" />
|
||||
<!--<PackageVersion Include="CommunityToolkit.Mvvm" Version="7.1.2" />-->
|
||||
<PackageVersion Include="System.Text.Encoding.CodePages" Version="6.0.0" />
|
||||
<PackageVersion Include="System.Diagnostics.PerformanceCounter" Version="6.0.1" />
|
||||
<PackageVersion Include="System.Net.Http.WinHttpHandler" Version="6.0.1" />
|
||||
|
||||
<!-- AppCenter.* -->
|
||||
<PackageVersion Include="Microsoft.AppCenter" Version="4.5.1" />
|
||||
<PackageVersion Include="Microsoft.AppCenter.Analytics" Version="4.5.1" />
|
||||
<PackageVersion Include="Microsoft.AppCenter.Crashes" Version="4.5.1" />
|
||||
<PackageVersion Include="Aigio.Avalonia.AppCenter" Version="4.5.1" />
|
||||
<PackageVersion Include="Aigio.Avalonia.AppCenter.Analytics" Version="4.5.1" />
|
||||
<PackageVersion Include="Aigio.Avalonia.AppCenter.Crashes" Version="4.5.1" />
|
||||
<PackageVersion Include="Microsoft.AppCenter" Version="4.5.3" />
|
||||
<PackageVersion Include="Microsoft.AppCenter.Analytics" Version="4.5.3" />
|
||||
<PackageVersion Include="Microsoft.AppCenter.Crashes" Version="4.5.3" />
|
||||
<PackageVersion Include="Aigio.Avalonia.AppCenter" Version="4.5.3" />
|
||||
<PackageVersion Include="Aigio.Avalonia.AppCenter.Analytics" Version="4.5.3" />
|
||||
<PackageVersion Include="Aigio.Avalonia.AppCenter.Crashes" Version="4.5.3" />
|
||||
<!--<PackageVersion Include="nor0x.AppCenter" Version="4.4.0-XMAC" />
|
||||
<PackageVersion Include="nor0x.AppCenter.Analytics" Version="4.4.0-XMAC" />
|
||||
<PackageVersion Include="nor0x.AppCenter.Crashes" Version="4.4.0-XMAC" />-->
|
||||
@@ -112,18 +114,18 @@
|
||||
<PackageVersion Include="NUnit3TestAdapter" Version="4.2.1" />
|
||||
<PackageVersion Include="nunit.xamarin" Version="3.6.1" />
|
||||
<PackageVersion Include="PCLStorage" Version="1.0.2" />
|
||||
<PackageVersion Include="Moq" Version="4.18.1" />
|
||||
<PackageVersion Include="Moq" Version="4.18.2" />
|
||||
|
||||
<!-- NLog -->
|
||||
<PackageVersion Include="NLog" Version="5.0.1" />
|
||||
<PackageVersion Include="NLog.Extensions.Logging" Version="5.0.1" />
|
||||
<PackageVersion Include="NLog.Web.AspNetCore" Version="5.1.0" />
|
||||
<PackageVersion Include="NLog" Version="5.0.2" />
|
||||
<PackageVersion Include="NLog.Extensions.Logging" Version="5.0.2" />
|
||||
<PackageVersion Include="NLog.Web.AspNetCore" Version="5.1.1" />
|
||||
<PackageVersion Include="AspNetCore2.NETStandard.NLog.Web.AspNetCore" Version="5.0.0" />
|
||||
|
||||
<!-- ReactiveUI -->
|
||||
<PackageVersion Include="System.Reactive" Version="5.0.0" />
|
||||
<PackageVersion Include="Splat" Version="14.4.1" />
|
||||
<PackageVersion Include="DynamicData" Version="7.9.7" />
|
||||
<PackageVersion Include="DynamicData" Version="7.9.14" />
|
||||
<PackageVersion Include="ReactiveUI" Version="18.3.1" />
|
||||
<PackageVersion Include="ReactiveUI.Fody" Version="18.3.1" />
|
||||
<PackageVersion Include="ReactiveUI.XamForms" Version="18.3.1" />
|
||||
@@ -131,11 +133,11 @@
|
||||
<PackageVersion Include="ReactiveUI.Maui" Version="18.3.1" />
|
||||
<!-- SkiaSharp & HarfBuzzSharp -->
|
||||
<!-- https://github.com/AvaloniaUI/Avalonia/blob/master/build/HarfBuzzSharp.props -->
|
||||
<PackageVersion Include="HarfBuzzSharp" Version="2.8.2.1-preview.91" />
|
||||
<PackageVersion Include="HarfBuzzSharp.NativeAssets.Linux" Version="2.8.2.1-preview.91" />
|
||||
<PackageVersion Include="HarfBuzzSharp" Version="2.8.2.1-preview.104" />
|
||||
<PackageVersion Include="HarfBuzzSharp.NativeAssets.Linux" Version="2.8.2.1-preview.104" />
|
||||
<!-- https://github.com/AvaloniaUI/Avalonia/blob/master/build/SkiaSharp.props -->
|
||||
<PackageVersion Include="SkiaSharp" Version="2.88.1-preview.91" />
|
||||
<PackageVersion Include="SkiaSharp.NativeAssets.Linux" Version="2.88.1-preview.91" />
|
||||
<PackageVersion Include="SkiaSharp" Version="2.88.1-preview.104" />
|
||||
<PackageVersion Include="SkiaSharp.NativeAssets.Linux" Version="2.88.1-preview.104" />
|
||||
|
||||
<!-- AutoMapper -->
|
||||
<PackageVersion Include="AutoMapper" Version="11.0.1" />
|
||||
@@ -191,7 +193,7 @@
|
||||
<PackageVersion Include="NPOI" Version="2.5.6" />
|
||||
|
||||
<!-- Github API -->
|
||||
<PackageVersion Include="Octokit" Version="1.0.0" />
|
||||
<PackageVersion Include="Octokit" Version="2.0.1" />
|
||||
|
||||
<!-- ORM -->
|
||||
<PackageVersion Include="sqlite-net-pcl" Version="1.8.116" />
|
||||
@@ -209,7 +211,7 @@
|
||||
<PackageVersion Include="Newtonsoft.Json" Version="13.0.1" />
|
||||
|
||||
<!-- Html Parser -->
|
||||
<PackageVersion Include="HtmlAgilityPack" Version="1.11.43" />
|
||||
<PackageVersion Include="HtmlAgilityPack" Version="1.11.45" />
|
||||
|
||||
<!-- Clipboard -->
|
||||
<PackageVersion Include="TextCopy" Version="6.1.0" />
|
||||
@@ -237,31 +239,31 @@
|
||||
<PackageVersion Include="Polly" Version="7.2.3" />
|
||||
|
||||
<!--Avalonia Start-->
|
||||
<PackageVersion Include="Avalonia" Version="0.10.17" />
|
||||
<PackageVersion Include="Avalonia.Desktop" Version="0.10.17" />
|
||||
<PackageVersion Include="Avalonia.Skia" Version="0.10.17" />
|
||||
<PackageVersion Include="Avalonia.Diagnostics" Version="0.10.17" />
|
||||
<PackageVersion Include="Avalonia.ReactiveUI" Version="0.10.17" />
|
||||
<PackageVersion Include="Avalonia" Version="0.10.18" />
|
||||
<PackageVersion Include="Avalonia.Desktop" Version="0.10.18" />
|
||||
<PackageVersion Include="Avalonia.Skia" Version="0.10.18" />
|
||||
<PackageVersion Include="Avalonia.Diagnostics" Version="0.10.18" />
|
||||
<PackageVersion Include="Avalonia.ReactiveUI" Version="0.10.18" />
|
||||
<!--<PackageVersion Include="Avalonia.Direct2D1" Version="0.10.16" />-->
|
||||
<PackageVersion Include="Avalonia.Controls.DataGrid" Version="0.10.17" />
|
||||
<PackageVersion Include="Avalonia.Controls.DataGrid" Version="0.10.18" />
|
||||
<PackageVersion Include="Avalonia.Xaml.Behaviors" Version="0.10.17" />
|
||||
<!--<PackageVersion Include="Avalonia.AvaloniaEdit" Version="0.10.12.2" />-->
|
||||
<!--<PackageVersion Include="CefNet.Avalonia" Version="93.1.21240.857" />-->
|
||||
<!--<PackageVersion Include="LibVLCSharp.Avalonia" Version="3.5.1" />-->
|
||||
<!--<PackageVersion Include="FluentAvaloniaUI" Version="1.4.1" />-->
|
||||
<PackageVersion Include="LiveChartsCore.SkiaSharpView.Avalonia" Version="2.0.0-beta.330" />
|
||||
<PackageVersion Include="LiveChartsCore.SkiaSharpView.Avalonia" Version="2.0.0-beta.350" />
|
||||
<PackageVersion Include="JumpLists.Win32.Avalonia" Version="1.0.0" />
|
||||
<PackageVersion Include="Avalonia.Native" Version="0.10.17" />
|
||||
<PackageVersion Include="Avalonia.Win32" Version="0.10.17" />
|
||||
<PackageVersion Include="Avalonia.X11" Version="0.10.17" />
|
||||
<PackageVersion Include="Avalonia.Native" Version="0.10.18" />
|
||||
<PackageVersion Include="Avalonia.Win32" Version="0.10.18" />
|
||||
<PackageVersion Include="Avalonia.X11" Version="0.10.18" />
|
||||
<!--<PackageVersion Include="Avalonia.HtmlRenderer" Version="0.10.0" />-->
|
||||
<PackageVersion Include="WebView2.Avalonia" Version="1.0.1264.42-preview.220713.10" />
|
||||
<!--Avalonia End-->
|
||||
|
||||
<!-- Xamarin.Android -->
|
||||
<PackageVersion Include="Xamarin.Build.Download" Version="0.11.2" />
|
||||
<PackageVersion Include="Xamarin.Google.Dagger" Version="2.40.5" />
|
||||
<PackageVersion Include="Xamarin.Google.MLKit.BarcodeScanning" Version="116.1.2.5" />
|
||||
<PackageVersion Include="Xamarin.Build.Download" Version="0.11.3" />
|
||||
<PackageVersion Include="Xamarin.Google.Dagger" Version="2.41.0.2" />
|
||||
<PackageVersion Include="Xamarin.Google.MLKit.BarcodeScanning" Version="117.0.2.2" />
|
||||
<PackageVersion Include="Plugin.CurrentActivity" Version="2.1.0.4" />
|
||||
<PackageVersion Include="de.hdodenhof.circleimageview" Version="3.1.0.4" />
|
||||
<PackageVersion Include="Square.OkHttp3" Version="4.9.3" />
|
||||
@@ -273,29 +275,29 @@
|
||||
<!-- V2Ray -->
|
||||
<PackageVersion Include="XAB.AndroidLibV2rayLite" Version="1.0.0.1" />
|
||||
<!--<PackageVersion Include="Karamunting.KotlinX.Coroutines.Core" Version="1.4.2" />-->
|
||||
<PackageVersion Include="Xamarin.KotlinX.Coroutines.Core" Version="1.6.2.1" />
|
||||
<PackageVersion Include="Xamarin.KotlinX.Coroutines.Android" Version="1.6.2.1" />
|
||||
<PackageVersion Include="Xamarin.KotlinX.Coroutines.Core" Version="1.6.4" />
|
||||
<PackageVersion Include="Xamarin.KotlinX.Coroutines.Android" Version="1.6.4" />
|
||||
<PackageVersion Include="Xamarin.Android.ReactiveX.RxJava3.RxJava" Version="3.1.5.1" />
|
||||
|
||||
<!-- Xamarin.AndroidX -->
|
||||
<PackageVersion Include="Xamarin.AndroidX.Migration" Version="1.0.10" />
|
||||
<PackageVersion Include="Xamarin.AndroidX.AppCompat" Version="1.4.2.1" />
|
||||
<PackageVersion Include="Xamarin.AndroidX.Emoji.AppCompat" Version="1.1.0.9" />
|
||||
<PackageVersion Include="Xamarin.AndroidX.Fragment" Version="1.4.1.2" />
|
||||
<PackageVersion Include="Xamarin.AndroidX.Fragment" Version="1.5.1" />
|
||||
<PackageVersion Include="Xamarin.AndroidX.RecyclerView" Version="1.2.1.7" />
|
||||
<PackageVersion Include="Xamarin.Google.Android.Material" Version="1.6.1.1" />
|
||||
<PackageVersion Include="Xamarin.AndroidX.SwipeRefreshLayout" Version="1.1.0.9" />
|
||||
<PackageVersion Include="Xamarin.AndroidX.ConstraintLayout" Version="2.1.4.1" />
|
||||
<PackageVersion Include="Xamarin.AndroidX.Navigation.UI" Version="2.4.2.1" />
|
||||
<PackageVersion Include="Xamarin.AndroidX.Navigation.Fragment" Version="2.4.2.1" />
|
||||
<PackageVersion Include="Xamarin.AndroidX.Navigation.UI" Version="2.5.1" />
|
||||
<PackageVersion Include="Xamarin.AndroidX.Navigation.Fragment" Version="2.5.1" />
|
||||
<PackageVersion Include="Xamarin.AndroidX.ViewPager2" Version="1.0.0.16" />
|
||||
<PackageVersion Include="Xamarin.AndroidX.Activity" Version="1.4.0.4" />
|
||||
<PackageVersion Include="Xamarin.AndroidX.Activity" Version="1.5.1" />
|
||||
<PackageVersion Include="Xamarin.AndroidX.Biometric" Version="1.1.0.10" />
|
||||
<PackageVersion Include="Xamarin.AndroidX.ExifInterface" Version="1.3.3.6" />
|
||||
<PackageVersion Include="Xamarin.AndroidX.Legacy.Support.Core.UI" Version="1.0.0.15" />
|
||||
<PackageVersion Include="Xamarin.AndroidX.Lifecycle.LiveData" Version="2.4.1.2" />
|
||||
<PackageVersion Include="Xamarin.AndroidX.Camera.Camera2" Version="1.1.0-beta02" />
|
||||
<PackageVersion Include="Xamarin.AndroidX.Camera.Lifecycle" Version="1.1.0-beta02" />
|
||||
<PackageVersion Include="Xamarin.AndroidX.Lifecycle.LiveData" Version="2.5.1" />
|
||||
<PackageVersion Include="Xamarin.AndroidX.Camera.Camera2" Version="1.1.0" />
|
||||
<PackageVersion Include="Xamarin.AndroidX.Camera.Lifecycle" Version="1.1.0" />
|
||||
<PackageVersion Include="Xamarin.AndroidX.Camera.View" Version="1.1.0-beta02" />
|
||||
<!--<PackageVersion Include="Xamarin.AndroidX.Window" Version="1.0.0.9" />-->
|
||||
<PackageVersion Include="Xamarin.AndroidX.WebKit" Version="1.4.0.8" />
|
||||
@@ -305,7 +307,7 @@
|
||||
|
||||
<!-- UWP -->
|
||||
<PackageVersion Include="Microsoft.Toolkit.Uwp.Notifications" Version="7.1.2" />
|
||||
<PackageVersion Include="Microsoft.WindowsAppSDK" Version="1.1.3" />
|
||||
<PackageVersion Include="Microsoft.WindowsAppSDK" Version="1.1.4" />
|
||||
|
||||
<!-- Code Style -->
|
||||
<PackageVersion Include="StyleCop.Analyzers" Version="1.2.0-beta.435" />
|
||||
@@ -321,5 +323,8 @@
|
||||
|
||||
<!-- ArchiSteamFarm -->
|
||||
<PackageVersion Include="ArchiSteamFarm.Library" Version="5.2.6.3-preview.220608" />
|
||||
|
||||
<!-- DBreeze(key value store embedded) -->
|
||||
<PackageVersion Include="DBreeze" Version="1.107.0" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
<configuration>
|
||||
<packageSources>
|
||||
<add key="LocalNuGetPackages" value="./references/NuGetPackages" />
|
||||
</packageSources>
|
||||
</configuration>
|
||||
+2
-2
@@ -17,7 +17,7 @@ English | [简体中文](./README.md)
|
||||
[](https://docs.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-10)
|
||||
|
||||
[](https://github.com/AvaloniaUI/Avalonia)
|
||||
[](https://github.com/xamarin/Xamarin.Forms)
|
||||
[](https://github.com/dotnet/maui)
|
||||
[](https://github.com/ant-design/ant-design)
|
||||
[](https://github.com/ant-design-blazor/ant-design-blazor)
|
||||
|
||||
@@ -183,7 +183,7 @@ Read what we [milestones](https://github.com/BeyondDimension/SteamTools/mileston
|
||||
- [Avalonia for Visual Studio(Optional)](https://marketplace.visualstudio.com/items?itemName=AvaloniaTeam.AvaloniaforVisualStudio)
|
||||
- [NUnit VS Templates(Optional)](https://marketplace.visualstudio.com/items?itemName=NUnitDevelopers.NUnitTemplatesforVisualStudio)
|
||||
|
||||
[OpenJDK 11](https://docs.microsoft.com/java/openjdk/download#openjdk-11)
|
||||
[OpenJDK 17](https://docs.microsoft.com/en-us/java/openjdk/download#openjdk-17)
|
||||
[Android Studio 2021.1.1 Or Higher](https://developer.android.com/studio/)
|
||||
[Xcode 13 Or Higher](https://developer.apple.com/xcode/)
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
[](https://docs.microsoft.com/zh-cn/dotnet/csharp/whats-new/csharp-10)
|
||||
|
||||
[](https://github.com/AvaloniaUI/Avalonia)
|
||||
[](https://github.com/xamarin/Xamarin.Forms)
|
||||
[](https://github.com/dotnet/maui)
|
||||
[](https://github.com/ant-design/ant-design)
|
||||
[](https://github.com/ant-design-blazor/ant-design-blazor)
|
||||
|
||||
@@ -194,7 +194,7 @@ OS | Version | Architectures |
|
||||
- [Avalonia for Visual Studio(可选)](https://marketplace.visualstudio.com/items?itemName=AvaloniaTeam.AvaloniaforVisualStudio)
|
||||
- [NUnit VS Templates(可选)](https://marketplace.visualstudio.com/items?itemName=NUnitDevelopers.NUnitTemplatesforVisualStudio)
|
||||
|
||||
[OpenJDK 11](https://docs.microsoft.com/zh-cn/java/openjdk/download#openjdk-11)
|
||||
[OpenJDK 17](https://docs.microsoft.com/zh-cn/java/openjdk/download#openjdk-17)
|
||||
[Android Studio 2021.1.1 或更高版本](https://developer.android.google.cn/studio/)
|
||||
[Xcode 13 或更高版本](https://developer.apple.com/xcode/)
|
||||
|
||||
|
||||
+1
-64
@@ -14,6 +14,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "_Root", "_Root", "{66839CD8
|
||||
download-guide.md = download-guide.md
|
||||
global.json = global.json
|
||||
packaging\Info.plist = packaging\Info.plist
|
||||
NuGet.Config = NuGet.Config
|
||||
packaging\package.net472.ps1 = packaging\package.net472.ps1
|
||||
README.en.md = README.en.md
|
||||
README.md = README.md
|
||||
@@ -173,8 +174,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Gameloop.Vdf", "references\
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ST.Client.Android.Shadowsocks", "src\ST.Client.Android.Shadowsocks\ST.Client.Android.Shadowsocks.csproj", "{920EF29F-406F-43A9-B002-29DD6B0B4FBF}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ST.Client.Android.App.Modern", "src\ST.Client.Android.App.Modern\ST.Client.Android.App.Modern.csproj", "{5E2ABB4A-FDD6-47B0-946D-FAAD31301B82}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ResSecrets", "ResSecrets", "{17667B53-1A09-45A6-855A-0F07F9AE044F}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ST.Client.Android.ResSecrets", "src\ST.Client.Android.ResSecrets\ST.Client.Android.ResSecrets.csproj", "{68DD3E9C-A96C-4128-8D95-DCD65D1FA277}"
|
||||
@@ -189,8 +188,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ST.Client.Windows.ResSecret
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Desktop", "src\Avalonia.Desktop\Avalonia.Desktop.csproj", "{FA9F0E89-E410-400A-86F3-C603E51E62A8}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ST.Client.Avalonia.App.MsixPackage", "src\ST.Client.Desktop.Avalonia.App.Bridge2\ST.Client.Avalonia.App.MsixPackage.csproj", "{7E7C34F0-1391-473C-8A1C-CE3EAF5D66DB}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FDELauncher", "src\FDELauncher\FDELauncher.csproj", "{7AAA8A01-A4FE-49AD-9664-AC53C4C8EAC6}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Net.Http.Headers", "src\Microsoft.Net.Http.Headers\Microsoft.Net.Http.Headers.csproj", "{C7F109B9-EA88-4B7E-ADB5-D6407686ADE0}"
|
||||
@@ -1937,36 +1934,6 @@ Global
|
||||
{920EF29F-406F-43A9-B002-29DD6B0B4FBF}.Release|x64.Build.0 = Release|Any CPU
|
||||
{920EF29F-406F-43A9-B002-29DD6B0B4FBF}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{920EF29F-406F-43A9-B002-29DD6B0B4FBF}.Release|x86.Build.0 = Release|Any CPU
|
||||
{5E2ABB4A-FDD6-47B0-946D-FAAD31301B82}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{5E2ABB4A-FDD6-47B0-946D-FAAD31301B82}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{5E2ABB4A-FDD6-47B0-946D-FAAD31301B82}.Debug|Any CPU.Deploy.0 = Debug|Any CPU
|
||||
{5E2ABB4A-FDD6-47B0-946D-FAAD31301B82}.Debug|ARM.ActiveCfg = Debug|Any CPU
|
||||
{5E2ABB4A-FDD6-47B0-946D-FAAD31301B82}.Debug|ARM.Build.0 = Debug|Any CPU
|
||||
{5E2ABB4A-FDD6-47B0-946D-FAAD31301B82}.Debug|ARM64.ActiveCfg = Debug|Any CPU
|
||||
{5E2ABB4A-FDD6-47B0-946D-FAAD31301B82}.Debug|ARM64.Build.0 = Debug|Any CPU
|
||||
{5E2ABB4A-FDD6-47B0-946D-FAAD31301B82}.Debug|iPhone.ActiveCfg = Debug|Any CPU
|
||||
{5E2ABB4A-FDD6-47B0-946D-FAAD31301B82}.Debug|iPhone.Build.0 = Debug|Any CPU
|
||||
{5E2ABB4A-FDD6-47B0-946D-FAAD31301B82}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
|
||||
{5E2ABB4A-FDD6-47B0-946D-FAAD31301B82}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
|
||||
{5E2ABB4A-FDD6-47B0-946D-FAAD31301B82}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{5E2ABB4A-FDD6-47B0-946D-FAAD31301B82}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{5E2ABB4A-FDD6-47B0-946D-FAAD31301B82}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{5E2ABB4A-FDD6-47B0-946D-FAAD31301B82}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{5E2ABB4A-FDD6-47B0-946D-FAAD31301B82}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{5E2ABB4A-FDD6-47B0-946D-FAAD31301B82}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{5E2ABB4A-FDD6-47B0-946D-FAAD31301B82}.Release|Any CPU.Deploy.0 = Release|Any CPU
|
||||
{5E2ABB4A-FDD6-47B0-946D-FAAD31301B82}.Release|ARM.ActiveCfg = Release|Any CPU
|
||||
{5E2ABB4A-FDD6-47B0-946D-FAAD31301B82}.Release|ARM.Build.0 = Release|Any CPU
|
||||
{5E2ABB4A-FDD6-47B0-946D-FAAD31301B82}.Release|ARM64.ActiveCfg = Release|Any CPU
|
||||
{5E2ABB4A-FDD6-47B0-946D-FAAD31301B82}.Release|ARM64.Build.0 = Release|Any CPU
|
||||
{5E2ABB4A-FDD6-47B0-946D-FAAD31301B82}.Release|iPhone.ActiveCfg = Release|Any CPU
|
||||
{5E2ABB4A-FDD6-47B0-946D-FAAD31301B82}.Release|iPhone.Build.0 = Release|Any CPU
|
||||
{5E2ABB4A-FDD6-47B0-946D-FAAD31301B82}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
|
||||
{5E2ABB4A-FDD6-47B0-946D-FAAD31301B82}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
|
||||
{5E2ABB4A-FDD6-47B0-946D-FAAD31301B82}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{5E2ABB4A-FDD6-47B0-946D-FAAD31301B82}.Release|x64.Build.0 = Release|Any CPU
|
||||
{5E2ABB4A-FDD6-47B0-946D-FAAD31301B82}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{5E2ABB4A-FDD6-47B0-946D-FAAD31301B82}.Release|x86.Build.0 = Release|Any CPU
|
||||
{68DD3E9C-A96C-4128-8D95-DCD65D1FA277}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{68DD3E9C-A96C-4128-8D95-DCD65D1FA277}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{68DD3E9C-A96C-4128-8D95-DCD65D1FA277}.Debug|ARM.ActiveCfg = Debug|Any CPU
|
||||
@@ -2135,36 +2102,6 @@ Global
|
||||
{FA9F0E89-E410-400A-86F3-C603E51E62A8}.Release|x64.Build.0 = Release|Any CPU
|
||||
{FA9F0E89-E410-400A-86F3-C603E51E62A8}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{FA9F0E89-E410-400A-86F3-C603E51E62A8}.Release|x86.Build.0 = Release|Any CPU
|
||||
{7E7C34F0-1391-473C-8A1C-CE3EAF5D66DB}.Debug|Any CPU.ActiveCfg = Debug|x64
|
||||
{7E7C34F0-1391-473C-8A1C-CE3EAF5D66DB}.Debug|Any CPU.Build.0 = Debug|x64
|
||||
{7E7C34F0-1391-473C-8A1C-CE3EAF5D66DB}.Debug|ARM.ActiveCfg = Debug|Any CPU
|
||||
{7E7C34F0-1391-473C-8A1C-CE3EAF5D66DB}.Debug|ARM.Build.0 = Debug|Any CPU
|
||||
{7E7C34F0-1391-473C-8A1C-CE3EAF5D66DB}.Debug|ARM64.ActiveCfg = Debug|arm64
|
||||
{7E7C34F0-1391-473C-8A1C-CE3EAF5D66DB}.Debug|ARM64.Build.0 = Debug|arm64
|
||||
{7E7C34F0-1391-473C-8A1C-CE3EAF5D66DB}.Debug|iPhone.ActiveCfg = Debug|Any CPU
|
||||
{7E7C34F0-1391-473C-8A1C-CE3EAF5D66DB}.Debug|iPhone.Build.0 = Debug|Any CPU
|
||||
{7E7C34F0-1391-473C-8A1C-CE3EAF5D66DB}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
|
||||
{7E7C34F0-1391-473C-8A1C-CE3EAF5D66DB}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
|
||||
{7E7C34F0-1391-473C-8A1C-CE3EAF5D66DB}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{7E7C34F0-1391-473C-8A1C-CE3EAF5D66DB}.Debug|x64.Build.0 = Debug|x64
|
||||
{7E7C34F0-1391-473C-8A1C-CE3EAF5D66DB}.Debug|x64.Deploy.0 = Debug|x64
|
||||
{7E7C34F0-1391-473C-8A1C-CE3EAF5D66DB}.Debug|x86.ActiveCfg = Debug|x86
|
||||
{7E7C34F0-1391-473C-8A1C-CE3EAF5D66DB}.Debug|x86.Build.0 = Debug|x86
|
||||
{7E7C34F0-1391-473C-8A1C-CE3EAF5D66DB}.Release|Any CPU.ActiveCfg = Release|x64
|
||||
{7E7C34F0-1391-473C-8A1C-CE3EAF5D66DB}.Release|Any CPU.Build.0 = Release|x64
|
||||
{7E7C34F0-1391-473C-8A1C-CE3EAF5D66DB}.Release|ARM.ActiveCfg = Release|Any CPU
|
||||
{7E7C34F0-1391-473C-8A1C-CE3EAF5D66DB}.Release|ARM.Build.0 = Release|Any CPU
|
||||
{7E7C34F0-1391-473C-8A1C-CE3EAF5D66DB}.Release|ARM64.ActiveCfg = Release|arm64
|
||||
{7E7C34F0-1391-473C-8A1C-CE3EAF5D66DB}.Release|ARM64.Build.0 = Release|arm64
|
||||
{7E7C34F0-1391-473C-8A1C-CE3EAF5D66DB}.Release|iPhone.ActiveCfg = Release|Any CPU
|
||||
{7E7C34F0-1391-473C-8A1C-CE3EAF5D66DB}.Release|iPhone.Build.0 = Release|Any CPU
|
||||
{7E7C34F0-1391-473C-8A1C-CE3EAF5D66DB}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
|
||||
{7E7C34F0-1391-473C-8A1C-CE3EAF5D66DB}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
|
||||
{7E7C34F0-1391-473C-8A1C-CE3EAF5D66DB}.Release|x64.ActiveCfg = Release|x64
|
||||
{7E7C34F0-1391-473C-8A1C-CE3EAF5D66DB}.Release|x64.Build.0 = Release|x64
|
||||
{7E7C34F0-1391-473C-8A1C-CE3EAF5D66DB}.Release|x64.Deploy.0 = Release|x64
|
||||
{7E7C34F0-1391-473C-8A1C-CE3EAF5D66DB}.Release|x86.ActiveCfg = Release|x86
|
||||
{7E7C34F0-1391-473C-8A1C-CE3EAF5D66DB}.Release|x86.Build.0 = Release|x86
|
||||
{7AAA8A01-A4FE-49AD-9664-AC53C4C8EAC6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{7AAA8A01-A4FE-49AD-9664-AC53C4C8EAC6}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{7AAA8A01-A4FE-49AD-9664-AC53C4C8EAC6}.Debug|ARM.ActiveCfg = Debug|Any CPU
|
||||
|
||||
@@ -7,9 +7,9 @@
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>net.steampp.app</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>2.8.1</string>
|
||||
<string>2.8.3</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>2.8.1.0</string>
|
||||
<string>2.8.3.0</string>
|
||||
<key>LSMinimumSystemVersion</key>
|
||||
<string>10.15</string>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
dotnet publish "..\src\ST.Client.Desktop.Avalonia.App\ST.Client.Avalonia.App.csproj" -c Release -p:PublishProfile=win-x64 -p:DeployOnBuild=true -p:ExtraDefineConstants=win-x64 --nologo
|
||||
Submodule references/ArchiSteamFarm updated: c0f13bf7fd...1d900cc7b7
Submodule references/FluentAvalonia updated: 8098c1c22a...50c719a4b9
Binary file not shown.
Binary file not shown.
+33
-46
@@ -3,44 +3,31 @@
|
||||
2. 因经济状况原因,现已停止短信服务节约开销,后续会推出邮箱注册登录,对于仅使用手机号登录的用户请绑定第三方快速登录,否则注销后将无法再次登录,需要等待至邮箱服务推出后支持会暂时在开放短信服务提供换绑邮箱。
|
||||
3. 自动更新目前仅 Windows 端可用,且由于下载渠道限速可能导致无法更新成功,推荐在官网链接的网盘或群文件中下载压缩包解压覆盖更新(应用商店版由商店更新不受此影响)
|
||||
4. 在 Android 上因系统限制,目前的加速功能无法正常使用,所以此功能已放弃继续开发,如仍想使用需要自行导入证书到系统目录,使用 adb 工具或 Magisk 之类的软件操作,未来会使用不需要证书的加速功能替换此功能
|
||||
5. fde 版本需要安装 [ASP.NET Core 运行时 6.0.7 (x64) 与 .NET Core 运行时 6.0.7 (x64)](https://dotnet.microsoft.com/zh-cn/download/dotnet/6.0)
|
||||
5. fde 版本需要安装 [ASP.NET Core 运行时 6.0.8 (x64) 与 .NET Core 运行时 6.0.8 (x64)](https://dotnet.microsoft.com/zh-cn/download/dotnet/6.0)
|
||||
6. Windows x86 与 x64 版本令牌本机加密互不兼容,使用两者版本时注意令牌加密后的文件不能共用。
|
||||
7. 由于新版本加速功能重构,调整了部分加速项目,这会影响旧版本程序使用加速功能
|
||||
8. 为了能继续维持开发,从此版本开始将会添加程序内广告,赞助用户可以在设置中关闭所有广告
|
||||
|
||||
### 版本亮点
|
||||
1. 新增 Steam 云存档管理功能,可自行上传或删除 Steam 云存档
|
||||
2. 库存游戏支持筛选支持 Steam 云存档的游戏
|
||||
3. ASF 升级至 V5.2.7.7
|
||||
4. .NET 运行时升级至 6.0.7,使用 fde 版本需要升级运行时
|
||||
5. 库存游戏中解锁成就与挂时长支持 macOS 与 Linux 系统
|
||||
6. 在设置中可关闭托盘,关闭托盘后关闭主窗口即退出程序
|
||||
7. 使用 Yarp.ReverseProxy 重写了反代加速和脚本功能,大幅提升稳定性与性能
|
||||
8. Windows 新增 DNS 驱动拦截模式进行本地加速
|
||||
9. 令牌交易现在支持查看交易详情,可以确认交易方的Steam注册时间等信息
|
||||
10. 恢复 Windows x86(32 位) 版本发布
|
||||
1. .NET 运行时升级至 6.0.8,使用 fde 版本需要升级运行时
|
||||
2. ASF 升级至 V5.2.8.4
|
||||
|
||||
### 修复问题
|
||||
1. 修复 窗口在某些情况下最大化或最小化恢复时窗口大小会变化的问题
|
||||
2. 修复 库存游戏 编辑 Steam 游戏封面时选择自定义图片失败的问题
|
||||
3. 修复 程序内在显示某些图片时会出现错乱马赛克的问题
|
||||
4. 修复 Linux 上点击关于页面可能因字体引发闪退
|
||||
5. 修复 MIUI Android 11 ~ 12 中绑定或换绑手机号页面闪退
|
||||
6. 修复 Android 本地加速中已知问题弹窗显示时不应同时跳转引导证书页
|
||||
7. 修复 令牌交易确认要求输本地令牌密钥时点击取消也能进行交易确认的问题
|
||||
8. 修复 令牌加载输入密码解密时点击取消或输入错误密码没有移除此前的数据问题
|
||||
9. 修复 2.8.1 中出现因脚本导致的启动加速服务失败
|
||||
10. 修复 网络加速中图标在跟随系统的浅色模式下字体颜色不应为白色
|
||||
11. 修复 系统代理模式与 PAC 代理模式中监听地址为 0.0.0.0 时出现的错误
|
||||
12. 修复 切换页面 UI 布局错乱
|
||||
1. 修复 2.8.0 ~ 2.8.2 中 AppData/Config.mpo 文件大小为 0 时导致的程序无法正常启动
|
||||
2. 修复 Windows 上版本更新在 2.8.0 ~ 2.8.2 中非 FDE 版本识别为 FDE 版
|
||||
3. 改进 调整代理模式顺序,Hosts 模式置顶
|
||||
4. 修复 划词翻译等一些 JS 脚本
|
||||
5. 改进 macOS 上的证书安装
|
||||
6. 尝试修复 网络加速中能导致内存泄露的问题
|
||||
7. 改进 非简中默认隐藏加速与脚本仅在 Windows 上生效
|
||||
8. 改进 减少 Android 上的启动时间
|
||||
9. 修复 退出程序时可能引发的闪退
|
||||
10. 修复 macOS/Linux 上设置页面打不开的问题
|
||||
11. 修复 网络加速中网页上出现 Because https://... actually points to WattToolkit itself...
|
||||
12. 尝试修复 ASF 控制台中输入时卡死
|
||||
|
||||
<!--
|
||||
|
||||
TODO:
|
||||
Android 第三方快速登录回调改为 URL Scheme
|
||||
修复 Android 检查更新检测不到最新版本的问题
|
||||
|
||||
|
||||
### 已知问题
|
||||
- 除 Windows 之外的平台此软件自动更新尚不可用
|
||||
- Desktop
|
||||
@@ -74,7 +61,7 @@ Android 第三方快速登录回调改为 URL Scheme
|
||||
|
||||
|
||||
[](https://steampp.net)
|
||||
[]()
|
||||
[]()
|
||||
|
||||
|
||||
##### [不知道该下载哪个文件?](./download-guide.md)
|
||||
@@ -83,28 +70,28 @@ Android 第三方快速登录回调改为 URL Scheme
|
||||
### 文件校验
|
||||
| File | Checksum (SHA256) |
|
||||
| ---- | ---- |
|
||||
| Steam++_win_x64_v2.8.1.7z | SHA256 |
|
||||
| Steam++_win_x64_fde_v2.8.1.7z | SHA256 |
|
||||
| Steam++_win_x64_v2.8.3.7z | SHA256 |
|
||||
| Steam++_win_x64_fde_v2.8.3.7z | SHA256 |
|
||||
| | |
|
||||
| Steam++_win_x64_v2.8.1.exe | SHA256 |
|
||||
| Steam++_win_x64_fde_v2.8.1.exe | SHA256 |
|
||||
| Steam++_win_x64_v2.8.3.exe | SHA256 |
|
||||
| Steam++_win_x64_fde_v2.8.3.exe | SHA256 |
|
||||
| | |
|
||||
| Steam++_win_x86_v2.8.1.7z | SHA256 |
|
||||
| Steam++_win_x86_fde_v2.8.1.7z | SHA256 |
|
||||
| Steam++_win_x86_v2.8.3.7z | SHA256 |
|
||||
| Steam++_win_x86_fde_v2.8.3.7z | SHA256 |
|
||||
| | |
|
||||
| Steam++_win_x86_v2.8.1.exe | SHA256 |
|
||||
| Steam++_win_x86_fde_v2.8.1.exe | SHA256 |
|
||||
| Steam++_win_x86_v2.8.3.exe | SHA256 |
|
||||
| Steam++_win_x86_fde_v2.8.3.exe | SHA256 |
|
||||
| | |
|
||||
| Steam++_linux_x64_v2.8.1.7z | SHA256 |
|
||||
| Steam++_linux_arm64_v2.8.1.7z | SHA256 |
|
||||
| Steam++_linux_x64_v2.8.3.7z | SHA256 |
|
||||
| Steam++_linux_arm64_v2.8.3.7z | SHA256 |
|
||||
| | |
|
||||
| Steam++_linux_x64_v2.8.1.deb | SHA256 |
|
||||
| Steam++_linux_arm64_v2.8.1.deb | SHA256 |
|
||||
| Steam++_linux_x64_v2.8.3.deb | SHA256 |
|
||||
| Steam++_linux_arm64_v2.8.3.deb | SHA256 |
|
||||
| | |
|
||||
| Steam++_linux_x64_v2.8.1.rpm | SHA256 |
|
||||
| Steam++_linux_arm64_v2.8.1.rpm | SHA256 |
|
||||
| Steam++_linux_x64_v2.8.3.rpm | SHA256 |
|
||||
| Steam++_linux_arm64_v2.8.3.rpm | SHA256 |
|
||||
| | |
|
||||
| Steam++_macos_x64_v2.8.1.dmg | SHA256 |
|
||||
| Steam++_macos_arm64_v2.8.1.dmg | SHA256 |
|
||||
| Steam++_macos_x64_v2.8.3.dmg | SHA256 |
|
||||
| Steam++_macos_arm64_v2.8.3.dmg | SHA256 |
|
||||
| | |
|
||||
| Steam++_android_v2.8.1.apk | SHA256 |
|
||||
| Steam++_android_v2.8.3.apk | SHA256 |
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 10 KiB |
@@ -5,7 +5,11 @@ using Android.OS;
|
||||
using Android.Webkit;
|
||||
using Android.Util;
|
||||
using Android.Opengl;
|
||||
#if NET6_0_OR_GREATER
|
||||
using XEPlatform = Microsoft.Maui.ApplicationModel.Platform;
|
||||
#else
|
||||
using XEPlatform = Xamarin.Essentials.Platform;
|
||||
#endif
|
||||
using Process = System.Diagnostics.Process;
|
||||
using AndroidApplication = Android.App.Application;
|
||||
#endif
|
||||
@@ -543,7 +547,7 @@ namespace System.Application.UI
|
||||
b.Append(Essentials.IsSupported.ToLowerString());
|
||||
b.AppendLine();
|
||||
|
||||
#if __ANDROID__
|
||||
#if __ANDROID__ && !NET6_0_MAUI_LIB
|
||||
b.Append("[startup.track] ");
|
||||
b.AppendLine();
|
||||
b.Append(MainApplication.StartupTrack);
|
||||
|
||||
@@ -0,0 +1,219 @@
|
||||
// https://github.com/AvaloniaUI/Avalonia/blob/0.10.17/src/Avalonia.Controls/ApplicationLifetimes/ClassicDesktopStyleApplicationLifetime.cs
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Threading;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Controls.ApplicationLifetimes;
|
||||
using Avalonia.Interactivity;
|
||||
using Avalonia.Platform;
|
||||
using Avalonia.Threading;
|
||||
|
||||
namespace Avalonia.Controls.ApplicationLifetimes
|
||||
{
|
||||
sealed class ClassicDesktopStyleApplicationLifetime2 : IClassicDesktopStyleApplicationLifetime, IDisposable
|
||||
{
|
||||
private int _exitCode;
|
||||
private CancellationTokenSource? _cts;
|
||||
private bool _isShuttingDown;
|
||||
private HashSet<Window> _windows = new();
|
||||
|
||||
private static ClassicDesktopStyleApplicationLifetime2? _activeLifetime;
|
||||
|
||||
static ClassicDesktopStyleApplicationLifetime2()
|
||||
{
|
||||
Window.WindowOpenedEvent.AddClassHandler(typeof(Window), OnWindowOpened);
|
||||
Window.WindowClosedEvent.AddClassHandler(typeof(Window), WindowClosedEvent);
|
||||
}
|
||||
|
||||
private static void WindowClosedEvent(object sender, RoutedEventArgs e)
|
||||
{
|
||||
_activeLifetime?._windows.Remove((Window)sender);
|
||||
_activeLifetime?.HandleWindowClosed((Window)sender);
|
||||
}
|
||||
|
||||
private static void OnWindowOpened(object sender, RoutedEventArgs e)
|
||||
{
|
||||
_activeLifetime?._windows.Add((Window)sender);
|
||||
}
|
||||
|
||||
public ClassicDesktopStyleApplicationLifetime2()
|
||||
{
|
||||
if (_activeLifetime != null)
|
||||
throw new InvalidOperationException(
|
||||
"Can not have multiple active ClassicDesktopStyleApplicationLifetime instances and the previously created one was not disposed");
|
||||
_activeLifetime = this;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public event EventHandler<ControlledApplicationLifetimeStartupEventArgs>? Startup;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public event EventHandler<ShutdownRequestedEventArgs>? ShutdownRequested;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public event EventHandler<ControlledApplicationLifetimeExitEventArgs>? Exit;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the arguments passed to the AppBuilder Start method.
|
||||
/// </summary>
|
||||
public string[]? Args { get; set; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public ShutdownMode ShutdownMode { get; set; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public Window? MainWindow { get; set; }
|
||||
|
||||
public IReadOnlyList<Window> Windows => _windows.ToList();
|
||||
|
||||
private void HandleWindowClosed(Window window)
|
||||
{
|
||||
if (window == null)
|
||||
return;
|
||||
|
||||
if (_isShuttingDown)
|
||||
return;
|
||||
|
||||
if (ShutdownMode == ShutdownMode.OnLastWindowClose && _windows.Count == 0)
|
||||
TryShutdown();
|
||||
else if (ShutdownMode == ShutdownMode.OnMainWindowClose && ReferenceEquals(window, MainWindow))
|
||||
TryShutdown();
|
||||
}
|
||||
|
||||
public void Shutdown(int exitCode = 0)
|
||||
{
|
||||
DoShutdown(new ShutdownRequestedEventArgs(), true, exitCode);
|
||||
}
|
||||
|
||||
public bool TryShutdown(int exitCode = 0)
|
||||
{
|
||||
return DoShutdown(new ShutdownRequestedEventArgs(), false, exitCode);
|
||||
}
|
||||
|
||||
public int Start(string[] args)
|
||||
{
|
||||
Startup?.Invoke(this, new ControlledApplicationLifetimeStartupEventArgs(args));
|
||||
|
||||
var options = AvaloniaLocator.Current.GetService<ClassicDesktopStyleApplicationLifetimeOptions>();
|
||||
|
||||
if (options != null && options.ProcessUrlActivationCommandLine && args.Length > 0)
|
||||
{
|
||||
if (Application.Current is IApplicationPlatformEvents events)
|
||||
{
|
||||
events.RaiseUrlsOpened(args);
|
||||
}
|
||||
}
|
||||
|
||||
var lifetimeEvents = AvaloniaLocator.Current.GetService<IPlatformLifetimeEventsImpl>();
|
||||
|
||||
if (lifetimeEvents != null)
|
||||
lifetimeEvents.ShutdownRequested += OnShutdownRequested;
|
||||
|
||||
_cts = new CancellationTokenSource();
|
||||
|
||||
// Note due to a bug in the JIT we wrap this in a method, otherwise MainWindow
|
||||
// gets stuffed into a local var and can not be GCed until after the program stops.
|
||||
// this method never exits until program end.
|
||||
ShowMainWindow();
|
||||
|
||||
Dispatcher.UIThread.MainLoop(_cts.Token);
|
||||
Environment.ExitCode = _exitCode;
|
||||
return _exitCode;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
private void ShowMainWindow()
|
||||
{
|
||||
MainWindow?.Show();
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (_activeLifetime == this)
|
||||
_activeLifetime = null;
|
||||
}
|
||||
|
||||
private bool DoShutdown(ShutdownRequestedEventArgs e, bool force = false, int exitCode = 0)
|
||||
{
|
||||
if (!force)
|
||||
{
|
||||
ShutdownRequested?.Invoke(this, e);
|
||||
|
||||
if (e.Cancel)
|
||||
return false;
|
||||
|
||||
// https://appcenter.ms/orgs/BeyondDimension/apps/Steam/crashes/errors/3780037517u/overview
|
||||
// ClassicDesktopStyleApplicationLifetime.DoShutdown (ShutdownRequestedEventArgs e, Boolean force, Int32 exitCode) /_/src/Avalonia.Controls/ApplicationLifetimes/ClassicDesktopStyleApplicationLifetime.cs, line 147
|
||||
// System.InvalidOperationException: Application is already shutting down.
|
||||
// v2.8.2 | 73 users | 76 reports
|
||||
// Exception Stack:
|
||||
// System.InvalidOperationException: Application is already shutting down.
|
||||
// at Avalonia.Controls.ApplicationLifetimes.ClassicDesktopStyleApplicationLifetime.DoShutdown(ShutdownRequestedEventArgs e, Boolean force, Int32 exitCode) in /_/src/Avalonia.Controls/ApplicationLifetimes/ClassicDesktopStyleApplicationLifetime.cs:line 147
|
||||
// at Avalonia.Controls.ApplicationLifetimes.ClassicDesktopStyleApplicationLifetime.OnShutdownRequested(Object sender, ShutdownRequestedEventArgs e) in /_/src/Avalonia.Controls/ApplicationLifetimes/ClassicDesktopStyleApplicationLifetime.cs:line 186
|
||||
// at Avalonia.Win32.Win32Platform.WndProc(IntPtr hWnd, UInt32 msg, IntPtr wParam, IntPtr lParam) in /_/src/Windows/Avalonia.Win32/Win32Platform.cs:line 283
|
||||
if (_isShuttingDown)
|
||||
// throw new InvalidOperationException("Application is already shutting down.");
|
||||
return true;
|
||||
}
|
||||
|
||||
_exitCode = exitCode;
|
||||
_isShuttingDown = true;
|
||||
|
||||
try
|
||||
{
|
||||
// When an OS shutdown request is received, try to close all non-owned windows. Windows can cancel
|
||||
// shutdown by setting e.Cancel = true in the Closing event. Owned windows will be shutdown by their
|
||||
// owners.
|
||||
foreach (var w in Windows)
|
||||
{
|
||||
if (w.Owner is null)
|
||||
{
|
||||
w.Close();
|
||||
}
|
||||
}
|
||||
|
||||
if (!force && Windows.Count > 0)
|
||||
{
|
||||
e.Cancel = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
var args = new ControlledApplicationLifetimeExitEventArgs(exitCode);
|
||||
Exit?.Invoke(this, args);
|
||||
_exitCode = args.ApplicationExitCode;
|
||||
}
|
||||
finally
|
||||
{
|
||||
_cts?.Cancel();
|
||||
_cts = null;
|
||||
_isShuttingDown = false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void OnShutdownRequested(object sender, ShutdownRequestedEventArgs e) => DoShutdown(e);
|
||||
}
|
||||
}
|
||||
|
||||
namespace Avalonia
|
||||
{
|
||||
public static class ClassicDesktopStyleApplicationLifetimeExtensions
|
||||
{
|
||||
public static int StartWithClassicDesktopLifetime2<T>(
|
||||
this T builder, string[] args, ShutdownMode shutdownMode = ShutdownMode.OnLastWindowClose)
|
||||
where T : AppBuilderBase<T>, new()
|
||||
{
|
||||
var lifetime = new ClassicDesktopStyleApplicationLifetime2()
|
||||
{
|
||||
Args = args,
|
||||
ShutdownMode = shutdownMode,
|
||||
};
|
||||
builder.SetupWithLifetime(lifetime);
|
||||
return lifetime.Start(args);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,7 +3,11 @@ using Microsoft.Extensions.DependencyInjection;
|
||||
using AndroidToast = Android.Widget.Toast;
|
||||
using AndroidToastLength = Android.Widget.ToastLength;
|
||||
using JException = Java.Lang.Exception;
|
||||
#if NET6_0_OR_GREATER
|
||||
using XEPlatform = Microsoft.Maui.ApplicationModel.Platform;
|
||||
#else
|
||||
using XEPlatform = Xamarin.Essentials.Platform;
|
||||
#endif
|
||||
|
||||
namespace System.Application.Services.Implementation
|
||||
{
|
||||
|
||||
@@ -7,7 +7,11 @@ using AndroidX.AppCompat.App;
|
||||
using AndroidX.Fragment.App;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Threading.Tasks;
|
||||
#if NET6_0_OR_GREATER
|
||||
using Microsoft.Maui.ApplicationModel;
|
||||
#else
|
||||
using Xamarin.Essentials;
|
||||
#endif
|
||||
using Fragment = AndroidX.Fragment.App.Fragment;
|
||||
using Toolbar = AndroidX.AppCompat.Widget.Toolbar;
|
||||
|
||||
|
||||
@@ -3,9 +3,17 @@ using Android.OS;
|
||||
using Android.Provider;
|
||||
using Android.Webkit;
|
||||
using System.IO;
|
||||
#if NET6_0_OR_GREATER
|
||||
using Microsoft.Maui.ApplicationModel;
|
||||
#else
|
||||
using Xamarin.Essentials;
|
||||
#endif
|
||||
using AndroidUri = Android.Net.Uri;
|
||||
#if NET6_0_OR_GREATER
|
||||
using XEPlatform = Microsoft.Maui.ApplicationModel.Platform;
|
||||
#else
|
||||
using XEPlatform = Xamarin.Essentials.Platform;
|
||||
#endif
|
||||
|
||||
// ReSharper disable once CheckNamespace
|
||||
namespace System
|
||||
|
||||
@@ -11,11 +11,22 @@ namespace System.Application;
|
||||
/// </summary>
|
||||
public static class MainThread2
|
||||
{
|
||||
static IMainThreadPlatformService? mInterface;
|
||||
|
||||
static IMainThreadPlatformService Interface
|
||||
{
|
||||
get
|
||||
{
|
||||
if (mInterface == null) mInterface = IMainThreadPlatformService.Instance;
|
||||
return mInterface;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取当前是否为主线程。
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool IsMainThread() => IMainThreadPlatformService.Instance.PlatformIsMainThread;
|
||||
public static bool IsMainThread() => Interface.PlatformIsMainThread;
|
||||
|
||||
/// <summary>
|
||||
/// 调用应用程序主线程上的操作。
|
||||
@@ -33,7 +44,7 @@ public static class MainThread2
|
||||
{
|
||||
try
|
||||
{
|
||||
IMainThreadPlatformService.Instance.PlatformBeginInvokeOnMainThread(action, priority);
|
||||
Interface.PlatformBeginInvokeOnMainThread(action, priority);
|
||||
}
|
||||
catch (InvalidOperationException)
|
||||
{
|
||||
|
||||
+130
-5
@@ -1,6 +1,7 @@
|
||||
using SQLite;
|
||||
using System.Application.Entities;
|
||||
using System.Application.Repositories;
|
||||
using System.Application.Settings;
|
||||
using System.Diagnostics;
|
||||
using System.Globalization;
|
||||
using SQLiteNotNull = SQLite.NotNullAttribute;
|
||||
@@ -8,8 +9,13 @@ using SQLiteTable = SQLite.TableAttribute;
|
||||
|
||||
namespace System.Application.Services.Implementation;
|
||||
|
||||
internal sealed class PreferencesPlatformServiceImpl : IPreferencesGenericPlatformService
|
||||
#if DBREEZE
|
||||
partial class PreferencesPlatformServiceImplV2
|
||||
#else
|
||||
sealed class PreferencesPlatformServiceImpl : IPreferencesGenericPlatformService
|
||||
#endif
|
||||
{
|
||||
#if !DBREEZE
|
||||
readonly SQLiteConnection conn;
|
||||
|
||||
public PreferencesPlatformServiceImpl()
|
||||
@@ -17,9 +23,9 @@ internal sealed class PreferencesPlatformServiceImpl : IPreferencesGenericPlatfo
|
||||
conn = Repository.GetDbConnectionSync<Entity>();
|
||||
}
|
||||
|
||||
static string GetId(string key) => $"{key}_K";
|
||||
internal static string GetId(string key) => $"{key}_K";
|
||||
|
||||
static string GetId(string key, string? sharedName)
|
||||
internal static string GetId(string key, string? sharedName)
|
||||
{
|
||||
if (sharedName == null) return GetId(key);
|
||||
return $"{key}_{sharedName}_S";
|
||||
@@ -75,12 +81,14 @@ internal sealed class PreferencesPlatformServiceImpl : IPreferencesGenericPlatfo
|
||||
});
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
const string TableName = "1984415E";
|
||||
const string ColumnName_Id = "0F5E4BAA";
|
||||
const string ColumnName_Value = "4FC331D7";
|
||||
const string ColumnName_SharedName = "F6A739AA";
|
||||
|
||||
#if !DBREEZE
|
||||
const string Sql_Delete_Where_SharedName_Equals =
|
||||
$"DELETE FROM \"{TableName}\" WHERE \"{ColumnName_SharedName}\" = ?";
|
||||
|
||||
@@ -101,12 +109,12 @@ internal sealed class PreferencesPlatformServiceImpl : IPreferencesGenericPlatfo
|
||||
|
||||
//const string Sql_Select_Value_Where_Key_Equals_And_SharedName_Equals =
|
||||
// $"SELECT \"{ColumnName_Value}\" FROM \"{TableName}\" WHERE \"{ColumnName_Id}\" = ? AND \"{ColumnName_SharedName}\" = ?";
|
||||
#endif
|
||||
|
||||
[SQLiteTable(TableName)]
|
||||
[DebuggerDisplay("{DebuggerDisplay(),nq}")]
|
||||
public sealed class Entity : IEntity<string>
|
||||
{
|
||||
#pragma warning disable CS8618 // 在退出构造函数时,不可为 null 的字段必须包含非 null 值。请考虑声明为可以为 null。
|
||||
[Column(ColumnName_Id)]
|
||||
[PrimaryKey]
|
||||
[SQLiteNotNull]
|
||||
@@ -115,7 +123,6 @@ internal sealed class PreferencesPlatformServiceImpl : IPreferencesGenericPlatfo
|
||||
[Column(ColumnName_Value)]
|
||||
[SQLiteNotNull]
|
||||
public string Value { get; set; } = string.Empty;
|
||||
#pragma warning restore CS8618 // 在退出构造函数时,不可为 null 的字段必须包含非 null 值。请考虑声明为可以为 null。
|
||||
|
||||
[Column(ColumnName_SharedName)]
|
||||
[SQLiteNotNull]
|
||||
@@ -124,3 +131,121 @@ internal sealed class PreferencesPlatformServiceImpl : IPreferencesGenericPlatfo
|
||||
string DebuggerDisplay() => SharedName == string.Empty ? $"{Id}, {Value}" : $"{Id}, {Value}, {SharedName}";
|
||||
}
|
||||
}
|
||||
|
||||
#if DBREEZE
|
||||
public sealed partial class PreferencesPlatformServiceImplV2 : IPreferencesGenericPlatformService
|
||||
{
|
||||
static string GetTableName(string? sharedName) => $"Preferences.{sharedName}";
|
||||
|
||||
public void PlatformSet<T>(string key, T? value, string? sharedName) where T : notnull, IConvertible
|
||||
{
|
||||
if (value == null)
|
||||
{
|
||||
PlatformRemove(key, sharedName);
|
||||
}
|
||||
else
|
||||
{
|
||||
using var t = SettingsProviderV3.Provider.GetTransaction();
|
||||
t.Insert(GetTableName(sharedName), key, value.ToString(CultureInfo.InvariantCulture));
|
||||
t.Commit();
|
||||
}
|
||||
}
|
||||
|
||||
public void PlatformClear(string? sharedName)
|
||||
{
|
||||
using var t = SettingsProviderV3.Provider.GetTransaction();
|
||||
t.RemoveAllKeys(GetTableName(sharedName), true);
|
||||
t.Commit();
|
||||
}
|
||||
|
||||
public bool PlatformContainsKey(string key, string? sharedName)
|
||||
{
|
||||
using var t = SettingsProviderV3.Provider.GetTransaction();
|
||||
var row = t.Select<string, string>(GetTableName(sharedName), key);
|
||||
if (row == null) return false;
|
||||
return row.Exists;
|
||||
}
|
||||
|
||||
public T? PlatformGet<T>(string key, T? defaultValue, string? sharedName) where T : notnull, IConvertible
|
||||
{
|
||||
using var t = SettingsProviderV3.Provider.GetTransaction();
|
||||
var row = t.Select<string, string>(GetTableName(sharedName), key);
|
||||
if (row == null || !row.Exists) return defaultValue;
|
||||
var value = ConvertibleHelper.Convert<T>(row.Value);
|
||||
return value;
|
||||
}
|
||||
|
||||
public void PlatformRemove(string key, string? sharedName)
|
||||
{
|
||||
using var t = SettingsProviderV3.Provider.GetTransaction();
|
||||
t.RemoveKey(GetTableName(sharedName), key);
|
||||
t.Commit();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 将数据从
|
||||
/// <para>使用 SQLiteConnection 的 PreferencesPlatformServiceImpl</para>
|
||||
/// <para>迁移到</para>
|
||||
/// <para>使用 DBreeze 的 PreferencesPlatformServiceImplV2</para>
|
||||
/// </summary>
|
||||
public static void Migrate()
|
||||
{
|
||||
var dbPath = Repository.DataBaseDirectory;
|
||||
bool isOK = false;
|
||||
bool exists = false;
|
||||
try
|
||||
{
|
||||
var dbFilePath = Path.Combine(dbPath, "application2.dbf");
|
||||
try
|
||||
{
|
||||
exists = File.Exists(dbFilePath);
|
||||
}
|
||||
catch
|
||||
{
|
||||
|
||||
}
|
||||
if (!exists) return;
|
||||
var conn = new SQLiteConnection(dbPath, SQLiteOpenFlags.ReadWrite | SQLiteOpenFlags.Create | SQLiteOpenFlags.SharedCache);
|
||||
var items = conn.Table<Entity>().ToArray();
|
||||
if (items.Any())
|
||||
{
|
||||
var itemGroups = items.GroupBy(x => x.SharedName).ToArray();
|
||||
foreach (var itemGroup in itemGroups)
|
||||
{
|
||||
using var t = SettingsProviderV3.Provider.GetTransaction();
|
||||
foreach (var item in itemGroup)
|
||||
{
|
||||
if (item.Value != null)
|
||||
{
|
||||
string key;
|
||||
if (string.IsNullOrEmpty(item.SharedName)) key = item.Id.TrimEnd("_K");
|
||||
else key = item.Id.TrimEnd($"_{item.SharedName}_S");
|
||||
var tableName = GetTableName(item.SharedName);
|
||||
t.Insert(tableName, key, item.Value.ToString(CultureInfo.InvariantCulture));
|
||||
}
|
||||
}
|
||||
t.Commit();
|
||||
}
|
||||
}
|
||||
conn.Close();
|
||||
conn.Dispose();
|
||||
isOK = true;
|
||||
}
|
||||
catch
|
||||
{
|
||||
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (isOK)
|
||||
{
|
||||
var files = Directory.GetFiles(dbPath, "application2*");
|
||||
foreach (var item in files)
|
||||
{
|
||||
IOPath.FileTryDelete(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -62,7 +62,11 @@ public static class ServiceCollectionExtensions
|
||||
/// <returns></returns>
|
||||
public static IServiceCollection AddRepositoryPreferences(this IServiceCollection services)
|
||||
{
|
||||
#if DBREEZE
|
||||
services.AddSingleton<IPreferencesPlatformService, PreferencesPlatformServiceImplV2>();
|
||||
#else
|
||||
services.AddSingleton<IPreferencesPlatformService, PreferencesPlatformServiceImpl>();
|
||||
#endif
|
||||
return services;
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
#if !DBREEZE
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
|
||||
namespace System.Application.Settings;
|
||||
@@ -60,7 +61,7 @@ public sealed class FileSettingsProvider : ISerializationProvider
|
||||
|
||||
lock (_sync)
|
||||
{
|
||||
using var stream = new FileStream(_path, FileMode.Create, FileAccess.ReadWrite, FileShare.ReadWrite | FileShare.Delete);
|
||||
using var stream = OpenReadWrite(_path);
|
||||
if (stream.Position > 0)
|
||||
{
|
||||
stream.Position = 0;
|
||||
@@ -71,6 +72,28 @@ public sealed class FileSettingsProvider : ISerializationProvider
|
||||
}
|
||||
}
|
||||
|
||||
static FileStream OpenReadWrite(string path)
|
||||
{
|
||||
try
|
||||
{
|
||||
return OpenReadWrite();
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
if (File.Exists(path))
|
||||
{
|
||||
if (IOPath.FileTryDelete(path))
|
||||
{
|
||||
// 直接打开文件流失败,尝试删除后再打开一次
|
||||
return OpenReadWrite();
|
||||
}
|
||||
}
|
||||
throw;
|
||||
}
|
||||
|
||||
FileStream OpenReadWrite() => new(path, FileMode.Create, FileAccess.ReadWrite, FileShare.ReadWrite | FileShare.Delete);
|
||||
}
|
||||
|
||||
public void Load()
|
||||
{
|
||||
if (File.Exists(_path))
|
||||
@@ -82,7 +105,7 @@ public sealed class FileSettingsProvider : ISerializationProvider
|
||||
{
|
||||
stream.Position = 0;
|
||||
}
|
||||
var source = Serializable.DMP<IDictionary<string, object?>>(stream);
|
||||
var source = stream.Length == 0 ? null : Serializable.DMP<IDictionary<string, object?>>(stream);
|
||||
//var source = XamlServices.Load(stream) as IDictionary<string, object>;
|
||||
_settings = source == null
|
||||
? new SortedDictionary<string, object?>()
|
||||
@@ -107,4 +130,5 @@ public sealed class FileSettingsProvider : ISerializationProvider
|
||||
add { }
|
||||
remove { }
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -1,3 +1,4 @@
|
||||
#if !DBREEZE
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
|
||||
namespace System.Application.Settings;
|
||||
@@ -22,4 +23,5 @@ public interface ISerializationProvider
|
||||
bool RemoveValue(string key);
|
||||
|
||||
string ToJsonString();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -1,3 +1,4 @@
|
||||
#if !DBREEZE
|
||||
using System.ComponentModel;
|
||||
using System.Diagnostics;
|
||||
|
||||
@@ -194,4 +195,5 @@ public class SerializableProperty<T> : INotifyPropertyChanged
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -1,3 +1,4 @@
|
||||
#if !DBREEZE
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace System.Application.Settings;
|
||||
@@ -43,23 +44,96 @@ public static class SettingsHost
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(nameof(SettingsHost), "Config Save", ex);
|
||||
Log.Error(nameof(SettingsHost), ex, "Config Save");
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 尝试备份配置
|
||||
/// </summary>
|
||||
/// <param name="bakLocalFilePath"></param>
|
||||
static void TryBackup(string bakLocalFilePath)
|
||||
{
|
||||
try
|
||||
{
|
||||
var localFilePath = SettingsHostBase.LocalFilePath;
|
||||
if (File.Exists(localFilePath))
|
||||
{
|
||||
File.Copy(localFilePath, bakLocalFilePath, true);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(nameof(SettingsHost), ex, "Config TryBackUp");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 尝试还原备份配置,返回是否有备份文件
|
||||
/// </summary>
|
||||
/// <param name="bakLocalFilePath"></param>
|
||||
/// <returns></returns>
|
||||
static bool TryRestoreBackup(string bakLocalFilePath)
|
||||
{
|
||||
try
|
||||
{
|
||||
var exists = File.Exists(bakLocalFilePath);
|
||||
if (exists)
|
||||
{
|
||||
var localFilePath = SettingsHostBase.LocalFilePath;
|
||||
File.Copy(bakLocalFilePath, localFilePath, true);
|
||||
}
|
||||
return exists;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(nameof(SettingsHost), ex, "Config TryRestoreBackup");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 尝试删除配置
|
||||
/// </summary>
|
||||
static void TryDelete()
|
||||
{
|
||||
try
|
||||
{
|
||||
var localFilePath = SettingsHostBase.LocalFilePath;
|
||||
if (File.Exists(localFilePath))
|
||||
{
|
||||
File.Delete(localFilePath);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(nameof(SettingsHost), ex, "Config TryDelete");
|
||||
}
|
||||
}
|
||||
|
||||
public static void Load()
|
||||
{
|
||||
var isLoad = false;
|
||||
var bakLocalFilePath = $"{SettingsHostBase.LocalFilePath}.bak";
|
||||
try
|
||||
{
|
||||
SettingsHostBase.Local.Load();
|
||||
isLoad = true;
|
||||
|
||||
TryBackup(bakLocalFilePath);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(nameof(SettingsHost), "Config Load", ex);
|
||||
if (!TryRestoreBackup(bakLocalFilePath))
|
||||
{
|
||||
// 还原备份文件失败,删除当前配置
|
||||
TryDelete();
|
||||
}
|
||||
|
||||
File.Delete(SettingsHostBase.LocalFilePath);
|
||||
SettingsHostBase.Local.Load();
|
||||
Log.Error(nameof(SettingsHost), ex, $"Config Load, isLoad: {isLoad}");
|
||||
|
||||
if (!isLoad) SettingsHostBase.Local.Load();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -85,5 +159,6 @@ public abstract class SettingsHost2<TSettings> : SettingsHostBase where TSetting
|
||||
|
||||
static string GetKey(string propertyName) => $"{CategoryName}.{propertyName}";
|
||||
|
||||
public static SerializableProperty<T> GetProperty<T>(T? defaultValue, bool autoSave, [CallerMemberName] string propertyName = "") => GetProperty(GetKey, Local, defaultValue, autoSave, propertyName);
|
||||
}
|
||||
public static SerializableProperty<T> GetProperty<T>(T? defaultValue, bool autoSave = true, [CallerMemberName] string propertyName = "") => GetProperty(GetKey, Local, defaultValue, autoSave, propertyName);
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,168 @@
|
||||
#if DBREEZE
|
||||
using DBreeze;
|
||||
using System.ComponentModel;
|
||||
using System.Diagnostics;
|
||||
using static System.Application.Settings.ISerializableProperty;
|
||||
|
||||
namespace System.Application.Settings;
|
||||
|
||||
public interface ISerializableProperty : INotifyPropertyChanged
|
||||
{
|
||||
public const string tableName = "SerializableProperty";
|
||||
}
|
||||
|
||||
[DebuggerDisplay("Value={Value}, Key={Key}, Default={Default}")]
|
||||
public sealed class SerializableProperty<T> : ISerializableProperty, INotifyPropertyChanged
|
||||
{
|
||||
T? _value;
|
||||
bool _cached;
|
||||
DBreezeEngine? _Provider;
|
||||
|
||||
public string Key { get; }
|
||||
|
||||
public DBreezeEngine Provider { get => _Provider ?? throw new ArgumentNullException(nameof(_Provider)); set => _Provider = value; }
|
||||
|
||||
public T? Default { get; }
|
||||
|
||||
public T? Value
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!_cached)
|
||||
{
|
||||
{
|
||||
using var t = Provider.GetTransaction();
|
||||
var row = t.Select<string, byte[]>(tableName, Key);
|
||||
if (row?.Value == null)
|
||||
{
|
||||
_value = Default;
|
||||
}
|
||||
else
|
||||
{
|
||||
try
|
||||
{
|
||||
_value = Serializable.DMP<T>(row.Value);
|
||||
}
|
||||
catch
|
||||
{
|
||||
_value = Default;
|
||||
}
|
||||
}
|
||||
}
|
||||
_cached = true;
|
||||
}
|
||||
|
||||
return _value;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
var comparer = EqualityComparer<T?>.Default;
|
||||
if (_cached && comparer.Equals(_value, value)) return;
|
||||
var old = _value;
|
||||
_value = value;
|
||||
_cached = true;
|
||||
SetValue(comparer, old, value);
|
||||
}
|
||||
}
|
||||
|
||||
void SetValue(EqualityComparer<T?> comparer, T? oldValue, T? newValue)
|
||||
{
|
||||
{
|
||||
using var t = Provider.GetTransaction();
|
||||
if (comparer.Equals(newValue, Default))
|
||||
t.RemoveKey(tableName, Key);
|
||||
else
|
||||
t.Insert(tableName, Key, Serializable.SMP(newValue));
|
||||
t.Commit();
|
||||
}
|
||||
OnValueChanged(oldValue, newValue);
|
||||
}
|
||||
|
||||
public override string ToString() => Value?.ToString() ?? string.Empty;
|
||||
|
||||
public void RaiseValueChanged()
|
||||
{
|
||||
var comparer = EqualityComparer<T?>.Default;
|
||||
SetValue(comparer, _value, _value);
|
||||
}
|
||||
|
||||
public SerializableProperty(string key, DBreezeEngine provider) : this(key, provider, default)
|
||||
{
|
||||
}
|
||||
|
||||
public SerializableProperty(string key, DBreezeEngine provider, T? defaultValue)
|
||||
{
|
||||
Key = key;
|
||||
Provider = provider;
|
||||
Default = defaultValue;
|
||||
}
|
||||
|
||||
public IDisposable Subscribe(Action<T?> listener, bool notifyOnInitialValue = true)
|
||||
{
|
||||
if (notifyOnInitialValue)
|
||||
listener(Value);
|
||||
return new ValueChangedEventListener(this, listener);
|
||||
}
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
var comparer = EqualityComparer<T?>.Default;
|
||||
SetValue(comparer, _value, Default);
|
||||
}
|
||||
|
||||
sealed class ValueChangedEventListener : IDisposable
|
||||
{
|
||||
private readonly Action<T?> _listener;
|
||||
private readonly SerializableProperty<T> _source;
|
||||
|
||||
public ValueChangedEventListener(SerializableProperty<T> property, Action<T?> listener)
|
||||
{
|
||||
_listener = listener;
|
||||
_source = property;
|
||||
_source.ValueChanged += HandleValueChanged;
|
||||
}
|
||||
|
||||
private void HandleValueChanged(object? sender, ValueChangedEventArgs<T?> args)
|
||||
{
|
||||
_listener(args.NewValue);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_source.ValueChanged -= HandleValueChanged;
|
||||
}
|
||||
}
|
||||
|
||||
public static implicit operator T?(SerializableProperty<T> property) => property.Value;
|
||||
|
||||
#region events
|
||||
|
||||
public event EventHandler<ValueChangedEventArgs<T?>>? ValueChanged;
|
||||
|
||||
void OnValueChanged(T? oldValue, T? newValue) => ValueChanged?.Invoke(this, new ValueChangedEventArgs<T?>(oldValue, newValue));
|
||||
|
||||
readonly Dictionary<PropertyChangedEventHandler, EventHandler<ValueChangedEventArgs<T>>> _handlers = new();
|
||||
|
||||
event PropertyChangedEventHandler? INotifyPropertyChanged.PropertyChanged
|
||||
{
|
||||
add
|
||||
{
|
||||
if (value == null) return;
|
||||
ValueChanged += _handlers[value] = (sender, args) => value(sender, new PropertyChangedEventArgs(nameof(Value)));
|
||||
}
|
||||
|
||||
remove
|
||||
{
|
||||
if (value == null) return;
|
||||
if (_handlers.TryGetValue(value, out var handler))
|
||||
{
|
||||
ValueChanged -= handler;
|
||||
_handlers.Remove(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,28 @@
|
||||
#if DBREEZE
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace System.Application.Settings;
|
||||
|
||||
public static class SettingsHost
|
||||
{
|
||||
public static void Load() => SettingsProviderV3.Load();
|
||||
|
||||
[Obsolete]
|
||||
public static void Save()
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public abstract class SettingsHost2<TSettings> where TSettings : SettingsHost2<TSettings>, new()
|
||||
{
|
||||
static readonly string CategoryName = typeof(TSettings).Name;
|
||||
|
||||
static string GetKey(string propertyName) => $"{CategoryName}.{propertyName}";
|
||||
|
||||
public static SerializableProperty<T> GetProperty<T>(T? defaultValue, [CallerMemberName] string propertyName = "") => new(GetKey(propertyName), SettingsProviderV3.Provider, defaultValue);
|
||||
|
||||
[Obsolete("autoSave N/A")]
|
||||
public static SerializableProperty<T> GetProperty<T>(T? defaultValue, bool autoSave, [CallerMemberName] string propertyName = "") => new(GetKey(propertyName), SettingsProviderV3.Provider, defaultValue);
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,90 @@
|
||||
#if DBREEZE
|
||||
using DBreeze;
|
||||
using DBreeze.Utils;
|
||||
|
||||
namespace System.Application.Settings;
|
||||
|
||||
public static class SettingsProviderV3
|
||||
{
|
||||
static DBreezeEngine? _Provider;
|
||||
static readonly object lock_load = new();
|
||||
|
||||
public static DBreezeEngine Provider
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_Provider == null) Load();
|
||||
return _Provider ?? throw new ArgumentNullException(nameof(_Provider));
|
||||
}
|
||||
set => _Provider = value;
|
||||
}
|
||||
|
||||
public static void Load()
|
||||
{
|
||||
lock (lock_load)
|
||||
{
|
||||
if (_Provider == null)
|
||||
{
|
||||
_Provider = new(Path.Combine(IOPath.AppDataDirectory, "Config"));
|
||||
|
||||
//Setting up NetJSON serializer (from NuGet) to be used by DBreeze
|
||||
CustomSerializator.ByteArraySerializator = value => Serializable.SMP(value == null ? typeof(object) : value.GetType(), value!);
|
||||
CustomSerializator.ByteArrayDeSerializator = (buffer, type) => Serializable.DMP(type, buffer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 将数据从
|
||||
/// <para>使用 <see cref="IDictionary{TKey, TValue}"/> + <see cref="MessagePack"/> 单文件 的 FileSettingsProvider</para>
|
||||
/// <para>迁移到</para>
|
||||
/// <para>使用 DBreeze 的 SettingsProviderV3</para>
|
||||
/// </summary>
|
||||
public static void Migrate()
|
||||
{
|
||||
var filePath = Path.Combine(IOPath.AppDataDirectory, "Config" + FileEx.MPO);
|
||||
bool exists = false;
|
||||
try
|
||||
{
|
||||
exists = File.Exists(filePath);
|
||||
}
|
||||
catch
|
||||
{
|
||||
|
||||
}
|
||||
if (exists)
|
||||
{
|
||||
try
|
||||
{
|
||||
using var stream = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite | FileShare.Delete);
|
||||
if (stream.Position > 0)
|
||||
{
|
||||
stream.Position = 0;
|
||||
}
|
||||
var source = stream.Length == 0 ? null : Serializable.DMP<IDictionary<string, object?>>(stream);
|
||||
if (source.Any_Nullable())
|
||||
{
|
||||
if (_Provider == null) Load();
|
||||
using var t = Provider.GetTransaction();
|
||||
foreach (var item in source)
|
||||
{
|
||||
if (item.Value == null) continue;
|
||||
var valueType = item.Value.GetType();
|
||||
var value = Serializable.SMP(valueType, item.Value);
|
||||
t.Insert(ISerializableProperty.tableName, item.Key, value);
|
||||
}
|
||||
t.Commit();
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
|
||||
}
|
||||
finally
|
||||
{
|
||||
IOPath.FileTryDelete(filePath);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -1,6 +1,6 @@
|
||||
namespace System.Application.Settings;
|
||||
|
||||
public sealed class ValueChangedEventArgs<T> : EventArgs where T : notnull
|
||||
public sealed class ValueChangedEventArgs<T> : EventArgs
|
||||
{
|
||||
public T? OldValue { get; }
|
||||
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
<PackageReference Include="gfoidl.Base64" />
|
||||
<PackageReference Include="MessagePack" />
|
||||
<PackageReference Include="Newtonsoft.Json" />
|
||||
<PackageReference Include="DBreeze" Condition=" $(DefineConstants.Contains('DBREEZE')) " />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup Condition=" $(TargetFramework.StartsWith('netstandard')) ">
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace System;
|
||||
|
||||
public static class Convert2
|
||||
{
|
||||
/// <summary>
|
||||
/// Converts the specified string, which encodes binary data as hex characters, to an equivalent 8-bit unsigned integer array.
|
||||
/// </summary>
|
||||
/// <param name="s">The string to convert.</param>
|
||||
/// <returns>An array of 8-bit unsigned integers that is equivalent to s.</returns>
|
||||
#if HEXMATE || NET5_0_OR_GREATER
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#endif
|
||||
public static byte[] FromHexString(string s)
|
||||
#if HEXMATE
|
||||
=> HexMate.Convert.FromHexString(s);
|
||||
#elif NET5_0_OR_GREATER
|
||||
=> Convert.FromHexString(s);
|
||||
#else
|
||||
{
|
||||
var bytes = new byte[s.Length / 2];
|
||||
for (var i = 0; i < bytes.Length; i++)
|
||||
{
|
||||
bytes[i] = Convert.ToByte(s.Substring(i * 2, 2), 16);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
@@ -77,10 +77,7 @@ public static partial class DI
|
||||
/// <inheritdoc cref="Get{T}"/>
|
||||
public static T? Get_Nullable<T>() where T : notnull
|
||||
{
|
||||
if (value == null)
|
||||
{
|
||||
throw GetDIGetFailException(typeof(T));
|
||||
}
|
||||
if (value == null) return default;
|
||||
return value.GetService<T>();
|
||||
}
|
||||
|
||||
@@ -97,10 +94,7 @@ public static partial class DI
|
||||
/// <inheritdoc cref="Get_Nullable{T}"/>
|
||||
public static object? Get_Nullable(Type serviceType)
|
||||
{
|
||||
if (value == null)
|
||||
{
|
||||
throw GetDIGetFailException(serviceType);
|
||||
}
|
||||
if (value == null) return default;
|
||||
return value.GetService(serviceType);
|
||||
}
|
||||
|
||||
|
||||
@@ -34,4 +34,23 @@ public static class ProcessExtensions
|
||||
process.Kill();
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 尝试 Process 组件在指定的毫秒数内等待关联进程退出。
|
||||
/// </summary>
|
||||
/// <param name="process"></param>
|
||||
/// <param name="milliseconds"></param>
|
||||
/// <returns></returns>
|
||||
public static bool TryWaitForExit(this Process process, int milliseconds = 9000)
|
||||
{
|
||||
try
|
||||
{
|
||||
return process.WaitForExit(milliseconds);
|
||||
}
|
||||
catch
|
||||
{
|
||||
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -3,11 +3,11 @@ namespace System;
|
||||
|
||||
public static partial class StringExtensions
|
||||
{
|
||||
public static string TrimStart(this string s, string trimString)
|
||||
public static string TrimStart(this string s, string value)
|
||||
{
|
||||
if (trimString.StartsWith(trimString))
|
||||
if (s.StartsWith(value))
|
||||
{
|
||||
return s[trimString.Length..];
|
||||
return s[value.Length..];
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -15,11 +15,11 @@ public static partial class StringExtensions
|
||||
}
|
||||
}
|
||||
|
||||
public static string TrimEnd(this string s, string trimString)
|
||||
public static string TrimEnd(this string s, string value)
|
||||
{
|
||||
if (trimString.EndsWith(trimString))
|
||||
if (s.EndsWith(value))
|
||||
{
|
||||
return s[..^trimString.Length];
|
||||
return s[..^value.Length];
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -27,11 +27,11 @@ public static partial class StringExtensions
|
||||
}
|
||||
}
|
||||
|
||||
public static string TrimStart(this string s, string trimString, StringComparison comparisonType)
|
||||
public static string TrimStart(this string s, string value, StringComparison comparisonType)
|
||||
{
|
||||
if (trimString.StartsWith(trimString, comparisonType))
|
||||
if (s.StartsWith(value, comparisonType))
|
||||
{
|
||||
return s[trimString.Length..];
|
||||
return s[value.Length..];
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -39,11 +39,11 @@ public static partial class StringExtensions
|
||||
}
|
||||
}
|
||||
|
||||
public static string TrimEnd(this string s, string trimString, StringComparison comparisonType)
|
||||
public static string TrimEnd(this string s, string value, StringComparison comparisonType)
|
||||
{
|
||||
if (trimString.EndsWith(trimString, comparisonType))
|
||||
if (s.EndsWith(value, comparisonType))
|
||||
{
|
||||
return s[..^trimString.Length];
|
||||
return s[..^value.Length];
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -132,6 +132,11 @@ public static class FileEx
|
||||
public const string Qt = ".qt";
|
||||
public const string Wmv = ".wmv";
|
||||
|
||||
/// <summary>
|
||||
/// 导入注册条目 (.reg) 文件是 Regedit.exe 的功能,Regedt32.exe 不支持。您可以使用 Regedit.exe 对基于 Windows NT 4.0 或基于 Windows 2000 的计算机上注册表进行一些更改,但某些更改需要 Regedt32.exe。
|
||||
/// </summary>
|
||||
public const string Reg = ".reg";
|
||||
|
||||
public static readonly string[] ImageFileExtensions = new string[]
|
||||
{
|
||||
PNG,
|
||||
|
||||
@@ -10,43 +10,43 @@ namespace HexMate;
|
||||
|
||||
static class Convert
|
||||
{
|
||||
// /// <summary>
|
||||
// /// Converts the specified string, which encodes binary data as hex characters, to an equivalent 8-bit unsigned integer array.
|
||||
// /// </summary>
|
||||
// /// <param name="s">The string to convert.</param>
|
||||
// /// <returns>An array of 8-bit unsigned integers that is equivalent to <paramref name="s"/>.</returns>
|
||||
// /// <exception cref="ArgumentNullException"><paramref name="s"/> is <code>null</code>.</exception>
|
||||
// /// <exception cref="FormatException">The length of <paramref name="s"/>, ignoring white-space characters, is not zero or a multiple of 2.</exception>
|
||||
// /// <exception cref="FormatException">The format of <paramref name="s"/> is invalid. <paramref name="s"/> contains a non-hex character.</exception>
|
||||
// public static byte[] FromHexString(string s)
|
||||
// {
|
||||
// if (s == null)
|
||||
// throw new ArgumentNullException(nameof(s));
|
||||
// if (s.Length == 0)
|
||||
// return Array.Empty<byte>();
|
||||
/// <summary>
|
||||
/// Converts the specified string, which encodes binary data as hex characters, to an equivalent 8-bit unsigned integer array.
|
||||
/// </summary>
|
||||
/// <param name="s">The string to convert.</param>
|
||||
/// <returns>An array of 8-bit unsigned integers that is equivalent to <paramref name="s"/>.</returns>
|
||||
/// <exception cref="ArgumentNullException"><paramref name="s"/> is <code>null</code>.</exception>
|
||||
/// <exception cref="FormatException">The length of <paramref name="s"/>, ignoring white-space characters, is not zero or a multiple of 2.</exception>
|
||||
/// <exception cref="FormatException">The format of <paramref name="s"/> is invalid. <paramref name="s"/> contains a non-hex character.</exception>
|
||||
public static byte[] FromHexString(string s)
|
||||
{
|
||||
if (s == null)
|
||||
throw new ArgumentNullException(nameof(s));
|
||||
if (s.Length == 0)
|
||||
return Array.Empty<byte>();
|
||||
|
||||
// unsafe
|
||||
// {
|
||||
// fixed (char* inPtr = s)
|
||||
// {
|
||||
// var resultLength = FromHex_ComputeResultLength(inPtr, s.Length);
|
||||
//#if NET5_0_OR_GREATER
|
||||
// var result = GC.AllocateUninitializedArray<byte>(resultLength);
|
||||
//#else
|
||||
// var result = new byte[resultLength];
|
||||
//#endif
|
||||
// fixed (byte* outPtr = result)
|
||||
// {
|
||||
// var res = ConvertFromHexArray(outPtr, result.Length, inPtr, s.Length);
|
||||
// if (res < 0)
|
||||
// throw new FormatException(SR.Format_BadHexChar);
|
||||
// Debug.Assert(res == result.Length);
|
||||
// }
|
||||
unsafe
|
||||
{
|
||||
fixed (char* inPtr = s)
|
||||
{
|
||||
var resultLength = FromHex_ComputeResultLength(inPtr, s.Length);
|
||||
#if NET5_0_OR_GREATER
|
||||
var result = GC.AllocateUninitializedArray<byte>(resultLength);
|
||||
#else
|
||||
var result = new byte[resultLength];
|
||||
#endif
|
||||
fixed (byte* outPtr = result)
|
||||
{
|
||||
var res = ConvertFromHexArray(outPtr, result.Length, inPtr, s.Length);
|
||||
if (res < 0)
|
||||
throw new FormatException(SR.Format_BadHexChar);
|
||||
Debug.Assert(res == result.Length);
|
||||
}
|
||||
|
||||
// return result;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts an array of 8-bit unsigned integers to its equivalent string representation that is encoded with hex characters.
|
||||
@@ -251,108 +251,108 @@ static class Convert
|
||||
return false;
|
||||
}
|
||||
|
||||
// private static unsafe int FromHex_ComputeResultLength(char* inputPtr, int inputLength)
|
||||
// => CharHelper.CountUsefulCharacters(inputPtr, inputLength) / 2;
|
||||
private static unsafe int FromHex_ComputeResultLength(char* inputPtr, int inputLength)
|
||||
=> CharHelper.CountUsefulCharacters(inputPtr, inputLength) / 2;
|
||||
|
||||
// private static unsafe int ConvertFromHexArray(byte* outData, int outLength, char* inChars, int inLength)
|
||||
// {
|
||||
// Debug.Assert(inLength != 0);
|
||||
// if (inLength == 1)
|
||||
// {
|
||||
// return errInvalidData;
|
||||
// }
|
||||
private static unsafe int ConvertFromHexArray(byte* outData, int outLength, char* inChars, int inLength)
|
||||
{
|
||||
Debug.Assert(inLength != 0);
|
||||
if (inLength == 1)
|
||||
{
|
||||
return errInvalidData;
|
||||
}
|
||||
|
||||
// var charsToRead = inLength;
|
||||
// var remainingIn = charsToRead;
|
||||
// var bytesToWrite = outLength;
|
||||
// var remainingOut = bytesToWrite;
|
||||
// var src = inChars;
|
||||
// var last = src;
|
||||
// var srcEnd = src + inLength;
|
||||
// var dest = outData;
|
||||
var charsToRead = inLength;
|
||||
var remainingIn = charsToRead;
|
||||
var bytesToWrite = outLength;
|
||||
var remainingOut = bytesToWrite;
|
||||
var src = inChars;
|
||||
var last = src;
|
||||
var srcEnd = src + inLength;
|
||||
var dest = outData;
|
||||
|
||||
// while (remainingOut > 0)
|
||||
// {
|
||||
//#if NETCOREAPP3_0_OR_GREATER
|
||||
// // Ignore errors in the SIMD part, and handle them in scalar part below
|
||||
// if (Avx2.IsSupported && remainingIn >= 64 && remainingOut >= 32)
|
||||
// {
|
||||
// Utf16HexParser.Avx2.TryParse(ref src, ref dest, remainingOut);
|
||||
// remainingIn = charsToRead - (int)(src - inChars);
|
||||
// remainingOut = bytesToWrite - (int)(dest - outData);
|
||||
// }
|
||||
while (remainingOut > 0)
|
||||
{
|
||||
#if NETCOREAPP3_0_OR_GREATER
|
||||
// Ignore errors in the SIMD part, and handle them in scalar part below
|
||||
if (Avx2.IsSupported && remainingIn >= 64 && remainingOut >= 32)
|
||||
{
|
||||
Utf16HexParser.Avx2.TryParse(ref src, ref dest, remainingOut);
|
||||
remainingIn = charsToRead - (int)(src - inChars);
|
||||
remainingOut = bytesToWrite - (int)(dest - outData);
|
||||
}
|
||||
|
||||
// if (remainingIn >= 32 && remainingOut >= 16)
|
||||
// {
|
||||
// if (Ssse3.IsSupported)
|
||||
// {
|
||||
// Utf16HexParser.Ssse3.TryParse(ref src, ref dest, remainingOut);
|
||||
// remainingIn = charsToRead - (int)(src - inChars);
|
||||
// remainingOut = bytesToWrite - (int)(dest - outData);
|
||||
// }
|
||||
// else if (Sse2.IsSupported)
|
||||
// {
|
||||
// Utf16HexParser.Sse2.TryParse(ref src, ref dest, remainingOut);
|
||||
// remainingIn = charsToRead - (int)(src - inChars);
|
||||
// remainingOut = bytesToWrite - (int)(dest - outData);
|
||||
// }
|
||||
// }
|
||||
//#endif
|
||||
//#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP2_1_OR_GREATER
|
||||
// if (!Utf16HexParser.Span.TryParse(ref src, remainingIn, ref dest, remainingOut))
|
||||
//#else
|
||||
// if (!Utf16HexParser.Fixed.TryParse(ref src, remainingIn, ref dest, remainingOut))
|
||||
//#endif
|
||||
// {
|
||||
// goto InvalidData;
|
||||
// }
|
||||
// remainingIn = charsToRead - (int)(src - inChars);
|
||||
// remainingOut = bytesToWrite - (int)(dest - outData);
|
||||
if (remainingIn >= 32 && remainingOut >= 16)
|
||||
{
|
||||
if (Ssse3.IsSupported)
|
||||
{
|
||||
Utf16HexParser.Ssse3.TryParse(ref src, ref dest, remainingOut);
|
||||
remainingIn = charsToRead - (int)(src - inChars);
|
||||
remainingOut = bytesToWrite - (int)(dest - outData);
|
||||
}
|
||||
else if (Sse2.IsSupported)
|
||||
{
|
||||
Utf16HexParser.Sse2.TryParse(ref src, ref dest, remainingOut);
|
||||
remainingIn = charsToRead - (int)(src - inChars);
|
||||
remainingOut = bytesToWrite - (int)(dest - outData);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP2_1_OR_GREATER
|
||||
if (!Utf16HexParser.Span.TryParse(ref src, remainingIn, ref dest, remainingOut))
|
||||
#else
|
||||
if (!Utf16HexParser.Fixed.TryParse(ref src, remainingIn, ref dest, remainingOut))
|
||||
#endif
|
||||
{
|
||||
goto InvalidData;
|
||||
}
|
||||
remainingIn = charsToRead - (int)(src - inChars);
|
||||
remainingOut = bytesToWrite - (int)(dest - outData);
|
||||
|
||||
// if (src >= srcEnd)
|
||||
// {
|
||||
// goto Ok;
|
||||
// }
|
||||
if (src >= srcEnd)
|
||||
{
|
||||
goto Ok;
|
||||
}
|
||||
|
||||
// if (src == last)
|
||||
// {
|
||||
// // No progress == invalid data
|
||||
// goto InvalidData;
|
||||
// }
|
||||
if (src == last)
|
||||
{
|
||||
// No progress == invalid data
|
||||
goto InvalidData;
|
||||
}
|
||||
|
||||
// last = src;
|
||||
// }
|
||||
last = src;
|
||||
}
|
||||
|
||||
// // No more space in destination
|
||||
// No more space in destination
|
||||
|
||||
// if (src < srcEnd)
|
||||
// {
|
||||
// while (IsWhitespace(*src))
|
||||
// {
|
||||
// // Skip past any consecutive trailing white-space in the input
|
||||
// if (++src >= srcEnd)
|
||||
// {
|
||||
// goto Ok;
|
||||
// }
|
||||
// }
|
||||
if (src < srcEnd)
|
||||
{
|
||||
while (IsWhitespace(*src))
|
||||
{
|
||||
// Skip past any consecutive trailing white-space in the input
|
||||
if (++src >= srcEnd)
|
||||
{
|
||||
goto Ok;
|
||||
}
|
||||
}
|
||||
|
||||
// if (IsValid(*src))
|
||||
// {
|
||||
// goto OutLenTooShort;
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// goto InvalidData;
|
||||
// }
|
||||
// }
|
||||
if (IsValid(*src))
|
||||
{
|
||||
goto OutLenTooShort;
|
||||
}
|
||||
else
|
||||
{
|
||||
goto InvalidData;
|
||||
}
|
||||
}
|
||||
|
||||
// Ok:
|
||||
// return (int)(dest - outData);
|
||||
Ok:
|
||||
return (int)(dest - outData);
|
||||
|
||||
// InvalidData:
|
||||
// return errInvalidData;
|
||||
InvalidData:
|
||||
return errInvalidData;
|
||||
|
||||
// OutLenTooShort:
|
||||
// return errDestTooShort;
|
||||
// }
|
||||
OutLenTooShort:
|
||||
return errDestTooShort;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,76 +1,76 @@
|
||||
//using System.Diagnostics;
|
||||
//#if NETCOREAPP3_0_OR_GREATER
|
||||
//using System.Numerics;
|
||||
//using System.Runtime.Intrinsics;
|
||||
//using System.Runtime.Intrinsics.X86;
|
||||
//#endif
|
||||
using System.Diagnostics;
|
||||
#if NETCOREAPP3_0_OR_GREATER
|
||||
using System.Numerics;
|
||||
using System.Runtime.Intrinsics;
|
||||
using System.Runtime.Intrinsics.X86;
|
||||
#endif
|
||||
|
||||
//namespace HexMate;
|
||||
namespace HexMate;
|
||||
|
||||
//internal static unsafe class CharHelper
|
||||
//{
|
||||
// internal static int CountUsefulCharacters(char* inputPtr, int inputLength)
|
||||
// {
|
||||
// Debug.Assert(inputLength > 0);
|
||||
internal static unsafe class CharHelper
|
||||
{
|
||||
internal static int CountUsefulCharacters(char* inputPtr, int inputLength)
|
||||
{
|
||||
Debug.Assert(inputLength > 0);
|
||||
|
||||
// // All characters above ' ' (space) are considered useful, as it is good enough, if this method
|
||||
// // returns an upper bound on the number of bytes encoded in the given input.
|
||||
// var byteLength = inputLength * 2;
|
||||
// var usefulCharacters = 0;
|
||||
//#if NETCOREAPP3_0_OR_GREATER
|
||||
// var byteSrc = (byte*)inputPtr;
|
||||
// All characters above ' ' (space) are considered useful, as it is good enough, if this method
|
||||
// returns an upper bound on the number of bytes encoded in the given input.
|
||||
var byteLength = inputLength * 2;
|
||||
var usefulCharacters = 0;
|
||||
#if NETCOREAPP3_0_OR_GREATER
|
||||
var byteSrc = (byte*)inputPtr;
|
||||
|
||||
// if (Avx2.IsSupported && byteLength >= 32)
|
||||
// {
|
||||
// var space = Vector256.Create(' ').AsInt16();
|
||||
// var bytesToProcess = FastMath.RoundDownTo32(byteLength);
|
||||
// var end = byteSrc + bytesToProcess;
|
||||
// while (byteSrc != end)
|
||||
// {
|
||||
// var input = Avx.LoadVector256(byteSrc).AsInt16();
|
||||
if (Avx2.IsSupported && byteLength >= 32)
|
||||
{
|
||||
var space = Vector256.Create(' ').AsInt16();
|
||||
var bytesToProcess = FastMath.RoundDownTo32(byteLength);
|
||||
var end = byteSrc + bytesToProcess;
|
||||
while (byteSrc != end)
|
||||
{
|
||||
var input = Avx.LoadVector256(byteSrc).AsInt16();
|
||||
|
||||
// var cnt = Avx2.CompareGreaterThan(input, space);
|
||||
// var mask = (uint)Avx2.MoveMask(cnt.AsByte());
|
||||
// usefulCharacters += BitOperations.PopCount(mask) >> 1;
|
||||
var cnt = Avx2.CompareGreaterThan(input, space);
|
||||
var mask = (uint)Avx2.MoveMask(cnt.AsByte());
|
||||
usefulCharacters += BitOperations.PopCount(mask) >> 1;
|
||||
|
||||
// byteSrc += 32;
|
||||
// }
|
||||
byteSrc += 32;
|
||||
}
|
||||
|
||||
// byteLength -= bytesToProcess;
|
||||
// }
|
||||
byteLength -= bytesToProcess;
|
||||
}
|
||||
|
||||
// if (Sse2.IsSupported && byteLength >= 16)
|
||||
// {
|
||||
// var space = Vector128.Create(' ').AsInt16();
|
||||
// var bytesToProcess = FastMath.RoundDownTo16(byteLength);
|
||||
// var end = byteSrc + bytesToProcess;
|
||||
// while (byteSrc != end)
|
||||
// {
|
||||
// var input = Sse2.LoadVector128(byteSrc).AsInt16();
|
||||
if (Sse2.IsSupported && byteLength >= 16)
|
||||
{
|
||||
var space = Vector128.Create(' ').AsInt16();
|
||||
var bytesToProcess = FastMath.RoundDownTo16(byteLength);
|
||||
var end = byteSrc + bytesToProcess;
|
||||
while (byteSrc != end)
|
||||
{
|
||||
var input = Sse2.LoadVector128(byteSrc).AsInt16();
|
||||
|
||||
// var cnt = Sse2.CompareGreaterThan(input, space);
|
||||
// var mask = (uint)Sse2.MoveMask(cnt.AsByte());
|
||||
// usefulCharacters += BitOperations.PopCount(mask) >> 1;
|
||||
var cnt = Sse2.CompareGreaterThan(input, space);
|
||||
var mask = (uint)Sse2.MoveMask(cnt.AsByte());
|
||||
usefulCharacters += BitOperations.PopCount(mask) >> 1;
|
||||
|
||||
// byteSrc += 16;
|
||||
// }
|
||||
byteSrc += 16;
|
||||
}
|
||||
|
||||
// byteLength -= bytesToProcess;
|
||||
// }
|
||||
byteLength -= bytesToProcess;
|
||||
}
|
||||
|
||||
// inputPtr = (char*)byteSrc;
|
||||
//#endif
|
||||
// const uint intSpace = ' ';
|
||||
inputPtr = (char*)byteSrc;
|
||||
#endif
|
||||
const uint intSpace = ' ';
|
||||
|
||||
// var charEnd = inputPtr + (byteLength / 2);
|
||||
// while (inputPtr < charEnd)
|
||||
// {
|
||||
// uint c = *inputPtr++;
|
||||
var charEnd = inputPtr + (byteLength / 2);
|
||||
while (inputPtr < charEnd)
|
||||
{
|
||||
uint c = *inputPtr++;
|
||||
|
||||
// if (c > intSpace)
|
||||
// usefulCharacters++;
|
||||
// }
|
||||
if (c > intSpace)
|
||||
usefulCharacters++;
|
||||
}
|
||||
|
||||
// return usefulCharacters;
|
||||
// }
|
||||
//}
|
||||
return usefulCharacters;
|
||||
}
|
||||
}
|
||||
@@ -1,130 +1,130 @@
|
||||
//#if NETCOREAPP3_0_OR_GREATER
|
||||
#if NETCOREAPP3_0_OR_GREATER
|
||||
|
||||
//using System.Diagnostics;
|
||||
//using System.Runtime.CompilerServices;
|
||||
//using System.Runtime.Intrinsics;
|
||||
//using static HexMate.VectorConstants;
|
||||
//using static HexMate.VectorUtils;
|
||||
//using static System.Runtime.Intrinsics.X86.Avx;
|
||||
//using static System.Runtime.Intrinsics.X86.Avx2;
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.Intrinsics;
|
||||
using static HexMate.VectorConstants;
|
||||
using static HexMate.VectorUtils;
|
||||
using static System.Runtime.Intrinsics.X86.Avx;
|
||||
using static System.Runtime.Intrinsics.X86.Avx2;
|
||||
|
||||
//namespace HexMate;
|
||||
namespace HexMate;
|
||||
|
||||
//internal static partial class Utf16HexParser
|
||||
//{
|
||||
// internal static unsafe class Avx2
|
||||
// {
|
||||
// [MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
// internal static bool TryParse(ref char* srcBytes, ref byte* destBytes, int destLength)
|
||||
// {
|
||||
// Debug.Assert(System.Runtime.Intrinsics.X86.Avx2.IsSupported);
|
||||
internal static partial class Utf16HexParser
|
||||
{
|
||||
internal static unsafe class Avx2
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
internal static bool TryParse(ref char* srcBytes, ref byte* destBytes, int destLength)
|
||||
{
|
||||
Debug.Assert(System.Runtime.Intrinsics.X86.Avx2.IsSupported);
|
||||
|
||||
// var x0F = Vector256.Create((byte)0x0F);
|
||||
// var xF0 = Vector256.Create((byte)0xF0);
|
||||
// var digHexSelector = ReadVector256(s_upperLowerDigHexSelector);
|
||||
// var digits = ReadVector256(s_digits);
|
||||
// var hexs = ReadVector256(s_hexs);
|
||||
// var evenBytes = ReadVector256(s_evenBytes);
|
||||
// var oddBytes = ReadVector256(s_oddBytes);
|
||||
// var src = (byte*)srcBytes;
|
||||
// var dest = destBytes;
|
||||
var x0F = Vector256.Create((byte)0x0F);
|
||||
var xF0 = Vector256.Create((byte)0xF0);
|
||||
var digHexSelector = ReadVector256(s_upperLowerDigHexSelector);
|
||||
var digits = ReadVector256(s_digits);
|
||||
var hexs = ReadVector256(s_hexs);
|
||||
var evenBytes = ReadVector256(s_evenBytes);
|
||||
var oddBytes = ReadVector256(s_oddBytes);
|
||||
var src = (byte*)srcBytes;
|
||||
var dest = destBytes;
|
||||
|
||||
// var target = dest + FastMath.RoundDownTo32(destLength);
|
||||
// int leftOk, rightOk;
|
||||
// while (dest != target)
|
||||
// {
|
||||
// var a = LoadVector256(src).AsInt16();
|
||||
// src += 32;
|
||||
// var b = LoadVector256(src).AsInt16();
|
||||
// src += 32;
|
||||
// var c = LoadVector256(src).AsInt16();
|
||||
// src += 32;
|
||||
// var d = LoadVector256(src).AsInt16();
|
||||
// src += 32;
|
||||
var target = dest + FastMath.RoundDownTo32(destLength);
|
||||
int leftOk, rightOk;
|
||||
while (dest != target)
|
||||
{
|
||||
var a = LoadVector256(src).AsInt16();
|
||||
src += 32;
|
||||
var b = LoadVector256(src).AsInt16();
|
||||
src += 32;
|
||||
var c = LoadVector256(src).AsInt16();
|
||||
src += 32;
|
||||
var d = LoadVector256(src).AsInt16();
|
||||
src += 32;
|
||||
|
||||
// var ab = PackUnsignedSaturate(a, b);
|
||||
// var cd = PackUnsignedSaturate(c, d);
|
||||
var ab = PackUnsignedSaturate(a, b);
|
||||
var cd = PackUnsignedSaturate(c, d);
|
||||
|
||||
// var inputLeft = Permute4x64(ab.AsUInt64(), 0b11_01_10_00).AsByte();
|
||||
// var inputRight = Permute4x64(cd.AsUInt64(), 0b11_01_10_00).AsByte();
|
||||
var inputLeft = Permute4x64(ab.AsUInt64(), 0b11_01_10_00).AsByte();
|
||||
var inputRight = Permute4x64(cd.AsUInt64(), 0b11_01_10_00).AsByte();
|
||||
|
||||
// var loNibbleLeft = And(inputLeft, x0F);
|
||||
// var loNibbleRight = And(inputRight, x0F);
|
||||
var loNibbleLeft = And(inputLeft, x0F);
|
||||
var loNibbleRight = And(inputRight, x0F);
|
||||
|
||||
// var hiNibbleLeft = And(inputLeft, xF0);
|
||||
// var hiNibbleRight = And(inputRight, xF0);
|
||||
var hiNibbleLeft = And(inputLeft, xF0);
|
||||
var hiNibbleRight = And(inputRight, xF0);
|
||||
|
||||
// var leftDigits = Shuffle(digits, loNibbleLeft);
|
||||
// var leftHex = Shuffle(hexs, loNibbleLeft);
|
||||
var leftDigits = Shuffle(digits, loNibbleLeft);
|
||||
var leftHex = Shuffle(hexs, loNibbleLeft);
|
||||
|
||||
// var hiNibbleShLeft = ShiftRightLogical(hiNibbleLeft.AsInt16(), 4).AsByte();
|
||||
// var hiNibbleShRight = ShiftRightLogical(hiNibbleRight.AsInt16(), 4).AsByte();
|
||||
var hiNibbleShLeft = ShiftRightLogical(hiNibbleLeft.AsInt16(), 4).AsByte();
|
||||
var hiNibbleShRight = ShiftRightLogical(hiNibbleRight.AsInt16(), 4).AsByte();
|
||||
|
||||
// var rightDigits = Shuffle(digits, loNibbleRight);
|
||||
// var rightHex = Shuffle(hexs, loNibbleRight);
|
||||
var rightDigits = Shuffle(digits, loNibbleRight);
|
||||
var rightHex = Shuffle(hexs, loNibbleRight);
|
||||
|
||||
// var magicLeft = Shuffle(digHexSelector, hiNibbleShLeft);
|
||||
// var magicRight = Shuffle(digHexSelector, hiNibbleShRight);
|
||||
var magicLeft = Shuffle(digHexSelector, hiNibbleShLeft);
|
||||
var magicRight = Shuffle(digHexSelector, hiNibbleShRight);
|
||||
|
||||
// var valueLeft = BlendVariable(leftDigits, leftHex, magicLeft);
|
||||
// var valueRight = BlendVariable(rightDigits, rightHex, magicRight);
|
||||
var valueLeft = BlendVariable(leftDigits, leftHex, magicLeft);
|
||||
var valueRight = BlendVariable(rightDigits, rightHex, magicRight);
|
||||
|
||||
// var errLeft = ShiftLeftLogical(magicLeft.AsInt16(), 7).AsByte();
|
||||
// var errRight = ShiftLeftLogical(magicRight.AsInt16(), 7).AsByte();
|
||||
var errLeft = ShiftLeftLogical(magicLeft.AsInt16(), 7).AsByte();
|
||||
var errRight = ShiftLeftLogical(magicRight.AsInt16(), 7).AsByte();
|
||||
|
||||
// var evenBytesLeft = Shuffle(valueLeft, evenBytes);
|
||||
// var oddBytesLeft = Shuffle(valueLeft, oddBytes);
|
||||
// var evenBytesRight = Shuffle(valueRight, evenBytes);
|
||||
// var oddBytesRight = Shuffle(valueRight, oddBytes);
|
||||
var evenBytesLeft = Shuffle(valueLeft, evenBytes);
|
||||
var oddBytesLeft = Shuffle(valueLeft, oddBytes);
|
||||
var evenBytesRight = Shuffle(valueRight, evenBytes);
|
||||
var oddBytesRight = Shuffle(valueRight, oddBytes);
|
||||
|
||||
// evenBytesLeft = ShiftLeftLogical(evenBytesLeft.AsUInt16(), 4).AsByte();
|
||||
// evenBytesRight = ShiftLeftLogical(evenBytesRight.AsUInt16(), 4).AsByte();
|
||||
evenBytesLeft = ShiftLeftLogical(evenBytesLeft.AsUInt16(), 4).AsByte();
|
||||
evenBytesRight = ShiftLeftLogical(evenBytesRight.AsUInt16(), 4).AsByte();
|
||||
|
||||
// evenBytesLeft = Or(evenBytesLeft, oddBytesLeft);
|
||||
// evenBytesRight = Or(evenBytesRight, oddBytesRight);
|
||||
evenBytesLeft = Or(evenBytesLeft, oddBytesLeft);
|
||||
evenBytesRight = Or(evenBytesRight, oddBytesRight);
|
||||
|
||||
// var result = Merge(evenBytesLeft, evenBytesRight);
|
||||
var result = Merge(evenBytesLeft, evenBytesRight);
|
||||
|
||||
// var validationResultLeft = Or(errLeft, valueLeft);
|
||||
// var validationResultRight = Or(errRight, valueRight);
|
||||
var validationResultLeft = Or(errLeft, valueLeft);
|
||||
var validationResultRight = Or(errRight, valueRight);
|
||||
|
||||
// leftOk = MoveMask(validationResultLeft);
|
||||
// rightOk = MoveMask(validationResultRight);
|
||||
leftOk = MoveMask(validationResultLeft);
|
||||
rightOk = MoveMask(validationResultRight);
|
||||
|
||||
// if ((leftOk | rightOk) != 0) goto Err;
|
||||
if ((leftOk | rightOk) != 0) goto Err;
|
||||
|
||||
// Store(dest, result);
|
||||
// dest += 32;
|
||||
// }
|
||||
Store(dest, result);
|
||||
dest += 32;
|
||||
}
|
||||
|
||||
// srcBytes = (char*)src;
|
||||
// destBytes = dest;
|
||||
// return true;
|
||||
srcBytes = (char*)src;
|
||||
destBytes = dest;
|
||||
return true;
|
||||
|
||||
// Err:
|
||||
// if (leftOk != 0)
|
||||
// {
|
||||
// srcBytes = (char*)(src - 128);
|
||||
// destBytes = dest;
|
||||
// return false;
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// Debug.Assert(rightOk != 0);
|
||||
// srcBytes = (char*)(src - 64);
|
||||
// destBytes = dest;
|
||||
// return false;
|
||||
// }
|
||||
// }
|
||||
Err:
|
||||
if (leftOk != 0)
|
||||
{
|
||||
srcBytes = (char*)(src - 128);
|
||||
destBytes = dest;
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.Assert(rightOk != 0);
|
||||
srcBytes = (char*)(src - 64);
|
||||
destBytes = dest;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// [MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
// private static Vector256<byte> Merge(Vector256<byte> a, Vector256<byte> b)
|
||||
// {
|
||||
// var a1 = Permute4x64(a.AsUInt64(), 0b11_10_10_00);
|
||||
// var b1 = Permute4x64(b.AsUInt64(), 0b11_00_01_00);
|
||||
// return Blend(a1.AsUInt32(), b1.AsUInt32(), 0b1111_0000).AsByte();
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static Vector256<byte> Merge(Vector256<byte> a, Vector256<byte> b)
|
||||
{
|
||||
var a1 = Permute4x64(a.AsUInt64(), 0b11_10_10_00);
|
||||
var b1 = Permute4x64(b.AsUInt64(), 0b11_00_01_00);
|
||||
return Blend(a1.AsUInt32(), b1.AsUInt32(), 0b1111_0000).AsByte();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//#endif
|
||||
#endif
|
||||
@@ -1,60 +1,60 @@
|
||||
//using System.Runtime.CompilerServices;
|
||||
//using static HexMate.ScalarConstants;
|
||||
using System.Runtime.CompilerServices;
|
||||
using static HexMate.ScalarConstants;
|
||||
|
||||
//namespace HexMate;
|
||||
namespace HexMate;
|
||||
|
||||
//internal static partial class Utf16HexParser
|
||||
//{
|
||||
// internal static unsafe class Fixed
|
||||
// {
|
||||
// [MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
// internal static bool TryParse(ref char* srcBytes, int srcLength, ref byte* destBytes, int destLength)
|
||||
// {
|
||||
// var src = srcBytes;
|
||||
// var dest = destBytes;
|
||||
internal static partial class Utf16HexParser
|
||||
{
|
||||
internal static unsafe class Fixed
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
internal static bool TryParse(ref char* srcBytes, int srcLength, ref byte* destBytes, int destLength)
|
||||
{
|
||||
var src = srcBytes;
|
||||
var dest = destBytes;
|
||||
|
||||
// var srcTarget = src + srcLength;
|
||||
// var destTarget = dest + destLength;
|
||||
var srcTarget = src + srcLength;
|
||||
var destTarget = dest + destLength;
|
||||
|
||||
// fixed (byte* lut = LookupUpperLower)
|
||||
// {
|
||||
// int hi, lo;
|
||||
// while (srcTarget > src && destTarget > dest)
|
||||
// {
|
||||
// ReadHi:
|
||||
// int valHi = *src++;
|
||||
// if (valHi > 0xFF) { goto Err; }
|
||||
// hi = lut[valHi];
|
||||
// if (hi >= 0xFE)
|
||||
// {
|
||||
// if (hi == 0xFF) goto Err;
|
||||
// if (srcTarget > src) goto ReadHi; // skip whitespace
|
||||
// }
|
||||
// *dest |= (byte)(hi << 4);
|
||||
fixed (byte* lut = LookupUpperLower)
|
||||
{
|
||||
int hi, lo;
|
||||
while (srcTarget > src && destTarget > dest)
|
||||
{
|
||||
ReadHi:
|
||||
int valHi = *src++;
|
||||
if (valHi > 0xFF) { goto Err; }
|
||||
hi = lut[valHi];
|
||||
if (hi >= 0xFE)
|
||||
{
|
||||
if (hi == 0xFF) goto Err;
|
||||
if (srcTarget > src) goto ReadHi; // skip whitespace
|
||||
}
|
||||
*dest |= (byte)(hi << 4);
|
||||
|
||||
// ReadLo:
|
||||
// int valLo = *src++;
|
||||
// if (valLo > 0xFF) goto Err;
|
||||
// lo = lut[valLo];
|
||||
// if (lo >= 0xFE)
|
||||
// {
|
||||
// if (lo == 0xFF) goto Err;
|
||||
// if (srcTarget > src) goto ReadLo; // skip whitespace
|
||||
// }
|
||||
// *dest |= (byte)lo;
|
||||
ReadLo:
|
||||
int valLo = *src++;
|
||||
if (valLo > 0xFF) goto Err;
|
||||
lo = lut[valLo];
|
||||
if (lo >= 0xFE)
|
||||
{
|
||||
if (lo == 0xFF) goto Err;
|
||||
if (srcTarget > src) goto ReadLo; // skip whitespace
|
||||
}
|
||||
*dest |= (byte)lo;
|
||||
|
||||
// dest++;
|
||||
// }
|
||||
// }
|
||||
dest++;
|
||||
}
|
||||
}
|
||||
|
||||
// srcBytes = src;
|
||||
// destBytes = dest;
|
||||
// return true;
|
||||
srcBytes = src;
|
||||
destBytes = dest;
|
||||
return true;
|
||||
|
||||
// Err:
|
||||
// srcBytes = src - 1;
|
||||
// destBytes = dest;
|
||||
// return false;
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
Err:
|
||||
srcBytes = src - 1;
|
||||
destBytes = dest;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,61 +1,61 @@
|
||||
//#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP2_1_OR_GREATER
|
||||
#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP2_1_OR_GREATER
|
||||
|
||||
//using System.Runtime.CompilerServices;
|
||||
//using static HexMate.ScalarConstants;
|
||||
using System.Runtime.CompilerServices;
|
||||
using static HexMate.ScalarConstants;
|
||||
|
||||
//namespace HexMate;
|
||||
namespace HexMate;
|
||||
|
||||
//internal static partial class Utf16HexParser
|
||||
//{
|
||||
// internal static unsafe class Span
|
||||
// {
|
||||
// [MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
// internal static bool TryParse(ref char* srcBytes, int srcLength, ref byte* destBytes, int destLength)
|
||||
// {
|
||||
// var src = srcBytes;
|
||||
// var dest = destBytes;
|
||||
internal static partial class Utf16HexParser
|
||||
{
|
||||
internal static unsafe class Span
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
internal static bool TryParse(ref char* srcBytes, int srcLength, ref byte* destBytes, int destLength)
|
||||
{
|
||||
var src = srcBytes;
|
||||
var dest = destBytes;
|
||||
|
||||
// var srcTarget = src + srcLength;
|
||||
// var destTarget = dest + destLength;
|
||||
var srcTarget = src + srcLength;
|
||||
var destTarget = dest + destLength;
|
||||
|
||||
// int hi, lo;
|
||||
// while (srcTarget > src && destTarget > dest)
|
||||
// {
|
||||
// ReadHi:
|
||||
// int valHi = *src++;
|
||||
// if (valHi > 0xFF) { goto Err; }
|
||||
// hi = LookupUpperLower[valHi];
|
||||
// if (hi >= 0xFE)
|
||||
// {
|
||||
// if (hi == 0xFF) goto Err;
|
||||
// if (srcTarget > src) goto ReadHi;
|
||||
// }
|
||||
// *dest |= (byte)(hi << 4);
|
||||
int hi, lo;
|
||||
while (srcTarget > src && destTarget > dest)
|
||||
{
|
||||
ReadHi:
|
||||
int valHi = *src++;
|
||||
if (valHi > 0xFF) { goto Err; }
|
||||
hi = LookupUpperLower[valHi];
|
||||
if (hi >= 0xFE)
|
||||
{
|
||||
if (hi == 0xFF) goto Err;
|
||||
if (srcTarget > src) goto ReadHi;
|
||||
}
|
||||
*dest |= (byte)(hi << 4);
|
||||
|
||||
// ReadLo:
|
||||
// int valLo = *src++;
|
||||
// if (valLo > 0xFF) goto Err;
|
||||
// lo = LookupUpperLower[valLo];
|
||||
// if (lo >= 0xFE)
|
||||
// {
|
||||
// if (lo == 0xFF) goto Err;
|
||||
// if (srcTarget > src) goto ReadLo;
|
||||
// }
|
||||
// *dest |= (byte)lo;
|
||||
ReadLo:
|
||||
int valLo = *src++;
|
||||
if (valLo > 0xFF) goto Err;
|
||||
lo = LookupUpperLower[valLo];
|
||||
if (lo >= 0xFE)
|
||||
{
|
||||
if (lo == 0xFF) goto Err;
|
||||
if (srcTarget > src) goto ReadLo;
|
||||
}
|
||||
*dest |= (byte)lo;
|
||||
|
||||
// dest++;
|
||||
// }
|
||||
dest++;
|
||||
}
|
||||
|
||||
// srcBytes = src;
|
||||
// destBytes = dest;
|
||||
// return true;
|
||||
srcBytes = src;
|
||||
destBytes = dest;
|
||||
return true;
|
||||
|
||||
// Err:
|
||||
// srcBytes = src - 1;
|
||||
// destBytes = dest;
|
||||
// return false;
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
Err:
|
||||
srcBytes = src - 1;
|
||||
destBytes = dest;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//#endif
|
||||
#endif
|
||||
@@ -1,142 +1,142 @@
|
||||
//#if NETCOREAPP3_0_OR_GREATER
|
||||
#if NETCOREAPP3_0_OR_GREATER
|
||||
|
||||
//using System.Diagnostics;
|
||||
//using System.Runtime.CompilerServices;
|
||||
//using System.Runtime.Intrinsics;
|
||||
//using static System.Runtime.Intrinsics.X86.Sse2;
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.Intrinsics;
|
||||
using static System.Runtime.Intrinsics.X86.Sse2;
|
||||
|
||||
//namespace HexMate;
|
||||
namespace HexMate;
|
||||
|
||||
//internal static partial class Utf16HexParser
|
||||
//{
|
||||
// internal static unsafe class Sse2
|
||||
// {
|
||||
// [MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
// internal static bool TryParse(ref char* srcBytes, ref byte* destBytes, int destLength)
|
||||
// {
|
||||
// Debug.Assert(System.Runtime.Intrinsics.X86.Sse2.IsSupported);
|
||||
internal static partial class Utf16HexParser
|
||||
{
|
||||
internal static unsafe class Sse2
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
internal static bool TryParse(ref char* srcBytes, ref byte* destBytes, int destLength)
|
||||
{
|
||||
Debug.Assert(System.Runtime.Intrinsics.X86.Sse2.IsSupported);
|
||||
|
||||
// var x05 = Vector128.Create((byte)0x05);
|
||||
// var x09 = Vector128.Create((byte)0x09);
|
||||
// var x0A = Vector128.Create((byte)0x0A);
|
||||
// var x20 = Vector128.Create((byte)0x20);
|
||||
// var x30 = Vector128.Create((byte)0x30);
|
||||
// var x40 = Vector128.Create((byte)0x40);
|
||||
// var x41 = Vector128.Create((byte)0x41);
|
||||
// var x60 = Vector128.Create((byte)0x60);
|
||||
// var xFF = Vector128.Create((byte)0xFF);
|
||||
// var src = (byte*)srcBytes;
|
||||
// var dest = destBytes;
|
||||
var x05 = Vector128.Create((byte)0x05);
|
||||
var x09 = Vector128.Create((byte)0x09);
|
||||
var x0A = Vector128.Create((byte)0x0A);
|
||||
var x20 = Vector128.Create((byte)0x20);
|
||||
var x30 = Vector128.Create((byte)0x30);
|
||||
var x40 = Vector128.Create((byte)0x40);
|
||||
var x41 = Vector128.Create((byte)0x41);
|
||||
var x60 = Vector128.Create((byte)0x60);
|
||||
var xFF = Vector128.Create((byte)0xFF);
|
||||
var src = (byte*)srcBytes;
|
||||
var dest = destBytes;
|
||||
|
||||
// var target = dest + FastMath.RoundDownTo16(destLength);
|
||||
// int leftOk, rightOk;
|
||||
// while (dest != target)
|
||||
// {
|
||||
// var a = LoadVector128(src).AsInt16();
|
||||
// src += 16;
|
||||
// var b = LoadVector128(src).AsInt16();
|
||||
// src += 16;
|
||||
// var c = LoadVector128(src).AsInt16();
|
||||
// src += 16;
|
||||
// var d = LoadVector128(src).AsInt16();
|
||||
// src += 16;
|
||||
var target = dest + FastMath.RoundDownTo16(destLength);
|
||||
int leftOk, rightOk;
|
||||
while (dest != target)
|
||||
{
|
||||
var a = LoadVector128(src).AsInt16();
|
||||
src += 16;
|
||||
var b = LoadVector128(src).AsInt16();
|
||||
src += 16;
|
||||
var c = LoadVector128(src).AsInt16();
|
||||
src += 16;
|
||||
var d = LoadVector128(src).AsInt16();
|
||||
src += 16;
|
||||
|
||||
// var inputLeft = PackUnsignedSaturate(a, b);
|
||||
// var inputRight = PackUnsignedSaturate(c, d);
|
||||
var inputLeft = PackUnsignedSaturate(a, b);
|
||||
var inputRight = PackUnsignedSaturate(c, d);
|
||||
|
||||
// var g40Left = CompareGreaterThan(inputLeft.AsSByte(), x40.AsSByte()).AsByte();
|
||||
// var g60Left = CompareGreaterThan(inputLeft.AsSByte(), x60.AsSByte()).AsByte();
|
||||
// var g40Right = CompareGreaterThan(inputRight.AsSByte(), x40.AsSByte()).AsByte();
|
||||
// var g60Right = CompareGreaterThan(inputRight.AsSByte(), x60.AsSByte()).AsByte();
|
||||
var g40Left = CompareGreaterThan(inputLeft.AsSByte(), x40.AsSByte()).AsByte();
|
||||
var g60Left = CompareGreaterThan(inputLeft.AsSByte(), x60.AsSByte()).AsByte();
|
||||
var g40Right = CompareGreaterThan(inputRight.AsSByte(), x40.AsSByte()).AsByte();
|
||||
var g60Right = CompareGreaterThan(inputRight.AsSByte(), x60.AsSByte()).AsByte();
|
||||
|
||||
// var corr40Left = And(x41, g40Left);
|
||||
// var corr60Left = And(x20, g60Left);
|
||||
var corr40Left = And(x41, g40Left);
|
||||
var corr60Left = And(x20, g60Left);
|
||||
|
||||
// var corr40Right = And(x41, g40Right);
|
||||
// var corr60Right = And(x20, g60Right);
|
||||
var corr40Right = And(x41, g40Right);
|
||||
var corr60Right = And(x20, g60Right);
|
||||
|
||||
// var sub30Left = Subtract(inputLeft, x30);
|
||||
// var sub40Left = Subtract(inputLeft, corr40Left);
|
||||
var sub30Left = Subtract(inputLeft, x30);
|
||||
var sub40Left = Subtract(inputLeft, corr40Left);
|
||||
|
||||
// var sub30Right = Subtract(inputRight, x30);
|
||||
// var sub40Right = Subtract(inputRight, corr40Right);
|
||||
var sub30Right = Subtract(inputRight, x30);
|
||||
var sub40Right = Subtract(inputRight, corr40Right);
|
||||
|
||||
// var sub60Left = Subtract(sub40Left, corr60Left);
|
||||
// var sub60Right = Subtract(sub40Right, corr60Right);
|
||||
var sub60Left = Subtract(sub40Left, corr60Left);
|
||||
var sub60Right = Subtract(sub40Right, corr60Right);
|
||||
|
||||
// var maskAfLeft = Or(g60Left, g40Left);
|
||||
// var maskAfRight = Or(g60Right, g40Right);
|
||||
var maskAfLeft = Or(g60Left, g40Left);
|
||||
var maskAfRight = Or(g60Right, g40Right);
|
||||
|
||||
// var maskDigLeft = Xor(maskAfLeft, xFF);
|
||||
// var maskDigRight = Xor(maskAfRight, xFF);
|
||||
var maskDigLeft = Xor(maskAfLeft, xFF);
|
||||
var maskDigRight = Xor(maskAfRight, xFF);
|
||||
|
||||
// var afLeft = And(sub60Left, maskAfLeft);
|
||||
// var corrAfLeft = And(maskAfLeft, x0A);
|
||||
var afLeft = And(sub60Left, maskAfLeft);
|
||||
var corrAfLeft = And(maskAfLeft, x0A);
|
||||
|
||||
// var afRight = And(sub60Right, maskAfRight);
|
||||
// var corrAfRight = And(maskAfRight, x0A);
|
||||
var afRight = And(sub60Right, maskAfRight);
|
||||
var corrAfRight = And(maskAfRight, x0A);
|
||||
|
||||
// var digLeft = And(sub30Left, maskDigLeft);
|
||||
// var corrLeft = Add(afLeft, corrAfLeft);
|
||||
var digLeft = And(sub30Left, maskDigLeft);
|
||||
var corrLeft = Add(afLeft, corrAfLeft);
|
||||
|
||||
// var digRight = And(sub30Right, maskDigRight);
|
||||
// var corrRight = Add(afRight, corrAfRight);
|
||||
var digRight = And(sub30Right, maskDigRight);
|
||||
var corrRight = Add(afRight, corrAfRight);
|
||||
|
||||
// var resultAfLeft = CompareGreaterThan(afLeft.AsSByte(), x05.AsSByte()).AsByte();
|
||||
// var resultDigLeft = CompareGreaterThan(digLeft.AsSByte(), x09.AsSByte()).AsByte();
|
||||
var resultAfLeft = CompareGreaterThan(afLeft.AsSByte(), x05.AsSByte()).AsByte();
|
||||
var resultDigLeft = CompareGreaterThan(digLeft.AsSByte(), x09.AsSByte()).AsByte();
|
||||
|
||||
// var resultAfRight = CompareGreaterThan(afRight.AsSByte(), x05.AsSByte()).AsByte();
|
||||
// var resultDigRight = CompareGreaterThan(digRight.AsSByte(), x09.AsSByte()).AsByte();
|
||||
var resultAfRight = CompareGreaterThan(afRight.AsSByte(), x05.AsSByte()).AsByte();
|
||||
var resultDigRight = CompareGreaterThan(digRight.AsSByte(), x09.AsSByte()).AsByte();
|
||||
|
||||
// var valueLeft = Or(corrLeft, digLeft);
|
||||
// var valueRight = Or(corrRight, digRight);
|
||||
var valueLeft = Or(corrLeft, digLeft);
|
||||
var valueRight = Or(corrRight, digRight);
|
||||
|
||||
// var digAfLeft = Or(digLeft, afLeft);
|
||||
// var digAfRight = Or(digRight, afRight);
|
||||
var digAfLeft = Or(digLeft, afLeft);
|
||||
var digAfRight = Or(digRight, afRight);
|
||||
|
||||
// var digAfOutOfRangeLeft = Or(resultAfLeft, resultDigLeft);
|
||||
// var digAfOutOfRangeRight = Or(resultAfRight, resultDigRight);
|
||||
var digAfOutOfRangeLeft = Or(resultAfLeft, resultDigLeft);
|
||||
var digAfOutOfRangeRight = Or(resultAfRight, resultDigRight);
|
||||
|
||||
// var resLeft = Or(digAfLeft, digAfOutOfRangeLeft);
|
||||
// var resRight = Or(digAfRight, digAfOutOfRangeRight);
|
||||
var resLeft = Or(digAfLeft, digAfOutOfRangeLeft);
|
||||
var resRight = Or(digAfRight, digAfOutOfRangeRight);
|
||||
|
||||
// var sh12Left = ShiftLeftLogical(valueLeft.AsUInt16(), 12).AsByte();
|
||||
// var sh12Right = ShiftLeftLogical(valueRight.AsUInt16(), 12).AsByte();
|
||||
var sh12Left = ShiftLeftLogical(valueLeft.AsUInt16(), 12).AsByte();
|
||||
var sh12Right = ShiftLeftLogical(valueRight.AsUInt16(), 12).AsByte();
|
||||
|
||||
// var unp1 = ShiftRightLogical(Or(valueLeft, sh12Left).AsUInt16(), 8).AsByte();
|
||||
// var unp2 = ShiftRightLogical(Or(valueRight, sh12Right).AsUInt16(), 8).AsByte();
|
||||
var unp1 = ShiftRightLogical(Or(valueLeft, sh12Left).AsUInt16(), 8).AsByte();
|
||||
var unp2 = ShiftRightLogical(Or(valueRight, sh12Right).AsUInt16(), 8).AsByte();
|
||||
|
||||
// var result = PackUnsignedSaturate(unp1.AsInt16(), unp2.AsInt16()).AsByte();
|
||||
var result = PackUnsignedSaturate(unp1.AsInt16(), unp2.AsInt16()).AsByte();
|
||||
|
||||
// leftOk = MoveMask(resLeft);
|
||||
// rightOk = MoveMask(resRight);
|
||||
leftOk = MoveMask(resLeft);
|
||||
rightOk = MoveMask(resRight);
|
||||
|
||||
// if ((leftOk | rightOk) != 0) goto Err;
|
||||
if ((leftOk | rightOk) != 0) goto Err;
|
||||
|
||||
// Store(dest, result);
|
||||
// dest += 16;
|
||||
// }
|
||||
Store(dest, result);
|
||||
dest += 16;
|
||||
}
|
||||
|
||||
// srcBytes = (char*)src;
|
||||
// destBytes = dest;
|
||||
// return true;
|
||||
srcBytes = (char*)src;
|
||||
destBytes = dest;
|
||||
return true;
|
||||
|
||||
// Err:
|
||||
// if (leftOk != 0)
|
||||
// {
|
||||
// srcBytes = (char*)(src - 64);
|
||||
// destBytes = dest;
|
||||
// return false;
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// Debug.Assert(rightOk != 0);
|
||||
// srcBytes = (char*)(src - 32);
|
||||
// destBytes = dest;
|
||||
// return false;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
Err:
|
||||
if (leftOk != 0)
|
||||
{
|
||||
srcBytes = (char*)(src - 64);
|
||||
destBytes = dest;
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.Assert(rightOk != 0);
|
||||
srcBytes = (char*)(src - 32);
|
||||
destBytes = dest;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//#endif
|
||||
#endif
|
||||
@@ -1,129 +1,129 @@
|
||||
//#if NETCOREAPP3_0_OR_GREATER
|
||||
#if NETCOREAPP3_0_OR_GREATER
|
||||
|
||||
//using System.Diagnostics;
|
||||
//using System.Runtime.CompilerServices;
|
||||
//using System.Runtime.Intrinsics;
|
||||
//using static HexMate.VectorConstants;
|
||||
//using static HexMate.VectorUtils;
|
||||
//using static System.Runtime.Intrinsics.X86.Sse2;
|
||||
//using static System.Runtime.Intrinsics.X86.Ssse3;
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.Intrinsics;
|
||||
using static HexMate.VectorConstants;
|
||||
using static HexMate.VectorUtils;
|
||||
using static System.Runtime.Intrinsics.X86.Sse2;
|
||||
using static System.Runtime.Intrinsics.X86.Ssse3;
|
||||
|
||||
//namespace HexMate;
|
||||
namespace HexMate;
|
||||
|
||||
//internal static partial class Utf16HexParser
|
||||
//{
|
||||
// internal static unsafe class Ssse3
|
||||
// {
|
||||
// [MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
// internal static bool TryParse(ref char* srcBytes, ref byte* destBytes, int destLength)
|
||||
// {
|
||||
// Debug.Assert(System.Runtime.Intrinsics.X86.Ssse3.IsSupported);
|
||||
internal static partial class Utf16HexParser
|
||||
{
|
||||
internal static unsafe class Ssse3
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
internal static bool TryParse(ref char* srcBytes, ref byte* destBytes, int destLength)
|
||||
{
|
||||
Debug.Assert(System.Runtime.Intrinsics.X86.Ssse3.IsSupported);
|
||||
|
||||
// var x0F = Vector128.Create((byte)0x0F);
|
||||
// var x30 = Vector128.Create((byte)0x30);
|
||||
// var x40 = Vector128.Create((byte)0x40);
|
||||
// var x60 = Vector128.Create((byte)0x60);
|
||||
// var xF0 = Vector128.Create((byte)0xF0);
|
||||
// var validDig = ReadVector128(s_validDig);
|
||||
// var validHex = ReadVector128(s_validHex);
|
||||
// var evenBytes = ReadVector128(s_evenBytes);
|
||||
// var oddBytes = ReadVector128(s_oddBytes);
|
||||
// var src = (byte*)srcBytes;
|
||||
// var dest = destBytes;
|
||||
var x0F = Vector128.Create((byte)0x0F);
|
||||
var x30 = Vector128.Create((byte)0x30);
|
||||
var x40 = Vector128.Create((byte)0x40);
|
||||
var x60 = Vector128.Create((byte)0x60);
|
||||
var xF0 = Vector128.Create((byte)0xF0);
|
||||
var validDig = ReadVector128(s_validDig);
|
||||
var validHex = ReadVector128(s_validHex);
|
||||
var evenBytes = ReadVector128(s_evenBytes);
|
||||
var oddBytes = ReadVector128(s_oddBytes);
|
||||
var src = (byte*)srcBytes;
|
||||
var dest = destBytes;
|
||||
|
||||
// var target = dest + FastMath.RoundDownTo16(destLength);
|
||||
// int leftOk, rightOk;
|
||||
// while (dest != target)
|
||||
// {
|
||||
// var a = LoadVector128(src).AsInt16();
|
||||
// src += 16;
|
||||
// var b = LoadVector128(src).AsInt16();
|
||||
// src += 16;
|
||||
// var c = LoadVector128(src).AsInt16();
|
||||
// src += 16;
|
||||
// var d = LoadVector128(src).AsInt16();
|
||||
// src += 16;
|
||||
var target = dest + FastMath.RoundDownTo16(destLength);
|
||||
int leftOk, rightOk;
|
||||
while (dest != target)
|
||||
{
|
||||
var a = LoadVector128(src).AsInt16();
|
||||
src += 16;
|
||||
var b = LoadVector128(src).AsInt16();
|
||||
src += 16;
|
||||
var c = LoadVector128(src).AsInt16();
|
||||
src += 16;
|
||||
var d = LoadVector128(src).AsInt16();
|
||||
src += 16;
|
||||
|
||||
// var inputLeft = PackUnsignedSaturate(a, b);
|
||||
// var inputRight = PackUnsignedSaturate(c, d);
|
||||
var inputLeft = PackUnsignedSaturate(a, b);
|
||||
var inputRight = PackUnsignedSaturate(c, d);
|
||||
|
||||
// var hiNibbleLeft = And(inputLeft, xF0);
|
||||
// var hiNibbleRight = And(inputRight, xF0);
|
||||
var hiNibbleLeft = And(inputLeft, xF0);
|
||||
var hiNibbleRight = And(inputRight, xF0);
|
||||
|
||||
// var upperHexMaskLeft = CompareEqual(hiNibbleLeft, x40);
|
||||
// var lowerHexMaskLeft = CompareEqual(hiNibbleLeft, x60);
|
||||
// var digitMaskLeft = CompareEqual(hiNibbleLeft, x30);
|
||||
// var upperHexMaskRight = CompareEqual(hiNibbleRight, x40);
|
||||
// var lowerHexMaskRight = CompareEqual(hiNibbleRight, x60);
|
||||
// var digitMaskRight = CompareEqual(hiNibbleRight, x30);
|
||||
var upperHexMaskLeft = CompareEqual(hiNibbleLeft, x40);
|
||||
var lowerHexMaskLeft = CompareEqual(hiNibbleLeft, x60);
|
||||
var digitMaskLeft = CompareEqual(hiNibbleLeft, x30);
|
||||
var upperHexMaskRight = CompareEqual(hiNibbleRight, x40);
|
||||
var lowerHexMaskRight = CompareEqual(hiNibbleRight, x60);
|
||||
var digitMaskRight = CompareEqual(hiNibbleRight, x30);
|
||||
|
||||
// var hexMaskLeft = Or(upperHexMaskLeft, lowerHexMaskLeft);
|
||||
// var hexMaskRight = Or(upperHexMaskRight, lowerHexMaskRight);
|
||||
var hexMaskLeft = Or(upperHexMaskLeft, lowerHexMaskLeft);
|
||||
var hexMaskRight = Or(upperHexMaskRight, lowerHexMaskRight);
|
||||
|
||||
// var hiNibbleValidLeft = Or(digitMaskLeft, hexMaskLeft);
|
||||
// var hiNibbleValidRight = Or(digitMaskRight, hexMaskRight);
|
||||
var hiNibbleValidLeft = Or(digitMaskLeft, hexMaskLeft);
|
||||
var hiNibbleValidRight = Or(digitMaskRight, hexMaskRight);
|
||||
|
||||
// var digitsAndFLeft = Or(inputLeft, hexMaskLeft);
|
||||
// var hexAndFLeft = Or(inputLeft, digitMaskLeft);
|
||||
// var digitsAndFRight = Or(inputRight, hexMaskRight);
|
||||
// var hexAndFRight = Or(inputRight, digitMaskRight);
|
||||
var digitsAndFLeft = Or(inputLeft, hexMaskLeft);
|
||||
var hexAndFLeft = Or(inputLeft, digitMaskLeft);
|
||||
var digitsAndFRight = Or(inputRight, hexMaskRight);
|
||||
var hexAndFRight = Or(inputRight, digitMaskRight);
|
||||
|
||||
// var okDig1 = Shuffle(validDig, digitsAndFLeft);
|
||||
// var okHex1 = Shuffle(validHex, hexAndFLeft);
|
||||
// var okDig2 = Shuffle(validDig, digitsAndFRight);
|
||||
// var okHex2 = Shuffle(validHex, hexAndFRight);
|
||||
var okDig1 = Shuffle(validDig, digitsAndFLeft);
|
||||
var okHex1 = Shuffle(validHex, hexAndFLeft);
|
||||
var okDig2 = Shuffle(validDig, digitsAndFRight);
|
||||
var okHex2 = Shuffle(validHex, hexAndFRight);
|
||||
|
||||
// var loNibbleValidLeft = Or(okDig1, okHex1);
|
||||
// var loNibbleValidRight = Or(okDig2, okHex2);
|
||||
var loNibbleValidLeft = Or(okDig1, okHex1);
|
||||
var loNibbleValidRight = Or(okDig2, okHex2);
|
||||
|
||||
// var validLeft = And(loNibbleValidLeft, hiNibbleValidLeft);
|
||||
// var validRight = And(loNibbleValidRight, hiNibbleValidRight);
|
||||
var validLeft = And(loNibbleValidLeft, hiNibbleValidLeft);
|
||||
var validRight = And(loNibbleValidRight, hiNibbleValidRight);
|
||||
|
||||
// loNibbleValidLeft = And(loNibbleValidLeft, x0F);
|
||||
// loNibbleValidRight = And(loNibbleValidRight, x0F);
|
||||
loNibbleValidLeft = And(loNibbleValidLeft, x0F);
|
||||
loNibbleValidRight = And(loNibbleValidRight, x0F);
|
||||
|
||||
// var evenBytesLeft = Shuffle(loNibbleValidLeft, evenBytes);
|
||||
// var oddBytesLeft = Shuffle(loNibbleValidLeft, oddBytes);
|
||||
// var evenBytesRight = Shuffle(loNibbleValidRight, evenBytes);
|
||||
// var oddBytesRight = Shuffle(loNibbleValidRight, oddBytes);
|
||||
var evenBytesLeft = Shuffle(loNibbleValidLeft, evenBytes);
|
||||
var oddBytesLeft = Shuffle(loNibbleValidLeft, oddBytes);
|
||||
var evenBytesRight = Shuffle(loNibbleValidRight, evenBytes);
|
||||
var oddBytesRight = Shuffle(loNibbleValidRight, oddBytes);
|
||||
|
||||
// evenBytesLeft = ShiftLeftLogical(evenBytesLeft.AsUInt16(), 4).AsByte();
|
||||
// evenBytesRight = ShiftLeftLogical(evenBytesRight.AsUInt16(), 4).AsByte();
|
||||
evenBytesLeft = ShiftLeftLogical(evenBytesLeft.AsUInt16(), 4).AsByte();
|
||||
evenBytesRight = ShiftLeftLogical(evenBytesRight.AsUInt16(), 4).AsByte();
|
||||
|
||||
// evenBytesLeft = Or(evenBytesLeft, oddBytesLeft);
|
||||
// evenBytesRight = Or(evenBytesRight, oddBytesRight);
|
||||
evenBytesLeft = Or(evenBytesLeft, oddBytesLeft);
|
||||
evenBytesRight = Or(evenBytesRight, oddBytesRight);
|
||||
|
||||
// var result = UnpackLow(evenBytesLeft.AsInt64(), evenBytesRight.AsInt64()).AsByte();
|
||||
var result = UnpackLow(evenBytesLeft.AsInt64(), evenBytesRight.AsInt64()).AsByte();
|
||||
|
||||
// leftOk = MoveMask(validLeft);
|
||||
// rightOk = MoveMask(validRight);
|
||||
leftOk = MoveMask(validLeft);
|
||||
rightOk = MoveMask(validRight);
|
||||
|
||||
// if ((leftOk & rightOk) != 0xFFFF) goto Err;
|
||||
if ((leftOk & rightOk) != 0xFFFF) goto Err;
|
||||
|
||||
// Store(dest, result);
|
||||
// dest += 16;
|
||||
// }
|
||||
Store(dest, result);
|
||||
dest += 16;
|
||||
}
|
||||
|
||||
// srcBytes = (char*)src;
|
||||
// destBytes = dest;
|
||||
// return true;
|
||||
srcBytes = (char*)src;
|
||||
destBytes = dest;
|
||||
return true;
|
||||
|
||||
// Err:
|
||||
// if (leftOk != 0xFFFF)
|
||||
// {
|
||||
// srcBytes = (char*)(src - 64);
|
||||
// destBytes = dest;
|
||||
// return false;
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// Debug.Assert(rightOk != 0xFFFF);
|
||||
// srcBytes = (char*)(src - 32);
|
||||
// destBytes = dest;
|
||||
// return false;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
Err:
|
||||
if (leftOk != 0xFFFF)
|
||||
{
|
||||
srcBytes = (char*)(src - 64);
|
||||
destBytes = dest;
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.Assert(rightOk != 0xFFFF);
|
||||
srcBytes = (char*)(src - 32);
|
||||
destBytes = dest;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//#endif
|
||||
#endif
|
||||
@@ -153,6 +153,89 @@ public static class IOPath
|
||||
}
|
||||
}
|
||||
|
||||
static readonly object lock_GetCacheFilePath = new();
|
||||
|
||||
/// <summary>
|
||||
/// 根据缓存子文件夹名称与文件扩展名获取一个缓存文件路径
|
||||
/// </summary>
|
||||
/// <param name="dirName">缓存子文件夹名称</param>
|
||||
/// <param name="fileNamePrefix">文件名前缀</param>
|
||||
/// <param name="fileEx">文件扩展名</param>
|
||||
public static string GetCacheFilePath(string dirName, string fileNamePrefix, string fileEx)
|
||||
{
|
||||
lock (lock_GetCacheFilePath)
|
||||
{
|
||||
var cacheDirPath = Path.Combine(CacheDirectory, dirName);
|
||||
if (!Directory.Exists(cacheDirPath))
|
||||
{
|
||||
Directory.CreateDirectory(cacheDirPath);
|
||||
return GetCacheFilePath();
|
||||
}
|
||||
else
|
||||
{
|
||||
string cacheFilePath;
|
||||
do
|
||||
{
|
||||
cacheFilePath = GetCacheFilePath();
|
||||
} while (File.Exists(cacheFilePath));
|
||||
return cacheFilePath;
|
||||
}
|
||||
|
||||
string GetCacheFilePath() => Path.Combine(cacheDirPath, GetCacheFileName());
|
||||
string GetCacheFileName() => $"{fileNamePrefix}_{DateTimeOffset.Now.Ticks}{Random2.GenerateRandomString(4)}{fileEx}";
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 尝试延时一段时间后删除文件
|
||||
/// </summary>
|
||||
/// <param name="filePath">要删除的文件路径</param>
|
||||
/// <param name="millisecondsDelay">延时等待的毫秒数</param>
|
||||
public static async void TryDeleteInDelay(string filePath, int millisecondsDelay = 9000)
|
||||
{
|
||||
await Task.Delay(millisecondsDelay);
|
||||
FileTryDelete(filePath);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 启动进程后尝试延时一段时间后删除文件
|
||||
/// </summary>
|
||||
/// <param name="process">启动的进程</param>
|
||||
/// <param name="filePath">要删除的文件路径</param>
|
||||
/// <param name="millisecondsDelay">延时等待的毫秒数</param>
|
||||
/// <param name="processWaitMillisecondsDelay">启动的进程等待退出的毫秒数</param>
|
||||
public static void TryDeleteInDelay(Process? process, string filePath, int millisecondsDelay = 9000, int processWaitMillisecondsDelay = 9000)
|
||||
{
|
||||
if (process != null)
|
||||
{
|
||||
var waitForExitResult = process.TryWaitForExit(processWaitMillisecondsDelay);
|
||||
if (!waitForExitResult)
|
||||
{
|
||||
try
|
||||
{
|
||||
process.KillEntireProcessTree();
|
||||
}
|
||||
catch
|
||||
{
|
||||
|
||||
}
|
||||
TryDeleteInDelay(filePath, millisecondsDelay);
|
||||
return;
|
||||
}
|
||||
}
|
||||
FileTryDelete(filePath);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 尝试根据缓存子文件夹名称删除整个缓存子文件夹
|
||||
/// </summary>
|
||||
/// <param name="dirName">缓存子文件夹名称</param>
|
||||
public static void TryDeleteCacheSubDir(string dirName)
|
||||
{
|
||||
var cacheDirPath = Path.Combine(CacheDirectory, dirName);
|
||||
DirTryDelete(cacheDirPath);
|
||||
}
|
||||
|
||||
public abstract class FileSystemBase
|
||||
{
|
||||
protected FileSystemBase()
|
||||
@@ -291,6 +374,10 @@ public static class IOPath
|
||||
{
|
||||
paths[i] = item;
|
||||
}
|
||||
else
|
||||
{
|
||||
DirCreateByNotExists(paths[i]);
|
||||
}
|
||||
}
|
||||
|
||||
InitFileSystem(GetAppDataDirectory, GetCacheDirectory);
|
||||
|
||||
+17
-13
@@ -11,7 +11,11 @@ namespace System;
|
||||
/// </summary>
|
||||
public static class Log
|
||||
{
|
||||
internal static ILoggerFactory Factory => DI.Get<ILoggerFactory>();
|
||||
public static Func<ILoggerFactory>? LoggerFactory { private get; set; }
|
||||
|
||||
static ILoggerFactory? factory;
|
||||
|
||||
public static ILoggerFactory Factory => factory ??= (DI.IsConfigured ? DI.Get<ILoggerFactory>() : null) ?? LoggerFactory?.Invoke() ?? throw new ArgumentNullException(nameof(LoggerFactory));
|
||||
|
||||
public static ILogger CreateLogger(string tag) => Factory.CreateLogger(tag);
|
||||
|
||||
@@ -32,21 +36,21 @@ public static class Log
|
||||
}
|
||||
|
||||
[Conditional("DEBUG")]
|
||||
public static void Debug(string tag, Exception exception, string msg)
|
||||
public static void Debug(string tag, Exception? exception, string msg)
|
||||
{
|
||||
var logger = CreateLogger(tag);
|
||||
logger.LogDebug(exception, msg);
|
||||
}
|
||||
|
||||
[Conditional("DEBUG")]
|
||||
public static void Debug(string tag, Exception exception, string msg, params object?[] args)
|
||||
public static void Debug(string tag, Exception? exception, string msg, params object?[] args)
|
||||
{
|
||||
var logger = CreateLogger(tag);
|
||||
logger.LogDebug(exception, msg, args);
|
||||
}
|
||||
|
||||
[Conditional("DEBUG")]
|
||||
public static void Debug(string tag, EventId eventId, Exception exception, string msg, params object?[] args)
|
||||
public static void Debug(string tag, EventId eventId, Exception? exception, string msg, params object?[] args)
|
||||
{
|
||||
var logger = CreateLogger(tag);
|
||||
logger.LogDebug(eventId, exception, msg, args);
|
||||
@@ -75,19 +79,19 @@ public static class Log
|
||||
logger.LogError(msg, args);
|
||||
}
|
||||
|
||||
public static void Error(string tag, Exception exception, string msg)
|
||||
public static void Error(string tag, Exception? exception, string msg)
|
||||
{
|
||||
var logger = CreateLogger(tag);
|
||||
logger.LogError(exception, msg);
|
||||
}
|
||||
|
||||
public static void Error(string tag, Exception exception, string msg, params object?[] args)
|
||||
public static void Error(string tag, Exception? exception, string msg, params object?[] args)
|
||||
{
|
||||
var logger = CreateLogger(tag);
|
||||
logger.LogError(exception, msg, args);
|
||||
}
|
||||
|
||||
public static void Error(string tag, EventId eventId, Exception exception, string msg, params object?[] args)
|
||||
public static void Error(string tag, EventId eventId, Exception? exception, string msg, params object?[] args)
|
||||
{
|
||||
var logger = CreateLogger(tag);
|
||||
logger.LogError(eventId, exception, msg, args);
|
||||
@@ -115,19 +119,19 @@ public static class Log
|
||||
logger.LogInformation(msg, args);
|
||||
}
|
||||
|
||||
public static void Info(string tag, Exception exception, string msg)
|
||||
public static void Info(string tag, Exception? exception, string msg)
|
||||
{
|
||||
var logger = CreateLogger(tag);
|
||||
logger.LogInformation(exception, msg);
|
||||
}
|
||||
|
||||
public static void Info(string tag, Exception exception, string msg, params object?[] args)
|
||||
public static void Info(string tag, Exception? exception, string msg, params object?[] args)
|
||||
{
|
||||
var logger = CreateLogger(tag);
|
||||
logger.LogInformation(exception, msg, args);
|
||||
}
|
||||
|
||||
public static void Info(string tag, EventId eventId, Exception exception, string msg, params object?[] args)
|
||||
public static void Info(string tag, EventId eventId, Exception? exception, string msg, params object?[] args)
|
||||
{
|
||||
var logger = CreateLogger(tag);
|
||||
logger.LogInformation(eventId, exception, msg, args);
|
||||
@@ -155,19 +159,19 @@ public static class Log
|
||||
logger.LogWarning(msg, args);
|
||||
}
|
||||
|
||||
public static void Warn(string tag, Exception exception, string msg)
|
||||
public static void Warn(string tag, Exception? exception, string msg)
|
||||
{
|
||||
var logger = CreateLogger(tag);
|
||||
logger.LogWarning(exception, msg);
|
||||
}
|
||||
|
||||
public static void Warn(string tag, Exception exception, string msg, params object?[] args)
|
||||
public static void Warn(string tag, Exception? exception, string msg, params object?[] args)
|
||||
{
|
||||
var logger = CreateLogger(tag);
|
||||
logger.LogWarning(exception, msg, args);
|
||||
}
|
||||
|
||||
public static void Warn(string tag, EventId eventId, Exception exception, string msg, params object?[] args)
|
||||
public static void Warn(string tag, EventId eventId, Exception? exception, string msg, params object?[] args)
|
||||
{
|
||||
var logger = CreateLogger(tag);
|
||||
logger.LogWarning(eventId, exception, msg, args);
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
#if !NETSTANDARD
|
||||
|
||||
using System.Security.Authentication;
|
||||
|
||||
namespace System.Net.Http;
|
||||
|
||||
partial class GeneralHttpClientFactory
|
||||
{
|
||||
public static SocketsHttpHandler CreateSocketsHttpHandler(SocketsHttpHandler? handler = null)
|
||||
{
|
||||
handler ??= new();
|
||||
if (OperatingSystem2.IsWindows7())
|
||||
{
|
||||
// https://github.com/dotnet/runtime/issues/25722
|
||||
handler.SslOptions.EnabledSslProtocols = SslProtocols.Tls12 | SslProtocols.Tls13;
|
||||
}
|
||||
return handler;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -58,7 +58,7 @@ partial class GeneralHttpClientFactory
|
||||
/// <summary>
|
||||
/// https://github.com/dotnet/runtime/blob/v6.0.0/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/HttpNoProxy.cs
|
||||
/// </summary>
|
||||
sealed class HttpNoProxy : IWebProxy
|
||||
public sealed class HttpNoProxy : IWebProxy
|
||||
{
|
||||
public static readonly HttpNoProxy Instance = new();
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ namespace System.Properties
|
||||
/// <item> macOS:\packaging\build-osx-app.sh <CFBundleVersion> <CFBundleShortVersionString> </item>
|
||||
/// </list>
|
||||
/// </summary>
|
||||
public const string Version = "2.8.1";
|
||||
public const string Version = "2.8.4";
|
||||
|
||||
//public const string InfoVersion = Version + "-beta";
|
||||
}
|
||||
|
||||
@@ -163,6 +163,18 @@ public static class AESUtils
|
||||
return result.Base64UrlEncode();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// AES加密(String → String)
|
||||
/// </summary>
|
||||
/// <param name="aes"></param>
|
||||
/// <param name="text"></param>
|
||||
/// <returns></returns>
|
||||
public static string EncryptHex(this Aes aes, string text)
|
||||
{
|
||||
var result = EncryptToByteArray(aes, text);
|
||||
return result.ToHexString(true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// AES加密(String → String)
|
||||
/// </summary>
|
||||
@@ -239,6 +251,18 @@ public static class AESUtils
|
||||
return Decrypt(aes, data);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// (解密) String → ByteArray
|
||||
/// </summary>
|
||||
/// <param name="aes"></param>
|
||||
/// <param name="text"></param>
|
||||
/// <returns></returns>
|
||||
public static byte[] DecryptHexToByteArray(this Aes aes, string text)
|
||||
{
|
||||
var data = Convert2.FromHexString(text);
|
||||
return Decrypt(aes, data);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// (解密) String → ByteArray
|
||||
/// </summary>
|
||||
@@ -263,6 +287,18 @@ public static class AESUtils
|
||||
return Encoding.UTF8.GetString(result);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// (解密) String → String
|
||||
/// </summary>
|
||||
/// <param name="aes"></param>
|
||||
/// <param name="text"></param>
|
||||
/// <returns></returns>
|
||||
public static string DecryptHex(this Aes aes, string text)
|
||||
{
|
||||
var result = DecryptHexToByteArray(aes, text);
|
||||
return Encoding.UTF8.GetString(result);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// (解密) String → String
|
||||
/// </summary>
|
||||
|
||||
@@ -149,16 +149,16 @@ public static partial class RSAUtils
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(oaepHashAlgorithmName))
|
||||
{
|
||||
try
|
||||
{
|
||||
var hashAlgorithm = new HashAlgorithmName(oaepHashAlgorithmName);
|
||||
return RSAEncryptionPadding.CreateOaep(hashAlgorithm);
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
if (oaepHashAlgorithmName.Equals(nameof(HashAlgorithmName.SHA1), StringComparison.OrdinalIgnoreCase))
|
||||
return RSAEncryptionPadding.OaepSHA1;
|
||||
if (oaepHashAlgorithmName.Equals(nameof(HashAlgorithmName.SHA256), StringComparison.OrdinalIgnoreCase))
|
||||
return RSAEncryptionPadding.OaepSHA256;
|
||||
if (oaepHashAlgorithmName.Equals(nameof(HashAlgorithmName.SHA384), StringComparison.OrdinalIgnoreCase))
|
||||
return RSAEncryptionPadding.OaepSHA384;
|
||||
if (oaepHashAlgorithmName.Equals(nameof(HashAlgorithmName.SHA512), StringComparison.OrdinalIgnoreCase))
|
||||
return RSAEncryptionPadding.OaepSHA512;
|
||||
}
|
||||
return Padding;
|
||||
return RSAEncryptionPadding.OaepSHA256;
|
||||
}
|
||||
|
||||
public static RSAEncryptionPadding DefaultPadding
|
||||
@@ -176,7 +176,7 @@ public static partial class RSAUtils
|
||||
/// <param name="rsa"></param>
|
||||
/// <param name="data"></param>
|
||||
/// <returns></returns>
|
||||
[Obsolete()]
|
||||
[Obsolete("", true)]
|
||||
public static byte[] Encrypt(this RSA rsa, byte[] data) => rsa.Encrypt(data, Padding);
|
||||
|
||||
/// <summary>
|
||||
@@ -260,6 +260,20 @@ public static partial class RSAUtils
|
||||
return bytes.Base64UrlEncode();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// RSA加密(ByteArray → String)
|
||||
/// </summary>
|
||||
/// <param name="rsa"></param>
|
||||
/// <param name="data"></param>
|
||||
/// <param name="padding"></param>
|
||||
/// <returns></returns>
|
||||
public static string EncryptToHexString(this RSA rsa, byte[] data, RSAEncryptionPadding? padding = null)
|
||||
{
|
||||
padding ??= DefaultPadding;
|
||||
var bytes = rsa.Encrypt(data, padding);
|
||||
return bytes.ToHexString();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// RSA加密(ByteArray → String)
|
||||
/// </summary>
|
||||
@@ -283,7 +297,7 @@ public static partial class RSAUtils
|
||||
/// <param name="rsa"></param>
|
||||
/// <param name="data"></param>
|
||||
/// <returns></returns>
|
||||
[Obsolete]
|
||||
[Obsolete("", true)]
|
||||
public static byte[] Decrypt(this RSA rsa, byte[] data) => rsa.Decrypt(data, Padding);
|
||||
|
||||
/// <summary>
|
||||
@@ -314,6 +328,20 @@ public static partial class RSAUtils
|
||||
return rsa.Decrypt(bytes, padding);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// RSA解密(String → ByteArray)
|
||||
/// </summary>
|
||||
/// <param name="rsa"></param>
|
||||
/// <param name="text"></param>
|
||||
/// <param name="padding"></param>
|
||||
/// <returns></returns>
|
||||
public static byte[] DecryptToByteArrayHex(this RSA rsa, string text, RSAEncryptionPadding? padding = null)
|
||||
{
|
||||
var bytes = Convert2.FromHexString(text);
|
||||
padding ??= DefaultPadding;
|
||||
return rsa.Decrypt(bytes, padding);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// RSA解密(String → ByteArray)
|
||||
/// </summary>
|
||||
|
||||
@@ -87,7 +87,7 @@ public struct ShortGuid
|
||||
/// </summary>
|
||||
/// <param name="obj"></param>
|
||||
/// <returns></returns>
|
||||
public override bool Equals(object obj)
|
||||
public override bool Equals(object? obj)
|
||||
{
|
||||
if (obj is ShortGuid shortGuid)
|
||||
{
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
using System.Net;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace System.Application.Middleware;
|
||||
|
||||
// https://github.com/dotnet/efcore/issues/19526#issuecomment-989036617
|
||||
/* 2022-07-25 13:09:15.3924||ERROR|Microsoft.EntityFrameworkCore.Query|An exception occurred while iterating over the results of a query for context type 'System.Application.Data.ApplicationDbContext'.
|
||||
System.Threading.Tasks.TaskCanceledException: A task was canceled.
|
||||
at Microsoft.EntityFrameworkCore.Storage.RelationalConnection.OpenInternalAsync(Boolean errorsExpected, CancellationToken cancellationToken)
|
||||
at Microsoft.EntityFrameworkCore.Storage.RelationalConnection.OpenInternalAsync(Boolean errorsExpected, CancellationToken cancellationToken)
|
||||
at Microsoft.EntityFrameworkCore.Storage.RelationalConnection.OpenAsync(CancellationToken cancellationToken, Boolean errorsExpected)
|
||||
at Microsoft.EntityFrameworkCore.Storage.RelationalCommand.ExecuteReaderAsync(RelationalCommandParameterObject parameterObject, CancellationToken cancellationToken)
|
||||
at Microsoft.EntityFrameworkCore.Query.Internal.SingleQueryingEnumerable`1.AsyncEnumerator.InitializeReaderAsync(AsyncEnumerator enumerator, CancellationToken cancellationToken)
|
||||
at Microsoft.EntityFrameworkCore.SqlServer.Storage.Internal.SqlServerExecutionStrategy.ExecuteAsync[TState,TResult](TState state, Func`4 operation, Func`4 verifySucceeded, CancellationToken cancellationToken)
|
||||
at Microsoft.EntityFrameworkCore.Query.Internal.SingleQueryingEnumerable`1.AsyncEnumerator.MoveNextAsync() System.Threading.Tasks.TaskCanceledException: A task was canceled.
|
||||
at Microsoft.EntityFrameworkCore.Storage.RelationalConnection.OpenInternalAsync(Boolean errorsExpected, CancellationToken cancellationToken)
|
||||
at Microsoft.EntityFrameworkCore.Storage.RelationalConnection.OpenInternalAsync(Boolean errorsExpected, CancellationToken cancellationToken)
|
||||
at Microsoft.EntityFrameworkCore.Storage.RelationalConnection.OpenAsync(CancellationToken cancellationToken, Boolean errorsExpected)
|
||||
at Microsoft.EntityFrameworkCore.Storage.RelationalCommand.ExecuteReaderAsync(RelationalCommandParameterObject parameterObject, CancellationToken cancellationToken)
|
||||
at Microsoft.EntityFrameworkCore.Query.Internal.SingleQueryingEnumerable`1.AsyncEnumerator.InitializeReaderAsync(AsyncEnumerator enumerator, CancellationToken cancellationToken)
|
||||
at Microsoft.EntityFrameworkCore.SqlServer.Storage.Internal.SqlServerExecutionStrategy.ExecuteAsync[TState,TResult](TState state, Func`4 operation, Func`4 verifySucceeded, CancellationToken cancellationToken)
|
||||
at Microsoft.EntityFrameworkCore.Query.Internal.SingleQueryingEnumerable`1.AsyncEnumerator.MoveNextAsync()|url: https://api.steampp.net/ExternalLoginCallback|action: ExternalLoginCallback
|
||||
*/
|
||||
|
||||
public sealed class StandardizedExceptionMiddleware
|
||||
{
|
||||
readonly RequestDelegate _next;
|
||||
//readonly ILogger<StandardizedExceptionMiddleware> _logger;
|
||||
|
||||
public StandardizedExceptionMiddleware(RequestDelegate next/*, ILogger<StandardizedExceptionMiddleware> logger*/)
|
||||
{
|
||||
_next = next;
|
||||
//_logger = logger;
|
||||
}
|
||||
|
||||
public async Task Invoke(HttpContext context)
|
||||
{
|
||||
try
|
||||
{
|
||||
await _next(context).ConfigureAwait(false);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (IsCancellationException(ex))
|
||||
{
|
||||
if (context.RequestAborted.IsCancellationRequested)
|
||||
{
|
||||
// Try to ensure cancelled requests don't get reported as 5xx errors
|
||||
context.Response.StatusCode = (int)HttpStatusCode.BadRequest;
|
||||
}
|
||||
return;
|
||||
}
|
||||
// This exception wasn't handled so let it bubble up
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
static bool IsCancellationException(Exception ex)
|
||||
{
|
||||
if (ex == null) return false;
|
||||
if (ex is OperationCanceledException)
|
||||
return true;
|
||||
if (ex is IOException && ex.Message == "The client reset the request stream.")
|
||||
return true;
|
||||
if (ex is Microsoft.AspNetCore.Connections.ConnectionResetException)
|
||||
return true;
|
||||
// .NET SQL has a number of different exceptions thrown when operations are cancelled
|
||||
if (ex.Source == "Microsoft.Data.SqlClient" && ex.Message == "Operation cancelled by user.")
|
||||
return true;
|
||||
if ((ex is Microsoft.Data.SqlClient.SqlException /*|| ex is System.Data.SqlClient.SqlException*/) &&
|
||||
ex.Message.Contains("Operation cancelled by user"))
|
||||
return true;
|
||||
if (ex.InnerException is not null) return IsCancellationException(ex.InnerException);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
using System.Net.Security;
|
||||
using System.Net.Sockets;
|
||||
|
||||
namespace System.Net.Http;
|
||||
|
||||
public sealed class ReverseProxyHttpClientHandler : DelegatingHandler
|
||||
{
|
||||
readonly TimeSpan connectTimeout = TimeSpan.FromSeconds(10d);
|
||||
|
||||
public ReverseProxyHttpClientHandler()
|
||||
{
|
||||
InnerHandler = new SocketsHttpHandler()
|
||||
{
|
||||
Proxy = GeneralHttpClientFactory.HttpNoProxy.Instance,
|
||||
UseProxy = false,
|
||||
UseCookies = false,
|
||||
AllowAutoRedirect = false,
|
||||
AutomaticDecompression = DecompressionMethods.None,
|
||||
ConnectCallback = ConnectCallback,
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 发送请求
|
||||
/// </summary>
|
||||
/// <param name="request"></param>
|
||||
/// <param name="cancellationToken"></param>
|
||||
/// <returns></returns>
|
||||
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
|
||||
{
|
||||
var uri = request.RequestUri;
|
||||
if (uri == null)
|
||||
{
|
||||
throw new ApplicationException("The requested URI must be specified.");
|
||||
}
|
||||
|
||||
// 设置请求头 host,修改协议为 http
|
||||
request.Headers.Host = uri.Host;
|
||||
request.RequestUri = new UriBuilder(uri) { Scheme = Uri.UriSchemeHttp }.Uri;
|
||||
|
||||
return await base.SendAsync(request, cancellationToken);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 连接回调
|
||||
/// </summary>
|
||||
/// <param name="context"></param>
|
||||
/// <param name="cancellationToken"></param>
|
||||
/// <returns></returns>
|
||||
async ValueTask<Stream> ConnectCallback(SocketsHttpConnectionContext context, CancellationToken cancellationToken)
|
||||
{
|
||||
var innerExceptions = new List<Exception>();
|
||||
var ipEndPoints = (await Dns.GetHostAddressesAsync("steamcommunity.rmbgame.net", cancellationToken)).Select(s => new IPEndPoint(s, context.DnsEndPoint.Port));
|
||||
|
||||
foreach (var ipEndPoint in ipEndPoints)
|
||||
{
|
||||
try
|
||||
{
|
||||
using var timeoutTokenSource = new CancellationTokenSource(connectTimeout);
|
||||
using var linkedTokenSource = CancellationTokenSource.CreateLinkedTokenSource(timeoutTokenSource.Token, cancellationToken);
|
||||
return await ConnectAsync(context, ipEndPoint, linkedTokenSource.Token);
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
innerExceptions.Add(new TimeoutException(
|
||||
$"HTTP connection to {ipEndPoint.Address} timed out."));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
innerExceptions.Add(ex);
|
||||
}
|
||||
}
|
||||
|
||||
throw new AggregateException("Could not find any IP that can be successfully connected.", innerExceptions);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 建立连接
|
||||
/// </summary>
|
||||
/// <param name="context"></param>
|
||||
/// <param name="ipEndPoint"></param>
|
||||
/// <param name="cancellationToken"></param>
|
||||
/// <returns></returns>
|
||||
static async ValueTask<Stream> ConnectAsync(SocketsHttpConnectionContext context, IPEndPoint ipEndPoint, CancellationToken cancellationToken)
|
||||
{
|
||||
var socket = new Socket(ipEndPoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
|
||||
await socket.ConnectAsync(ipEndPoint, cancellationToken);
|
||||
var stream = new NetworkStream(socket, ownsSocket: true);
|
||||
|
||||
if (context.DnsEndPoint.Port != 443)
|
||||
{
|
||||
return stream;
|
||||
}
|
||||
|
||||
var sslStream = new SslStream(stream, leaveInnerStreamOpen: false);
|
||||
await sslStream.AuthenticateAsClientAsync(new SslClientAuthenticationOptions
|
||||
{
|
||||
TargetHost = string.Empty,
|
||||
RemoteCertificateValidationCallback = (_, _, _, _) => true,
|
||||
}, cancellationToken);
|
||||
|
||||
return sslStream;
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,7 @@
|
||||
#if NET35
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace System
|
||||
{
|
||||
@@ -54,6 +55,8 @@ namespace System
|
||||
public static bool Is64BitOperatingSystem => Is64BitProcess || (IntPtr.Size == 4 && Is32BitProcessOn64BitProcessor());
|
||||
|
||||
public static bool Is64BitProcess => IntPtr.Size == 8;
|
||||
|
||||
public static string ProcessPath => Application.ExecutablePath;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -4,7 +4,7 @@
|
||||
<OutputType>WinExe</OutputType>
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
<TargetFramework>net35</TargetFramework>
|
||||
<ApplicationManifest>..\ST.Client.Desktop.Avalonia.App\Properties\app.manifest</ApplicationManifest>
|
||||
<ApplicationManifest>Properties\app.manifest</ApplicationManifest>
|
||||
<AssemblyName>Steam++.Launcher</AssemblyName>
|
||||
<ApplicationIcon>..\ST.Client.Desktop.Avalonia\Application\UI\Assets\Icon.ico</ApplicationIcon>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
|
||||
@@ -4,6 +4,7 @@ using System.Diagnostics.CodeAnalysis;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Windows.Forms;
|
||||
using NuGet.Versioning;
|
||||
using Microsoft.Win32;
|
||||
using Microsoft.DotNet.Tools.Uninstall.Windows;
|
||||
using Microsoft.DotNet.Tools.Uninstall.Shared.BundleInfo;
|
||||
using FDELauncher.Properties;
|
||||
@@ -16,8 +17,10 @@ internal static class Program
|
||||
{
|
||||
const string ExecutiveName = "Steam++";
|
||||
static BundleArch matchArch;
|
||||
static readonly SemanticVersion runtimeVersion = new(6, 0, 7);
|
||||
static readonly SemanticVersion sdkVersion = new(6, 0, 107);
|
||||
static readonly SemanticVersion runtimeVersion = new(6, 0, 8);
|
||||
static readonly SemanticVersion sdkVersion1 = new(6, 0, 108);
|
||||
static readonly SemanticVersion sdkVersion3 = new(6, 0, 303);
|
||||
static readonly SemanticVersion sdkVersion4 = new(6, 0, 400);
|
||||
|
||||
/// <summary>
|
||||
/// 应用程序的主入口点。
|
||||
@@ -27,6 +30,11 @@ internal static class Program
|
||||
{
|
||||
try
|
||||
{
|
||||
if (IsProgramInCompatibilityMode())
|
||||
{
|
||||
MessageBox.Show(R.ProgramInCompatibilityModeError, R.Error, MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||
return 0;
|
||||
}
|
||||
if (args.Length == 1 && args[0] == "--query")
|
||||
{
|
||||
var installed = RegistryQuery.GetAllInstalledBundles().ToArray();
|
||||
@@ -50,7 +58,7 @@ internal static class Program
|
||||
if (RuntimeInformation2.OSArchitecture != Architecture.Arm64) throw new PlatformNotSupportedException();
|
||||
break;
|
||||
}
|
||||
if (IsRuntimeInstalled(matchArch, runtimeVersion, sdkVersion))
|
||||
if (IsRuntimeInstalled(matchArch, runtimeVersion, sdkVersion1, sdkVersion3, sdkVersion4))
|
||||
{
|
||||
Run();
|
||||
}
|
||||
@@ -187,7 +195,7 @@ internal static class Program
|
||||
/// 是否已安装运行时或 SDK
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
static bool IsRuntimeInstalled(BundleArch matchArch, SemanticVersion runtimeVersion, SemanticVersion sdkVersion)
|
||||
static bool IsRuntimeInstalled(BundleArch matchArch, SemanticVersion runtimeVersion, SemanticVersion sdkVersion1, SemanticVersion sdkVersion3, SemanticVersion sdkVersion4)
|
||||
{
|
||||
// ArchiSteamFarm 依赖 ASP.NET Core
|
||||
// ASP.NET Core 运行时安装程序除 Hosting Bundle 不包含 .NET 运行时 缺少文件 C:\Program Files\dotnet\host\fxr\{version}\hostfxr.dll
|
||||
@@ -196,7 +204,12 @@ internal static class Program
|
||||
// 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);
|
||||
var query_sdk = installed.Where(m => m.Version.Type == BundleType.Sdk &&
|
||||
(
|
||||
(m.Version.SemVer >= sdkVersion1 && m.Version.SemVer < new SemanticVersion(sdkVersion1.Major, sdkVersion1.Minor, 199)) ||
|
||||
(m.Version.SemVer >= sdkVersion3 && m.Version.SemVer < new SemanticVersion(sdkVersion3.Major, sdkVersion3.Minor, 399)) ||
|
||||
(m.Version.SemVer >= sdkVersion4 && m.Version.SemVer < new SemanticVersion(sdkVersion4.Major, sdkVersion4.Minor, 499))
|
||||
));
|
||||
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);
|
||||
@@ -259,4 +272,28 @@ internal static class Program
|
||||
UseShellExecute = false,
|
||||
});
|
||||
}
|
||||
|
||||
static bool IsProgramInCompatibilityMode()
|
||||
{
|
||||
try
|
||||
{
|
||||
foreach (var item in new[] { Registry.CurrentUser, Registry.LocalMachine })
|
||||
{
|
||||
using var layers = item.OpenSubKey(@"SOFTWARE\Microsoft\Windows NT\CurrentVersion\AppCompatFlags\Layers");
|
||||
var value = layers?.GetValue(Environment2.ProcessPath)?.ToString();
|
||||
if (value != null)
|
||||
{
|
||||
value = value.ToUpperInvariant();
|
||||
if (value.Contains("WIN8")) return true;
|
||||
if (value.Contains("WIN7")) return true;
|
||||
if (value.Contains("VISTA")) return true;
|
||||
if (value.Contains("WINXP")) return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -149,6 +149,29 @@ static class Resources
|
||||
Language.Russian => "Это приложение поддерживает только 64-битные операционные системы",
|
||||
_ => "This application only supports 64-bit operating systems",
|
||||
});
|
||||
|
||||
public static string Error => GetString(l => l switch
|
||||
{
|
||||
Language.ChineseSimplified => "错误",
|
||||
Language.ChineseTraditional => "錯誤",
|
||||
Language.Italian => "errore",
|
||||
Language.Japanese => "エラー",
|
||||
Language.Korean => "잘못",
|
||||
Language.Russian => "ошибка",
|
||||
_ => "Error",
|
||||
});
|
||||
|
||||
public static string ProgramInCompatibilityModeError => GetString(l => l switch
|
||||
{
|
||||
Language.ChineseSimplified => "不能在 Windows 兼容模式中运行,请关闭后重试",
|
||||
Language.ChineseTraditional => "不能在 Windows 相容模式中運行,請關閉後重試",
|
||||
Language.Spanish => "El modo de compatibilidad de programas de Windows está activado. Desactívelo y vuelva a intentarlo",
|
||||
Language.Italian => "La modalità di compatibilità dei programmi di Windows è attiva. Disattivarla e riprovare",
|
||||
Language.Japanese => "Windowsプログラムの互換性モードがオンになっています。これをオフにしてから、もう一度試してください。",
|
||||
Language.Korean => "Windows 프로그램 호환 모드가 열렸습니다. 닫고 다시 시도하십시오.",
|
||||
Language.Russian => "режим совместимости Windows был открыт. пожалуйста, выключите его и попробуйте снова",
|
||||
_ => "Windows Program Compatibility mode is on. Turn it off and then try again",
|
||||
});
|
||||
}
|
||||
|
||||
enum Language
|
||||
|
||||
@@ -5,6 +5,6 @@ namespace System.Properties
|
||||
/// <summary>
|
||||
/// 版本号
|
||||
/// </summary>
|
||||
public const string Version = "1.0.1";
|
||||
public const string Version = "1.1.0";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
|
||||
<assemblyIdentity version="1.0.0.0" name="MyApplication.app"/>
|
||||
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v2">
|
||||
<security>
|
||||
<requestedPrivileges xmlns="urn:schemas-microsoft-com:asm.v3">
|
||||
<!-- UAC 清单选项
|
||||
如果想要更改 Windows 用户帐户控制级别,请使用
|
||||
以下节点之一替换 requestedExecutionLevel 节点。
|
||||
|
||||
<requestedExecutionLevel level="asInvoker" uiAccess="false" />
|
||||
<requestedExecutionLevel level="requireAdministrator" uiAccess="false" />
|
||||
<requestedExecutionLevel level="highestAvailable" uiAccess="false" />
|
||||
|
||||
指定 requestedExecutionLevel 元素将禁用文件和注册表虚拟化。
|
||||
如果你的应用程序需要此虚拟化来实现向后兼容性,则移除此
|
||||
元素。
|
||||
-->
|
||||
<requestedExecutionLevel level="requireAdministrator" uiAccess="false" />
|
||||
</requestedPrivileges>
|
||||
</security>
|
||||
</trustInfo>
|
||||
|
||||
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
|
||||
<application>
|
||||
<!-- 设计此应用程序与其一起工作且已针对此应用程序进行测试的
|
||||
Windows 版本的列表。取消评论适当的元素,
|
||||
Windows 将自动选择最兼容的环境。 -->
|
||||
|
||||
<!-- Windows Vista -->
|
||||
<supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}" />
|
||||
|
||||
<!-- Windows 7 -->
|
||||
<supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}" />
|
||||
|
||||
<!-- Windows 8 -->
|
||||
<supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}" />
|
||||
|
||||
<!-- Windows 8.1 -->
|
||||
<supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}" />
|
||||
|
||||
<!-- Windows 10 -->
|
||||
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}" />
|
||||
|
||||
</application>
|
||||
</compatibility>
|
||||
|
||||
<!-- 指示该应用程序可感知 DPI 且 Windows 在 DPI 较高时将不会对其进行
|
||||
自动缩放。Windows Presentation Foundation (WPF)应用程序自动感知 DPI,无需
|
||||
选择加入。选择加入此设置的 Windows 窗体应用程序(面向 .NET Framework 4.6)还应
|
||||
在其 app.config 中将 "EnableWindowsFormsHighDpiAutoResizing" 设置设置为 "true"。
|
||||
|
||||
将应用程序设为感知长路径。请参阅 https://docs.microsoft.com/windows/win32/fileio/maximum-file-path-limitation -->
|
||||
<!--
|
||||
<application xmlns="urn:schemas-microsoft-com:asm.v3">
|
||||
<windowsSettings>
|
||||
<dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true</dpiAware>
|
||||
<longPathAware xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">true</longPathAware>
|
||||
</windowsSettings>
|
||||
</application>
|
||||
-->
|
||||
|
||||
<!-- 启用 Windows 公共控件和对话框的主题(Windows XP 和更高版本) -->
|
||||
<!--
|
||||
<dependency>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity
|
||||
type="win32"
|
||||
name="Microsoft.Windows.Common-Controls"
|
||||
version="6.0.0.0"
|
||||
processorArchitecture="*"
|
||||
publicKeyToken="6595b64144ccf1df"
|
||||
language="*"
|
||||
/>
|
||||
</dependentAssembly>
|
||||
</dependency>
|
||||
-->
|
||||
|
||||
</assembly>
|
||||
@@ -1,15 +1,17 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netstandard2.1</TargetFramework>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<SignAssembly>true</SignAssembly>
|
||||
<DelaySign>false</DelaySign>
|
||||
<AssemblyOriginatorKeyFile>$(MSBuildProjectDirectory)\..\..\avalonia.snk</AssemblyOriginatorKeyFile>
|
||||
<Version>2.2.255.255</Version>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Include="..\AssemblyInfo.Version.Max.cs">
|
||||
<LinkBase>Properties</LinkBase>
|
||||
</Compile>
|
||||
</ItemGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)'=='Release'">
|
||||
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Extensions.Primitives" />
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:versionCode="20220514" android:versionName="2.8.0" package="net.steampp.app" android:installLocation="auto">
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:versionCode="20220803" android:versionName="2.8.3" package="net.steampp.app" android:installLocation="auto">
|
||||
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="31" />
|
||||
<application android:fullBackupContent="@xml/auto_backup_rules"></application>
|
||||
<!-- Required for API Level 30 to make sure the app can detect browsers and other apps where communication is needed.-->
|
||||
|
||||
@@ -19,7 +19,8 @@
|
||||
<AotAssemblies>false</AotAssemblies>
|
||||
<EnableLLVM>false</EnableLLVM>
|
||||
<AndroidEnableProfiledAot>false</AndroidEnableProfiledAot>
|
||||
<AndroidLinkMode>None</AndroidLinkMode>
|
||||
<AndroidLinkMode>Full</AndroidLinkMode>
|
||||
<AndroidLinkTool>r8</AndroidLinkTool>
|
||||
<PublishTrimmed>false</PublishTrimmed>
|
||||
<AndroidFragmentType>AndroidX.Fragment.App.Fragment</AndroidFragmentType>
|
||||
</PropertyGroup>
|
||||
@@ -187,7 +188,7 @@
|
||||
<PackageReference Include="Xamarin.Build.Download" />
|
||||
<!--<PackageReference Include="Xamarin.AndroidX.Window" />-->
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
<ItemGroup>
|
||||
<AndroidAsset Include="..\..\references\Android Assets\cncity.txt" Link="Assets\cncity.txt" />
|
||||
</ItemGroup>
|
||||
@@ -553,6 +554,9 @@
|
||||
<AndroidResource Include="..\ST.Client.Mobile.Droid.Design\ui\src\main\res\layout\activity_toolbar_tablayout_viewpager2.xml">
|
||||
<Link>Resources\layout\activity_toolbar_tablayout_viewpager2.xml</Link>
|
||||
</AndroidResource>
|
||||
<AndroidResource Include="..\ST.Client.Mobile.Droid.Design\ui\src\main\res\layout\activity_toolbar_webview.xml">
|
||||
<Link>Resources\layout\activity_toolbar_webview.xml</Link>
|
||||
</AndroidResource>
|
||||
<AndroidResource Include="..\ST.Client.Mobile.Droid.Design\ui\src\main\res\layout\fragment_about.xml">
|
||||
<Link>Resources\layout\fragment_about.xml</Link>
|
||||
</AndroidResource>
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
#if NET6_0_OR_GREATER
|
||||
using System.Drawing;
|
||||
using AndroidColor = Android.Graphics.Color;
|
||||
|
||||
namespace System;
|
||||
|
||||
public static partial class ColorExtensions
|
||||
{
|
||||
public static Color ToSystemColor(this AndroidColor color) =>
|
||||
Color.FromArgb(color.A, color.R, color.G, color.B);
|
||||
|
||||
public static AndroidColor ToPlatformColor(this Color color) =>
|
||||
new(color.R, color.G, color.B, color.A);
|
||||
}
|
||||
#endif
|
||||
@@ -4,28 +4,29 @@ using Android.OS;
|
||||
using NativeService = System.Application.Services.Native.IServiceBase;
|
||||
|
||||
// ReSharper disable once CheckNamespace
|
||||
namespace System
|
||||
namespace System;
|
||||
|
||||
partial class ContextExtensions
|
||||
{
|
||||
partial class ContextExtensions
|
||||
/// <summary>
|
||||
/// 启动或关闭前台服务
|
||||
/// </summary>
|
||||
/// <typeparam name="TService"></typeparam>
|
||||
/// <param name="context"></param>
|
||||
/// <param name="startOrStop"></param>
|
||||
public static void StartOrStopForegroundService<TService>(this Context context, bool startOrStop) where TService : Service, NativeService
|
||||
{
|
||||
/// <summary>
|
||||
/// 启动或关闭前台服务
|
||||
/// </summary>
|
||||
/// <typeparam name="TService"></typeparam>
|
||||
/// <param name="context"></param>
|
||||
/// <param name="startOrStop"></param>
|
||||
public static void StartOrStopForegroundService<TService>(this Context context, bool startOrStop) where TService : Service, NativeService
|
||||
Intent intent = new(context, typeof(TService));
|
||||
intent.SetAction(startOrStop ? NativeService.START : NativeService.STOP);
|
||||
if (Build.VERSION.SdkInt >= BuildVersionCodes.O)
|
||||
{
|
||||
Intent intent = new(context, typeof(TService));
|
||||
intent.SetAction(startOrStop ? NativeService.START : NativeService.STOP);
|
||||
if (Build.VERSION.SdkInt >= BuildVersionCodes.O)
|
||||
{
|
||||
context.StartForegroundService(intent);
|
||||
}
|
||||
else
|
||||
{
|
||||
context.StartService(intent);
|
||||
}
|
||||
#pragma warning disable CA1416 // 验证平台兼容性
|
||||
context.StartForegroundService(intent);
|
||||
#pragma warning restore CA1416 // 验证平台兼容性
|
||||
}
|
||||
else
|
||||
{
|
||||
context.StartService(intent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
using System.Threading.Tasks;
|
||||
using Android.Telephony;
|
||||
#if NET6_0_OR_GREATER
|
||||
using XEPlatform = Microsoft.Maui.ApplicationModel.Platform;
|
||||
#else
|
||||
using XEPlatform = Xamarin.Essentials.Platform;
|
||||
#endif
|
||||
|
||||
namespace System.Application;
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<Project Sdk="MSBuild.Sdk.Extras">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>MonoAndroid12.0</TargetFrameworks>
|
||||
<TargetFrameworks>MonoAndroid12.0;net6.0-android</TargetFrameworks>
|
||||
<RootNamespace>System.Application</RootNamespace>
|
||||
<AssemblyName>Xamarin.Forms.Platform.Android.UnitTests</AssemblyName>
|
||||
<DefineConstants>SHADOWSOCKS;$(DefineConstants)</DefineConstants>
|
||||
@@ -9,6 +9,11 @@
|
||||
<SupportedOSPlatformVersion Condition="$(TargetFramework.Contains('-android'))">21.0</SupportedOSPlatformVersion>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition=" !($(TargetFramework.StartsWith('MonoAndroid'))) ">
|
||||
<UseMaui>true</UseMaui>
|
||||
<DefineConstants>NET6_0_MAUI_LIB;$(DefineConstants)</DefineConstants>
|
||||
</PropertyGroup>
|
||||
|
||||
<!--当前包是否为应用商店渠道包(IS_STORE_PACKAGE)-->
|
||||
<!--<PropertyGroup>
|
||||
<DefineConstants>IS_STORE_PACKAGE;$(DefineConstants)</DefineConstants>
|
||||
@@ -25,7 +30,7 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Include="..\Startup.cs" Link="UI\MainApplication.Startup.cs" />
|
||||
<Compile Condition="!$(TargetFramework.Contains('-android'))" Include="..\Startup.cs" Link="UI\MainApplication.Startup.cs" />
|
||||
<Compile Include="..\Common.CoreLib\Properties\AssemblyInfo.cs">
|
||||
<Link>Properties\AssemblyInfo.cs</Link>
|
||||
</Compile>
|
||||
@@ -106,7 +111,7 @@
|
||||
<LinkBase>UI\Adapters</LinkBase>
|
||||
</Compile>
|
||||
<Compile Include="..\Common.ClientLib.Droid\Application\DarkModeUtil.cs" />
|
||||
<Compile Include="..\ST.Client.Desktop.Avalonia\ServiceCollectionExtensions.AddWindowManager.cs" />
|
||||
<Compile Condition="!$(TargetFramework.Contains('-android'))" Include="..\ST.Client.Desktop.Avalonia\ServiceCollectionExtensions.AddWindowManager.cs" />
|
||||
<Compile Include="..\ST.Client.Desktop.Avalonia\ServiceCollectionExtensions.AddApplicationUpdateService.cs" />
|
||||
<AndroidResource Include="Resources\**\*" />
|
||||
<Compile Remove="Services\V2Ray\**" />
|
||||
@@ -149,6 +154,7 @@
|
||||
<PackageReference Include="Xamarin.Google.MLKit.BarcodeScanning" />
|
||||
<PackageReference Include="Xamarin.Build.Download" />
|
||||
<!--<PackageReference Include="Xamarin.AndroidX.Window" />-->
|
||||
<PackageReference Include="Microsoft.Net.Http.Headers" />
|
||||
</ItemGroup>
|
||||
|
||||
<!--<ItemGroup Condition="'$(Configuration)'=='Debug'">
|
||||
@@ -157,7 +163,8 @@
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Common.CoreLib\Common.CoreLib.csproj" />
|
||||
<ProjectReference Include="..\Common.Essentials.Xamarin\Common.Essentials.Xamarin.csproj" />
|
||||
<ProjectReference Condition=" ($(TargetFramework.StartsWith('MonoAndroid'))) " Include="..\Common.Essentials.Xamarin\Common.Essentials.Xamarin.csproj" />
|
||||
<ProjectReference Condition=" !($(TargetFramework.StartsWith('MonoAndroid'))) " Include="..\Common.Essentials.Maui\Common.Essentials.Maui.csproj" />
|
||||
<ProjectReference Condition=" $(DefineConstants.Contains(`V2RAY`)) " Include="..\ST.Client.Android.V2Ray\ST.Client.Android.V2Ray.csproj" />
|
||||
<ProjectReference Condition=" $(DefineConstants.Contains(`SHADOWSOCKS`)) " Include="..\ST.Client.Android.Shadowsocks\ST.Client.Android.Shadowsocks.csproj" />
|
||||
<ProjectReference Include="..\ST.Client.AppCenter\ST.Client.AppCenter.csproj" />
|
||||
|
||||
@@ -1,7 +1,15 @@
|
||||
using Android.OS;
|
||||
using System.IO;
|
||||
#if NET6_0_OR_GREATER
|
||||
using Microsoft.Maui.Devices;
|
||||
#else
|
||||
using Xamarin.Essentials;
|
||||
#endif
|
||||
#if NET6_0_OR_GREATER
|
||||
using XEDeviceType = Microsoft.Maui.Devices.DeviceType;
|
||||
#else
|
||||
using XEDeviceType = Xamarin.Essentials.DeviceType;
|
||||
#endif
|
||||
|
||||
namespace System.Application.Services.Implementation
|
||||
{
|
||||
|
||||
+4
@@ -2,7 +2,11 @@ using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using ASF_FS = System.Application.Services.Native.ArchiSteamFarmForegroundService;
|
||||
#if NET6_0_OR_GREATER
|
||||
using XEPlatform = Microsoft.Maui.ApplicationModel.Platform;
|
||||
#else
|
||||
using XEPlatform = Xamarin.Essentials.Platform;
|
||||
#endif
|
||||
using Android.App;
|
||||
using System.Application.Services.Native;
|
||||
|
||||
|
||||
@@ -4,7 +4,11 @@ using System.Net;
|
||||
using AndroidX.AppCompat.App;
|
||||
using System.Application.UI.Activities;
|
||||
using AndroidApplication = Android.App.Application;
|
||||
#if NET6_0_OR_GREATER
|
||||
using XEPlatform = Microsoft.Maui.ApplicationModel.Platform;
|
||||
#else
|
||||
using XEPlatform = Xamarin.Essentials.Platform;
|
||||
#endif
|
||||
|
||||
namespace System.Application.Services.Implementation
|
||||
{
|
||||
|
||||
@@ -12,288 +12,305 @@ using System.Application.UI.Resx;
|
||||
using System.Application.UI.ViewModels;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Threading.Tasks;
|
||||
using System.Collections.Generic;
|
||||
#if NET6_0_OR_GREATER
|
||||
using Microsoft.Maui.ApplicationModel;
|
||||
using static Microsoft.Maui.ApplicationModel.Platform;
|
||||
#else
|
||||
using Xamarin.Essentials;
|
||||
using static Xamarin.Essentials.Platform;
|
||||
#endif
|
||||
using AlertDialog = AndroidX.AppCompat.App.AlertDialog;
|
||||
|
||||
namespace System.Application.Services.Implementation
|
||||
namespace System.Application.Services.Implementation;
|
||||
|
||||
/// <inheritdoc cref="IWindowManager"/>
|
||||
public class AndroidWindowManagerImpl : IWindowManagerImpl
|
||||
{
|
||||
/// <inheritdoc cref="IWindowManager"/>
|
||||
public class AndroidWindowManagerImpl : IWindowManagerImpl
|
||||
/* AlertDialog
|
||||
* =======================================
|
||||
* = Title =
|
||||
* = Message(Content) =
|
||||
* = Neutral Negative Positive =
|
||||
* =======================================
|
||||
*/
|
||||
|
||||
protected static string PositiveButtonText => AppResources.Confirm;
|
||||
|
||||
protected static string NegativeButtonText => AppResources.Cancel;
|
||||
|
||||
protected static string NeutralButtonText => AppResources.RememberChooseNotToAskAgain;
|
||||
|
||||
protected async Task<bool> PlatformShowWindow(CustomWindow customWindow, PageViewModel? viewModel = null, string title = "")
|
||||
{
|
||||
/* AlertDialog
|
||||
* =======================================
|
||||
* = Title =
|
||||
* = Message(Content) =
|
||||
* = Neutral Negative Positive =
|
||||
* =======================================
|
||||
*/
|
||||
|
||||
protected static string PositiveButtonText => AppResources.Confirm;
|
||||
|
||||
protected static string NegativeButtonText => AppResources.Cancel;
|
||||
|
||||
protected static string NeutralButtonText => AppResources.RememberChooseNotToAskAgain;
|
||||
|
||||
protected async Task<bool> PlatformShowWindow(CustomWindow customWindow, PageViewModel? viewModel = null, string title = "")
|
||||
Activity currentActivity;
|
||||
do
|
||||
{
|
||||
Activity currentActivity;
|
||||
do
|
||||
{
|
||||
currentActivity = await WaitForActivityAsync();
|
||||
await Task.Delay(100);
|
||||
} while (!currentActivity.HasValue());
|
||||
return await MainThread.InvokeOnMainThreadAsync(async () =>
|
||||
{
|
||||
return await PlatformShowWindowCore(currentActivity, customWindow, viewModel, title);
|
||||
});
|
||||
currentActivity = await WaitForActivityAsync();
|
||||
await Task.Delay(100);
|
||||
} while (!currentActivity.HasValue());
|
||||
return await MainThread.InvokeOnMainThreadAsync(async () =>
|
||||
{
|
||||
return await PlatformShowWindowCore(currentActivity, customWindow, viewModel, title);
|
||||
});
|
||||
}
|
||||
|
||||
bool IWindowManager.UseMyAuthenticatorWrapper => true;
|
||||
|
||||
protected Task<bool> PlatformShowWindowCore(Activity currentActivity, CustomWindow customWindow, PageViewModel? viewModel = null, string title = "")
|
||||
{
|
||||
switch (customWindow)
|
||||
{
|
||||
case CustomWindow.ShowAuth:
|
||||
case CustomWindow.AuthTrade:
|
||||
if (viewModel is IMyAuthenticatorWrapper viewModel_auth_w && viewModel_auth_w.MyAuthenticator != null)
|
||||
{
|
||||
switch (customWindow)
|
||||
{
|
||||
case CustomWindow.ShowAuth:
|
||||
// Android Activity 传参需要序列化后再反序列化,不能直接传递
|
||||
// 所以此处传递 Id,在 Activity 中从关联的集合中根据 Id 取值
|
||||
SteamAuthDataActivity.StartActivity(currentActivity, viewModel_auth_w.MyAuthenticator.Id);
|
||||
break;
|
||||
case CustomWindow.AuthTrade:
|
||||
SteamAuthTradeActivity.StartActivity(currentActivity, viewModel_auth_w.MyAuthenticator.Id);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return Task.FromResult(false);
|
||||
}
|
||||
|
||||
bool IWindowManager.UseMyAuthenticatorWrapper => true;
|
||||
|
||||
protected Task<bool> PlatformShowWindowCore(Activity currentActivity, CustomWindow customWindow, PageViewModel? viewModel = null, string title = "")
|
||||
var activityType = customWindow switch
|
||||
{
|
||||
switch (customWindow)
|
||||
{
|
||||
case CustomWindow.ShowAuth:
|
||||
case CustomWindow.AuthTrade:
|
||||
if (viewModel is IMyAuthenticatorWrapper viewModel_auth_w && viewModel_auth_w.MyAuthenticator != null)
|
||||
{
|
||||
switch (customWindow)
|
||||
{
|
||||
case CustomWindow.ShowAuth:
|
||||
// Android Activity 传参需要序列化后再反序列化,不能直接传递
|
||||
// 所以此处传递 Id,在 Activity 中从关联的集合中根据 Id 取值
|
||||
SteamAuthDataActivity.StartActivity(currentActivity, viewModel_auth_w.MyAuthenticator.Id);
|
||||
break;
|
||||
case CustomWindow.AuthTrade:
|
||||
SteamAuthTradeActivity.StartActivity(currentActivity, viewModel_auth_w.MyAuthenticator.Id);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return Task.FromResult(false);
|
||||
}
|
||||
CustomWindow.AddAuth => typeof(AddAuthActivity),
|
||||
CustomWindow.ExportAuth => typeof(ExportAuthActivity),
|
||||
CustomWindow.EncryptionAuth => typeof(EncryptionAuthActivity),
|
||||
CustomWindow.ProxySettings => typeof(ProxySettingsActivity),
|
||||
_ => null,
|
||||
};
|
||||
if (activityType != null)
|
||||
{
|
||||
currentActivity.StartActivity(activityType);
|
||||
return Task.FromResult(false);
|
||||
}
|
||||
|
||||
var activityType = customWindow switch
|
||||
{
|
||||
CustomWindow.AddAuth => typeof(AddAuthActivity),
|
||||
CustomWindow.ExportAuth => typeof(ExportAuthActivity),
|
||||
CustomWindow.EncryptionAuth => typeof(EncryptionAuthActivity),
|
||||
CustomWindow.ProxySettings => typeof(ProxySettingsActivity),
|
||||
_ => null,
|
||||
};
|
||||
if (activityType != null)
|
||||
{
|
||||
currentActivity.StartActivity(activityType);
|
||||
return Task.FromResult(false);
|
||||
}
|
||||
TaskCompletionSource<bool> tcs = new();
|
||||
try
|
||||
{
|
||||
AlertDialog? dialog = null;
|
||||
|
||||
TaskCompletionSource<bool> tcs = new();
|
||||
try
|
||||
void SetConfirmButton(MaterialAlertDialogBuilder b)
|
||||
{
|
||||
AlertDialog? dialog = null;
|
||||
|
||||
void SetConfirmButton(MaterialAlertDialogBuilder b)
|
||||
b.SetPositiveButton(PositiveButtonText, (_, e) =>
|
||||
{
|
||||
b.SetPositiveButton(PositiveButtonText, (_, e) =>
|
||||
{
|
||||
tcs.TrySetResult(true);
|
||||
});
|
||||
}
|
||||
void SetCancelButton(MaterialAlertDialogBuilder b)
|
||||
{
|
||||
b.SetNegativeButton(NegativeButtonText, (_, e) =>
|
||||
{
|
||||
tcs.TrySetResult(false);
|
||||
});
|
||||
}
|
||||
void SetNeutralButton(MaterialAlertDialogBuilder b, MessageBoxWindowViewModel vm)
|
||||
{
|
||||
b.SetNeutralButton(NeutralButtonText, (_, e) =>
|
||||
{
|
||||
vm.RememberChoose = true;
|
||||
tcs.TrySetResult(true);
|
||||
});
|
||||
}
|
||||
|
||||
Action<AlertDialog>? CreateMessageBoxDialogWindow(MaterialAlertDialogBuilder b)
|
||||
{
|
||||
SetConfirmButton(b);
|
||||
var isCancelcBtn = false;
|
||||
if (viewModel is MessageBoxWindowViewModel viewModel_mb)
|
||||
{
|
||||
b.SetMessage(viewModel_mb.Content);
|
||||
isCancelcBtn = viewModel_mb.IsCancelcBtn;
|
||||
if (viewModel_mb.IsShowRememberChoose) SetNeutralButton(b, viewModel_mb);
|
||||
}
|
||||
if (isCancelcBtn) SetCancelButton(b);
|
||||
return null;
|
||||
}
|
||||
Action<AlertDialog>? CreateTextBoxDialogWindowCore(MaterialAlertDialogBuilder b, Action<textbox_password, TextBoxWindowViewModel>? action = null)
|
||||
{
|
||||
if (viewModel is not TextBoxWindowViewModel viewModel_tb) throw new NotSupportedException();
|
||||
// https://material.io/components/text-fields/android
|
||||
// https://material.io/components/dialogs#specs
|
||||
IDialogInterfaceOnClickListener? listener = null;
|
||||
b.SetPositiveButton(PositiveButtonText, listener);
|
||||
SetCancelButton(b);
|
||||
var view = LayoutInflater.From(b.Context)!.Inflate(Resource.Layout.textbox_password, null, false)!;
|
||||
var binding = new textbox_password(view);
|
||||
var selectionIndex = 0;
|
||||
if (!string.IsNullOrEmpty(viewModel_tb.Value))
|
||||
{
|
||||
binding.tbPassword.Text = viewModel_tb.Value;
|
||||
selectionIndex = viewModel_tb.Value!.Length;
|
||||
}
|
||||
binding.layoutPassword.Hint = viewModel_tb.Placeholder;
|
||||
if (viewModel_tb.MaxLength > 0)
|
||||
binding.tbPassword.SetMaxLength(viewModel_tb.MaxLength);
|
||||
action?.Invoke(binding, viewModel_tb);
|
||||
b.SetView(view);
|
||||
return d =>
|
||||
{
|
||||
if (binding.tbPassword.KeyListener != null)
|
||||
{
|
||||
currentActivity.ShowSoftInput(binding.tbPassword, inOnCreate: true);
|
||||
if (selectionIndex > 0)
|
||||
{
|
||||
binding.tbPassword.SetSelection(selectionIndex);
|
||||
}
|
||||
}
|
||||
d.GetButton((int)DialogButtonType.Positive).Click += (_, _) =>
|
||||
{
|
||||
if (viewModel is ITextBoxWindowViewModel viewModel_tb)
|
||||
{
|
||||
viewModel_tb.Value = binding.tbPassword.Text;
|
||||
if (viewModel_tb.InputValidator())
|
||||
{
|
||||
d.Dismiss();
|
||||
tcs.TrySetResult(true);
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
}
|
||||
Action<AlertDialog>? CreateTextBoxDialogWindow(MaterialAlertDialogBuilder b) => CreateTextBoxDialogWindowCore(b, static (binding, viewModel) =>
|
||||
{
|
||||
switch (viewModel.InputType)
|
||||
{
|
||||
case TextBoxWindowViewModel.TextBoxInputType.TextBox:
|
||||
binding.layoutPassword.EndIconMode = TextInputLayout.EndIconClearText;
|
||||
binding.tbPassword.InputType = InputTypes.ClassText | InputTypes.TextVariationNormal;
|
||||
break;
|
||||
case TextBoxWindowViewModel.TextBoxInputType.ReadOnlyText:
|
||||
binding.layoutPassword.EndIconMode = TextInputLayout.EndIconNone;
|
||||
binding.tbPassword.InputType = InputTypes.ClassText | InputTypes.TextVariationNormal;
|
||||
binding.tbPassword.SetReadOnly();
|
||||
break;
|
||||
}
|
||||
tcs.TrySetResult(true);
|
||||
});
|
||||
|
||||
Func<MaterialAlertDialogBuilder, Action<AlertDialog>?> @delegate = customWindow switch
|
||||
{
|
||||
CustomWindow.MessageBox => CreateMessageBoxDialogWindow,
|
||||
CustomWindow.TextBox => CreateTextBoxDialogWindow,
|
||||
_ => throw new NotImplementedException(),
|
||||
};
|
||||
if (!string.IsNullOrEmpty(title) && viewModel != null)
|
||||
{
|
||||
viewModel.Title = title;
|
||||
}
|
||||
var builder = new MaterialAlertDialogBuilder(currentActivity);
|
||||
if (!string.IsNullOrWhiteSpace(viewModel?.Title))
|
||||
{
|
||||
builder.SetTitle(viewModel!.Title);
|
||||
}
|
||||
var dialog_delegate = @delegate(builder);
|
||||
dialog = builder.Create();
|
||||
dialog.SetCanceledOnTouchOutside(false);
|
||||
dialog.CancelEvent += (_, _) =>
|
||||
}
|
||||
void SetCancelButton(MaterialAlertDialogBuilder b)
|
||||
{
|
||||
b.SetNegativeButton(NegativeButtonText, (_, e) =>
|
||||
{
|
||||
tcs.TrySetResult(false);
|
||||
};
|
||||
if (dialog_delegate != null)
|
||||
});
|
||||
}
|
||||
void SetNeutralButton(MaterialAlertDialogBuilder b, MessageBoxWindowViewModel vm)
|
||||
{
|
||||
b.SetNeutralButton(NeutralButtonText, (_, e) =>
|
||||
{
|
||||
dialog.ShowEvent += (_, _) =>
|
||||
vm.RememberChoose = true;
|
||||
tcs.TrySetResult(true);
|
||||
});
|
||||
}
|
||||
|
||||
Action<AlertDialog>? CreateMessageBoxDialogWindow(MaterialAlertDialogBuilder b)
|
||||
{
|
||||
SetConfirmButton(b);
|
||||
var isCancelcBtn = false;
|
||||
if (viewModel is MessageBoxWindowViewModel viewModel_mb)
|
||||
{
|
||||
b.SetMessage(viewModel_mb.Content);
|
||||
isCancelcBtn = viewModel_mb.IsCancelcBtn;
|
||||
if (viewModel_mb.IsShowRememberChoose) SetNeutralButton(b, viewModel_mb);
|
||||
}
|
||||
if (isCancelcBtn) SetCancelButton(b);
|
||||
return null;
|
||||
}
|
||||
Action<AlertDialog>? CreateTextBoxDialogWindowCore(MaterialAlertDialogBuilder b, Action<textbox_password, TextBoxWindowViewModel>? action = null)
|
||||
{
|
||||
if (viewModel is not TextBoxWindowViewModel viewModel_tb) throw new NotSupportedException();
|
||||
// https://material.io/components/text-fields/android
|
||||
// https://material.io/components/dialogs#specs
|
||||
IDialogInterfaceOnClickListener? listener = null;
|
||||
b.SetPositiveButton(PositiveButtonText, listener);
|
||||
SetCancelButton(b);
|
||||
var view = LayoutInflater.From(b.Context)!.Inflate(Resource.Layout.textbox_password, null, false)!;
|
||||
var binding = new textbox_password(view);
|
||||
var selectionIndex = 0;
|
||||
if (!string.IsNullOrEmpty(viewModel_tb.Value))
|
||||
{
|
||||
binding.tbPassword.Text = viewModel_tb.Value;
|
||||
selectionIndex = viewModel_tb.Value!.Length;
|
||||
}
|
||||
binding.layoutPassword.Hint = viewModel_tb.Placeholder;
|
||||
if (viewModel_tb.MaxLength > 0)
|
||||
binding.tbPassword.SetMaxLength(viewModel_tb.MaxLength);
|
||||
action?.Invoke(binding, viewModel_tb);
|
||||
b.SetView(view);
|
||||
return d =>
|
||||
{
|
||||
if (binding.tbPassword.KeyListener != null)
|
||||
{
|
||||
dialog_delegate?.Invoke(dialog);
|
||||
currentActivity.ShowSoftInput(binding.tbPassword, inOnCreate: true);
|
||||
if (selectionIndex > 0)
|
||||
{
|
||||
binding.tbPassword.SetSelection(selectionIndex);
|
||||
}
|
||||
}
|
||||
d.GetButton((int)DialogButtonType.Positive).Click += (_, _) =>
|
||||
{
|
||||
if (viewModel is ITextBoxWindowViewModel viewModel_tb)
|
||||
{
|
||||
viewModel_tb.Value = binding.tbPassword.Text;
|
||||
if (viewModel_tb.InputValidator())
|
||||
{
|
||||
d.Dismiss();
|
||||
tcs.TrySetResult(true);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
dialog.Show();
|
||||
};
|
||||
}
|
||||
catch (Exception e)
|
||||
Action<AlertDialog>? CreateTextBoxDialogWindow(MaterialAlertDialogBuilder b) => CreateTextBoxDialogWindowCore(b, static (binding, viewModel) =>
|
||||
{
|
||||
tcs.TrySetException(e);
|
||||
}
|
||||
return tcs.Task;
|
||||
}
|
||||
|
||||
public virtual Type? WindowType => typeof(Activity);
|
||||
|
||||
public virtual Task Show<TWindowViewModel>(CustomWindow customWindow, TWindowViewModel? viewModel = null, string title = "", ResizeMode resizeMode = ResizeMode.NoResize, bool isDialog = false, bool isParent = true) where TWindowViewModel : WindowViewModel, new()
|
||||
{
|
||||
return PlatformShowWindow(customWindow, viewModel, title);
|
||||
}
|
||||
|
||||
public virtual Task Show(Type typeWindowViewModel, CustomWindow customWindow, WindowViewModel? viewModel = null, string title = "", ResizeMode resizeMode = ResizeMode.NoResize, bool isDialog = false, bool isParent = true)
|
||||
{
|
||||
return PlatformShowWindow(customWindow, viewModel, title);
|
||||
}
|
||||
|
||||
public virtual Task Show(CustomWindow customWindow, WindowViewModel? viewModel = null, string title = "", ResizeMode resizeMode = ResizeMode.NoResize, bool isDialog = false, bool isParent = true)
|
||||
{
|
||||
return PlatformShowWindow(customWindow, viewModel, title);
|
||||
}
|
||||
|
||||
public virtual Task<bool> ShowDialog<TWindowViewModel>(CustomWindow customWindow, TWindowViewModel? viewModel = null, string title = "", ResizeMode resizeMode = ResizeMode.NoResize, bool isDialog = true, bool isParent = true) where TWindowViewModel : WindowViewModel, new()
|
||||
{
|
||||
return PlatformShowWindow(customWindow, viewModel, title);
|
||||
}
|
||||
|
||||
public virtual Task ShowDialog(Type typeWindowViewModel, CustomWindow customWindow, WindowViewModel? viewModel = null, string title = "", ResizeMode resizeMode = ResizeMode.NoResize, bool isDialog = true)
|
||||
{
|
||||
return PlatformShowWindow(customWindow, viewModel, title);
|
||||
}
|
||||
|
||||
public virtual Task ShowDialog(CustomWindow customWindow, WindowViewModel? viewModel = null, string title = "", ResizeMode resizeMode = ResizeMode.NoResize, bool isDialog = true)
|
||||
{
|
||||
return PlatformShowWindow(customWindow, viewModel, title);
|
||||
}
|
||||
|
||||
protected bool IsMatchCurrentActivityViewModel(object vm, [NotNullWhen(true)] out Activity? activity)
|
||||
{
|
||||
foreach (var item in MainApplication.Activities)
|
||||
{
|
||||
if (item is IViewFor vf && vf.ViewModel == vm)
|
||||
switch (viewModel.InputType)
|
||||
{
|
||||
activity = item;
|
||||
return true;
|
||||
case TextBoxWindowViewModel.TextBoxInputType.TextBox:
|
||||
binding.layoutPassword.EndIconMode = TextInputLayout.EndIconClearText;
|
||||
binding.tbPassword.InputType = InputTypes.ClassText | InputTypes.TextVariationNormal;
|
||||
break;
|
||||
case TextBoxWindowViewModel.TextBoxInputType.ReadOnlyText:
|
||||
binding.layoutPassword.EndIconMode = TextInputLayout.EndIconNone;
|
||||
binding.tbPassword.InputType = InputTypes.ClassText | InputTypes.TextVariationNormal;
|
||||
binding.tbPassword.SetReadOnly();
|
||||
break;
|
||||
}
|
||||
}
|
||||
activity = null;
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
public virtual void CloseWindow(WindowViewModel vm)
|
||||
{
|
||||
if (IsMatchCurrentActivityViewModel(vm, out var activity))
|
||||
Func<MaterialAlertDialogBuilder, Action<AlertDialog>?> @delegate = customWindow switch
|
||||
{
|
||||
activity.OnBackPressed();
|
||||
}
|
||||
}
|
||||
|
||||
public virtual bool IsVisibleWindow(WindowViewModel vm)
|
||||
{
|
||||
if (IsMatchCurrentActivityViewModel(vm, out var activity))
|
||||
CustomWindow.MessageBox => CreateMessageBoxDialogWindow,
|
||||
CustomWindow.TextBox => CreateTextBoxDialogWindow,
|
||||
_ => throw new NotImplementedException(),
|
||||
};
|
||||
if (!string.IsNullOrEmpty(title) && viewModel != null)
|
||||
{
|
||||
return activity.HasValue();
|
||||
viewModel.Title = title;
|
||||
}
|
||||
return default;
|
||||
var builder = new MaterialAlertDialogBuilder(currentActivity);
|
||||
if (!string.IsNullOrWhiteSpace(viewModel?.Title))
|
||||
{
|
||||
builder.SetTitle(viewModel!.Title);
|
||||
}
|
||||
var dialog_delegate = @delegate(builder);
|
||||
dialog = builder.Create();
|
||||
dialog.SetCanceledOnTouchOutside(false);
|
||||
dialog.CancelEvent += (_, _) =>
|
||||
{
|
||||
tcs.TrySetResult(false);
|
||||
};
|
||||
if (dialog_delegate != null)
|
||||
{
|
||||
dialog.ShowEvent += (_, _) =>
|
||||
{
|
||||
dialog_delegate?.Invoke(dialog);
|
||||
};
|
||||
}
|
||||
dialog.Show();
|
||||
}
|
||||
|
||||
public virtual void HideWindow(WindowViewModel vm)
|
||||
catch (Exception e)
|
||||
{
|
||||
CloseWindow(vm);
|
||||
tcs.TrySetException(e);
|
||||
}
|
||||
return tcs.Task;
|
||||
}
|
||||
|
||||
public virtual Type? WindowType => typeof(Activity);
|
||||
|
||||
public virtual Task Show<TWindowViewModel>(CustomWindow customWindow, TWindowViewModel? viewModel = null, string title = "", ResizeMode resizeMode = ResizeMode.NoResize, bool isDialog = false, bool isParent = true) where TWindowViewModel : WindowViewModel, new()
|
||||
{
|
||||
return PlatformShowWindow(customWindow, viewModel, title);
|
||||
}
|
||||
|
||||
public virtual Task Show(Type typeWindowViewModel, CustomWindow customWindow, WindowViewModel? viewModel = null, string title = "", ResizeMode resizeMode = ResizeMode.NoResize, bool isDialog = false, bool isParent = true)
|
||||
{
|
||||
return PlatformShowWindow(customWindow, viewModel, title);
|
||||
}
|
||||
|
||||
public virtual Task Show(CustomWindow customWindow, WindowViewModel? viewModel = null, string title = "", ResizeMode resizeMode = ResizeMode.NoResize, bool isDialog = false, bool isParent = true)
|
||||
{
|
||||
return PlatformShowWindow(customWindow, viewModel, title);
|
||||
}
|
||||
|
||||
public virtual Task<bool> ShowDialog<TWindowViewModel>(CustomWindow customWindow, TWindowViewModel? viewModel = null, string title = "", ResizeMode resizeMode = ResizeMode.NoResize, bool isDialog = true, bool isParent = true) where TWindowViewModel : WindowViewModel, new()
|
||||
{
|
||||
return PlatformShowWindow(customWindow, viewModel, title);
|
||||
}
|
||||
|
||||
public virtual Task ShowDialog(Type typeWindowViewModel, CustomWindow customWindow, WindowViewModel? viewModel = null, string title = "", ResizeMode resizeMode = ResizeMode.NoResize, bool isDialog = true)
|
||||
{
|
||||
return PlatformShowWindow(customWindow, viewModel, title);
|
||||
}
|
||||
|
||||
public virtual Task ShowDialog(CustomWindow customWindow, WindowViewModel? viewModel = null, string title = "", ResizeMode resizeMode = ResizeMode.NoResize, bool isDialog = true)
|
||||
{
|
||||
return PlatformShowWindow(customWindow, viewModel, title);
|
||||
}
|
||||
|
||||
protected virtual IEnumerable<Activity> Activities
|
||||
{
|
||||
get
|
||||
{
|
||||
#if NET6_0_MAUI_LIB
|
||||
throw new NotImplementedException();
|
||||
#else
|
||||
return MainApplication.Activities;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
protected bool IsMatchCurrentActivityViewModel(object vm, [NotNullWhen(true)] out Activity? activity)
|
||||
{
|
||||
foreach (var item in Activities)
|
||||
{
|
||||
if (item is IViewFor vf && vf.ViewModel == vm)
|
||||
{
|
||||
activity = item;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
activity = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
public virtual void CloseWindow(WindowViewModel vm)
|
||||
{
|
||||
if (IsMatchCurrentActivityViewModel(vm, out var activity))
|
||||
{
|
||||
activity.OnBackPressed();
|
||||
}
|
||||
}
|
||||
|
||||
public virtual bool IsVisibleWindow(WindowViewModel vm)
|
||||
{
|
||||
if (IsMatchCurrentActivityViewModel(vm, out var activity))
|
||||
{
|
||||
return activity.HasValue();
|
||||
}
|
||||
return default;
|
||||
}
|
||||
|
||||
public virtual void HideWindow(WindowViewModel vm)
|
||||
{
|
||||
CloseWindow(vm);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,11 @@ using Android.OS;
|
||||
#endif
|
||||
using Microsoft.Extensions.Options;
|
||||
using System.Application.Models;
|
||||
#if NET6_0_OR_GREATER
|
||||
using XEPlatform = Microsoft.Maui.ApplicationModel.Platform;
|
||||
#else
|
||||
using XEPlatform = Xamarin.Essentials.Platform;
|
||||
#endif
|
||||
using System.Application.UI;
|
||||
|
||||
namespace System.Application.Services.Implementation
|
||||
|
||||
@@ -1,11 +1,17 @@
|
||||
#if ANDROID || __ANDROID__
|
||||
using Android.Content;
|
||||
using Android.Net;
|
||||
#endif
|
||||
using System.Application.Services;
|
||||
using System.Application.Services.Implementation;
|
||||
using System.Collections.Generic;
|
||||
using System.Net;
|
||||
using System.Threading.Tasks;
|
||||
#if NET6_0_OR_GREATER
|
||||
using XEPlatform = Microsoft.Maui.ApplicationModel.Platform;
|
||||
#else
|
||||
using XEPlatform = Xamarin.Essentials.Platform;
|
||||
#endif
|
||||
|
||||
namespace System.Application.Services.Implementation
|
||||
{
|
||||
|
||||
@@ -3,7 +3,11 @@ using System.Linq;
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
using static AndroidX.Activity.Result.ActivityResultTask;
|
||||
#if NET6_0_OR_GREATER
|
||||
using XEPlatform = Microsoft.Maui.ApplicationModel.Platform;
|
||||
#else
|
||||
using XEPlatform = Xamarin.Essentials.Platform;
|
||||
#endif
|
||||
|
||||
namespace System.Application.Services.Implementation
|
||||
{
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
using System.Threading.Tasks;
|
||||
#if NET6_0_OR_GREATER
|
||||
using XEPermissions = Microsoft.Maui.ApplicationModel.Permissions;
|
||||
#else
|
||||
using XEPermissions = Xamarin.Essentials.Permissions;
|
||||
#endif
|
||||
|
||||
namespace System.Application.Services.Implementation.Permissions;
|
||||
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
using AndroidX.Biometric;
|
||||
using System.Threading.Tasks;
|
||||
#if NET6_0_OR_GREATER
|
||||
using XEPlatform = Microsoft.Maui.ApplicationModel.Platform;
|
||||
#else
|
||||
using XEPlatform = Xamarin.Essentials.Platform;
|
||||
#endif
|
||||
|
||||
namespace System.Application.Services.Implementation
|
||||
{
|
||||
|
||||
@@ -14,10 +14,10 @@ namespace System.Application.Services.Implementation
|
||||
#endif
|
||||
}
|
||||
|
||||
public static AndroidClientHandler CreateAndroidClientHandler()
|
||||
public static AndroidClientHandler CreateAndroidClientHandler(AndroidClientHandler? handler = null)
|
||||
{
|
||||
// https://docs.microsoft.com/zh-cn/xamarin/android/app-fundamentals/http-stack?context=xamarin%2Fcross-platform&tabs=macos
|
||||
var handler = new AndroidClientHandler();
|
||||
handler ??= new AndroidClientHandler();
|
||||
AddFiddlerRootCertificate(handler);
|
||||
GeneralHttpClientFactory.SetProxyToHandler(GeneralHttpClientFactory.DefaultProxy, handler);
|
||||
return handler;
|
||||
|
||||
@@ -16,15 +16,22 @@ using System.Application.Services;
|
||||
using System.Application.UI.Resx;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Xamarin.Essentials;
|
||||
using Xamarin.Google.MLKit.Vision.BarCode;
|
||||
using Xamarin.Google.MLKit.Vision.Common;
|
||||
using Xamarin.Google.MLKit.Vision.Barcode.Common;
|
||||
using AUri = Android.Net.Uri;
|
||||
using Fragment = AndroidX.Fragment.App.Fragment;
|
||||
using GmsTask = Android.Gms.Tasks.Task;
|
||||
using JException = Java.Lang.Exception;
|
||||
using JObject = Java.Lang.Object;
|
||||
#if NET6_0_OR_GREATER
|
||||
using Microsoft.Maui.ApplicationModel;
|
||||
using Microsoft.Maui.Media;
|
||||
using XEPlatform = Microsoft.Maui.ApplicationModel.Platform;
|
||||
#else
|
||||
using Xamarin.Essentials;
|
||||
using XEPlatform = Xamarin.Essentials.Platform;
|
||||
#endif
|
||||
|
||||
namespace System.Application.UI.Activities
|
||||
{
|
||||
|
||||
@@ -20,7 +20,13 @@ using System.Threading.Tasks;
|
||||
using static AndroidX.Activity.Result.ActivityResultTask;
|
||||
using _ThisAssembly = System.Properties.ThisAssembly;
|
||||
using ASettings = Android.Provider.Settings;
|
||||
#if NET6_0_OR_GREATER
|
||||
using Microsoft.Maui.ApplicationModel;
|
||||
using XEPlatform = Microsoft.Maui.ApplicationModel.Platform;
|
||||
#else
|
||||
using Xamarin.Essentials;
|
||||
using XEPlatform = Xamarin.Essentials.Platform;
|
||||
#endif
|
||||
|
||||
namespace System.Application.UI.Activities
|
||||
{
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
#if !NET6_0_MAUI_LIB
|
||||
using Android.App;
|
||||
using Android.Content;
|
||||
using Android.Content.PM;
|
||||
@@ -15,155 +16,155 @@ using Fragment = AndroidX.Fragment.App.Fragment;
|
||||
using Google.Android.Material.Navigation;
|
||||
using AndroidX.ViewPager2.Widget;
|
||||
|
||||
namespace System.Application.UI.Activities
|
||||
namespace System.Application.UI.Activities;
|
||||
|
||||
/// <summary>
|
||||
/// 使用 ViewPager2 + BottomNavigationView 实现的 MainActivity
|
||||
/// </summary>
|
||||
[Register(JavaPackageConstants.Activities + nameof(MainActivity))]
|
||||
[Activity(Theme = ManifestConstants.MainTheme2_NoActionBar,
|
||||
LaunchMode = LaunchMode.SingleTask,
|
||||
ConfigurationChanges = ManifestConstants.ConfigurationChanges)]
|
||||
internal sealed partial class MainActivity : BaseActivity<activity_main_3>, ViewPagerAdapter.IHost, BottomNavigationView.IOnItemSelectedListener
|
||||
{
|
||||
/// <summary>
|
||||
/// 使用 ViewPager2 + BottomNavigationView 实现的 MainActivity
|
||||
/// </summary>
|
||||
[Register(JavaPackageConstants.Activities + nameof(MainActivity))]
|
||||
[Activity(Theme = ManifestConstants.MainTheme2_NoActionBar,
|
||||
LaunchMode = LaunchMode.SingleTask,
|
||||
ConfigurationChanges = ManifestConstants.ConfigurationChanges)]
|
||||
internal sealed partial class MainActivity : BaseActivity<activity_main_3>, ViewPagerAdapter.IHost, BottomNavigationView.IOnItemSelectedListener
|
||||
protected override int? LayoutResource => Resource.Layout.activity_main_3;
|
||||
|
||||
protected override bool BackToHome => true;
|
||||
|
||||
void SetBottomNavigationMenuTitle(int resId, CharSequence value)
|
||||
{
|
||||
protected override int? LayoutResource => Resource.Layout.activity_main_3;
|
||||
if (binding == null) return;
|
||||
var menu = binding.nav_view.Menu;
|
||||
var menuItem = menu.FindItem(resId);
|
||||
if (menuItem == null) return;
|
||||
menuItem.SetTitle(value);
|
||||
}
|
||||
|
||||
protected override bool BackToHome => true;
|
||||
void SetSubPageTitle(int resId, string value)
|
||||
{
|
||||
var value_ = value.ToJavaString();
|
||||
SetBottomNavigationMenuTitle(resId, value_);
|
||||
this.SetNavigationGraphTitle(resId, value_);
|
||||
}
|
||||
|
||||
void SetBottomNavigationMenuTitle(int resId, CharSequence value)
|
||||
static MainActivity? mainActivity;
|
||||
|
||||
public static MainActivity Instance => mainActivity ?? throw new ArgumentNullException(nameof(mainActivity));
|
||||
|
||||
(Type fragmentType, Func<string> getTitle, int menuId, TabItemViewModel.TabItemId tabItemId)[]? fragments;
|
||||
|
||||
protected override void OnCreate2(Bundle? savedInstanceState)
|
||||
{
|
||||
base.OnCreate2(savedInstanceState);
|
||||
|
||||
mainActivity = this;
|
||||
|
||||
fragments = new (Type fragmentType, Func<string> getTitle, int menuId, TabItemViewModel.TabItemId tabItemId)[]
|
||||
{
|
||||
(typeof(LocalAuthFragment), () => LocalAuthPageViewModel.DisplayName, Resource.Id.navigation_local_auth, TabItemViewModel.TabItemId.LocalAuth),
|
||||
(typeof(ASFPlusFragment), () => ArchiSteamFarmPlusPageViewModel.DisplayName, Resource.Id.navigation_asf_plus, TabItemViewModel.TabItemId.ArchiSteamFarmPlus),
|
||||
(typeof(CommunityFixFragment), () => AppResources.CommunityFix, Resource.Id.navigation_community_fix, TabItemViewModel.TabItemId.CommunityProxy),
|
||||
(typeof(MyFragment), () => MyPageViewModel.DisplayName, Resource.Id.navigation_my, default),
|
||||
};
|
||||
|
||||
SetSupportActionBar(binding!.toolbar);
|
||||
|
||||
R.Subscribe(() =>
|
||||
{
|
||||
if (binding == null) return;
|
||||
var menu = binding.nav_view.Menu;
|
||||
var menuItem = menu.FindItem(resId);
|
||||
if (menuItem == null) return;
|
||||
menuItem.SetTitle(value);
|
||||
}
|
||||
|
||||
void SetSubPageTitle(int resId, string value)
|
||||
{
|
||||
var value_ = value.ToJavaString();
|
||||
SetBottomNavigationMenuTitle(resId, value_);
|
||||
this.SetNavigationGraphTitle(resId, value_);
|
||||
}
|
||||
|
||||
static MainActivity? mainActivity;
|
||||
|
||||
public static MainActivity Instance => mainActivity ?? throw new ArgumentNullException(nameof(mainActivity));
|
||||
|
||||
(Type fragmentType, Func<string> getTitle, int menuId, TabItemViewModel.TabItemId tabItemId)[]? fragments;
|
||||
|
||||
protected override void OnCreate2(Bundle? savedInstanceState)
|
||||
{
|
||||
base.OnCreate2(savedInstanceState);
|
||||
|
||||
mainActivity = this;
|
||||
|
||||
fragments = new (Type fragmentType, Func<string> getTitle, int menuId, TabItemViewModel.TabItemId tabItemId)[]
|
||||
foreach (var (_, getTitle, menuId, _) in fragments)
|
||||
{
|
||||
(typeof(LocalAuthFragment), () => LocalAuthPageViewModel.DisplayName, Resource.Id.navigation_local_auth, TabItemViewModel.TabItemId.LocalAuth),
|
||||
(typeof(ASFPlusFragment), () => ArchiSteamFarmPlusPageViewModel.DisplayName, Resource.Id.navigation_asf_plus, TabItemViewModel.TabItemId.ArchiSteamFarmPlus),
|
||||
(typeof(CommunityFixFragment), () => AppResources.CommunityFix, Resource.Id.navigation_community_fix, TabItemViewModel.TabItemId.CommunityProxy),
|
||||
(typeof(MyFragment), () => MyPageViewModel.DisplayName, Resource.Id.navigation_my, default),
|
||||
};
|
||||
|
||||
SetSupportActionBar(binding!.toolbar);
|
||||
|
||||
R.Subscribe(() =>
|
||||
{
|
||||
if (binding == null) return;
|
||||
foreach (var (_, getTitle, menuId, _) in fragments)
|
||||
{
|
||||
var title = getTitle();
|
||||
SetSubPageTitle(menuId, title);
|
||||
}
|
||||
SetTitleByPosition(binding.viewPager2.CurrentItem);
|
||||
}).AddTo(this);
|
||||
|
||||
var adapter = new ViewPagerAdapter(this, this);
|
||||
binding.viewPager2.Adapter = adapter;
|
||||
binding.viewPager2.RegisterOnPageChangeCallback(new OnPageChangeCallback(this));
|
||||
binding.nav_view.SetOnItemSelectedListener(this);
|
||||
}
|
||||
|
||||
sealed class OnPageChangeCallback : ViewPager2.OnPageChangeCallback
|
||||
{
|
||||
readonly MainActivity thiz;
|
||||
|
||||
public OnPageChangeCallback(MainActivity thiz)
|
||||
{
|
||||
this.thiz = thiz;
|
||||
var title = getTitle();
|
||||
SetSubPageTitle(menuId, title);
|
||||
}
|
||||
SetTitleByPosition(binding.viewPager2.CurrentItem);
|
||||
}).AddTo(this);
|
||||
|
||||
public override void OnPageSelected(int position)
|
||||
{
|
||||
thiz.OnPageSelected(position);
|
||||
}
|
||||
var adapter = new ViewPagerAdapter(this, this);
|
||||
binding.viewPager2.Adapter = adapter;
|
||||
binding.viewPager2.RegisterOnPageChangeCallback(new OnPageChangeCallback(this));
|
||||
binding.nav_view.SetOnItemSelectedListener(this);
|
||||
}
|
||||
|
||||
sealed class OnPageChangeCallback : ViewPager2.OnPageChangeCallback
|
||||
{
|
||||
readonly MainActivity thiz;
|
||||
|
||||
public OnPageChangeCallback(MainActivity thiz)
|
||||
{
|
||||
this.thiz = thiz;
|
||||
}
|
||||
|
||||
protected override void OnNewIntent(Intent? intent)
|
||||
public override void OnPageSelected(int position)
|
||||
{
|
||||
base.OnNewIntent(intent);
|
||||
thiz.OnPageSelected(position);
|
||||
}
|
||||
}
|
||||
|
||||
if (binding == null || fragments == null) return;
|
||||
var action = intent?.Action;
|
||||
if (!string.IsNullOrWhiteSpace(action))
|
||||
protected override void OnNewIntent(Intent? intent)
|
||||
{
|
||||
base.OnNewIntent(intent);
|
||||
|
||||
if (binding == null || fragments == null) return;
|
||||
var action = intent?.Action;
|
||||
if (!string.IsNullOrWhiteSpace(action))
|
||||
{
|
||||
if (Enum.TryParse<TabItemViewModel.TabItemId>(action, out var tabItem))
|
||||
{
|
||||
if (Enum.TryParse<TabItemViewModel.TabItemId>(action, out var tabItem))
|
||||
for (int i = 0; i < fragments!.Length; i++)
|
||||
{
|
||||
for (int i = 0; i < fragments!.Length; i++)
|
||||
if (fragments[i].tabItemId == tabItem)
|
||||
{
|
||||
if (fragments[i].tabItemId == tabItem)
|
||||
{
|
||||
Navigate(i);
|
||||
}
|
||||
Navigate(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int ViewPagerAdapter.IHost.ItemCount => fragments!.Length;
|
||||
int ViewPagerAdapter.IHost.ItemCount => fragments!.Length;
|
||||
|
||||
Fragment ViewPagerAdapter.IHost.CreateFragment(int position) => (Fragment)Activator.CreateInstance(fragments![position].fragmentType);
|
||||
Fragment ViewPagerAdapter.IHost.CreateFragment(int position) => (Fragment)Activator.CreateInstance(fragments![position].fragmentType);
|
||||
|
||||
bool NavigationBarView.IOnItemSelectedListener.OnNavigationItemSelected(IMenuItem item)
|
||||
bool NavigationBarView.IOnItemSelectedListener.OnNavigationItemSelected(IMenuItem item)
|
||||
{
|
||||
if (binding == null || fragments == null) return false;
|
||||
var itemId = item.ItemId;
|
||||
for (int i = 0; i < fragments!.Length; i++)
|
||||
{
|
||||
if (binding == null || fragments == null) return false;
|
||||
var itemId = item.ItemId;
|
||||
for (int i = 0; i < fragments!.Length; i++)
|
||||
if (fragments[i].menuId == itemId)
|
||||
{
|
||||
if (fragments[i].menuId == itemId)
|
||||
{
|
||||
Navigate(i);
|
||||
return true;
|
||||
}
|
||||
Navigate(i);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void Navigate(int position)
|
||||
{
|
||||
binding!.viewPager2.SetCurrentItem(position, false);
|
||||
SetTitleByPosition(position);
|
||||
SetChecked(position);
|
||||
}
|
||||
void Navigate(int position)
|
||||
{
|
||||
binding!.viewPager2.SetCurrentItem(position, false);
|
||||
SetTitleByPosition(position);
|
||||
SetChecked(position);
|
||||
}
|
||||
|
||||
void SetTitleByPosition(int position)
|
||||
{
|
||||
var title = fragments![position].getTitle();
|
||||
Title = title;
|
||||
}
|
||||
void SetTitleByPosition(int position)
|
||||
{
|
||||
var title = fragments![position].getTitle();
|
||||
Title = title;
|
||||
}
|
||||
|
||||
void OnPageSelected(int position)
|
||||
{
|
||||
if (binding == null) return;
|
||||
SetTitleByPosition(position);
|
||||
SetChecked(position);
|
||||
}
|
||||
void OnPageSelected(int position)
|
||||
{
|
||||
if (binding == null) return;
|
||||
SetTitleByPosition(position);
|
||||
SetChecked(position);
|
||||
}
|
||||
|
||||
void SetChecked(int position)
|
||||
{
|
||||
var menuItem = binding!.nav_view.Menu.FindItem(fragments![position].menuId);
|
||||
if (menuItem != null && !menuItem.IsChecked) menuItem.SetChecked(true);
|
||||
}
|
||||
void SetChecked(int position)
|
||||
{
|
||||
var menuItem = binding!.nav_view.Menu.FindItem(fragments![position].menuId);
|
||||
if (menuItem != null && !menuItem.IsChecked) menuItem.SetChecked(true);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
#if !NET6_0_MAUI_LIB
|
||||
using Android.App;
|
||||
using Android.Content.PM;
|
||||
using Android.OS;
|
||||
@@ -5,31 +6,31 @@ using Android.Runtime;
|
||||
using Android.Views;
|
||||
using System.Application.Security;
|
||||
|
||||
namespace System.Application.UI.Activities
|
||||
{
|
||||
/// <summary>
|
||||
/// 页面 - 启动屏幕
|
||||
/// </summary>
|
||||
[Register(JavaPackageConstants.Activities + nameof(SplashActivity))]
|
||||
[Activity(Theme = ManifestConstants.MainTheme2_Splash,
|
||||
MainLauncher = true,
|
||||
LaunchMode = LaunchMode.SingleTask,
|
||||
ConfigurationChanges = ManifestConstants.ConfigurationChanges,
|
||||
NoHistory = true)]
|
||||
internal sealed class SplashActivity : Activity
|
||||
{
|
||||
protected override void OnCreate(Bundle? savedInstanceState)
|
||||
{
|
||||
base.OnCreate(savedInstanceState);
|
||||
//if (!DeviceSecurityCheckUtil.IsAllowStart(this)) return;
|
||||
StartActivity(IAndroidApplication.Instance.MainActivityType);
|
||||
OverridePendingTransition(Resource.Animation.abc_fade_in, Resource.Animation.abc_fade_out);
|
||||
}
|
||||
namespace System.Application.UI.Activities;
|
||||
|
||||
public override void OnBackPressed()
|
||||
{
|
||||
GoToPlatformPages.MockHomePressed(this);
|
||||
return;
|
||||
}
|
||||
/// <summary>
|
||||
/// 页面 - 启动屏幕
|
||||
/// </summary>
|
||||
[Register(JavaPackageConstants.Activities + nameof(SplashActivity))]
|
||||
[Activity(Theme = ManifestConstants.MainTheme2_Splash,
|
||||
MainLauncher = true,
|
||||
LaunchMode = LaunchMode.SingleTask,
|
||||
ConfigurationChanges = ManifestConstants.ConfigurationChanges,
|
||||
NoHistory = true)]
|
||||
internal sealed class SplashActivity : Activity
|
||||
{
|
||||
protected override void OnCreate(Bundle? savedInstanceState)
|
||||
{
|
||||
base.OnCreate(savedInstanceState);
|
||||
//if (!DeviceSecurityCheckUtil.IsAllowStart(this)) return;
|
||||
StartActivity(IAndroidApplication.Instance.MainActivityType);
|
||||
OverridePendingTransition(Resource.Animation.abc_fade_in, Resource.Animation.abc_fade_out);
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnBackPressed()
|
||||
{
|
||||
GoToPlatformPages.MockHomePressed(this);
|
||||
return;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -2,7 +2,11 @@ using Android.Views;
|
||||
using Binding;
|
||||
using ReactiveUI;
|
||||
using System.Application.UI.ViewModels;
|
||||
#if NET6_0_OR_GREATER
|
||||
using Microsoft.Maui.ApplicationModel;
|
||||
#else
|
||||
using Xamarin.Essentials;
|
||||
#endif
|
||||
using TViewHolder = System.Application.UI.Adapters.FastLoginChannelViewHolder;
|
||||
using TViewModel = System.Application.UI.ViewModels.LoginOrRegisterWindowViewModel.FastLoginChannelViewModel;
|
||||
|
||||
|
||||
@@ -1,14 +1,18 @@
|
||||
using Android.Views;
|
||||
using Binding;
|
||||
using ReactiveUI;
|
||||
using System.Threading.Tasks;
|
||||
using System.Application.UI.Resx;
|
||||
using System.Application.UI.ViewModels;
|
||||
using System.Application.UI.Activities;
|
||||
using static System.Application.UI.Resx.AppResources;
|
||||
#if NET6_0_OR_GREATER
|
||||
using XEPlatform = Microsoft.Maui.ApplicationModel.Platform;
|
||||
#else
|
||||
using XEPlatform = Xamarin.Essentials.Platform;
|
||||
#endif
|
||||
using TViewHolder = System.Application.UI.Adapters.SteamAuthTradeConfirmationViewHolder;
|
||||
using TViewModel = WinAuth.WinAuthSteamClient.Confirmation;
|
||||
using static System.Application.UI.Resx.AppResources;
|
||||
using System.Application.UI.Activities;
|
||||
using XEPlatform = Xamarin.Essentials.Platform;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace System.Application.UI.Adapters
|
||||
{
|
||||
|
||||
@@ -13,7 +13,11 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reactive.Disposables;
|
||||
using System.Reflection;
|
||||
#if NET6_0_OR_GREATER
|
||||
using XEPlatform = Microsoft.Maui.ApplicationModel.Platform;
|
||||
#else
|
||||
using XEPlatform = Xamarin.Essentials.Platform;
|
||||
#endif
|
||||
|
||||
// ReSharper disable once CheckNamespace
|
||||
namespace System.Application.UI.Activities
|
||||
|
||||
@@ -13,7 +13,11 @@ using System.Application.UI.Adapters;
|
||||
using System.Application.UI.Resx;
|
||||
using System.Application.UI.ViewModels;
|
||||
using System.Text;
|
||||
#if NET6_0_OR_GREATER
|
||||
using Microsoft.Maui.ApplicationModel;
|
||||
#else
|
||||
using Xamarin.Essentials;
|
||||
#endif
|
||||
using static System.Application.UI.ViewModels.AboutPageViewModel;
|
||||
using M = System.Application.UI.ViewModels.AboutPageViewModel;
|
||||
using V = Binding.fragment_about;
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
#if !NET6_0_MAUI_LIB
|
||||
using AndroidApplication = Android.App.Application;
|
||||
|
||||
namespace System.Application.UI
|
||||
@@ -10,3 +8,4 @@ namespace System.Application.UI
|
||||
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -1,3 +1,4 @@
|
||||
#if !NET6_0_MAUI_LIB
|
||||
using System.Application.Services;
|
||||
using System.Application.UI.Activities;
|
||||
using System.Collections.Generic;
|
||||
@@ -25,10 +26,11 @@ namespace System.Application.UI
|
||||
VisualStudioAppCenterSDK.Init();
|
||||
}
|
||||
|
||||
void IApplication.IProgramHost.ConfigureServices(DILevel level) => ConfigureServices(this, level);
|
||||
void IApplication.IProgramHost.ConfigureServices(DILevel level, bool isTrace) => ConfigureServices(this, level, isTrace: isTrace);
|
||||
|
||||
void IApplication.IProgramHost.OnStartup() => OnStartup(this);
|
||||
|
||||
IApplication IApplication.IProgramHost.Application => this;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
#if !NET6_0_MAUI_LIB
|
||||
using Android.App;
|
||||
using Android.Runtime;
|
||||
using System.Application.UI.Resx;
|
||||
@@ -102,7 +103,7 @@ namespace System.Application.UI
|
||||
var level = DILevel.Min;
|
||||
if (IsMainProcess) level = DILevel.MainProcess;
|
||||
IApplication.IProgramHost host = this;
|
||||
host.ConfigureServices(level);
|
||||
host.ConfigureServices(level, isTrace);
|
||||
if (isTrace) StartWatchTrace.Record("Startup");
|
||||
|
||||
if (IsMainProcess)
|
||||
@@ -125,8 +126,6 @@ namespace System.Application.UI
|
||||
|
||||
if (IsMainProcess)
|
||||
{
|
||||
var vmService = IViewModelManager.Instance;
|
||||
|
||||
#if __XAMARIN_FORMS__
|
||||
Forms.Init(this, null);
|
||||
FormsMaterial.Init(this, null);
|
||||
@@ -136,8 +135,6 @@ namespace System.Application.UI
|
||||
_Current = new(RealTheme);
|
||||
if (isTrace) StartWatchTrace.Record("XFApp");
|
||||
#endif
|
||||
vmService.MainWindow!.Initialize();
|
||||
if (isTrace) StartWatchTrace.Record("MainVM.Init");
|
||||
}
|
||||
|
||||
StartupTrack = StartWatchTrace.ToString();
|
||||
@@ -290,4 +287,5 @@ namespace System.Application.UI
|
||||
|
||||
void IApplication.PlatformInitSettingSubscribe() => PlatformInitSettingSubscribe();
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -1,24 +1,27 @@
|
||||
using Android.Util;
|
||||
using AndroidX.SwipeRefreshLayout.Widget;
|
||||
#if NET6_0_OR_GREATER
|
||||
using XEPlatform = Microsoft.Maui.ApplicationModel.Platform;
|
||||
#else
|
||||
using XEPlatform = Xamarin.Essentials.Platform;
|
||||
#endif
|
||||
|
||||
namespace System.Application.UI
|
||||
namespace System.Application.UI;
|
||||
|
||||
public static class StylesEx
|
||||
{
|
||||
public static class Styles
|
||||
public static void InitDefaultStyles(this SwipeRefreshLayout swipeRefreshLayout)
|
||||
{
|
||||
public static void InitDefaultStyles(this SwipeRefreshLayout swipeRefreshLayout)
|
||||
{
|
||||
swipeRefreshLayout.SetColorSchemeColors(new[] { ColorPrimary });
|
||||
}
|
||||
swipeRefreshLayout.SetColorSchemeColors(new[] { ColorPrimary });
|
||||
}
|
||||
|
||||
public static int ColorPrimary
|
||||
public static int ColorPrimary
|
||||
{
|
||||
get
|
||||
{
|
||||
get
|
||||
{
|
||||
using TypedValue typedValue = new();
|
||||
XEPlatform.CurrentActivity.Theme!.ResolveAttribute(Resource.Attribute.colorPrimary, typedValue, true);
|
||||
return typedValue.Data;
|
||||
}
|
||||
using TypedValue typedValue = new();
|
||||
XEPlatform.CurrentActivity.Theme!.ResolveAttribute(Resource.Attribute.colorPrimary, typedValue, true);
|
||||
return typedValue.Data;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,7 +6,11 @@ using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Threading.Tasks;
|
||||
using AndroidAppActivity = Android.App.Activity;
|
||||
#if NET6_0_OR_GREATER
|
||||
using XEPlatform = Microsoft.Maui.ApplicationModel.Platform;
|
||||
#else
|
||||
using XEPlatform = Xamarin.Essentials.Platform;
|
||||
#endif
|
||||
using AndroidAppResult = Android.App.Result;
|
||||
|
||||
// 将 onActivityResult() 转换为 System.Threading.Tasks.Task
|
||||
|
||||
@@ -1,14 +1,29 @@
|
||||
<Project Sdk="MSBuild.Sdk.Extras">
|
||||
|
||||
<PropertyGroup>
|
||||
<!--<TargetFrameworks>MonoAndroid12.0;Xamarin.iOS10;net6.0-android;net6.0-windows</TargetFrameworks>-->
|
||||
<TargetFrameworks>MonoAndroid12.0;Xamarin.iOS10;net6.0-windows</TargetFrameworks>
|
||||
<TargetFrameworks>MonoAndroid12.0;Xamarin.iOS10;net6.0-android;net6.0-windows;net6.0-ios;net6.0-maccatalyst</TargetFrameworks>
|
||||
<RootNamespace>System.Application.AppCenter</RootNamespace>
|
||||
<AssemblyName>System.Application.SteamTools.Client.AppCenter</AssemblyName>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<AndroidUseIntermediateDesignerFile>false</AndroidUseIntermediateDesignerFile>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'ios'">
|
||||
<SupportedOSPlatformVersion>14.2</SupportedOSPlatformVersion>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'maccatalyst'">
|
||||
<SupportedOSPlatformVersion>14.0</SupportedOSPlatformVersion>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'android'">
|
||||
<SupportedOSPlatformVersion>21.0</SupportedOSPlatformVersion>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'windows'">
|
||||
<TargetPlatformMinVersion>7.0</TargetPlatformMinVersion>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Include="..\Common.CoreLib\Properties\AssemblyInfo.cs">
|
||||
<Link>Properties\AssemblyInfo.cs</Link>
|
||||
@@ -23,17 +38,17 @@
|
||||
<Link>Properties\AssemblyInfo.OS.cs</Link>
|
||||
</Compile>
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.AppCenter" />
|
||||
<PackageReference Include="Microsoft.AppCenter.Analytics" />
|
||||
<PackageReference Include="Microsoft.AppCenter.Crashes" />
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="StyleCop.Analyzers" PrivateAssets="All" />
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\ST.Client.Android.ResSecrets\ST.Client.Android.ResSecrets.csproj" Condition=" $(TargetFramework.StartsWith('MonoAndroid')) OR $(TargetFramework.Contains('-android')) " />
|
||||
<ProjectReference Include="..\ST.Client.iOS.ResSecrets\ST.Client.iOS.ResSecrets.csproj" Condition=" $(TargetFramework.StartsWith('Xamarin.iOS')) OR $(TargetFramework.Contains('-ios')) " />
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
using System.Application.UI;
|
||||
using System.Application.Services;
|
||||
using System.CommandLine;
|
||||
using System.CommandLine.NamingConventionBinder;
|
||||
using System.Globalization;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Runtime.Versioning;
|
||||
using System.Application.Settings;
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
using System.Security.Cryptography;
|
||||
using System.CommandLine;
|
||||
using System.CommandLine.NamingConventionBinder;
|
||||
using System.Application.UI;
|
||||
using System.Application.Services;
|
||||
using System.Application.Settings;
|
||||
using System.Application.UI.ViewModels;
|
||||
using LogLevel = Microsoft.Extensions.Logging.LogLevel;
|
||||
|
||||
@@ -22,7 +23,7 @@ public abstract class CommandLineHost : IDisposable
|
||||
|
||||
protected abstract IApplication.IDesktopProgramHost Host { get; }
|
||||
|
||||
protected abstract void ConfigureServices(DILevel level);
|
||||
protected abstract void ConfigureServices(DILevel level, bool isTrace = false);
|
||||
|
||||
#if StartWatchTrace
|
||||
protected abstract void StartWatchTraceRecord(string? mark = null, bool dispose = false);
|
||||
@@ -34,6 +35,26 @@ public abstract class CommandLineHost : IDisposable
|
||||
|
||||
public abstract IApplication? Application { get; }
|
||||
|
||||
Task<IApplication>? _WaitForApplication;
|
||||
|
||||
public Task<IApplication> WaitForApplicationAsync()
|
||||
{
|
||||
if (Application != null) return Task.FromResult(Application);
|
||||
lock (this)
|
||||
{
|
||||
if (_WaitForApplication == null)
|
||||
_WaitForApplication = Task.Run(async () =>
|
||||
{
|
||||
while (Application == null)
|
||||
{
|
||||
await Task.Delay(500);
|
||||
}
|
||||
return Application;
|
||||
});
|
||||
return _WaitForApplication;
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract void SetIsMainProcess(bool value);
|
||||
|
||||
protected abstract void SetIsCLTProcess(bool value);
|
||||
@@ -45,6 +66,34 @@ public abstract class CommandLineHost : IDisposable
|
||||
// https://docs.microsoft.com/zh-cn/archive/msdn-magazine/2019/march/net-parse-the-command-line-with-system-commandline
|
||||
var rootCommand = new RootCommand("命令行工具(Command Line Tools/CLT)");
|
||||
|
||||
bool InitAppInstance(Func<string>? sendMessage = null)
|
||||
{
|
||||
var isInitAppInstanceReset = false;
|
||||
initAppInstance: AppInstance = new();
|
||||
if (!AppInstance.IsFirst)
|
||||
{
|
||||
//Console.WriteLine("ApplicationInstance.SendMessage(string.Empty);");
|
||||
if (IApplication.SingletonInstance.SendMessage(sendMessage?.Invoke() ?? ""))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!isInitAppInstanceReset &&
|
||||
IApplication.SingletonInstance.TryKillCurrentAllProcess())
|
||||
{
|
||||
isInitAppInstanceReset = true;
|
||||
AppInstance.Dispose();
|
||||
goto initAppInstance;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
void MainHandler() => MainHandler_(null);
|
||||
void MainHandler_(Action? onInitStartuped, Func<string>? sendMessage = null)
|
||||
{
|
||||
@@ -58,68 +107,67 @@ public abstract class CommandLineHost : IDisposable
|
||||
onInitStartuped?.Invoke();
|
||||
if (Host.IsMainProcess)
|
||||
{
|
||||
var isInitAppInstanceReset = false;
|
||||
initAppInstance: AppInstance = new();
|
||||
if (!AppInstance.IsFirst)
|
||||
if (!InitAppInstance(sendMessage))
|
||||
{
|
||||
//Console.WriteLine("ApplicationInstance.SendMessage(string.Empty);");
|
||||
if (IApplication.SingletonInstance.SendMessage(sendMessage?.Invoke() ?? ""))
|
||||
{
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!isInitAppInstanceReset &&
|
||||
IApplication.SingletonInstance.TryKillCurrentAllProcess())
|
||||
{
|
||||
isInitAppInstanceReset = true;
|
||||
AppInstance.Dispose();
|
||||
goto initAppInstance;
|
||||
}
|
||||
else
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
AppInstance.MessageReceived += value =>
|
||||
AppInstance!.MessageReceived += async value =>
|
||||
{
|
||||
if (!string.IsNullOrEmpty(value))
|
||||
{
|
||||
var args = value.Split(' ', StringSplitOptions.RemoveEmptyEntries);
|
||||
if (args.Length >= 1)
|
||||
switch (value)
|
||||
{
|
||||
switch (args[0])
|
||||
{
|
||||
case key_proxy:
|
||||
if (args.Length >= 2)
|
||||
case key_shutdown:
|
||||
(await WaitForApplicationAsync()).Shutdown();
|
||||
return;
|
||||
default:
|
||||
var args = value.Split(' ', StringSplitOptions.RemoveEmptyEntries);
|
||||
if (args.Length >= 1)
|
||||
{
|
||||
switch (args[0])
|
||||
{
|
||||
ProxyMessageReceived();
|
||||
return;
|
||||
|
||||
}
|
||||
void ProxyMessageReceived()
|
||||
{
|
||||
var value = Enum.TryParse<EOnOff>(args[1], out var value_) ? value_ : default;
|
||||
MainThread2.BeginInvokeOnMainThread(() =>
|
||||
{
|
||||
ProxyService.Current.ProxyStatus = value switch
|
||||
case key_proxy:
|
||||
if (args.Length >= 2)
|
||||
{
|
||||
EOnOff.On => true,
|
||||
EOnOff.Off => false,
|
||||
_ => !ProxyService.Current.ProxyStatus,
|
||||
};
|
||||
});
|
||||
ProxyMessageReceived();
|
||||
return;
|
||||
|
||||
}
|
||||
void ProxyMessageReceived()
|
||||
{
|
||||
var value = Enum.TryParse<EOnOff>(args[1], out var value_) ? value_ : default;
|
||||
try
|
||||
{
|
||||
MainThread2.BeginInvokeOnMainThread(() =>
|
||||
{
|
||||
ProxyService.Current.ProxyStatus = value switch
|
||||
{
|
||||
EOnOff.On => true,
|
||||
EOnOff.Off => false,
|
||||
_ => !ProxyService.Current.ProxyStatus,
|
||||
};
|
||||
});
|
||||
}
|
||||
catch
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
var app = Application;
|
||||
if (app != null)
|
||||
try
|
||||
{
|
||||
var app = await WaitForApplicationAsync();
|
||||
MainThread2.BeginInvokeOnMainThread(app.RestoreMainWindow);
|
||||
}
|
||||
catch
|
||||
{
|
||||
|
||||
}
|
||||
};
|
||||
}
|
||||
//#if StartWatchTrace
|
||||
@@ -263,27 +311,27 @@ public abstract class CommandLineHost : IDisposable
|
||||
|
||||
// -clt show -config -cert
|
||||
var show = new Command("show", "显示信息");
|
||||
show.AddOption(new Option<bool>("-config", "显示 Config.mpo 值"));
|
||||
//show.AddOption(new Option<bool>("-config", "显示 Config.mpo 值"));
|
||||
show.AddOption(new Option<bool>("-cert", "显示当前根证书信息"));
|
||||
show.Handler = CommandHandler.Create((bool config, bool cert) =>
|
||||
show.Handler = CommandHandler.Create((/*bool config,*/ bool cert) =>
|
||||
{
|
||||
if (OperatingSystem.IsWindows()) if (!AttachConsole()) AllocConsole();
|
||||
|
||||
if (config)
|
||||
{
|
||||
Console.WriteLine("Config: ");
|
||||
try
|
||||
{
|
||||
SettingsHost.Load();
|
||||
var configValue = SettingsHostBase.Local.ToJsonString();
|
||||
Console.WriteLine(configValue);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Console.WriteLine(e.ToString());
|
||||
}
|
||||
Console.WriteLine();
|
||||
}
|
||||
//if (config)
|
||||
//{
|
||||
// Console.WriteLine("Config: ");
|
||||
// try
|
||||
// {
|
||||
// SettingsHost.Load();
|
||||
// var configValue = SettingsHostBase.Local.ToJsonString();
|
||||
// Console.WriteLine(configValue);
|
||||
// }
|
||||
// catch (Exception e)
|
||||
// {
|
||||
// Console.WriteLine(e.ToString());
|
||||
// }
|
||||
// Console.WriteLine();
|
||||
//}
|
||||
|
||||
if (cert)
|
||||
{
|
||||
@@ -337,11 +385,66 @@ public abstract class CommandLineHost : IDisposable
|
||||
});
|
||||
rootCommand.AddCommand(proxy);
|
||||
|
||||
// -clt ayaneo -path
|
||||
var ayaneo = new Command("ayaneo", "生成 ayaneo 数据在指定位置");
|
||||
ayaneo.AddOption(new Option<string>("-path", "json 生成路径"));
|
||||
ayaneo.Handler = CommandHandler.Create((string path) =>
|
||||
{
|
||||
ConfigureServices(DILevel.Steam);
|
||||
var steamService = ISteamService.Instance;
|
||||
var users = steamService.GetRememberUserList();
|
||||
|
||||
#pragma warning disable CA1416 // 验证平台兼容性
|
||||
IReadOnlyDictionary<long, string?>? accountRemarks = SteamAccountSettings.AccountRemarks.Value;
|
||||
#pragma warning restore CA1416 // 验证平台兼容性
|
||||
var sUsers = users.Select(s =>
|
||||
{
|
||||
if (accountRemarks?.TryGetValue(s.SteamId64, out var remark) == true &&
|
||||
!string.IsNullOrEmpty(remark))
|
||||
s.Remark = remark;
|
||||
|
||||
var title = s.SteamNickName ?? s.SteamId64.ToString(CultureInfo.InvariantCulture);
|
||||
if (!string.IsNullOrEmpty(s.Remark))
|
||||
title = s.SteamNickName + "(" + s.Remark + ")";
|
||||
|
||||
#pragma warning disable CS8619 // 值中的引用类型的为 Null 性与目标类型不匹配。
|
||||
return new
|
||||
{
|
||||
Name = title,
|
||||
Account = s.AccountName,
|
||||
};
|
||||
#pragma warning restore CS8619 // 值中的引用类型的为 Null 性与目标类型不匹配。
|
||||
});
|
||||
|
||||
var content = new
|
||||
{
|
||||
steamppPath = Path.Combine(IOPath.BaseDirectory, System.Application.Constants.HARDCODED_APP_NAME + ".exe"),
|
||||
steamUsers = sUsers,
|
||||
};
|
||||
if (string.IsNullOrWhiteSpace(path))
|
||||
{
|
||||
path = Path.Combine(IOPath.BaseDirectory, "steampp.json");
|
||||
}
|
||||
File.WriteAllText(path, Serializable.SJSON(content));
|
||||
});
|
||||
rootCommand.AddCommand(ayaneo);
|
||||
|
||||
// -clt shutdown
|
||||
var shutdown = new Command(key_shutdown, "安全结束正在运行的程序")
|
||||
{
|
||||
Handler = CommandHandler.Create(() =>
|
||||
{
|
||||
InitAppInstance(() => key_shutdown);
|
||||
}),
|
||||
};
|
||||
rootCommand.AddCommand(shutdown);
|
||||
|
||||
var r = rootCommand.InvokeAsync(args).GetAwaiter().GetResult();
|
||||
return r;
|
||||
}
|
||||
|
||||
const string key_proxy = "proxy";
|
||||
const string key_shutdown = "shutdown";
|
||||
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
@@ -350,6 +453,7 @@ public abstract class CommandLineHost : IDisposable
|
||||
if (disposing)
|
||||
{
|
||||
// TODO: 释放托管状态(托管对象)
|
||||
_WaitForApplication = null;
|
||||
AppInstance?.Dispose();
|
||||
Application?.Dispose();
|
||||
}
|
||||
|
||||
@@ -5,12 +5,13 @@
|
||||
xmlns:uap="http://schemas.microsoft.com/appx/manifest/uap/windows10"
|
||||
xmlns:rescap="http://schemas.microsoft.com/appx/manifest/foundation/windows10/restrictedcapabilities"
|
||||
xmlns:desktop="http://schemas.microsoft.com/appx/manifest/desktop/windows10"
|
||||
IgnorableNamespaces="uap rescap">
|
||||
xmlns:desktop2="http://schemas.microsoft.com/appx/manifest/desktop/windows10/2"
|
||||
IgnorableNamespaces="uap rescap desktop desktop2">
|
||||
|
||||
<Identity
|
||||
Name="4651ED44255E.47979655102CE"
|
||||
Publisher="CN=A90E406B-B2D3-4A23-B061-0FA1D65C4F66"
|
||||
Version="2.8.1.0" />
|
||||
Version="2.8.4.0" />
|
||||
|
||||
<Properties>
|
||||
<DisplayName>Watt Toolkit</DisplayName>
|
||||
@@ -59,6 +60,15 @@
|
||||
</Application>
|
||||
</Applications>
|
||||
|
||||
<Extensions>
|
||||
<desktop2:Extension Category="windows.firewallRules">
|
||||
<desktop2:FirewallRules Executable="Steam++\Steam++.exe">
|
||||
<desktop2:Rule Direction="in" IPProtocol="TCP" Profile="all" />
|
||||
<desktop2:Rule Direction="in" IPProtocol="UDP" Profile="all" />
|
||||
</desktop2:FirewallRules>
|
||||
</desktop2:Extension>
|
||||
</Extensions>
|
||||
|
||||
<Capabilities>
|
||||
<Capability Name="internetClient" />
|
||||
<rescap:Capability Name="runFullTrust" />
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
<WindowsSdkPackageVersion>10.0.19041.24</WindowsSdkPackageVersion>
|
||||
<UseWindowsForms Condition="'$(Configuration)'=='Debug'">true</UseWindowsForms>
|
||||
<UseWPF>false</UseWPF>
|
||||
<DefineConstants>AVALONIA;$(DefineConstants)</DefineConstants>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)'=='Release'">
|
||||
@@ -44,7 +45,7 @@
|
||||
.allowedextension
|
||||
</AllowedReferenceRelatedFileExtensions>
|
||||
</PropertyGroup>
|
||||
|
||||
|
||||
<ItemGroup Condition=" '$(Configuration)' == 'Debug' ">
|
||||
<RuntimeHostConfigurationOption Include="System.Net.SocketsHttpHandler.Http3Support" Value="true" />
|
||||
</ItemGroup>
|
||||
|
||||
@@ -1,16 +1,17 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<Package
|
||||
xmlns="http://schemas.microsoft.com/appx/manifest/foundation/windows10"
|
||||
xmlns:uap="http://schemas.microsoft.com/appx/manifest/uap/windows10"
|
||||
xmlns:rescap="http://schemas.microsoft.com/appx/manifest/foundation/windows10/restrictedcapabilities"
|
||||
xmlns:desktop="http://schemas.microsoft.com/appx/manifest/desktop/windows10"
|
||||
IgnorableNamespaces="uap rescap">
|
||||
xmlns:desktop2="http://schemas.microsoft.com/appx/manifest/desktop/windows10/2"
|
||||
IgnorableNamespaces="uap rescap desktop desktop2">
|
||||
|
||||
<Identity
|
||||
Name="4651ED44255E.47979655102CE"
|
||||
Publisher="CN=A90E406B-B2D3-4A23-B061-0FA1D65C4F66"
|
||||
Version="2.8.1.0" />
|
||||
Version="2.8.2.0" />
|
||||
|
||||
<Properties>
|
||||
<DisplayName>Watt Toolkit</DisplayName>
|
||||
@@ -59,6 +60,15 @@
|
||||
</Application>
|
||||
</Applications>
|
||||
|
||||
<Extensions>
|
||||
<desktop2:Extension Category="windows.firewallRules">
|
||||
<desktop2:FirewallRules Executable="Steam++.exe">
|
||||
<desktop2:Rule Direction="in" IPProtocol="TCP" Profile="all" />
|
||||
<desktop2:Rule Direction="in" IPProtocol="UDP" Profile="all" />
|
||||
</desktop2:FirewallRules>
|
||||
</desktop2:Extension>
|
||||
</Extensions>
|
||||
|
||||
<Capabilities>
|
||||
<Capability Name="internetClient" />
|
||||
<rescap:Capability Name="runFullTrust" />
|
||||
|
||||
@@ -1,14 +1,22 @@
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Text;
|
||||
using System.Runtime.Versioning;
|
||||
using System.Application.Settings;
|
||||
using System.Application.CommandLine;
|
||||
using System.Application.UI.Resx;
|
||||
using NLog;
|
||||
using Avalonia;
|
||||
using Avalonia.ReactiveUI;
|
||||
using Avalonia.Controls;
|
||||
|
||||
#if WINDOWS || WINDOWS_DESKTOP_BRIDGE
|
||||
using WPFMessageBox = System.Windows.MessageBox;
|
||||
using WPFMessageBoxButton = System.Windows.MessageBoxButton;
|
||||
using WPFMessageBoxImage = System.Windows.MessageBoxImage;
|
||||
#endif
|
||||
|
||||
#if MAC
|
||||
[assembly: SupportedOSPlatform("macOS")]
|
||||
#elif LINUX
|
||||
@@ -39,17 +47,42 @@ namespace System.Application.UI
|
||||
// fix The request was aborted: Could not create SSL/TLS secure channel
|
||||
TrySetSecurityProtocol();
|
||||
|
||||
var host = ProgramHost.Instance;
|
||||
|
||||
host.IsMainProcess = args.Length == 0;
|
||||
host.IsCLTProcess = !host.IsMainProcess && args.FirstOrDefault() == "-clt";
|
||||
|
||||
#if WINDOWS_DESKTOP_BRIDGE
|
||||
if (!DesktopBridgeHelper.Init()) return 0;
|
||||
InitWithUWP(args);
|
||||
InitWithUWP(ref args);
|
||||
#elif WINDOWS
|
||||
//#if NET7_0_OR_GREATER
|
||||
// if (/*Environment.Version >= new Version(7, 0) && */!OperatingSystem2.IsWindows10AtLeast())
|
||||
// {
|
||||
// // [TBD] .NET 7 中考虑弃用对 Windows 7/2008 R2/8.1/2012 R2 的支持
|
||||
// WPFMessageBox.Show("TBD", AppResources.Error, WPFMessageBoxButton.OK, WPFMessageBoxImage.Error);
|
||||
// return 0;
|
||||
// }
|
||||
//#endif
|
||||
if (OperatingSystem2.IsWindows10AtLeast() &&
|
||||
!OperatingSystem.IsWindowsVersionAtLeast(10, 0, 14393))
|
||||
{
|
||||
// Win10 最低需要 1607,低于此版本则弹窗错误中止运行
|
||||
// appcenter.ms 上有 10240,10586 的 crash 报告
|
||||
WPFMessageBox.Show(AppResources.Error_Lower_Than_Windows10_1607, AppResources.Error, WPFMessageBoxButton.OK, WPFMessageBoxImage.Error);
|
||||
return 0;
|
||||
}
|
||||
if (AppContext.BaseDirectory.StartsWith(Path.GetTempPath(), StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
// 检测当前目录 Temp\Rar$ 这类目录,可能是在压缩包中直接启动程序导致的,还有一堆 文件找不到/加载失败的异常
|
||||
// System.IO.DirectoryNotFoundException: Could not find a part of the path 'C:\Users\USER\AppData\Local\Temp\Rar$EXa15528.13350\Cache\switchproxy.reg'.
|
||||
// System.IO.FileLoadException ...
|
||||
// System.IO.FileNotFoundException: Could not load file or assembly ...
|
||||
WPFMessageBox.Show(AppResources.Error_BaseDir_StartsWith_Temp, AppResources.Error, WPFMessageBoxButton.OK, WPFMessageBoxImage.Error);
|
||||
return 0;
|
||||
}
|
||||
#elif MAC
|
||||
InitWithMAC(args);
|
||||
#endif
|
||||
var host = ProgramHost.Instance;
|
||||
host.IsMainProcess = args.Length == 0;
|
||||
host.IsCLTProcess = !host.IsMainProcess && args.FirstOrDefault() == "-clt";
|
||||
|
||||
OnCreateAppExecuting();
|
||||
// InitCefNetApp(args);
|
||||
|
||||
@@ -77,16 +110,17 @@ namespace System.Application.UI
|
||||
// Ensure to flush and stop internal timers/threads before application-exit (Avoid segmentation fault on Linux)
|
||||
PlatformApp?.Dispose();
|
||||
DI.Dispose();
|
||||
LogManager.Shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
static void TrySetSecurityProtocol(SecurityProtocolType type = SecurityProtocolType.Tls11 | SecurityProtocolType.Tls12 | SecurityProtocolType.Tls13)
|
||||
static void TrySetSecurityProtocol(SecurityProtocolType type = SecurityProtocolType.Tls12 | SecurityProtocolType.Tls13)
|
||||
{
|
||||
try
|
||||
{
|
||||
ServicePointManager.SecurityProtocol = type;
|
||||
}
|
||||
catch (NotSupportedException)
|
||||
catch
|
||||
{
|
||||
|
||||
}
|
||||
@@ -97,7 +131,7 @@ namespace System.Application.UI
|
||||
static void ShowArgs(string[] args) => WinFormsMessageBox.Show(string.Join(' ', args), "Main(string[] args)");
|
||||
#endif
|
||||
|
||||
static void InitWithUWP(string[] args)
|
||||
static void InitWithUWP(ref string[] args)
|
||||
{
|
||||
#if DEBUG && !MSIX_SINGLE_PROJECT
|
||||
ShowArgs(args);
|
||||
@@ -204,7 +238,7 @@ namespace System.Application.UI
|
||||
static void StartAvaloniaApp(string[] args, ShutdownMode shutdownMode = ShutdownMode.OnLastWindowClose)
|
||||
{
|
||||
var builder = BuildAvaloniaApp();
|
||||
builder.StartWithClassicDesktopLifetime(args, shutdownMode);
|
||||
builder.StartWithClassicDesktopLifetime2(args, shutdownMode);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,7 @@ using System.Application.Services;
|
||||
using Program = System.Application.UI.MauiProgram;
|
||||
#endif
|
||||
|
||||
// ReSharper disable once CheckNamespace
|
||||
namespace System.Application.UI;
|
||||
|
||||
static partial class
|
||||
@@ -37,7 +38,7 @@ static partial class
|
||||
|
||||
public EOnOff ProxyStatus { get; set; }
|
||||
|
||||
void IApplication.IProgramHost.ConfigureServices(DILevel level) => ConfigureServices(level);
|
||||
void IApplication.IProgramHost.ConfigureServices(DILevel level, bool isTrace) => ConfigureServices(level, isTrace);
|
||||
|
||||
public void InitVisualStudioAppCenterSDK()
|
||||
{
|
||||
@@ -55,6 +56,14 @@ static partial class
|
||||
IApplication IApplication.IProgramHost.Application => App.Instance;
|
||||
|
||||
void IApplication.IDesktopProgramHost.OnCreateAppExecuted(Action<IViewModelManager>? handlerViewModelManager, bool isTrace) => Program.OnCreateAppExecuted(this, handlerViewModelManager, isTrace);
|
||||
|
||||
DeploymentMode IApplication.IDesktopProgramHost.DeploymentMode => DeploymentMode.
|
||||
#if FRAMEWORK_DEPENDENT || !PUBLISH
|
||||
FDE
|
||||
#else
|
||||
SCD
|
||||
#endif
|
||||
;
|
||||
}
|
||||
|
||||
partial class ProgramHost : CommandLineHost
|
||||
@@ -63,7 +72,7 @@ static partial class
|
||||
|
||||
public Action<DILevel>? ConfigureServicesDelegate { get; set; }
|
||||
|
||||
protected override void ConfigureServices(DILevel level)
|
||||
protected override void ConfigureServices(DILevel level, bool isTrace = false)
|
||||
{
|
||||
if (ConfigureServicesDelegate != null)
|
||||
{
|
||||
@@ -71,7 +80,7 @@ static partial class
|
||||
}
|
||||
else
|
||||
{
|
||||
Program.ConfigureServices(this, level);
|
||||
Program.ConfigureServices(this, level, isTrace: isTrace);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
<RootNamespace>System.Application.UI</RootNamespace>
|
||||
<AssemblyName>Steam++</AssemblyName>
|
||||
<AspNetCoreHostingModel>OutOfProcess</AspNetCoreHostingModel>
|
||||
<DefineConstants>AVALONIA;$(DefineConstants)</DefineConstants>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)'=='Release'">
|
||||
|
||||
+57
@@ -0,0 +1,57 @@
|
||||
using System.Application.Services;
|
||||
using System.Application.Settings;
|
||||
using System.Application.UI.ViewModels;
|
||||
using System.Application.UI.Views;
|
||||
#if !MAUI
|
||||
using System.Application.UI.Views.Windows;
|
||||
#endif
|
||||
|
||||
namespace System.Application.UI;
|
||||
|
||||
partial class App
|
||||
{
|
||||
void HandlerViewModelManager(IViewModelManager vmService)
|
||||
{
|
||||
switch (vmService.MainWindow)
|
||||
{
|
||||
case CloudArchiveWindowViewModel:
|
||||
ProgramHost.IsMinimize = false;
|
||||
#if !MAUI
|
||||
MainWindow = new CloudArchiveWindow();
|
||||
#endif
|
||||
break;
|
||||
|
||||
case AchievementWindowViewModel:
|
||||
ProgramHost.IsMinimize = false;
|
||||
#if !MAUI
|
||||
MainWindow = new AchievementWindow();
|
||||
#endif
|
||||
break;
|
||||
|
||||
default:
|
||||
#region 主窗口启动时加载的资源
|
||||
#if !UI_DEMO
|
||||
compositeDisposable.Add(SettingsHost.Save);
|
||||
compositeDisposable.Add(ProxyService.Current.Exit);
|
||||
if (IApplication.IsDesktopPlatform)
|
||||
{
|
||||
compositeDisposable.Add(SteamConnectService.Current.Dispose);
|
||||
if (GeneralSettings.IsStartupAppMinimized.Value)
|
||||
ProgramHost.IsMinimize = true;
|
||||
}
|
||||
compositeDisposable.Add(ASFService.Current.StopASF);
|
||||
#endif
|
||||
#endregion
|
||||
#if !MAUI
|
||||
MainWindow = new MainWindow();
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
#if !MAUI
|
||||
MainWindow.DataContext = vmService.MainWindow;
|
||||
#else
|
||||
var mainWindowVM = (MainWindowViewModel?)vmService.MainWindow;
|
||||
MainPage = new AppShell(mainWindowVM.ThrowIsNull());
|
||||
#endif
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user