mirror of
https://jihulab.com/DGP-Studio/Snap.Hutao.git
synced 2025-11-19 21:02:53 +08:00
Compare commits
81 Commits
feat/feedb
...
feat/reord
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
974afa86ee | ||
|
|
7baf125f88 | ||
|
|
a4e782da78 | ||
|
|
d5551e5cdf | ||
|
|
f016a4a27f | ||
|
|
8b931b6d89 | ||
|
|
b942ceb914 | ||
|
|
f7a49e52e0 | ||
|
|
d4bd610fe2 | ||
|
|
a3dcfd3804 | ||
|
|
592525d149 | ||
|
|
83c4598df5 | ||
|
|
aa680388ad | ||
|
|
ba4f59de30 | ||
|
|
8780cf385e | ||
|
|
431cdd1253 | ||
|
|
9a8827fb40 | ||
|
|
d88a6ca301 | ||
|
|
5d401794e5 | ||
|
|
26396443dc | ||
|
|
6b755d934d | ||
|
|
7d5b057269 | ||
|
|
917c173eb2 | ||
|
|
28d702422e | ||
|
|
7612ab5da3 | ||
|
|
ab436ecb2f | ||
|
|
457e3ff4d5 | ||
|
|
224c4e52ea | ||
|
|
2a5c7b21fd | ||
|
|
53f8291a66 | ||
|
|
b621d5406a | ||
|
|
12f4847aea | ||
|
|
39a3d31f38 | ||
|
|
70ac0b13a5 | ||
|
|
a3520a4991 | ||
|
|
aed0284e4b | ||
|
|
96ef07cbe5 | ||
|
|
d1f37f37ac | ||
|
|
1fe09f3069 | ||
|
|
32d9355c3a | ||
|
|
71f170d51e | ||
|
|
60015b6354 | ||
|
|
eecae3ea4f | ||
|
|
1831166f1e | ||
|
|
a98915ea24 | ||
|
|
0d46656f57 | ||
|
|
c814a5c28f | ||
|
|
9c3d59cc6f | ||
|
|
890cf3f3ea | ||
|
|
196bbb54c3 | ||
|
|
0481b9e474 | ||
|
|
c4f3eb68e8 | ||
|
|
c2e9f3a926 | ||
|
|
fb1fe3e40f | ||
|
|
75ed512e4a | ||
|
|
dd4dd33d93 | ||
|
|
1e216e9823 | ||
|
|
f823cb5f1a | ||
|
|
2c6d25f0a3 | ||
|
|
817f768263 | ||
|
|
2998fbb167 | ||
|
|
8f0f94054d | ||
|
|
17a5d4d3a2 | ||
|
|
a1c604e68a | ||
|
|
948ec9a822 | ||
|
|
f83174d690 | ||
|
|
d686debbfb | ||
|
|
45248d75e1 | ||
|
|
22646cfab2 | ||
|
|
d0237a3c89 | ||
|
|
73c80fad10 | ||
|
|
320bed9fcb | ||
|
|
bb12aca3b4 | ||
|
|
c7b5d98fb1 | ||
|
|
7cc96f94f2 | ||
|
|
b35355f9a3 | ||
|
|
745815657d | ||
|
|
d93a9f41f3 | ||
|
|
910f099c6d | ||
|
|
e68449ec0c | ||
|
|
e484fbed21 |
4
.github/ISSUE_TEMPLATE/CHS-bug-report.yml
vendored
4
.github/ISSUE_TEMPLATE/CHS-bug-report.yml
vendored
@@ -51,7 +51,7 @@ body:
|
||||
description: |
|
||||
在胡桃工具箱的设置界面,你可以找到并复制你的设备 ID
|
||||
如果你的问题涉及程序崩溃,请填写该项,这将有助于我们定位问题
|
||||
如果你的程序已经无法启动,请下载并运行[此工具](https://github.com/DGP-Automation/ISSUE_TEMPLATES/releases/download/diagnosis_tools/Snap.Hutao.DiagTools.exe),它将显示你的设备 ID
|
||||
如果你的程序已经无法启动,请下载并运行[诊断工具](https://github.com/DGP-Automation/ISSUE_TEMPLATES/releases/download/diagnosis_tools/Snap.Hutao.DiagTools.exe),它将显示你的设备 ID
|
||||
validations:
|
||||
required: false
|
||||
|
||||
@@ -87,7 +87,7 @@ body:
|
||||
label: 发生了什么?
|
||||
description: |
|
||||
详细的描述问题发生前后的行为,以便我们解决问题。**如果你的问题涉及程序崩溃,你应当检查 Windows 事件查看器,并将相关的 `.Net 错误`详情附上**
|
||||
如果你无法找到该日志,请下载并运行[此工具](https://github.com/DGP-Automation/ISSUE_TEMPLATES/releases/download/diagnosis_tools/Snap.Hutao.DiagTools.exe),它将转储问题日志至工具运行目录中的 `Snap.Hutao Error Log.txt`
|
||||
如果你无法找到该日志,请下载并运行[诊断工具](https://github.com/DGP-Automation/ISSUE_TEMPLATES/releases/download/diagnosis_tools/Snap.Hutao.DiagTools.exe),它将转储问题日志至工具运行目录中的 `Snap.Hutao Error Log.txt`
|
||||
validations:
|
||||
required: true
|
||||
|
||||
|
||||
4
.github/ISSUE_TEMPLATE/ENG-bug-report.yml
vendored
4
.github/ISSUE_TEMPLATE/ENG-bug-report.yml
vendored
@@ -51,7 +51,7 @@ body:
|
||||
description: |
|
||||
In Snap Hutao's settings page, you can find and copy your device ID
|
||||
If your issue is about program crash, please fill this so we can dump the log and locate the source easier
|
||||
If your program cannot startup, please download and run [this tool](https://github.com/DGP-Automation/ISSUE_TEMPLATES/releases/download/diagnosis_tools/Snap.Hutao.DiagTools.exe), it will shows your device ID.
|
||||
If your program cannot startup, please download and run [Diagnosis Tool](https://github.com/DGP-Automation/ISSUE_TEMPLATES/releases/download/diagnosis_tools/Snap.Hutao.DiagTools.exe), it will shows your device ID.
|
||||
validations:
|
||||
required: false
|
||||
|
||||
@@ -87,7 +87,7 @@ body:
|
||||
label: What Happened?
|
||||
description: |
|
||||
Describe your issue in detail to help us identify the issue. **If your issue is about program crash, you should check Windows Event Viewer, and attach associated `.Net Error` details here**If your program cannot startup, please download and run [this PowerShell script](https://github.com/DGP-Studio/ISSUE_TEMPLATES/releases/download/get_device_id/GetHutaoDeviceId.ps1), it will shows your device ID.
|
||||
If you cannot find it, please download and run [this tool](https://github.com/DGP-Automation/ISSUE_TEMPLATES/releases/download/diagnosis_tools/Snap.Hutao.DiagTools.exe), it will dump the error log to `Snap.Hutao Error Log.txt` in the working directory of the tool.
|
||||
If you cannot find it, please download and run [Diagnosis Tool](https://github.com/DGP-Automation/ISSUE_TEMPLATES/releases/download/diagnosis_tools/Snap.Hutao.DiagTools.exe), it will dump the error log to `Snap.Hutao Error Log.txt` in the working directory of the tool.
|
||||
validations:
|
||||
required: true
|
||||
|
||||
|
||||
@@ -61,6 +61,9 @@ release:
|
||||
- name: "$THIS_SHA256SUMS_NAME"
|
||||
url: "https://$CI_SERVER_SHELL_SSH_HOST/$CI_PROJECT_PATH/-/jobs/$THIS_JOB_ID/artifacts/raw/$THIS_SHA256SUMS_NAME?inline=false"
|
||||
link_type: other
|
||||
- name: "artifact_archive"
|
||||
url: "https://$CI_SERVER_SHELL_SSH_HOST/$CI_PROJECT_PATH/-/jobs/$THIS_JOB_ID/artifacts/download?file_type=archive"
|
||||
link_type: other
|
||||
|
||||
Refresh:
|
||||
stage: refresh
|
||||
|
||||
@@ -44,9 +44,7 @@ Install with Snap Hutao MSIX package, can be installed with Windows built-in App
|
||||
|
||||
### 特定的原神项目 / Specific Genshin-related Projects
|
||||
|
||||
* [biuuu/genshin-wish-export](https://github.com/biuuu/genshin-wish-export)
|
||||
* [xunkong/xunkong](https://github.com/xunkong/xunkong)
|
||||
* [YuehaiTeam/cocogoat](https://github.com/YuehaiTeam/cocogoat)
|
||||
* [Scighost/Starward](https://github.com/Scighost/Starward)
|
||||
|
||||
### 使用的技术栈 / Tech Stack
|
||||
|
||||
@@ -57,7 +55,6 @@ Install with Snap Hutao MSIX package, can be installed with Windows built-in App
|
||||
* [dotnet/efcore](https://github.com/dotnet/efcore)
|
||||
* [dotnet/runtime](https://github.com/dotnet/runtime)
|
||||
* [DotNetAnalyzers/StyleCopAnalyzers](https://github.com/DotNetAnalyzers/StyleCopAnalyzers)
|
||||
* [microsoft/CsWin32](https://github.com/microsoft/CsWin32)
|
||||
* [microsoft/vs-validation](https://github.com/microsoft/vs-validation)
|
||||
* [microsoft/WindowsAppSDK](https://github.com/microsoft/WindowsAppSDK)
|
||||
* [microsoft/microsoft-ui-xaml](https://github.com/microsoft/microsoft-ui-xaml)
|
||||
|
||||
@@ -160,9 +160,39 @@ public sealed class GeniusInvokationDecoding
|
||||
|
||||
ushort[] testKnownResult =
|
||||
[
|
||||
060, 019, 001, 079, 120, 120, 129, 151, 151, 153, 153,
|
||||
181, 184, 184, 185, 185, 194, 194, 200, 200, 201, 201,
|
||||
217, 217, 219, 241, 241, 244, 244, 245, 245, 270, 270,
|
||||
060,
|
||||
019,
|
||||
001,
|
||||
079,
|
||||
120,
|
||||
120,
|
||||
129,
|
||||
151,
|
||||
151,
|
||||
153,
|
||||
153,
|
||||
181,
|
||||
184,
|
||||
184,
|
||||
185,
|
||||
185,
|
||||
194,
|
||||
194,
|
||||
200,
|
||||
200,
|
||||
201,
|
||||
201,
|
||||
217,
|
||||
217,
|
||||
219,
|
||||
241,
|
||||
241,
|
||||
244,
|
||||
244,
|
||||
245,
|
||||
245,
|
||||
270,
|
||||
270,
|
||||
];
|
||||
|
||||
CollectionAssert.AreEqual(resultArray, testKnownResult);
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="8.0.0" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.8.0" />
|
||||
<PackageReference Include="MSTest.TestAdapter" Version="3.1.1" />
|
||||
<PackageReference Include="MSTest.TestFramework" Version="3.1.1" />
|
||||
<PackageReference Include="MSTest.TestFramework" Version="3.2.0" />
|
||||
<PackageReference Include="coverlet.collector" Version="6.0.0">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
{
|
||||
"$schema": "https://raw.githubusercontent.com/microsoft/CsWin32/main/src/Microsoft.Windows.CsWin32/settings.schema.json",
|
||||
"allowMarshaling": true,
|
||||
"useSafeHandles": false,
|
||||
"comInterop": {
|
||||
"preserveSigMethods": [
|
||||
"IFileOpenDialog.Show",
|
||||
"IFileSaveDialog.Show"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -1,109 +0,0 @@
|
||||
// ADVAPI32
|
||||
RegCloseKey
|
||||
RegOpenKeyExW
|
||||
RegNotifyChangeKeyValue
|
||||
REG_NOTIFY_FILTER
|
||||
HKEY_CLASSES_ROOT
|
||||
HKEY_CURRENT_USER
|
||||
HKEY_LOCAL_MACHINE
|
||||
HKEY_USERS
|
||||
HKEY_CURRENT_CONFIG
|
||||
|
||||
// COMCTL32
|
||||
DefSubclassProc
|
||||
RemoveWindowSubclass
|
||||
SetWindowSubclass
|
||||
|
||||
// DWMAPI
|
||||
DwmSetWindowAttribute
|
||||
|
||||
// GDI32
|
||||
GetDeviceCaps
|
||||
|
||||
// KERNEL32
|
||||
AllocConsole
|
||||
CloseHandle
|
||||
CreateEventW
|
||||
CreateRemoteThread
|
||||
FreeConsole
|
||||
GetConsoleMode
|
||||
GetModuleHandleW
|
||||
GetProcAddress
|
||||
GetStdHandle
|
||||
K32EnumProcessModules
|
||||
K32GetModuleBaseNameW
|
||||
K32GetModuleInformation
|
||||
ReadProcessMemory
|
||||
SetConsoleMode
|
||||
SetConsoleTitle
|
||||
SetEvent
|
||||
VirtualAlloc
|
||||
VirtualAllocEx
|
||||
VirtualFree
|
||||
VirtualFreeEx
|
||||
WaitForSingleObject
|
||||
WriteProcessMemory
|
||||
|
||||
// OLE32
|
||||
CoCreateInstance
|
||||
CoWaitForMultipleObjects
|
||||
|
||||
// SHELL32
|
||||
SHCreateItemFromParsingName
|
||||
|
||||
// USER32
|
||||
AttachThreadInput
|
||||
FindWindowExW
|
||||
GetCursorPos
|
||||
GetDC
|
||||
GetDpiForWindow
|
||||
GetForegroundWindow
|
||||
GetWindowLongPtrW
|
||||
GetWindowPlacement
|
||||
GetWindowThreadProcessId
|
||||
ReleaseDC
|
||||
RegisterHotKey
|
||||
SendInput
|
||||
SetForegroundWindow
|
||||
SetWindowLongPtrW
|
||||
UnregisterHotKey
|
||||
|
||||
// COM
|
||||
FileOpenDialog
|
||||
FileSaveDialog
|
||||
IFileOpenDialog
|
||||
IFileSaveDialog
|
||||
IPersistFile
|
||||
IShellLinkDataList
|
||||
IShellLinkW
|
||||
ShellLink
|
||||
SHELL_LINK_DATA_FLAGS
|
||||
|
||||
// WinRT
|
||||
IMemoryBufferByteAccess
|
||||
|
||||
// Const value
|
||||
E_FAIL
|
||||
INFINITE
|
||||
RPC_E_WRONG_THREAD
|
||||
MAX_PATH
|
||||
WM_ERASEBKGND
|
||||
WM_GETMINMAXINFO
|
||||
WM_HOTKEY
|
||||
WM_NCRBUTTONDOWN
|
||||
WM_NCRBUTTONUP
|
||||
WM_NULL
|
||||
|
||||
// Type & Enum definition
|
||||
HRESULT_FROM_WIN32
|
||||
SLGP_FLAGS
|
||||
|
||||
// System.Threading
|
||||
LPTHREAD_START_ROUTINE
|
||||
|
||||
// UI.WindowsAndMessaging
|
||||
MINMAXINFO
|
||||
WINDOW_EX_STYLE
|
||||
|
||||
// System.Com
|
||||
CWMO_FLAGS
|
||||
@@ -1,17 +0,0 @@
|
||||
using System;
|
||||
using Windows.Win32.Foundation;
|
||||
using Windows.Win32.System.Com;
|
||||
|
||||
namespace Windows.Win32;
|
||||
|
||||
internal static partial class PInvoke
|
||||
{
|
||||
/// <inheritdoc cref="CoCreateInstance(Guid*, object, CLSCTX, Guid*, out object)"/>
|
||||
internal static unsafe HRESULT CoCreateInstance<TClass, TInterface>(object? pUnkOuter, CLSCTX dwClsContext, out TInterface ppv)
|
||||
where TInterface : class
|
||||
{
|
||||
HRESULT hr = CoCreateInstance(typeof(TClass).GUID, pUnkOuter, dwClsContext, typeof(TInterface).GUID, out object o);
|
||||
ppv = (TInterface)o;
|
||||
return hr;
|
||||
}
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0-windows10.0.22621.0</TargetFramework>
|
||||
<ImplicitUsings>disable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<PlatformTarget>x64</PlatformTarget>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Remove="NativeMethods.json" />
|
||||
<None Remove="NativeMethods.txt" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<AdditionalFiles Include="NativeMethods.json" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Windows.CsWin32" Version="0.3.49-beta">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -1,24 +0,0 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using System.Runtime.CompilerServices;
|
||||
using Windows.Win32.UI.WindowsAndMessaging;
|
||||
|
||||
[assembly: InternalsVisibleTo("Snap.Hutao")]
|
||||
|
||||
namespace Snap.Hutao.Win32;
|
||||
|
||||
/// <summary>
|
||||
/// 结构体封送
|
||||
/// </summary>
|
||||
internal static class StructMarshal
|
||||
{
|
||||
/// <summary>
|
||||
/// 构造一个新的 <see cref="Windows.Win32.UI.WindowsAndMessaging.WINDOWPLACEMENT"/>
|
||||
/// </summary>
|
||||
/// <returns>新的实例</returns>
|
||||
public static unsafe WINDOWPLACEMENT WINDOWPLACEMENT()
|
||||
{
|
||||
return new() { length = unchecked((uint)sizeof(WINDOWPLACEMENT)) };
|
||||
}
|
||||
}
|
||||
@@ -13,8 +13,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Snap.Hutao.Test", "Snap.Hutao.Test\Snap.Hutao.Test.csproj", "{D691BA9F-904C-4229-87A5-E14F2EFF2F64}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Snap.Hutao.Win32", "Snap.Hutao.Win32\Snap.Hutao.Win32.csproj", "{0F7ABEB2-5107-4037-B9DC-84D288FB0801}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
@@ -67,32 +65,16 @@ Global
|
||||
{D691BA9F-904C-4229-87A5-E14F2EFF2F64}.Release|x64.Build.0 = Release|Any CPU
|
||||
{D691BA9F-904C-4229-87A5-E14F2EFF2F64}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{D691BA9F-904C-4229-87A5-E14F2EFF2F64}.Release|x86.Build.0 = Release|Any CPU
|
||||
{0F7ABEB2-5107-4037-B9DC-84D288FB0801}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{0F7ABEB2-5107-4037-B9DC-84D288FB0801}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{0F7ABEB2-5107-4037-B9DC-84D288FB0801}.Debug|arm64.ActiveCfg = Debug|Any CPU
|
||||
{0F7ABEB2-5107-4037-B9DC-84D288FB0801}.Debug|arm64.Build.0 = Debug|Any CPU
|
||||
{0F7ABEB2-5107-4037-B9DC-84D288FB0801}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{0F7ABEB2-5107-4037-B9DC-84D288FB0801}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{0F7ABEB2-5107-4037-B9DC-84D288FB0801}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{0F7ABEB2-5107-4037-B9DC-84D288FB0801}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{0F7ABEB2-5107-4037-B9DC-84D288FB0801}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{0F7ABEB2-5107-4037-B9DC-84D288FB0801}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{0F7ABEB2-5107-4037-B9DC-84D288FB0801}.Release|arm64.ActiveCfg = Release|Any CPU
|
||||
{0F7ABEB2-5107-4037-B9DC-84D288FB0801}.Release|arm64.Build.0 = Release|Any CPU
|
||||
{0F7ABEB2-5107-4037-B9DC-84D288FB0801}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{0F7ABEB2-5107-4037-B9DC-84D288FB0801}.Release|x64.Build.0 = Release|Any CPU
|
||||
{0F7ABEB2-5107-4037-B9DC-84D288FB0801}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{0F7ABEB2-5107-4037-B9DC-84D288FB0801}.Release|x86.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
RESX_Rules = {"EnabledRules":["StringFormat","WhiteSpaceLead","WhiteSpaceTail","PunctuationLead"]}
|
||||
RESX_ShowErrorsInErrorList = False
|
||||
RESX_SortFileContentOnSave = True
|
||||
SolutionGuid = {E4449B1C-0E6A-4D19-955E-1CA491656ABA}
|
||||
RESX_NeutralResourcesLanguage = zh-CN
|
||||
RESX_AutoApplyExistingTranslations = False
|
||||
RESX_NeutralResourcesLanguage = zh-CN
|
||||
SolutionGuid = {E4449B1C-0E6A-4D19-955E-1CA491656ABA}
|
||||
RESX_SortFileContentOnSave = True
|
||||
RESX_ShowErrorsInErrorList = False
|
||||
RESX_Rules = {"EnabledRules":["StringFormat","WhiteSpaceLead","WhiteSpaceTail","PunctuationLead"]}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
|
||||
@@ -32,6 +32,7 @@
|
||||
x:Key="LargeGridViewItemStyle"
|
||||
BasedOn="{StaticResource DefaultGridViewItemStyle}"
|
||||
TargetType="GridViewItem">
|
||||
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
|
||||
<Setter Property="Margin" Value="0,0,12,12"/>
|
||||
</Style>
|
||||
<Style
|
||||
|
||||
@@ -6,6 +6,7 @@ using Microsoft.Windows.AppLifecycle;
|
||||
using Snap.Hutao.Core;
|
||||
using Snap.Hutao.Core.ExceptionService;
|
||||
using Snap.Hutao.Core.LifeCycle;
|
||||
using Snap.Hutao.Core.LifeCycle.InterProcess;
|
||||
using Snap.Hutao.Core.Shell;
|
||||
using System.Diagnostics;
|
||||
|
||||
@@ -36,8 +37,6 @@ public sealed partial class App : Application
|
||||
----------------------------------------------------------------
|
||||
""";
|
||||
|
||||
private const string AppInstanceKey = "main";
|
||||
|
||||
private readonly IServiceProvider serviceProvider;
|
||||
private readonly IActivation activation;
|
||||
private readonly ILogger<App> logger;
|
||||
@@ -50,7 +49,6 @@ public sealed partial class App : Application
|
||||
{
|
||||
// Load app resource
|
||||
InitializeComponent();
|
||||
|
||||
activation = serviceProvider.GetRequiredService<IActivation>();
|
||||
logger = serviceProvider.GetRequiredService<ILogger<App>>();
|
||||
serviceProvider.GetRequiredService<ExceptionRecorder>().Record(this);
|
||||
@@ -64,25 +62,21 @@ public sealed partial class App : Application
|
||||
try
|
||||
{
|
||||
AppActivationArguments activatedEventArgs = AppInstance.GetCurrent().GetActivatedEventArgs();
|
||||
AppInstance firstInstance = AppInstance.FindOrRegisterForKey(AppInstanceKey);
|
||||
|
||||
if (firstInstance.IsCurrent)
|
||||
if (serviceProvider.GetRequiredService<PrivateNamedPipeClient>().TryRedirectActivationTo(activatedEventArgs))
|
||||
{
|
||||
logger.LogInformation(ConsoleBanner);
|
||||
LogDiagnosticInformation();
|
||||
|
||||
// manually invoke
|
||||
activation.NonRedirectToActivate(firstInstance, activatedEventArgs);
|
||||
activation.InitializeWith(firstInstance);
|
||||
|
||||
serviceProvider.GetRequiredService<IJumpListInterop>().ConfigureAsync().SafeForget();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Redirect the activation (and args) to the "main" instance, and exit.
|
||||
firstInstance.RedirectActivationTo(activatedEventArgs);
|
||||
Process.GetCurrentProcess().Kill();
|
||||
Exit();
|
||||
return;
|
||||
}
|
||||
|
||||
logger.LogInformation(ConsoleBanner);
|
||||
LogDiagnosticInformation();
|
||||
|
||||
// manually invoke
|
||||
activation.Activate(HutaoActivationArguments.FromAppActivationArguments(activatedEventArgs));
|
||||
activation.Initialize();
|
||||
|
||||
serviceProvider.GetRequiredService<IJumpListInterop>().ConfigureAsync().SafeForget();
|
||||
}
|
||||
catch
|
||||
{
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using CommunityToolkit.WinUI;
|
||||
using Microsoft.UI.Xaml;
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
|
||||
namespace Snap.Hutao.Control.Helper;
|
||||
|
||||
[SuppressMessage("", "SH001")]
|
||||
[DependencyProperty("PaneCornerRadius", typeof(CornerRadius), default, nameof(OnPaneCornerRadiusChanged), IsAttached = true, AttachedType = typeof(NavigationView))]
|
||||
public sealed partial class NavigationViewHelper
|
||||
{
|
||||
private static void OnPaneCornerRadiusChanged(DependencyObject dp, DependencyPropertyChangedEventArgs args)
|
||||
{
|
||||
NavigationView navigationView = (NavigationView)dp;
|
||||
CornerRadius newValue = (CornerRadius)args.NewValue;
|
||||
|
||||
if (navigationView.IsLoaded)
|
||||
{
|
||||
SetNavigationViewPaneCornerRadius(navigationView, newValue);
|
||||
return;
|
||||
}
|
||||
|
||||
navigationView.Loaded += (s, e) =>
|
||||
{
|
||||
NavigationView loadedNavigationView = (NavigationView)s;
|
||||
SetNavigationViewPaneCornerRadius(loadedNavigationView, newValue);
|
||||
};
|
||||
}
|
||||
|
||||
private static void SetNavigationViewPaneCornerRadius(NavigationView navigationView, CornerRadius value)
|
||||
{
|
||||
if (navigationView.FindDescendant("RootSplitView") is SplitView splitView)
|
||||
{
|
||||
splitView.CornerRadius = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -7,7 +7,6 @@ using Microsoft.UI.Xaml.Controls;
|
||||
namespace Snap.Hutao.Control.Helper;
|
||||
|
||||
[SuppressMessage("", "SH001")]
|
||||
[DependencyProperty("LeftPanelMaxWidth", typeof(double), IsAttached = true, AttachedType = typeof(ScrollViewer))]
|
||||
[DependencyProperty("RightPanel", typeof(UIElement), IsAttached = true, AttachedType = typeof(ScrollViewer))]
|
||||
public sealed partial class ScrollViewerHelper
|
||||
{
|
||||
|
||||
@@ -20,4 +20,4 @@ public sealed partial class SettingsExpanderHelper
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -49,7 +49,8 @@ internal struct Rgba32
|
||||
/// <param name="code">RGBA 代码</param>
|
||||
public unsafe Rgba32(uint code)
|
||||
{
|
||||
// RRGGBBAA -> AABBGGRR
|
||||
// uint layout: 0xRRGGBBAA -> AABBGGRR
|
||||
// AABBGGRR -> RRGGBBAA
|
||||
fixed (Rgba32* pSelf = &this)
|
||||
{
|
||||
*(uint*)pSelf = BinaryPrimitives.ReverseEndianness(code);
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Snap.Hutao.Win32.System.WinRT;
|
||||
using Windows.Foundation;
|
||||
using Windows.Graphics.Imaging;
|
||||
using Windows.Win32;
|
||||
using Windows.Win32.System.WinRT;
|
||||
using WinRT;
|
||||
|
||||
namespace Snap.Hutao.Control.Media;
|
||||
|
||||
21
src/Snap.Hutao/Snap.Hutao/Control/Panel/UniformPanel.cs
Normal file
21
src/Snap.Hutao/Snap.Hutao/Control/Panel/UniformPanel.cs
Normal file
@@ -0,0 +1,21 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using CommunityToolkit.WinUI.Controls;
|
||||
|
||||
namespace Snap.Hutao.Control.Panel;
|
||||
|
||||
[DependencyProperty("MinItemWidth", typeof(double))]
|
||||
internal sealed partial class UniformPanel : UniformGrid
|
||||
{
|
||||
public UniformPanel()
|
||||
{
|
||||
Columns = 1;
|
||||
SizeChanged += OnSizeChanged;
|
||||
}
|
||||
|
||||
private void OnSizeChanged(object sender, Microsoft.UI.Xaml.SizeChangedEventArgs e)
|
||||
{
|
||||
Columns = (int)((e.NewSize.Width + ColumnSpacing) / (MinItemWidth + ColumnSpacing));
|
||||
}
|
||||
}
|
||||
@@ -8,6 +8,7 @@ using Microsoft.UI.Xaml.Media;
|
||||
using Snap.Hutao.Control.Extension;
|
||||
using Snap.Hutao.Control.Media;
|
||||
using Snap.Hutao.Control.Theme;
|
||||
using Snap.Hutao.Metadata;
|
||||
using Windows.Foundation;
|
||||
using Windows.UI;
|
||||
|
||||
@@ -23,8 +24,11 @@ namespace Snap.Hutao.Control.Text;
|
||||
[DependencyProperty("TextStyle", typeof(Style), default(Style), nameof(OnTextStyleChanged))]
|
||||
internal sealed partial class DescriptionTextBlock : ContentControl
|
||||
{
|
||||
private static readonly int ColorTagFullLength = "<color=#FFFFFFFF></color>".Length;
|
||||
private static readonly int ColorTagLeftLength = "<color=#FFFFFFFF>".Length;
|
||||
private static readonly int RgbaColorTagFullLength = "<color=#FFFFFFFF></color>".Length;
|
||||
private static readonly int RgbaColorTagLeftLength = "<color=#FFFFFFFF>".Length;
|
||||
|
||||
private static readonly int RgbColorTagFullLength = "<color=#FFFFFF></color>".Length;
|
||||
private static readonly int RgbColorTagLeftLength = "<color=#FFFFFF>".Length;
|
||||
|
||||
private static readonly int ItalicTagFullLength = "<i></i>".Length;
|
||||
private static readonly int ItalicTagLeftLength = "<i>".Length;
|
||||
@@ -51,9 +55,16 @@ internal sealed partial class DescriptionTextBlock : ContentControl
|
||||
private static void OnDescriptionChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
|
||||
{
|
||||
TextBlock textBlock = (TextBlock)((DescriptionTextBlock)d).Content;
|
||||
ReadOnlySpan<char> description = (string)e.NewValue;
|
||||
ReadOnlySpan<char> description = SpecialNameHandler.Handle((string)e.NewValue);
|
||||
|
||||
UpdateDescription(textBlock, description);
|
||||
try
|
||||
{
|
||||
UpdateDescription(textBlock, description);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_ = ex;
|
||||
}
|
||||
}
|
||||
|
||||
private static void OnTextStyleChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
|
||||
@@ -81,13 +92,32 @@ internal sealed partial class DescriptionTextBlock : ContentControl
|
||||
// color tag
|
||||
else if (description[i..].StartsWith("<c"))
|
||||
{
|
||||
AppendText(textBlock, description[last..i]);
|
||||
Rgba32 color = new(description.Slice(i + 8, 8).ToString());
|
||||
int length = description[(i + ColorTagLeftLength)..].IndexOf('<');
|
||||
AppendColorText(textBlock, description.Slice(i + ColorTagLeftLength, length), color);
|
||||
switch (description[i..].IndexOf('>'))
|
||||
{
|
||||
case 16: // RgbaColorTag
|
||||
{
|
||||
AppendText(textBlock, description[last..i]);
|
||||
Rgba32 color = new(description.Slice(i + 8, 8).ToString());
|
||||
int length = description[(i + RgbaColorTagLeftLength)..].IndexOf('<');
|
||||
AppendColorText(textBlock, description.Slice(i + RgbaColorTagLeftLength, length), color);
|
||||
|
||||
i += length + ColorTagFullLength;
|
||||
last = i;
|
||||
i += length + RgbaColorTagFullLength;
|
||||
last = i;
|
||||
break;
|
||||
}
|
||||
|
||||
case 14: // RgbColorTag
|
||||
{
|
||||
AppendText(textBlock, description[last..i]);
|
||||
Rgba32 color = new(description.Slice(i + 8, 6).ToString());
|
||||
int length = description[(i + RgbColorTagLeftLength)..].IndexOf('<');
|
||||
AppendColorText(textBlock, description.Slice(i + RgbColorTagLeftLength, length), color);
|
||||
|
||||
i += length + RgbColorTagFullLength;
|
||||
last = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// italic
|
||||
|
||||
@@ -24,10 +24,43 @@
|
||||
<Setter Property="BorderThickness" Value="1"/>
|
||||
<Setter Property="CornerRadius" Value="{ThemeResource ControlCornerRadius}"/>
|
||||
</Style>
|
||||
|
||||
<Style
|
||||
x:Key="AcrylicBaseHighBorderCardStyle"
|
||||
BasedOn="{StaticResource BorderCardStyle}"
|
||||
TargetType="Border">
|
||||
<Setter Property="BorderThickness" Value="0"/>
|
||||
<Setter Property="Background" Value="{ThemeResource SystemControlBaseHighAcrylicElementBrush}"/>
|
||||
</Style>
|
||||
|
||||
<Style
|
||||
x:Key="AcrylicBorderCardStyle"
|
||||
BasedOn="{StaticResource BorderCardStyle}"
|
||||
TargetType="Border">
|
||||
<Setter Property="BorderThickness" Value="0"/>
|
||||
<Setter Property="Background" Value="{ThemeResource SystemControlAcrylicElementMediumHighBrush}"/>
|
||||
</Style>
|
||||
|
||||
<Style x:Key="GridCardStyle" TargetType="Grid">
|
||||
<Setter Property="Background" Value="{ThemeResource CardBackgroundFillColorDefaultBrush}"/>
|
||||
<Setter Property="BorderBrush" Value="{ThemeResource CardStrokeColorDefaultBrush}"/>
|
||||
<Setter Property="BorderThickness" Value="1"/>
|
||||
<Setter Property="CornerRadius" Value="{ThemeResource ControlCornerRadius}"/>
|
||||
</Style>
|
||||
|
||||
<Style
|
||||
x:Key="AcrylicGridCardStyle"
|
||||
BasedOn="{StaticResource GridCardStyle}"
|
||||
TargetType="Grid">
|
||||
<Setter Property="BorderThickness" Value="0"/>
|
||||
<Setter Property="Background" Value="{ThemeResource SystemControlAcrylicElementMediumHighBrush}"/>
|
||||
</Style>
|
||||
|
||||
<Style
|
||||
x:Key="AcrylicBaseHighGridCardStyle"
|
||||
BasedOn="{StaticResource GridCardStyle}"
|
||||
TargetType="Grid">
|
||||
<Setter Property="BorderThickness" Value="0"/>
|
||||
<Setter Property="Background" Value="{ThemeResource SystemControlBaseHighAcrylicElementBrush}"/>
|
||||
</Style>
|
||||
</ResourceDictionary>
|
||||
|
||||
@@ -2,6 +2,53 @@
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:shch="using:Snap.Hutao.Control.Helper">
|
||||
<ResourceDictionary.ThemeDictionaries>
|
||||
<ResourceDictionary x:Key="Light">
|
||||
<AcrylicBrush
|
||||
x:Key="InfoBarErrorSeverityBackgroundBrush"
|
||||
FallbackColor="#FDE7E9"
|
||||
TintColor="#FDE7E9"
|
||||
TintOpacity="0.6"/>
|
||||
<AcrylicBrush
|
||||
x:Key="InfoBarWarningSeverityBackgroundBrush"
|
||||
FallbackColor="#FFF4CE"
|
||||
TintColor="#FFF4CE"
|
||||
TintOpacity="0.6"/>
|
||||
<AcrylicBrush
|
||||
x:Key="InfoBarSuccessSeverityBackgroundBrush"
|
||||
FallbackColor="#DFF6DD"
|
||||
TintColor="#DFF6DD"
|
||||
TintOpacity="0.6"/>
|
||||
<AcrylicBrush
|
||||
x:Key="InfoBarInformationalSeverityBackgroundBrush"
|
||||
FallbackColor="#80F6F6F6"
|
||||
TintColor="#80F6F6F6"
|
||||
TintOpacity="0.6"/>
|
||||
</ResourceDictionary>
|
||||
<ResourceDictionary x:Key="Dark">
|
||||
<AcrylicBrush
|
||||
x:Key="InfoBarErrorSeverityBackgroundBrush"
|
||||
FallbackColor="#442726"
|
||||
TintColor="#442726"
|
||||
TintOpacity="0.6"/>
|
||||
<AcrylicBrush
|
||||
x:Key="InfoBarWarningSeverityBackgroundBrush"
|
||||
FallbackColor="#433519"
|
||||
TintColor="#433519"
|
||||
TintOpacity="0.6"/>
|
||||
<AcrylicBrush
|
||||
x:Key="InfoBarSuccessSeverityBackgroundBrush"
|
||||
FallbackColor="#393D1B"
|
||||
TintColor="#393D1B"
|
||||
TintOpacity="0.6"/>
|
||||
<AcrylicBrush
|
||||
x:Key="InfoBarInformationalSeverityBackgroundBrush"
|
||||
FallbackColor="#34424d"
|
||||
TintColor="#34424d"
|
||||
TintOpacity="0.6"/>
|
||||
</ResourceDictionary>
|
||||
</ResourceDictionary.ThemeDictionaries>
|
||||
|
||||
<Thickness x:Key="InfoBarIconMargin">19,16,19,16</Thickness>
|
||||
<Thickness x:Key="InfoBarContentRootPadding">0,0,0,0</Thickness>
|
||||
<x:Double x:Key="InfoBarIconFontSize">20</x:Double>
|
||||
|
||||
@@ -9,6 +9,8 @@
|
||||
<x:Double x:Key="ContentDialogMinHeight">64</x:Double>
|
||||
<x:Double x:Key="LargeAppBarButtonWidth">100</x:Double>
|
||||
|
||||
<x:Double x:Key="AppBarThemeCompactActualHeight">50</x:Double>
|
||||
|
||||
<!-- ProgressBar -->
|
||||
<x:Double x:Key="LargeBackgroundProgressBarOpacity">0.2</x:Double>
|
||||
</ResourceDictionary>
|
||||
|
||||
@@ -1,5 +1,385 @@
|
||||
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
|
||||
<ResourceDictionary
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:cw="using:CommunityToolkit.WinUI">
|
||||
<x:Double x:Key="PivotHeaderItemFontSize">16</x:Double>
|
||||
<Thickness x:Key="PivotHeaderItemMargin">16,0,0,0</Thickness>
|
||||
<Thickness x:Key="PivotItemMargin">0</Thickness>
|
||||
|
||||
<Style x:Key="CardPivotStyle" TargetType="Pivot">
|
||||
<Setter Property="Margin" Value="0"/>
|
||||
<Setter Property="Padding" Value="0"/>
|
||||
<Setter Property="Background" Value="{ThemeResource PivotBackground}"/>
|
||||
<Setter Property="IsTabStop" Value="False"/>
|
||||
<Setter Property="ItemsPanel">
|
||||
<Setter.Value>
|
||||
<ItemsPanelTemplate>
|
||||
<Grid/>
|
||||
</ItemsPanelTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="Pivot">
|
||||
<Grid
|
||||
x:Name="RootElement"
|
||||
HorizontalAlignment="{TemplateBinding HorizontalAlignment}"
|
||||
VerticalAlignment="{TemplateBinding VerticalAlignment}"
|
||||
Background="{TemplateBinding Background}">
|
||||
<Grid.Resources>
|
||||
<Style x:Key="BaseContentControlStyle" TargetType="ContentControl">
|
||||
<Setter Property="FontFamily" Value="XamlAutoFontFamily"/>
|
||||
<Setter Property="FontWeight" Value="SemiBold"/>
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="ContentControl">
|
||||
<ContentPresenter
|
||||
Margin="{TemplateBinding Padding}"
|
||||
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
|
||||
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
|
||||
Content="{TemplateBinding Content}"
|
||||
ContentTemplate="{TemplateBinding ContentTemplate}"
|
||||
ContentTransitions="{TemplateBinding ContentTransitions}"
|
||||
OpticalMarginAlignment="TrimSideBearings"/>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
<Style
|
||||
x:Key="TitleContentControlStyle"
|
||||
BasedOn="{StaticResource BaseContentControlStyle}"
|
||||
TargetType="ContentControl">
|
||||
<Setter Property="FontFamily" Value="{ThemeResource PivotTitleFontFamily}"/>
|
||||
<Setter Property="FontWeight" Value="{ThemeResource PivotTitleThemeFontWeight}"/>
|
||||
<Setter Property="FontSize" Value="{ThemeResource PivotTitleFontSize}"/>
|
||||
</Style>
|
||||
</Grid.Resources>
|
||||
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="*"/>
|
||||
</Grid.RowDefinitions>
|
||||
<ContentControl
|
||||
x:Name="TitleContentControl"
|
||||
Grid.Row="0"
|
||||
Margin="{StaticResource PivotPortraitThemePadding}"
|
||||
Content="{TemplateBinding Title}"
|
||||
ContentTemplate="{TemplateBinding TitleTemplate}"
|
||||
IsTabStop="False"
|
||||
Style="{StaticResource TitleContentControlStyle}"
|
||||
Visibility="Collapsed"/>
|
||||
|
||||
<Grid Grid.Row="1">
|
||||
<Grid.Resources>
|
||||
<ControlTemplate x:Key="NextTemplate" TargetType="Button">
|
||||
<Border
|
||||
x:Name="Root"
|
||||
Background="{ThemeResource PivotNextButtonBackground}"
|
||||
BorderBrush="{ThemeResource PivotNextButtonBorderBrush}"
|
||||
BorderThickness="{ThemeResource PivotNavButtonBorderThemeThickness}">
|
||||
<FontIcon
|
||||
x:Name="Arrow"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
FontFamily="{ThemeResource SymbolThemeFontFamily}"
|
||||
FontSize="12"
|
||||
Foreground="{ThemeResource PivotNextButtonForeground}"
|
||||
Glyph=""
|
||||
MirroredWhenRightToLeft="True"
|
||||
UseLayoutRounding="False"/>
|
||||
<VisualStateManager.VisualStateGroups>
|
||||
<VisualStateGroup x:Name="CommonStates">
|
||||
<VisualState x:Name="Normal"/>
|
||||
<VisualState x:Name="PointerOver">
|
||||
<Storyboard>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="Root" Storyboard.TargetProperty="Background">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource PivotNextButtonBackgroundPointerOver}"/>
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="Root" Storyboard.TargetProperty="BorderBrush">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource PivotNextButtonBorderBrushPointerOver}"/>
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="Arrow" Storyboard.TargetProperty="Foreground">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource PivotNextButtonForegroundPointerOver}"/>
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
</Storyboard>
|
||||
</VisualState>
|
||||
<VisualState x:Name="Pressed">
|
||||
<Storyboard>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="Root" Storyboard.TargetProperty="Background">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource PivotNextButtonBackgroundPressed}"/>
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="Root" Storyboard.TargetProperty="BorderBrush">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource PivotNextButtonBorderBrushPressed}"/>
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="Arrow" Storyboard.TargetProperty="Foreground">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource PivotNextButtonForegroundPressed}"/>
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
</Storyboard>
|
||||
</VisualState>
|
||||
</VisualStateGroup>
|
||||
</VisualStateManager.VisualStateGroups>
|
||||
</Border>
|
||||
</ControlTemplate>
|
||||
<ControlTemplate x:Key="PreviousTemplate" TargetType="Button">
|
||||
<Border
|
||||
x:Name="Root"
|
||||
Background="{ThemeResource PivotPreviousButtonBackground}"
|
||||
BorderBrush="{ThemeResource PivotPreviousButtonBorderBrush}"
|
||||
BorderThickness="{ThemeResource PivotNavButtonBorderThemeThickness}">
|
||||
<FontIcon
|
||||
x:Name="Arrow"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
FontFamily="{ThemeResource SymbolThemeFontFamily}"
|
||||
FontSize="12"
|
||||
Foreground="{ThemeResource PivotPreviousButtonForeground}"
|
||||
Glyph=""
|
||||
MirroredWhenRightToLeft="True"
|
||||
UseLayoutRounding="False"/>
|
||||
<VisualStateManager.VisualStateGroups>
|
||||
<VisualStateGroup x:Name="CommonStates">
|
||||
<VisualState x:Name="Normal"/>
|
||||
<VisualState x:Name="PointerOver">
|
||||
<Storyboard>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="Root" Storyboard.TargetProperty="Background">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource PivotPreviousButtonBackgroundPointerOver}"/>
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="Root" Storyboard.TargetProperty="BorderBrush">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource PivotPreviousButtonBorderBrushPointerOver}"/>
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="Arrow" Storyboard.TargetProperty="Foreground">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource PivotPreviousButtonForegroundPointerOver}"/>
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
</Storyboard>
|
||||
</VisualState>
|
||||
<VisualState x:Name="Pressed">
|
||||
<Storyboard>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="Root" Storyboard.TargetProperty="Background">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource PivotPreviousButtonBackgroundPressed}"/>
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="Root" Storyboard.TargetProperty="BorderBrush">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource PivotPreviousButtonBorderBrushPressed}"/>
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="Arrow" Storyboard.TargetProperty="Foreground">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource PivotPreviousButtonForegroundPressed}"/>
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
</Storyboard>
|
||||
</VisualState>
|
||||
</VisualStateGroup>
|
||||
</VisualStateManager.VisualStateGroups>
|
||||
</Border>
|
||||
</ControlTemplate>
|
||||
</Grid.Resources>
|
||||
<ScrollViewer
|
||||
x:Name="ScrollViewer"
|
||||
Margin="{TemplateBinding Padding}"
|
||||
VerticalContentAlignment="Stretch"
|
||||
BringIntoViewOnFocusChange="False"
|
||||
HorizontalScrollBarVisibility="Hidden"
|
||||
HorizontalSnapPointsAlignment="Center"
|
||||
HorizontalSnapPointsType="MandatorySingle"
|
||||
Template="{StaticResource ScrollViewerScrollBarlessTemplate}"
|
||||
VerticalScrollBarVisibility="Disabled"
|
||||
VerticalScrollMode="Disabled"
|
||||
VerticalSnapPointsType="None"
|
||||
ZoomMode="Disabled">
|
||||
<PivotPanel x:Name="Panel" VerticalAlignment="Stretch">
|
||||
<Grid x:Name="PivotLayoutElement">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="*"/>
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.RenderTransform>
|
||||
<CompositeTransform x:Name="PivotLayoutElementTranslateTransform"/>
|
||||
</Grid.RenderTransform>
|
||||
|
||||
|
||||
|
||||
<ItemsPresenter x:Name="PivotItemPresenter" Grid.Row="1">
|
||||
<ItemsPresenter.RenderTransform>
|
||||
<TransformGroup>
|
||||
<TranslateTransform x:Name="ItemsPresenterTranslateTransform"/>
|
||||
<CompositeTransform x:Name="ItemsPresenterCompositeTransform"/>
|
||||
</TransformGroup>
|
||||
</ItemsPresenter.RenderTransform>
|
||||
</ItemsPresenter>
|
||||
|
||||
<Border
|
||||
Grid.Row="0"
|
||||
Margin="16,16,16,0"
|
||||
cw:Effects.Shadow="{ThemeResource CompatCardShadow}">
|
||||
<Grid Style="{ThemeResource AcrylicBaseHighGridCardStyle}">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<ContentPresenter
|
||||
x:Name="LeftHeaderPresenter"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Stretch"
|
||||
Content="{TemplateBinding LeftHeader}"
|
||||
ContentTemplate="{TemplateBinding LeftHeaderTemplate}"/>
|
||||
<ContentControl
|
||||
x:Name="HeaderClipper"
|
||||
Grid.Column="1"
|
||||
HorizontalContentAlignment="Stretch"
|
||||
UseSystemFocusVisuals="{StaticResource UseSystemFocusVisuals}">
|
||||
<ContentControl.Clip>
|
||||
<RectangleGeometry x:Name="HeaderClipperGeometry"/>
|
||||
</ContentControl.Clip>
|
||||
<Grid>
|
||||
<Grid.RenderTransform>
|
||||
<CompositeTransform x:Name="HeaderOffsetTranslateTransform"/>
|
||||
</Grid.RenderTransform>
|
||||
<PivotHeaderPanel x:Name="StaticHeader" Visibility="Collapsed">
|
||||
<PivotHeaderPanel.RenderTransform>
|
||||
<CompositeTransform x:Name="StaticHeaderTranslateTransform"/>
|
||||
</PivotHeaderPanel.RenderTransform>
|
||||
</PivotHeaderPanel>
|
||||
<PivotHeaderPanel x:Name="Header">
|
||||
<PivotHeaderPanel.RenderTransform>
|
||||
<CompositeTransform x:Name="HeaderTranslateTransform"/>
|
||||
</PivotHeaderPanel.RenderTransform>
|
||||
</PivotHeaderPanel>
|
||||
<Rectangle
|
||||
x:Name="FocusFollower"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Stretch"
|
||||
Control.IsTemplateFocusTarget="True"
|
||||
Fill="Transparent"
|
||||
IsHitTestVisible="False"/>
|
||||
</Grid>
|
||||
</ContentControl>
|
||||
<Button
|
||||
x:Name="PreviousButton"
|
||||
Grid.Column="1"
|
||||
Width="20"
|
||||
Height="36"
|
||||
Margin="{ThemeResource PivotNavButtonMargin}"
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Top"
|
||||
Background="Transparent"
|
||||
IsEnabled="False"
|
||||
IsTabStop="False"
|
||||
Opacity="0"
|
||||
Template="{StaticResource PreviousTemplate}"
|
||||
UseSystemFocusVisuals="False"/>
|
||||
<Button
|
||||
x:Name="NextButton"
|
||||
Grid.Column="1"
|
||||
Width="20"
|
||||
Height="36"
|
||||
Margin="{ThemeResource PivotNavButtonMargin}"
|
||||
HorizontalAlignment="Right"
|
||||
VerticalAlignment="Top"
|
||||
Background="Transparent"
|
||||
IsEnabled="False"
|
||||
IsTabStop="False"
|
||||
Opacity="0"
|
||||
Template="{StaticResource NextTemplate}"
|
||||
UseSystemFocusVisuals="False"/>
|
||||
<ContentPresenter
|
||||
x:Name="RightHeaderPresenter"
|
||||
Grid.Column="2"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Stretch"
|
||||
Content="{TemplateBinding RightHeader}"
|
||||
ContentTemplate="{TemplateBinding RightHeaderTemplate}"/>
|
||||
</Grid>
|
||||
</Border>
|
||||
|
||||
</Grid>
|
||||
</PivotPanel>
|
||||
</ScrollViewer>
|
||||
|
||||
</Grid>
|
||||
|
||||
<VisualStateManager.VisualStateGroups>
|
||||
<VisualStateGroup x:Name="Orientation">
|
||||
<VisualState x:Name="Portrait">
|
||||
|
||||
<Storyboard>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="TitleContentControl" Storyboard.TargetProperty="Margin">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource PivotPortraitThemePadding}"/>
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
</Storyboard>
|
||||
</VisualState>
|
||||
<VisualState x:Name="Landscape">
|
||||
|
||||
<Storyboard>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="TitleContentControl" Storyboard.TargetProperty="Margin">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource PivotLandscapeThemePadding}"/>
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
</Storyboard>
|
||||
</VisualState>
|
||||
|
||||
</VisualStateGroup>
|
||||
<VisualStateGroup x:Name="NavigationButtonsVisibility">
|
||||
<VisualState x:Name="NavigationButtonsHidden"/>
|
||||
<VisualState x:Name="NavigationButtonsVisible">
|
||||
|
||||
<Storyboard>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="NextButton" Storyboard.TargetProperty="Opacity">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="1"/>
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="NextButton" Storyboard.TargetProperty="IsEnabled">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="True"/>
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="PreviousButton" Storyboard.TargetProperty="Opacity">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="1"/>
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="PreviousButton" Storyboard.TargetProperty="IsEnabled">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="True"/>
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
</Storyboard>
|
||||
</VisualState>
|
||||
<VisualState x:Name="PreviousButtonVisible">
|
||||
|
||||
<Storyboard>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="PreviousButton" Storyboard.TargetProperty="Opacity">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="1"/>
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="PreviousButton" Storyboard.TargetProperty="IsEnabled">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="True"/>
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
</Storyboard>
|
||||
</VisualState>
|
||||
<VisualState x:Name="NextButtonVisible">
|
||||
|
||||
<Storyboard>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="NextButton" Storyboard.TargetProperty="Opacity">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="1"/>
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="NextButton" Storyboard.TargetProperty="IsEnabled">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="True"/>
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
</Storyboard>
|
||||
</VisualState>
|
||||
|
||||
</VisualStateGroup>
|
||||
<VisualStateGroup x:Name="HeaderStates">
|
||||
<VisualState x:Name="HeaderDynamic"/>
|
||||
<VisualState x:Name="HeaderStatic">
|
||||
|
||||
<Storyboard>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="Header" Storyboard.TargetProperty="Visibility">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="Collapsed"/>
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="StaticHeader" Storyboard.TargetProperty="Visibility">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="Visible"/>
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
</Storyboard>
|
||||
</VisualState>
|
||||
|
||||
</VisualStateGroup>
|
||||
|
||||
</VisualStateManager.VisualStateGroups>
|
||||
</Grid>
|
||||
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
</ResourceDictionary>
|
||||
@@ -42,7 +42,7 @@
|
||||
Grid.ColumnSpan="2"
|
||||
Margin="{TemplateBinding Padding}">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" MaxWidth="{Binding Path=(shch:ScrollViewerHelper.LeftPanelMaxWidth), RelativeSource={RelativeSource Mode=TemplatedParent}}"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<ScrollContentPresenter x:Name="ScrollContentPresenter" ContentTemplate="{TemplateBinding ContentTemplate}"/>
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
|
||||
<ResourceDictionary
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:cwc="using:CommunityToolkit.WinUI.Controls">
|
||||
<!-- Settings -->
|
||||
<x:Double x:Key="SettingsCardSpacing">3</x:Double>
|
||||
<x:Double x:Key="SettingsCardMinHeight">0</x:Double>
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
<x:String x:Key="DocumentLink_Home">https://hut.ao</x:String>
|
||||
<x:String x:Key="DocumentLink_MhyAccountSwitch">https://hut.ao/features/mhy-account-switch.html</x:String>
|
||||
<x:String x:Key="DocumentLink_Translate">https://translate.hut.ao</x:String>
|
||||
<x:String x:Key="DocumentLink_Loopback">https://hut.ao/zh/advanced/FAQ.html#%E5%A6%82%E4%BD%95%E9%80%9A%E8%BF%87%E7%BD%91%E7%BB%9C%E4%BB%A3%E7%90%86%E4%BD%BF%E7%94%A8%E8%83%A1%E6%A1%83%E5%B7%A5%E5%85%B7%E7%AE%B1</x:String>
|
||||
|
||||
<!-- Other -->
|
||||
<x:String x:Key="HolographicHat_GetToken_Release">https://github.com/HolographicHat/GetToken/releases/latest</x:String>
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Snap.Hutao.Model.Entity.Database;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Collections.Specialized;
|
||||
using System.Runtime.InteropServices;
|
||||
@@ -11,13 +12,12 @@ namespace Snap.Hutao.Core.Database;
|
||||
internal sealed class ObservableReorderableDbCollection<T> : ObservableCollection<T>
|
||||
where T : class, IReorderable
|
||||
{
|
||||
private readonly DbContext dbContext;
|
||||
private bool previousChangeIsRemoved;
|
||||
private readonly IServiceProvider serviceProvider;
|
||||
|
||||
public ObservableReorderableDbCollection(List<T> items, DbContext dbContext)
|
||||
public ObservableReorderableDbCollection(List<T> items, IServiceProvider serviceProvider)
|
||||
: base(AdjustIndex(items))
|
||||
{
|
||||
this.dbContext = dbContext;
|
||||
this.serviceProvider = serviceProvider;
|
||||
}
|
||||
|
||||
protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
|
||||
@@ -27,16 +27,8 @@ internal sealed class ObservableReorderableDbCollection<T> : ObservableCollectio
|
||||
switch (e.Action)
|
||||
{
|
||||
case NotifyCollectionChangedAction.Remove:
|
||||
previousChangeIsRemoved = true;
|
||||
break;
|
||||
case NotifyCollectionChangedAction.Add:
|
||||
if (!previousChangeIsRemoved)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
OnReorder();
|
||||
previousChangeIsRemoved = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -57,10 +49,15 @@ internal sealed class ObservableReorderableDbCollection<T> : ObservableCollectio
|
||||
{
|
||||
AdjustIndex((List<T>)Items);
|
||||
|
||||
DbSet<T> dbSet = dbContext.Set<T>();
|
||||
foreach (ref readonly T item in CollectionsMarshal.AsSpan((List<T>)Items))
|
||||
using (IServiceScope scope = serviceProvider.CreateScope())
|
||||
{
|
||||
dbSet.UpdateAndSave(item);
|
||||
AppDbContext appDbContext = scope.ServiceProvider.GetRequiredService<AppDbContext>();
|
||||
|
||||
DbSet<T> dbSet = appDbContext.Set<T>();
|
||||
foreach (ref readonly T item in CollectionsMarshal.AsSpan((List<T>)Items))
|
||||
{
|
||||
dbSet.UpdateAndSave(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,8 @@
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using CommunityToolkit.Mvvm.Messaging;
|
||||
using Snap.Hutao.Core.IO.Http.DynamicProxy;
|
||||
using Microsoft.Extensions.Http;
|
||||
using Snap.Hutao.Core.IO.Http.Proxy;
|
||||
using Snap.Hutao.Core.Logging;
|
||||
using Snap.Hutao.Service;
|
||||
using System.Globalization;
|
||||
@@ -33,7 +34,7 @@ internal static class DependencyInjection
|
||||
.AddJsonOptions()
|
||||
.AddDatabase()
|
||||
.AddInjections()
|
||||
.AddHttpClients()
|
||||
.AddAllHttpClients()
|
||||
|
||||
// Discrete services
|
||||
.AddSingleton<IMessenger, WeakReferenceMessenger>()
|
||||
@@ -43,7 +44,6 @@ internal static class DependencyInjection
|
||||
|
||||
serviceProvider.InitializeConsoleWindow();
|
||||
serviceProvider.InitializeCulture();
|
||||
serviceProvider.InitializedDynamicHttpProxy();
|
||||
|
||||
return serviceProvider;
|
||||
}
|
||||
@@ -70,9 +70,4 @@ internal static class DependencyInjection
|
||||
{
|
||||
_ = serviceProvider.GetRequiredService<ConsoleWindowLifeTime>();
|
||||
}
|
||||
|
||||
private static void InitializedDynamicHttpProxy(this IServiceProvider serviceProvider)
|
||||
{
|
||||
HttpClient.DefaultProxy = serviceProvider.GetRequiredService<DynamicHttpProxy>();
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Snap.Hutao.Core.IO.Http.Proxy;
|
||||
using Snap.Hutao.Web.Hoyolab;
|
||||
using System.Net.Http;
|
||||
|
||||
@@ -14,12 +15,26 @@ internal static partial class IocHttpClientConfiguration
|
||||
{
|
||||
private const string ApplicationJson = "application/json";
|
||||
|
||||
/// <summary>
|
||||
/// 添加 <see cref="HttpClient"/>
|
||||
/// 此方法将会自动生成
|
||||
/// </summary>
|
||||
/// <param name="services">集合</param>
|
||||
/// <returns>可继续操作的集合</returns>
|
||||
public static IServiceCollection AddAllHttpClients(this IServiceCollection services)
|
||||
{
|
||||
services
|
||||
.ConfigureHttpClientDefaults(clientBuilder =>
|
||||
{
|
||||
clientBuilder
|
||||
.ConfigurePrimaryHttpMessageHandler(() => new HttpClientHandler())
|
||||
.ConfigurePrimaryHttpMessageHandler((handler, provider) =>
|
||||
{
|
||||
HttpClientHandler clientHandler = (HttpClientHandler)handler;
|
||||
clientHandler.UseProxy = true;
|
||||
clientHandler.Proxy = provider.GetRequiredService<DynamicHttpProxy>();
|
||||
});
|
||||
})
|
||||
.AddHttpClients();
|
||||
|
||||
return services;
|
||||
}
|
||||
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public static partial IServiceCollection AddHttpClients(this IServiceCollection services);
|
||||
|
||||
/// <summary>
|
||||
@@ -89,6 +104,7 @@ internal static partial class IocHttpClientConfiguration
|
||||
/// HoYoLAB web
|
||||
/// </summary>
|
||||
/// <param name="client">配置后的客户端</param>
|
||||
[SuppressMessage("", "IDE0051")]
|
||||
private static void XRpc4Configuration(HttpClient client)
|
||||
{
|
||||
client.Timeout = Timeout.InfiniteTimeSpan;
|
||||
|
||||
@@ -11,10 +11,18 @@ internal sealed class HutaoException : Exception
|
||||
Kind = kind;
|
||||
}
|
||||
|
||||
public HutaoException(string message, Exception? innerException)
|
||||
private HutaoException(string message, Exception? innerException)
|
||||
: base($"{message}\n{innerException?.Message}", innerException)
|
||||
{
|
||||
}
|
||||
|
||||
public HutaoExceptionKind Kind { get; private set; }
|
||||
|
||||
public static void ThrowIf(bool condition, HutaoExceptionKind kind, string message, Exception? innerException = default)
|
||||
{
|
||||
if (condition)
|
||||
{
|
||||
throw new HutaoException(kind, message, innerException);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
using Snap.Hutao.Service.Game;
|
||||
using Snap.Hutao.Service.Game.Package;
|
||||
using System.IO;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace Snap.Hutao.Core.ExceptionService;
|
||||
@@ -35,6 +36,22 @@ internal static class ThrowHelper
|
||||
throw new GameFileOperationException(message, inner);
|
||||
}
|
||||
|
||||
[DoesNotReturn]
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
public static InvalidDataException InvalidData(string message, Exception? inner = default)
|
||||
{
|
||||
throw new InvalidDataException(message, inner);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
public static void InvalidDataIf([DoesNotReturnIf(true)] bool condition, string message, Exception? inner = default)
|
||||
{
|
||||
if (condition)
|
||||
{
|
||||
throw new InvalidDataException(message, inner);
|
||||
}
|
||||
}
|
||||
|
||||
[DoesNotReturn]
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
public static InvalidOperationException InvalidOperation(string message, Exception? inner = default)
|
||||
|
||||
@@ -0,0 +1,83 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using Snap.Hutao.Win32.Foundation;
|
||||
using Snap.Hutao.Win32.NetworkManagement.WindowsFirewall;
|
||||
using Snap.Hutao.Win32.Security;
|
||||
using System.Runtime.InteropServices;
|
||||
using static Snap.Hutao.Win32.AdvApi32;
|
||||
using static Snap.Hutao.Win32.ApiMsWinNetIsolation;
|
||||
using static Snap.Hutao.Win32.Macros;
|
||||
|
||||
namespace Snap.Hutao.Core.IO.Http.Loopback;
|
||||
|
||||
[Injection(InjectAs.Singleton)]
|
||||
internal sealed unsafe class LoopbackManager : ObservableObject
|
||||
{
|
||||
private readonly RuntimeOptions runtimeOptions;
|
||||
private readonly ITaskContext taskContext;
|
||||
|
||||
private readonly string hutaoContainerStringSID = default!;
|
||||
private bool isLoopbackEnabled;
|
||||
|
||||
public LoopbackManager(IServiceProvider serviceProvider)
|
||||
{
|
||||
runtimeOptions = serviceProvider.GetRequiredService<RuntimeOptions>();
|
||||
taskContext = serviceProvider.GetRequiredService<ITaskContext>();
|
||||
|
||||
INET_FIREWALL_APP_CONTAINER* pContainers = default;
|
||||
try
|
||||
{
|
||||
WIN32_ERROR error = NetworkIsolationEnumAppContainers(NETISO_FLAG.NETISO_FLAG_MAX, out uint acCount, out pContainers);
|
||||
Marshal.ThrowExceptionForHR(HRESULT_FROM_WIN32(error));
|
||||
for (uint i = 0; i < acCount; i++)
|
||||
{
|
||||
INET_FIREWALL_APP_CONTAINER* pContainer = pContainers + i;
|
||||
ReadOnlySpan<char> appContainerName = MemoryMarshal.CreateReadOnlySpanFromNullTerminated(pContainer->appContainerName);
|
||||
if (appContainerName.Equals(runtimeOptions.FamilyName, StringComparison.Ordinal))
|
||||
{
|
||||
ConvertSidToStringSidW(pContainer->appContainerSid, out PWSTR stringSid);
|
||||
hutaoContainerStringSID = MemoryMarshal.CreateReadOnlySpanFromNullTerminated(stringSid).ToString();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
// This function returns 1 rather than 0 specfied in the document.
|
||||
_ = NetworkIsolationFreeAppContainers(pContainers);
|
||||
}
|
||||
|
||||
WIN32_ERROR error2 = NetworkIsolationGetAppContainerConfig(out uint accCount, out SID_AND_ATTRIBUTES* pSids);
|
||||
Marshal.ThrowExceptionForHR(HRESULT_FROM_WIN32(error2));
|
||||
for (uint i = 0; i < accCount; i++)
|
||||
{
|
||||
ConvertSidToStringSidW((pSids + i)->Sid, out PWSTR stringSid);
|
||||
ReadOnlySpan<char> stringSidSpan = MemoryMarshal.CreateReadOnlySpanFromNullTerminated(stringSid);
|
||||
if (stringSidSpan.Equals(hutaoContainerStringSID, StringComparison.Ordinal))
|
||||
{
|
||||
IsLoopbackEnabled = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsLoopbackEnabled { get => isLoopbackEnabled; private set => SetProperty(ref isLoopbackEnabled, value); }
|
||||
|
||||
public void EnableLoopback()
|
||||
{
|
||||
NetworkIsolationGetAppContainerConfig(out uint accCount, out SID_AND_ATTRIBUTES* pSids);
|
||||
List<SID_AND_ATTRIBUTES> sids = new((int)(accCount + 1));
|
||||
for (uint i = 0; i < accCount; i++)
|
||||
{
|
||||
sids.Add(*(pSids + i));
|
||||
}
|
||||
|
||||
ConvertStringSidToSidW(hutaoContainerStringSID, out PSID pSid);
|
||||
SID_AND_ATTRIBUTES sidAndAttributes = default;
|
||||
sidAndAttributes.Sid = pSid;
|
||||
sids.Add(sidAndAttributes);
|
||||
IsLoopbackEnabled = NetworkIsolationSetAppContainerConfig(CollectionsMarshal.AsSpan(sids)) is WIN32_ERROR.ERROR_SUCCESS;
|
||||
}
|
||||
}
|
||||
@@ -1,49 +1,46 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using Snap.Hutao.Win32.Registry;
|
||||
using System.Net;
|
||||
using System.Reflection;
|
||||
|
||||
namespace Snap.Hutao.Core.IO.Http.DynamicProxy;
|
||||
namespace Snap.Hutao.Core.IO.Http.Proxy;
|
||||
|
||||
[Injection(InjectAs.Singleton)]
|
||||
internal sealed partial class DynamicHttpProxy : IWebProxy, IDisposable
|
||||
internal sealed partial class DynamicHttpProxy : ObservableObject, IWebProxy, IDisposable
|
||||
{
|
||||
private const string ProxySettingPath = @"HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Internet Settings\Connections";
|
||||
|
||||
private static readonly MethodInfo ConstructSystemProxyMethod;
|
||||
private static readonly Lazy<MethodInfo> LazyConstructSystemProxyMethod = new(GetConstructSystemProxyMethod);
|
||||
|
||||
private readonly IServiceProvider serviceProvider;
|
||||
private readonly RegistryWatcher watcher;
|
||||
|
||||
private IWebProxy innerProxy = default!;
|
||||
|
||||
static DynamicHttpProxy()
|
||||
public DynamicHttpProxy(IServiceProvider serviceProvider)
|
||||
{
|
||||
Type? systemProxyInfoType = typeof(System.Net.Http.SocketsHttpHandler).Assembly.GetType("System.Net.Http.SystemProxyInfo");
|
||||
ArgumentNullException.ThrowIfNull(systemProxyInfoType);
|
||||
this.serviceProvider = serviceProvider;
|
||||
UpdateInnerProxy();
|
||||
|
||||
MethodInfo? constructSystemProxyMethod = systemProxyInfoType.GetMethod("ConstructSystemProxy", BindingFlags.Static | BindingFlags.Public);
|
||||
ArgumentNullException.ThrowIfNull(constructSystemProxyMethod);
|
||||
ConstructSystemProxyMethod = constructSystemProxyMethod;
|
||||
}
|
||||
|
||||
public DynamicHttpProxy()
|
||||
{
|
||||
UpdateProxy();
|
||||
|
||||
watcher = new(ProxySettingPath, UpdateProxy);
|
||||
watcher = new(ProxySettingPath, OnSystemProxySettingsChanged);
|
||||
watcher.Start();
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public ICredentials? Credentials
|
||||
public string CurrentProxyUri
|
||||
{
|
||||
get => InnerProxy.Credentials;
|
||||
set => InnerProxy.Credentials = value;
|
||||
get
|
||||
{
|
||||
Uri? proxyUri = GetProxy("https://hut.ao".ToUri());
|
||||
return proxyUri is null
|
||||
? SH.ViewPageFeedbackCurrentProxyNoProxyDescription
|
||||
: proxyUri.AbsoluteUri;
|
||||
}
|
||||
}
|
||||
|
||||
private IWebProxy InnerProxy
|
||||
public IWebProxy InnerProxy
|
||||
{
|
||||
get => innerProxy;
|
||||
|
||||
@@ -60,13 +57,10 @@ internal sealed partial class DynamicHttpProxy : IWebProxy, IDisposable
|
||||
}
|
||||
}
|
||||
|
||||
[MemberNotNull(nameof(innerProxy))]
|
||||
public void UpdateProxy()
|
||||
public ICredentials? Credentials
|
||||
{
|
||||
IWebProxy? proxy = ConstructSystemProxyMethod.Invoke(default, default) as IWebProxy;
|
||||
ArgumentNullException.ThrowIfNull(proxy);
|
||||
|
||||
InnerProxy = proxy;
|
||||
get => InnerProxy.Credentials;
|
||||
set => InnerProxy.Credentials = value;
|
||||
}
|
||||
|
||||
public Uri? GetProxy(Uri destination)
|
||||
@@ -84,4 +78,33 @@ internal sealed partial class DynamicHttpProxy : IWebProxy, IDisposable
|
||||
(innerProxy as IDisposable)?.Dispose();
|
||||
watcher.Dispose();
|
||||
}
|
||||
|
||||
public void OnSystemProxySettingsChanged()
|
||||
{
|
||||
UpdateInnerProxy();
|
||||
|
||||
// TaskContext can't be injected directly since there are some recursive dependencies.
|
||||
ITaskContext taskContext = serviceProvider.GetRequiredService<ITaskContext>();
|
||||
taskContext.BeginInvokeOnMainThread(() => OnPropertyChanged(nameof(CurrentProxyUri)));
|
||||
}
|
||||
|
||||
private static MethodInfo GetConstructSystemProxyMethod()
|
||||
{
|
||||
Type? systemProxyInfoType = typeof(System.Net.Http.SocketsHttpHandler).Assembly.GetType("System.Net.Http.SystemProxyInfo");
|
||||
ArgumentNullException.ThrowIfNull(systemProxyInfoType);
|
||||
|
||||
MethodInfo? constructSystemProxyMethod = systemProxyInfoType.GetMethod("ConstructSystemProxy", BindingFlags.Static | BindingFlags.Public);
|
||||
ArgumentNullException.ThrowIfNull(constructSystemProxyMethod);
|
||||
|
||||
return constructSystemProxyMethod;
|
||||
}
|
||||
|
||||
[MemberNotNull(nameof(innerProxy))]
|
||||
private void UpdateInnerProxy()
|
||||
{
|
||||
IWebProxy? proxy = LazyConstructSystemProxyMethod.Value.Invoke(default, default) as IWebProxy;
|
||||
ArgumentNullException.ThrowIfNull(proxy);
|
||||
|
||||
InnerProxy = proxy;
|
||||
}
|
||||
}
|
||||
@@ -18,6 +18,7 @@ internal sealed class HttpShardCopyWorker<TStatus> : IDisposable
|
||||
private readonly long contentLength;
|
||||
private readonly int bufferSize;
|
||||
private readonly SafeFileHandle destFileHandle;
|
||||
private readonly int maxDegreeOfParallelism;
|
||||
private readonly List<Shard> shards;
|
||||
|
||||
private HttpShardCopyWorker(HttpShardCopyWorkerOptions<TStatus> options)
|
||||
@@ -28,6 +29,7 @@ internal sealed class HttpShardCopyWorker<TStatus> : IDisposable
|
||||
contentLength = options.ContentLength;
|
||||
bufferSize = options.BufferSize;
|
||||
destFileHandle = options.GetFileHandle();
|
||||
maxDegreeOfParallelism = options.MaxDegreeOfParallelism;
|
||||
shards = CalculateShards(contentLength);
|
||||
|
||||
static List<Shard> CalculateShards(long contentLength)
|
||||
@@ -56,7 +58,11 @@ internal sealed class HttpShardCopyWorker<TStatus> : IDisposable
|
||||
public Task CopyAsync(IProgress<TStatus> progress, CancellationToken token = default)
|
||||
{
|
||||
ShardProgress shardProgress = new(progress, statusFactory, contentLength);
|
||||
return Parallel.ForEachAsync(shards, token, (shard, token) => CopyShardAsync(shard, shardProgress, token));
|
||||
ParallelOptions parallelOptions = new()
|
||||
{
|
||||
MaxDegreeOfParallelism = maxDegreeOfParallelism,
|
||||
};
|
||||
return Parallel.ForEachAsync(shards, parallelOptions, (shard, token) => CopyShardAsync(shard, shardProgress, token));
|
||||
|
||||
async ValueTask CopyShardAsync(Shard shard, IProgress<ShardStatus> progress, CancellationToken token)
|
||||
{
|
||||
|
||||
@@ -22,6 +22,8 @@ internal sealed class HttpShardCopyWorkerOptions<TStatus>
|
||||
|
||||
public int BufferSize { get; set; } = 80 * 1024;
|
||||
|
||||
public int MaxDegreeOfParallelism { get; set; } = Environment.ProcessorCount;
|
||||
|
||||
public SafeFileHandle GetFileHandle()
|
||||
{
|
||||
return File.OpenHandle(DestinationFilePath, FileMode.Create, FileAccess.Write, FileShare.None, FileOptions.RandomAccess | FileOptions.Asynchronous, ContentLength);
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
using CommunityToolkit.WinUI.Notifications;
|
||||
using Microsoft.Extensions.Caching.Memory;
|
||||
using Microsoft.Windows.AppLifecycle;
|
||||
using Snap.Hutao.Core.LifeCycle.InterProcess;
|
||||
using Snap.Hutao.Core.Setting;
|
||||
using Snap.Hutao.Service.DailyNote;
|
||||
using Snap.Hutao.Service.Discord;
|
||||
@@ -24,24 +24,9 @@ namespace Snap.Hutao.Core.LifeCycle;
|
||||
[SuppressMessage("", "CA1001")]
|
||||
internal sealed partial class Activation : IActivation
|
||||
{
|
||||
/// <summary>
|
||||
/// 操作
|
||||
/// </summary>
|
||||
public const string Action = nameof(Action);
|
||||
|
||||
/// <summary>
|
||||
/// Uid
|
||||
/// </summary>
|
||||
public const string Uid = nameof(Uid);
|
||||
|
||||
/// <summary>
|
||||
/// 启动游戏启动参数
|
||||
/// </summary>
|
||||
public const string LaunchGame = nameof(LaunchGame);
|
||||
|
||||
/// <summary>
|
||||
/// 从剪贴板导入成就
|
||||
/// </summary>
|
||||
public const string ImportUIAFFromClipboard = nameof(ImportUIAFFromClipboard);
|
||||
|
||||
private const string CategoryAchievement = "ACHIEVEMENT";
|
||||
@@ -55,29 +40,20 @@ internal sealed partial class Activation : IActivation
|
||||
private readonly SemaphoreSlim activateSemaphore = new(1);
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void Activate(object? sender, AppActivationArguments args)
|
||||
public void Activate(HutaoActivationArguments args)
|
||||
{
|
||||
_ = sender;
|
||||
if (!ToastNotificationManagerCompat.WasCurrentProcessToastActivated())
|
||||
if (ToastNotificationManagerCompat.WasCurrentProcessToastActivated())
|
||||
{
|
||||
HandleActivationAsync(args, true).SafeForget();
|
||||
return;
|
||||
}
|
||||
|
||||
HandleActivationAsync(args).SafeForget();
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void NonRedirectToActivate(object? sender, AppActivationArguments args)
|
||||
public void Initialize()
|
||||
{
|
||||
_ = sender;
|
||||
if (!ToastNotificationManagerCompat.WasCurrentProcessToastActivated())
|
||||
{
|
||||
HandleActivationAsync(args, false).SafeForget();
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void InitializeWith(AppInstance appInstance)
|
||||
{
|
||||
appInstance.Activated += Activate;
|
||||
serviceProvider.GetRequiredService<PrivateNamedPipeServer>().RunAsync().SafeForget();
|
||||
ToastNotificationManagerCompat.OnActivated += NotificationActivate;
|
||||
}
|
||||
|
||||
@@ -95,44 +71,40 @@ internal sealed partial class Activation : IActivation
|
||||
}
|
||||
}
|
||||
|
||||
private async ValueTask HandleActivationAsync(AppActivationArguments args, bool isRedirected)
|
||||
private async ValueTask HandleActivationAsync(HutaoActivationArguments args)
|
||||
{
|
||||
if (activateSemaphore.CurrentCount > 0)
|
||||
{
|
||||
using (await activateSemaphore.EnterAsync().ConfigureAwait(false))
|
||||
{
|
||||
await HandleActivationCoreAsync(args, isRedirected).ConfigureAwait(false);
|
||||
await HandleActivationCoreAsync(args).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async ValueTask HandleActivationCoreAsync(AppActivationArguments args, bool isRedirected)
|
||||
private async ValueTask HandleActivationCoreAsync(HutaoActivationArguments args)
|
||||
{
|
||||
if (args.Kind == ExtendedActivationKind.Protocol)
|
||||
if (args.Kind is HutaoActivationKind.Protocol)
|
||||
{
|
||||
if (args.TryGetProtocolActivatedUri(out Uri? uri))
|
||||
{
|
||||
await HandleUrlActivationAsync(uri, isRedirected).ConfigureAwait(false);
|
||||
}
|
||||
ArgumentNullException.ThrowIfNull(args.ProtocolActivatedUri);
|
||||
await HandleUrlActivationAsync(args.ProtocolActivatedUri, args.IsRedirectTo).ConfigureAwait(false);
|
||||
}
|
||||
else if (args.Kind == ExtendedActivationKind.Launch)
|
||||
else if (args.Kind is HutaoActivationKind.Launch)
|
||||
{
|
||||
if (args.TryGetLaunchActivatedArgument(out string? arguments))
|
||||
ArgumentNullException.ThrowIfNull(args.LaunchActivatedArguments);
|
||||
switch (args.LaunchActivatedArguments)
|
||||
{
|
||||
switch (arguments)
|
||||
{
|
||||
case LaunchGame:
|
||||
{
|
||||
await HandleLaunchGameActionAsync().ConfigureAwait(false);
|
||||
break;
|
||||
}
|
||||
case LaunchGame:
|
||||
{
|
||||
await HandleLaunchGameActionAsync().ConfigureAwait(false);
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
{
|
||||
await HandleNormalLaunchActionAsync().ConfigureAwait(false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
default:
|
||||
{
|
||||
await HandleNormalLaunchActionAsync().ConfigureAwait(false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -194,7 +166,7 @@ internal sealed partial class Activation : IActivation
|
||||
.SafeForget();
|
||||
}
|
||||
|
||||
private async ValueTask HandleUrlActivationAsync(Uri uri, bool isRedirected)
|
||||
private async ValueTask HandleUrlActivationAsync(Uri uri, bool isRedirectTo)
|
||||
{
|
||||
UriBuilder builder = new(uri);
|
||||
|
||||
@@ -207,13 +179,13 @@ internal sealed partial class Activation : IActivation
|
||||
case CategoryAchievement:
|
||||
{
|
||||
await WaitMainWindowAsync().ConfigureAwait(false);
|
||||
await HandleAchievementActionAsync(action, parameter, isRedirected).ConfigureAwait(false);
|
||||
await HandleAchievementActionAsync(action, parameter, isRedirectTo).ConfigureAwait(false);
|
||||
break;
|
||||
}
|
||||
|
||||
case CategoryDailyNote:
|
||||
{
|
||||
await HandleDailyNoteActionAsync(action, parameter, isRedirected).ConfigureAwait(false);
|
||||
await HandleDailyNoteActionAsync(action, parameter, isRedirectTo).ConfigureAwait(false);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -225,10 +197,10 @@ internal sealed partial class Activation : IActivation
|
||||
}
|
||||
}
|
||||
|
||||
private async ValueTask HandleAchievementActionAsync(string action, string parameter, bool isRedirected)
|
||||
private async ValueTask HandleAchievementActionAsync(string action, string parameter, bool isRedirectTo)
|
||||
{
|
||||
_ = parameter;
|
||||
_ = isRedirected;
|
||||
_ = isRedirectTo;
|
||||
switch (action)
|
||||
{
|
||||
case UrlActionImport:
|
||||
@@ -245,7 +217,7 @@ internal sealed partial class Activation : IActivation
|
||||
}
|
||||
}
|
||||
|
||||
private async ValueTask HandleDailyNoteActionAsync(string action, string parameter, bool isRedirected)
|
||||
private async ValueTask HandleDailyNoteActionAsync(string action, string parameter, bool isRedirectTo)
|
||||
{
|
||||
_ = parameter;
|
||||
switch (action)
|
||||
@@ -264,7 +236,7 @@ internal sealed partial class Activation : IActivation
|
||||
}
|
||||
|
||||
// Check if it's redirected.
|
||||
if (!isRedirected)
|
||||
if (!isRedirectTo)
|
||||
{
|
||||
// It's a direct open process, should exit immediately.
|
||||
Process.GetCurrentProcess().Kill();
|
||||
|
||||
@@ -36,7 +36,7 @@ internal static class AppActivationArgumentsExtensions
|
||||
/// <param name="activatedEventArgs">应用程序激活参数</param>
|
||||
/// <param name="arguments">参数</param>
|
||||
/// <returns>是否存在参数</returns>
|
||||
public static bool TryGetLaunchActivatedArgument(this AppActivationArguments activatedEventArgs, [NotNullWhen(true)] out string? arguments)
|
||||
public static bool TryGetLaunchActivatedArguments(this AppActivationArguments activatedEventArgs, [NotNullWhen(true)] out string? arguments)
|
||||
{
|
||||
arguments = null;
|
||||
if (activatedEventArgs.Data is not ILaunchActivatedEventArgs launchArgs)
|
||||
|
||||
@@ -2,10 +2,11 @@
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Microsoft.Windows.AppLifecycle;
|
||||
using Windows.Win32.Foundation;
|
||||
using Windows.Win32.Security;
|
||||
using Windows.Win32.System.Com;
|
||||
using static Windows.Win32.PInvoke;
|
||||
using Snap.Hutao.Win32.Foundation;
|
||||
using Snap.Hutao.Win32.System.Com;
|
||||
using static Snap.Hutao.Win32.ConstValues;
|
||||
using static Snap.Hutao.Win32.Kernel32;
|
||||
using static Snap.Hutao.Win32.Ole32;
|
||||
|
||||
namespace Snap.Hutao.Core.LifeCycle;
|
||||
|
||||
@@ -18,7 +19,7 @@ internal static class AppInstanceExtension
|
||||
private static readonly WaitCallback RunActionWaitCallback = RunAction;
|
||||
|
||||
// Hold the reference here to prevent memory corruption.
|
||||
private static HANDLE redirectEventHandle = HANDLE.Null;
|
||||
private static HANDLE redirectEventHandle;
|
||||
|
||||
/// <summary>
|
||||
/// 同步非阻塞重定向
|
||||
@@ -27,19 +28,24 @@ internal static class AppInstanceExtension
|
||||
/// <param name="args">参数</param>
|
||||
public static unsafe void RedirectActivationTo(this AppInstance appInstance, AppActivationArguments args)
|
||||
{
|
||||
redirectEventHandle = CreateEvent(default(SECURITY_ATTRIBUTES*), true, false, null);
|
||||
|
||||
// use ThreadPool.UnsafeQueueUserWorkItem to cancel stacktrace
|
||||
// like ExecutionContext.SuppressFlow
|
||||
ThreadPool.UnsafeQueueUserWorkItem(RunActionWaitCallback, () =>
|
||||
try
|
||||
{
|
||||
appInstance.RedirectActivationToAsync(args).AsTask().Wait();
|
||||
SetEvent(redirectEventHandle);
|
||||
});
|
||||
redirectEventHandle = CreateEventW(default, true, false, default);
|
||||
|
||||
ReadOnlySpan<HANDLE> handles = new(ref redirectEventHandle);
|
||||
CoWaitForMultipleObjects((uint)CWMO_FLAGS.CWMO_DEFAULT, INFINITE, handles, out uint _);
|
||||
CloseHandle(redirectEventHandle);
|
||||
// use ThreadPool.UnsafeQueueUserWorkItem to cancel stacktrace
|
||||
// like ExecutionContext.SuppressFlow
|
||||
ThreadPool.UnsafeQueueUserWorkItem(RunActionWaitCallback, () =>
|
||||
{
|
||||
appInstance.RedirectActivationToAsync(args).AsTask().Wait();
|
||||
SetEvent(redirectEventHandle);
|
||||
});
|
||||
|
||||
CoWaitForMultipleObjects(CWMO_FLAGS.CWMO_DEFAULT, INFINITE, [redirectEventHandle], out uint _);
|
||||
}
|
||||
finally
|
||||
{
|
||||
CloseHandle(redirectEventHandle);
|
||||
}
|
||||
}
|
||||
|
||||
[SuppressMessage("", "SH007")]
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
using Microsoft.UI.Xaml;
|
||||
using Snap.Hutao.Core.Windowing;
|
||||
using Windows.Win32.Foundation;
|
||||
using Snap.Hutao.Win32.Foundation;
|
||||
using WinRT.Interop;
|
||||
|
||||
namespace Snap.Hutao.Core.LifeCycle;
|
||||
@@ -19,6 +19,6 @@ internal static class CurrentWindowReferenceExtension
|
||||
{
|
||||
return reference.Window is IWindowOptionsSource optionsSource
|
||||
? optionsSource.WindowOptions.Hwnd
|
||||
: (HWND)WindowNative.GetWindowHandle(reference.Window);
|
||||
: WindowNative.GetWindowHandle(reference.Window);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Microsoft.Windows.AppLifecycle;
|
||||
|
||||
namespace Snap.Hutao.Core.LifeCycle;
|
||||
|
||||
internal sealed class HutaoActivationArguments
|
||||
{
|
||||
public bool IsRedirectTo { get; set; }
|
||||
|
||||
public HutaoActivationKind Kind { get; set; }
|
||||
|
||||
public Uri? ProtocolActivatedUri { get; set; }
|
||||
|
||||
public string? LaunchActivatedArguments { get; set; }
|
||||
|
||||
public static HutaoActivationArguments FromAppActivationArguments(AppActivationArguments args, bool isRedirected = false)
|
||||
{
|
||||
HutaoActivationArguments result = new()
|
||||
{
|
||||
IsRedirectTo = isRedirected,
|
||||
};
|
||||
|
||||
switch (args.Kind)
|
||||
{
|
||||
case ExtendedActivationKind.Launch:
|
||||
{
|
||||
result.Kind = HutaoActivationKind.Launch;
|
||||
if (args.TryGetLaunchActivatedArguments(out string? arguments))
|
||||
{
|
||||
result.LaunchActivatedArguments = arguments;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case ExtendedActivationKind.Protocol:
|
||||
{
|
||||
result.Kind = HutaoActivationKind.Protocol;
|
||||
if (args.TryGetProtocolActivatedUri(out Uri? uri))
|
||||
{
|
||||
result.ProtocolActivatedUri = uri;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
namespace Snap.Hutao.Core.LifeCycle;
|
||||
|
||||
internal enum HutaoActivationKind
|
||||
{
|
||||
None,
|
||||
Launch,
|
||||
Protocol,
|
||||
}
|
||||
@@ -1,8 +1,6 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Microsoft.Windows.AppLifecycle;
|
||||
|
||||
namespace Snap.Hutao.Core.LifeCycle;
|
||||
|
||||
/// <summary>
|
||||
@@ -10,24 +8,7 @@ namespace Snap.Hutao.Core.LifeCycle;
|
||||
/// </summary>
|
||||
internal interface IActivation
|
||||
{
|
||||
/// <summary>
|
||||
/// 响应激活事件
|
||||
/// 激活事件一般不会在UI线程上触发
|
||||
/// </summary>
|
||||
/// <param name="sender">发送方</param>
|
||||
/// <param name="args">激活参数</param>
|
||||
void Activate(object? sender, AppActivationArguments args);
|
||||
void Activate(HutaoActivationArguments args);
|
||||
|
||||
/// <summary>
|
||||
/// 使用当前 App 实例初始化激活
|
||||
/// </summary>
|
||||
/// <param name="appInstance">App 实例</param>
|
||||
void InitializeWith(AppInstance appInstance);
|
||||
|
||||
/// <summary>
|
||||
/// 无转发触发激活事件
|
||||
/// </summary>
|
||||
/// <param name="sender">发送方</param>
|
||||
/// <param name="args">激活参数</param>
|
||||
void NonRedirectToActivate(object? sender, AppActivationArguments args);
|
||||
void Initialize();
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using System.IO.Pipes;
|
||||
|
||||
namespace Snap.Hutao.Core.LifeCycle.InterProcess;
|
||||
|
||||
internal static class NamedPipeClientStreamExtension
|
||||
{
|
||||
public static bool TryConnectOnce(this NamedPipeClientStream clientStream)
|
||||
{
|
||||
try
|
||||
{
|
||||
clientStream.Connect(TimeSpan.Zero);
|
||||
return true;
|
||||
}
|
||||
catch (TimeoutException)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
namespace Snap.Hutao.Core.LifeCycle.InterProcess;
|
||||
|
||||
internal enum PipePacketCommand : byte
|
||||
{
|
||||
None = 0,
|
||||
|
||||
RedirectActivation = 10,
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
namespace Snap.Hutao.Core.LifeCycle.InterProcess;
|
||||
|
||||
internal enum PipePacketContentType : byte
|
||||
{
|
||||
None = 0,
|
||||
Json = 1,
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Snap.Hutao.Core.LifeCycle.InterProcess;
|
||||
|
||||
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
||||
internal struct PipePacketHeader
|
||||
{
|
||||
public byte Version;
|
||||
public PipePacketType Type;
|
||||
public PipePacketCommand Command;
|
||||
public PipePacketContentType ContentType;
|
||||
public int ContentLength;
|
||||
public ulong Checksum;
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
namespace Snap.Hutao.Core.LifeCycle.InterProcess;
|
||||
|
||||
internal enum PipePacketType : byte
|
||||
{
|
||||
None = 0,
|
||||
Request = 1,
|
||||
Response = 2,
|
||||
Termination = 3,
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Microsoft.Windows.AppLifecycle;
|
||||
using System.IO.Hashing;
|
||||
using System.IO.Pipes;
|
||||
|
||||
namespace Snap.Hutao.Core.LifeCycle.InterProcess;
|
||||
|
||||
[Injection(InjectAs.Singleton)]
|
||||
internal sealed class PrivateNamedPipeClient : IDisposable
|
||||
{
|
||||
private readonly NamedPipeClientStream clientStream = new(".", "Snap.Hutao.PrivateNamedPipe", PipeDirection.InOut, PipeOptions.Asynchronous | PipeOptions.WriteThrough);
|
||||
|
||||
public unsafe bool TryRedirectActivationTo(AppActivationArguments args)
|
||||
{
|
||||
if (clientStream.TryConnectOnce())
|
||||
{
|
||||
{
|
||||
PipePacketHeader redirectActivationPacket = default;
|
||||
redirectActivationPacket.Version = 1;
|
||||
redirectActivationPacket.Type = PipePacketType.Request;
|
||||
redirectActivationPacket.Command = PipePacketCommand.RedirectActivation;
|
||||
redirectActivationPacket.ContentType = PipePacketContentType.Json;
|
||||
|
||||
HutaoActivationArguments hutaoArgs = HutaoActivationArguments.FromAppActivationArguments(args, isRedirected: true);
|
||||
byte[] jsonBytes = JsonSerializer.SerializeToUtf8Bytes(hutaoArgs);
|
||||
|
||||
redirectActivationPacket.ContentLength = jsonBytes.Length;
|
||||
redirectActivationPacket.Checksum = XxHash64.HashToUInt64(jsonBytes);
|
||||
|
||||
clientStream.Write(new(&redirectActivationPacket, sizeof(PipePacketHeader)));
|
||||
clientStream.Write(jsonBytes);
|
||||
}
|
||||
|
||||
{
|
||||
PipePacketHeader terminationPacket = default;
|
||||
terminationPacket.Version = 1;
|
||||
terminationPacket.Type = PipePacketType.Termination;
|
||||
|
||||
clientStream.Write(new(&terminationPacket, sizeof(PipePacketHeader)));
|
||||
}
|
||||
|
||||
clientStream.Flush();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
clientStream.Dispose();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
namespace Snap.Hutao.Core.LifeCycle.InterProcess;
|
||||
|
||||
[Injection(InjectAs.Singleton)]
|
||||
[ConstructorGenerated]
|
||||
internal sealed partial class PrivateNamedPipeMessageDispatcher
|
||||
{
|
||||
private readonly IServiceProvider serviceProvider;
|
||||
|
||||
public void RedirectActivation(HutaoActivationArguments? args)
|
||||
{
|
||||
if (args is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
serviceProvider.GetRequiredService<IActivation>().Activate(args);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Snap.Hutao.Core.ExceptionService;
|
||||
using System.IO.Hashing;
|
||||
using System.IO.Pipes;
|
||||
|
||||
namespace Snap.Hutao.Core.LifeCycle.InterProcess;
|
||||
|
||||
[Injection(InjectAs.Singleton)]
|
||||
[ConstructorGenerated]
|
||||
internal sealed partial class PrivateNamedPipeServer : IDisposable
|
||||
{
|
||||
private readonly PrivateNamedPipeMessageDispatcher messageDispatcher;
|
||||
|
||||
private readonly NamedPipeServerStream serverStream = new("Snap.Hutao.PrivateNamedPipe", PipeDirection.InOut, NamedPipeServerStream.MaxAllowedServerInstances, PipeTransmissionMode.Byte, PipeOptions.Asynchronous | PipeOptions.WriteThrough);
|
||||
private readonly CancellationTokenSource serverTokenSource = new();
|
||||
private readonly SemaphoreSlim serverSemaphore = new(1);
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
serverTokenSource.Cancel();
|
||||
serverSemaphore.Wait();
|
||||
serverSemaphore.Dispose();
|
||||
serverTokenSource.Dispose();
|
||||
|
||||
serverStream.Dispose();
|
||||
}
|
||||
|
||||
public async ValueTask RunAsync()
|
||||
{
|
||||
using (await serverSemaphore.EnterAsync(serverTokenSource.Token).ConfigureAwait(false))
|
||||
{
|
||||
while (!serverTokenSource.IsCancellationRequested)
|
||||
{
|
||||
try
|
||||
{
|
||||
await serverStream.WaitForConnectionAsync(serverTokenSource.Token).ConfigureAwait(false);
|
||||
RunPacketSession(serverStream, serverTokenSource.Token);
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static unsafe byte[] GetValidatedContent(NamedPipeServerStream serverStream, PipePacketHeader* header)
|
||||
{
|
||||
byte[] content = new byte[header->ContentLength];
|
||||
serverStream.ReadAtLeast(content, header->ContentLength, false);
|
||||
ThrowHelper.InvalidDataIf(XxHash64.HashToUInt64(content) != header->Checksum, "PipePacket Content Hash incorrect");
|
||||
return content;
|
||||
}
|
||||
|
||||
private unsafe void RunPacketSession(NamedPipeServerStream serverStream, CancellationToken token)
|
||||
{
|
||||
Span<byte> headerSpan = stackalloc byte[sizeof(PipePacketHeader)];
|
||||
bool sessionTerminated = false;
|
||||
while (serverStream.IsConnected && !sessionTerminated && !token.IsCancellationRequested)
|
||||
{
|
||||
serverStream.ReadExactly(headerSpan);
|
||||
fixed (byte* pHeader = headerSpan)
|
||||
{
|
||||
PipePacketHeader* header = (PipePacketHeader*)pHeader;
|
||||
|
||||
switch ((header->Type, header->Command, header->ContentType))
|
||||
{
|
||||
case (PipePacketType.Request, PipePacketCommand.RedirectActivation, PipePacketContentType.Json):
|
||||
ReadOnlySpan<byte> content = GetValidatedContent(serverStream, header);
|
||||
messageDispatcher.RedirectActivation(JsonSerializer.Deserialize<HutaoActivationArguments>(content));
|
||||
break;
|
||||
case (PipePacketType.Termination, _, _):
|
||||
serverStream.Disconnect();
|
||||
sessionTerminated = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,9 +2,9 @@
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Snap.Hutao.Core.Setting;
|
||||
using Windows.Win32.Foundation;
|
||||
using Windows.Win32.System.Console;
|
||||
using static Windows.Win32.PInvoke;
|
||||
using Snap.Hutao.Win32.Foundation;
|
||||
using Snap.Hutao.Win32.System.Console;
|
||||
using static Snap.Hutao.Win32.Kernel32;
|
||||
|
||||
namespace Snap.Hutao.Core.Logging;
|
||||
|
||||
@@ -26,7 +26,7 @@ internal sealed class ConsoleWindowLifeTime : IDisposable
|
||||
SetConsoleMode(inputHandle, mode);
|
||||
}
|
||||
|
||||
SetConsoleTitle("Snap Hutao Debug Console");
|
||||
SetConsoleTitleW("Snap Hutao Debug Console");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,113 +14,99 @@ namespace Snap.Hutao.Core;
|
||||
[Injection(InjectAs.Singleton)]
|
||||
internal sealed class RuntimeOptions
|
||||
{
|
||||
private readonly bool isWebView2Supported;
|
||||
private readonly string webView2Version = SH.CoreWebView2HelperVersionUndetected;
|
||||
private readonly Lazy<(Version Version, string UserAgent)> lazyVersionAndUserAgent = new(() =>
|
||||
{
|
||||
Version version = Package.Current.Id.Version.ToVersion();
|
||||
return (version, $"Snap Hutao/{version}");
|
||||
});
|
||||
|
||||
private bool? isElevated;
|
||||
private readonly Lazy<string> lazyDataFolder = new(() =>
|
||||
{
|
||||
string preferredPath = LocalSetting.Get(SettingKeys.DataFolderPath, string.Empty);
|
||||
|
||||
if (!string.IsNullOrEmpty(preferredPath))
|
||||
{
|
||||
Directory.CreateDirectory(preferredPath);
|
||||
return preferredPath;
|
||||
}
|
||||
|
||||
// Fallback to MyDocuments
|
||||
string myDocuments = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
|
||||
|
||||
#if RELEASE
|
||||
// 将测试版与正式版的文件目录分离
|
||||
string folderName = Package.Current.PublisherDisplayName == "DGP Studio CI" ? "HutaoAlpha" : "Hutao";
|
||||
#else
|
||||
// 使得迁移能正常生成
|
||||
string folderName = "Hutao";
|
||||
#endif
|
||||
string path = Path.GetFullPath(Path.Combine(myDocuments, folderName));
|
||||
Directory.CreateDirectory(path);
|
||||
return path;
|
||||
});
|
||||
|
||||
private readonly Lazy<string> lazyDeviceId = new(() =>
|
||||
{
|
||||
string userName = Environment.UserName;
|
||||
object? machineGuid = Registry.GetValue(@"HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Cryptography\", "MachineGuid", userName);
|
||||
return Convert.ToMd5HexString($"{userName}{machineGuid}");
|
||||
});
|
||||
|
||||
private readonly Lazy<(string Version, bool Supported)> lazyWebViewEnvironment = new(() =>
|
||||
{
|
||||
try
|
||||
{
|
||||
string version = CoreWebView2Environment.GetAvailableBrowserVersionString();
|
||||
return (version, true);
|
||||
}
|
||||
catch (FileNotFoundException)
|
||||
{
|
||||
return (SH.CoreWebView2HelperVersionUndetected, false);
|
||||
}
|
||||
});
|
||||
|
||||
private readonly Lazy<bool> lazyElevated = new(() =>
|
||||
{
|
||||
if (LocalSetting.Get(SettingKeys.OverrideElevationRequirement, false))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
using (WindowsIdentity identity = WindowsIdentity.GetCurrent())
|
||||
{
|
||||
WindowsPrincipal principal = new(identity);
|
||||
return principal.IsInRole(WindowsBuiltInRole.Administrator);
|
||||
}
|
||||
});
|
||||
|
||||
private readonly Lazy<string> lazyLocalCache = new(() => ApplicationData.Current.LocalCacheFolder.Path);
|
||||
private readonly Lazy<string> lazyInstalledLocation = new(() => Package.Current.InstalledLocation.Path);
|
||||
private readonly Lazy<string> lazyFamilyName = new(() => Package.Current.Id.FamilyName);
|
||||
|
||||
public RuntimeOptions(ILogger<RuntimeOptions> logger)
|
||||
{
|
||||
AppLaunchTime = DateTimeOffset.UtcNow;
|
||||
|
||||
DataFolder = GetDataFolderPath();
|
||||
LocalCache = ApplicationData.Current.LocalCacheFolder.Path;
|
||||
InstalledLocation = Package.Current.InstalledLocation.Path;
|
||||
FamilyName = Package.Current.Id.FamilyName;
|
||||
|
||||
Version = Package.Current.Id.Version.ToVersion();
|
||||
UserAgent = $"Snap Hutao/{Version}";
|
||||
|
||||
DeviceId = GetUniqueUserId();
|
||||
DetectWebView2Environment(logger, out webView2Version, out isWebView2Supported);
|
||||
|
||||
static string GetDataFolderPath()
|
||||
{
|
||||
string preferredPath = LocalSetting.Get(SettingKeys.DataFolderPath, string.Empty);
|
||||
|
||||
if (!string.IsNullOrEmpty(preferredPath))
|
||||
{
|
||||
Directory.CreateDirectory(preferredPath);
|
||||
return preferredPath;
|
||||
}
|
||||
|
||||
// Fallback to MyDocuments
|
||||
string myDocuments = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
|
||||
|
||||
#if RELEASE
|
||||
// 将测试版与正式版的文件目录分离
|
||||
string folderName = Package.Current.PublisherDisplayName == "DGP Studio CI" ? "HutaoAlpha" : "Hutao";
|
||||
#else
|
||||
// 使得迁移能正常生成
|
||||
string folderName = "Hutao";
|
||||
#endif
|
||||
string path = Path.GetFullPath(Path.Combine(myDocuments, folderName));
|
||||
Directory.CreateDirectory(path);
|
||||
return path;
|
||||
}
|
||||
|
||||
static string GetUniqueUserId()
|
||||
{
|
||||
string userName = Environment.UserName;
|
||||
object? machineGuid = Registry.GetValue(@"HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Cryptography\", "MachineGuid", userName);
|
||||
return Convert.ToMd5HexString($"{userName}{machineGuid}");
|
||||
}
|
||||
|
||||
static void DetectWebView2Environment(ILogger<RuntimeOptions> logger, out string webView2Version, out bool isWebView2Supported)
|
||||
{
|
||||
try
|
||||
{
|
||||
webView2Version = CoreWebView2Environment.GetAvailableBrowserVersionString();
|
||||
isWebView2Supported = true;
|
||||
}
|
||||
catch (FileNotFoundException ex)
|
||||
{
|
||||
webView2Version = SH.CoreWebView2HelperVersionUndetected;
|
||||
isWebView2Supported = false;
|
||||
logger.LogError(ex, "WebView2 Runtime not installed.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Version Version { get; }
|
||||
public Version Version { get => lazyVersionAndUserAgent.Value.Version; }
|
||||
|
||||
public string UserAgent { get; }
|
||||
public string UserAgent { get => lazyVersionAndUserAgent.Value.UserAgent; }
|
||||
|
||||
public string InstalledLocation { get; }
|
||||
public string InstalledLocation { get => lazyInstalledLocation.Value; }
|
||||
|
||||
public string DataFolder { get; }
|
||||
public string DataFolder { get => lazyDataFolder.Value; }
|
||||
|
||||
public string LocalCache { get; }
|
||||
public string LocalCache { get => lazyLocalCache.Value; }
|
||||
|
||||
public string FamilyName { get; }
|
||||
public string FamilyName { get => lazyFamilyName.Value; }
|
||||
|
||||
public string DeviceId { get; }
|
||||
public string DeviceId { get => lazyDeviceId.Value; }
|
||||
|
||||
public string WebView2Version { get => webView2Version; }
|
||||
public string WebView2Version { get => lazyWebViewEnvironment.Value.Version; }
|
||||
|
||||
public bool IsWebView2Supported { get => isWebView2Supported; }
|
||||
public bool IsWebView2Supported { get => lazyWebViewEnvironment.Value.Supported; }
|
||||
|
||||
public bool IsElevated
|
||||
{
|
||||
get
|
||||
{
|
||||
return isElevated ??= GetElevated();
|
||||
|
||||
static bool GetElevated()
|
||||
{
|
||||
if (LocalSetting.Get(SettingKeys.OverrideElevationRequirement, false))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
using (WindowsIdentity identity = WindowsIdentity.GetCurrent())
|
||||
{
|
||||
WindowsPrincipal principal = new(identity);
|
||||
return principal.IsInRole(WindowsBuiltInRole.Administrator);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
public bool IsElevated { get => lazyElevated.Value; }
|
||||
|
||||
public DateTimeOffset AppLaunchTime { get; }
|
||||
}
|
||||
@@ -22,7 +22,7 @@ internal static class SettingKeys
|
||||
public const string DataFolderPath = "DataFolderPath";
|
||||
public const string Major1Minor7Revision0GuideState = "Major1Minor7Revision0GuideState";
|
||||
public const string HotKeyMouseClickRepeatForever = "HotKeyMouseClickRepeatForever";
|
||||
public const string IsAllocConsoleDebugModeEnabled = "IsAllocConsoleDebugModeEnabled";
|
||||
public const string IsAllocConsoleDebugModeEnabled = "IsAllocConsoleDebugModeEnabled2";
|
||||
#endregion
|
||||
|
||||
#region Passport
|
||||
@@ -56,5 +56,11 @@ internal static class SettingKeys
|
||||
public const string SuppressMetadataInitialization = "SuppressMetadataInitialization";
|
||||
public const string OverrideElevationRequirement = "OverrideElevationRequirement";
|
||||
public const string OverrideUpdateVersionComparison = "OverrideUpdateVersionComparison";
|
||||
public const string OverridePackageConvertDirectoryPermissionsRequirement = "OverridePackageConvertDirectoryPermissionsRequirement";
|
||||
#endregion
|
||||
|
||||
#region Obsolete
|
||||
[Obsolete("重置调试控制台开关")]
|
||||
public const string IsAllocConsoleDebugModeEnabledLegacy1 = "IsAllocConsoleDebugModeEnabled";
|
||||
#endregion
|
||||
}
|
||||
@@ -68,7 +68,7 @@ internal sealed class ScheduleTaskInterop : IScheduleTaskInterop
|
||||
|
||||
public bool IsDailyNoteRefreshEnabled()
|
||||
{
|
||||
return WScriptExists(DailyNoteRefreshScriptName, out _);
|
||||
return TaskService.Instance.RootFolder.Tasks.Any(task => task.Name is DailyNoteRefreshTaskName);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -1,15 +1,14 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Snap.Hutao.Win32.Foundation;
|
||||
using Snap.Hutao.Win32.System.Com;
|
||||
using Snap.Hutao.Win32.UI.Shell;
|
||||
using Snap.Hutao.Win32.UI.WindowsAndMessaging;
|
||||
using System.IO;
|
||||
using System.Runtime.InteropServices;
|
||||
using Windows.Storage;
|
||||
using Windows.Win32;
|
||||
using Windows.Win32.Foundation;
|
||||
using Windows.Win32.System.Com;
|
||||
using Windows.Win32.UI.Shell;
|
||||
using Windows.Win32.UI.WindowsAndMessaging;
|
||||
using static Windows.Win32.PInvoke;
|
||||
using static Snap.Hutao.Win32.Macros;
|
||||
using static Snap.Hutao.Win32.Ole32;
|
||||
|
||||
namespace Snap.Hutao.Core.Shell;
|
||||
|
||||
@@ -34,31 +33,44 @@ internal sealed partial class ShellLinkInterop : IShellLinkInterop
|
||||
return false;
|
||||
}
|
||||
|
||||
HRESULT result = CoCreateInstance<ShellLink, IShellLinkW>(null, CLSCTX.CLSCTX_INPROC_SERVER, out IShellLinkW shellLink);
|
||||
Marshal.ThrowExceptionForHR(result);
|
||||
return UnsafeTryCreateDesktopShoutcutForElevatedLaunch(targetLogoPath);
|
||||
}
|
||||
|
||||
shellLink.SetPath($"shell:AppsFolder\\{runtimeOptions.FamilyName}!App");
|
||||
shellLink.SetShowCmd(SHOW_WINDOW_CMD.SW_NORMAL);
|
||||
shellLink.SetIconLocation(targetLogoPath, 0);
|
||||
private unsafe bool UnsafeTryCreateDesktopShoutcutForElevatedLaunch(string targetLogoPath)
|
||||
{
|
||||
bool result = false;
|
||||
|
||||
IShellLinkDataList shellLinkDataList = (IShellLinkDataList)shellLink;
|
||||
shellLinkDataList.GetFlags(out uint flags);
|
||||
flags |= (uint)SHELL_LINK_DATA_FLAGS.SLDF_RUNAS_USER;
|
||||
shellLinkDataList.SetFlags(flags);
|
||||
|
||||
string desktop = Environment.GetFolderPath(Environment.SpecialFolder.Desktop);
|
||||
string target = Path.Combine(desktop, $"{SH.FormatAppNameAndVersion(runtimeOptions.Version)}.lnk");
|
||||
|
||||
IPersistFile persistFile = (IPersistFile)shellLink;
|
||||
try
|
||||
// DO NOT revert if condition, COM interfaces need to be released properly
|
||||
HRESULT hr = CoCreateInstance(in ShellLink.CLSID, default, CLSCTX.CLSCTX_INPROC_SERVER, in IShellLinkW.IID, out IShellLinkW* pShellLink);
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
persistFile.Save(target, false);
|
||||
}
|
||||
catch (UnauthorizedAccessException)
|
||||
{
|
||||
return false;
|
||||
pShellLink->SetPath($"shell:AppsFolder\\{runtimeOptions.FamilyName}!App");
|
||||
pShellLink->SetShowCmd(SHOW_WINDOW_CMD.SW_NORMAL);
|
||||
pShellLink->SetIconLocation(targetLogoPath, 0);
|
||||
|
||||
if (SUCCEEDED(pShellLink->QueryInterface(in IShellLinkDataList.IID, out IShellLinkDataList* pShellLinkDataList)))
|
||||
{
|
||||
pShellLinkDataList->GetFlags(out uint flags);
|
||||
pShellLinkDataList->SetFlags(flags | (uint)SHELL_LINK_DATA_FLAGS.SLDF_RUNAS_USER);
|
||||
pShellLinkDataList->Release();
|
||||
}
|
||||
|
||||
if (SUCCEEDED(pShellLink->QueryInterface(in IPersistFile.IID, out IPersistFile* pPersistFile)))
|
||||
{
|
||||
string desktop = Environment.GetFolderPath(Environment.SpecialFolder.Desktop);
|
||||
string target = Path.Combine(desktop, $"{SH.FormatAppNameAndVersion(runtimeOptions.Version)}.lnk");
|
||||
|
||||
if (SUCCEEDED(pPersistFile->Save(target, false)))
|
||||
{
|
||||
result = true;
|
||||
}
|
||||
|
||||
pPersistFile->Release();
|
||||
}
|
||||
|
||||
pShellLink->Release();
|
||||
}
|
||||
|
||||
return true;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,7 @@
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Microsoft.UI.Windowing;
|
||||
using Snap.Hutao.Win32;
|
||||
using Windows.Graphics;
|
||||
|
||||
namespace Snap.Hutao.Core.Windowing;
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
namespace Snap.Hutao.Core.Windowing.Backdrop;
|
||||
|
||||
internal interface IBackdropNeedEraseBackground;
|
||||
@@ -62,6 +62,4 @@ internal sealed class TransparentBackdrop : SystemBackdrop, IDisposable, IBackdr
|
||||
{
|
||||
disconnectedTarget.SystemBackdrop = null;
|
||||
}
|
||||
}
|
||||
|
||||
internal interface IBackdropNeedEraseBackground;
|
||||
}
|
||||
@@ -5,11 +5,11 @@ using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using Snap.Hutao.Core.LifeCycle;
|
||||
using Snap.Hutao.Core.Setting;
|
||||
using Snap.Hutao.Model;
|
||||
using Snap.Hutao.Win32.Foundation;
|
||||
using Snap.Hutao.Win32.UI.Input.KeyboardAndMouse;
|
||||
using System.Text;
|
||||
using Windows.System;
|
||||
using Windows.Win32.Foundation;
|
||||
using Windows.Win32.UI.Input.KeyboardAndMouse;
|
||||
using static Windows.Win32.PInvoke;
|
||||
using static Snap.Hutao.Win32.User32;
|
||||
|
||||
namespace Snap.Hutao.Core.Windowing.HotKey;
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Snap.Hutao.Win32.UI.Input.KeyboardAndMouse;
|
||||
using System.Runtime.InteropServices;
|
||||
using Windows.Win32.UI.Input.KeyboardAndMouse;
|
||||
using static Windows.Win32.PInvoke;
|
||||
using static Snap.Hutao.Win32.User32;
|
||||
|
||||
namespace Snap.Hutao.Core.Windowing.HotKey;
|
||||
|
||||
@@ -14,7 +14,7 @@ internal sealed partial class HotKeyController : IHotKeyController
|
||||
{
|
||||
private static readonly WaitCallback RunMouseClickRepeatForever = MouseClickRepeatForever;
|
||||
|
||||
private readonly object locker = new();
|
||||
private readonly object syncRoot = new();
|
||||
|
||||
private readonly HotKeyOptions hotKeyOptions;
|
||||
|
||||
@@ -40,7 +40,8 @@ internal sealed partial class HotKeyController : IHotKeyController
|
||||
|
||||
private static unsafe INPUT CreateInputForMouseEvent(MOUSE_EVENT_FLAGS flags)
|
||||
{
|
||||
INPUT input = new() { type = INPUT_TYPE.INPUT_MOUSE, };
|
||||
INPUT input = default;
|
||||
input.type = INPUT_TYPE.INPUT_MOUSE;
|
||||
input.Anonymous.mi.dwFlags = flags;
|
||||
return input;
|
||||
}
|
||||
@@ -75,7 +76,7 @@ internal sealed partial class HotKeyController : IHotKeyController
|
||||
|
||||
private void ToggleMouseClickRepeatForever()
|
||||
{
|
||||
lock (locker)
|
||||
lock (syncRoot)
|
||||
{
|
||||
if (hotKeyOptions.IsMouseClickRepeatForeverOn)
|
||||
{
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Snap.Hutao.Win32.UI.Input.KeyboardAndMouse;
|
||||
using Windows.System;
|
||||
using Windows.Win32.UI.Input.KeyboardAndMouse;
|
||||
|
||||
namespace Snap.Hutao.Core.Windowing.HotKey;
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Windows.Win32.UI.WindowsAndMessaging;
|
||||
using Snap.Hutao.Win32.UI.WindowsAndMessaging;
|
||||
|
||||
namespace Snap.Hutao.Core.Windowing;
|
||||
|
||||
|
||||
@@ -9,14 +9,15 @@ using Microsoft.UI.Xaml.Media;
|
||||
using Snap.Hutao.Core.LifeCycle;
|
||||
using Snap.Hutao.Core.Setting;
|
||||
using Snap.Hutao.Service;
|
||||
using System.Diagnostics;
|
||||
using Snap.Hutao.Win32;
|
||||
using Snap.Hutao.Win32.Foundation;
|
||||
using Snap.Hutao.Win32.Graphics.Dwm;
|
||||
using Snap.Hutao.Win32.UI.WindowsAndMessaging;
|
||||
using System.IO;
|
||||
using Windows.Graphics;
|
||||
using Windows.UI;
|
||||
using Windows.Win32.Foundation;
|
||||
using Windows.Win32.Graphics.Dwm;
|
||||
using Windows.Win32.UI.WindowsAndMessaging;
|
||||
using static Windows.Win32.PInvoke;
|
||||
using static Snap.Hutao.Win32.DwmApi;
|
||||
using static Snap.Hutao.Win32.User32;
|
||||
|
||||
namespace Snap.Hutao.Core.Windowing;
|
||||
|
||||
@@ -105,11 +106,11 @@ internal sealed class WindowController
|
||||
return;
|
||||
}
|
||||
|
||||
WINDOWPLACEMENT windowPlacement = Win32.StructMarshal.WINDOWPLACEMENT();
|
||||
WINDOWPLACEMENT windowPlacement = WINDOWPLACEMENT.Create();
|
||||
GetWindowPlacement(options.Hwnd, ref windowPlacement);
|
||||
|
||||
// prevent save value when we are maximized.
|
||||
if (!windowPlacement.showCmd.HasFlag(SHOW_WINDOW_CMD.SW_SHOWMAXIMIZED))
|
||||
if (!windowPlacement.ShowCmd.HasFlag(SHOW_WINDOW_CMD.SW_SHOWMAXIMIZED))
|
||||
{
|
||||
double scale = 1.0 / options.GetRasterizationScale();
|
||||
LocalSetting.Set(SettingKeys.WindowRect, (CompactRect)window.AppWindow.GetRect().Scale(scale));
|
||||
@@ -195,7 +196,7 @@ internal sealed class WindowController
|
||||
private unsafe void UpdateImmersiveDarkMode(FrameworkElement titleBar, object discard)
|
||||
{
|
||||
BOOL isDarkMode = Control.Theme.ThemeHelper.IsDarkMode(titleBar.ActualTheme);
|
||||
DwmSetWindowAttribute(options.Hwnd, DWMWINDOWATTRIBUTE.DWMWA_USE_IMMERSIVE_DARK_MODE, &isDarkMode, unchecked((uint)sizeof(BOOL)));
|
||||
DwmSetWindowAttribute(options.Hwnd, DWMWINDOWATTRIBUTE.DWMWA_USE_IMMERSIVE_DARK_MODE, ref isDarkMode);
|
||||
}
|
||||
|
||||
private void UpdateDragRectangles()
|
||||
|
||||
@@ -2,11 +2,11 @@
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Microsoft.UI.Xaml;
|
||||
using Snap.Hutao.Win32.Foundation;
|
||||
using Snap.Hutao.Win32.UI.WindowsAndMessaging;
|
||||
using System.Runtime.CompilerServices;
|
||||
using Windows.Win32.Foundation;
|
||||
using Windows.Win32.UI.WindowsAndMessaging;
|
||||
using WinRT.Interop;
|
||||
using static Windows.Win32.PInvoke;
|
||||
using static Snap.Hutao.Win32.User32;
|
||||
|
||||
namespace Snap.Hutao.Core.Windowing;
|
||||
|
||||
@@ -24,8 +24,8 @@ internal static class WindowExtension
|
||||
public static void SetLayeredWindow(this Window window)
|
||||
{
|
||||
HWND hwnd = (HWND)WindowNative.GetWindowHandle(window);
|
||||
nint style = GetWindowLongPtr(hwnd, WINDOW_LONG_PTR_INDEX.GWL_EXSTYLE);
|
||||
nint style = GetWindowLongPtrW(hwnd, WINDOW_LONG_PTR_INDEX.GWL_EXSTYLE);
|
||||
style |= (nint)WINDOW_EX_STYLE.WS_EX_LAYERED;
|
||||
SetWindowLongPtr(hwnd, WINDOW_LONG_PTR_INDEX.GWL_EXSTYLE, style);
|
||||
SetWindowLongPtrW(hwnd, WINDOW_LONG_PTR_INDEX.GWL_EXSTYLE, style);
|
||||
}
|
||||
}
|
||||
@@ -4,10 +4,10 @@
|
||||
using Microsoft.UI.Input;
|
||||
using Microsoft.UI.Windowing;
|
||||
using Microsoft.UI.Xaml;
|
||||
using Snap.Hutao.Win32.Foundation;
|
||||
using Windows.Graphics;
|
||||
using Windows.Win32.Foundation;
|
||||
using WinRT.Interop;
|
||||
using static Windows.Win32.PInvoke;
|
||||
using static Snap.Hutao.Win32.User32;
|
||||
|
||||
namespace Snap.Hutao.Core.Windowing;
|
||||
|
||||
@@ -55,7 +55,7 @@ internal readonly struct WindowOptions
|
||||
/// <param name="persistSize">持久化尺寸</param>
|
||||
public WindowOptions(Window window, FrameworkElement titleBar, SizeInt32 initSize, bool persistSize = false)
|
||||
{
|
||||
Hwnd = (HWND)WindowNative.GetWindowHandle(window);
|
||||
Hwnd = WindowNative.GetWindowHandle(window);
|
||||
InputNonClientPointerSource = InputNonClientPointerSource.GetForWindowId(window.AppWindow.Id);
|
||||
TitleBar = titleBar;
|
||||
InitSize = initSize;
|
||||
@@ -80,8 +80,8 @@ internal readonly struct WindowOptions
|
||||
{
|
||||
HWND fgHwnd = GetForegroundWindow();
|
||||
|
||||
uint threadIdHwnd = GetWindowThreadProcessId(Hwnd);
|
||||
uint threadIdFgHwnd = GetWindowThreadProcessId(fgHwnd);
|
||||
uint threadIdHwnd = GetWindowThreadProcessId(Hwnd, default);
|
||||
uint threadIdFgHwnd = GetWindowThreadProcessId(fgHwnd, default);
|
||||
|
||||
if (threadIdHwnd != threadIdFgHwnd)
|
||||
{
|
||||
|
||||
@@ -4,10 +4,12 @@
|
||||
using Microsoft.UI.Xaml;
|
||||
using Snap.Hutao.Core.Windowing.Backdrop;
|
||||
using Snap.Hutao.Core.Windowing.HotKey;
|
||||
using Windows.Win32.Foundation;
|
||||
using Windows.Win32.UI.Shell;
|
||||
using Windows.Win32.UI.WindowsAndMessaging;
|
||||
using static Windows.Win32.PInvoke;
|
||||
using Snap.Hutao.Win32.Foundation;
|
||||
using Snap.Hutao.Win32.UI.Shell;
|
||||
using Snap.Hutao.Win32.UI.WindowsAndMessaging;
|
||||
using static Snap.Hutao.Win32.ComCtl32;
|
||||
using static Snap.Hutao.Win32.ConstValues;
|
||||
using static Snap.Hutao.Win32.User32;
|
||||
|
||||
namespace Snap.Hutao.Core.Windowing;
|
||||
|
||||
@@ -26,8 +28,8 @@ internal sealed class WindowSubclass : IDisposable
|
||||
private readonly IHotKeyController hotKeyController;
|
||||
|
||||
// We have to explicitly hold a reference to SUBCLASSPROC
|
||||
private SUBCLASSPROC? windowProc;
|
||||
private SUBCLASSPROC? legacyDragBarProc;
|
||||
private SUBCLASSPROC windowProc = default!;
|
||||
private SUBCLASSPROC legacyDragBarProc = default!;
|
||||
|
||||
public WindowSubclass(Window window, in WindowOptions options, IServiceProvider serviceProvider)
|
||||
{
|
||||
@@ -57,7 +59,7 @@ internal sealed class WindowSubclass : IDisposable
|
||||
}
|
||||
|
||||
titleBarHooked = false;
|
||||
HWND hwndDragBar = FindWindowEx(options.Hwnd, default, "DRAG_BAR_WINDOW_CLASS", default);
|
||||
HWND hwndDragBar = FindWindowExW(options.Hwnd, default, "DRAG_BAR_WINDOW_CLASS", default);
|
||||
|
||||
if (hwndDragBar.IsNull)
|
||||
{
|
||||
@@ -76,12 +78,12 @@ internal sealed class WindowSubclass : IDisposable
|
||||
hotKeyController.UnregisterAll();
|
||||
|
||||
RemoveWindowSubclass(options.Hwnd, windowProc, WindowSubclassId);
|
||||
windowProc = null;
|
||||
windowProc = default!;
|
||||
|
||||
if (options.UseLegacyDragBarImplementation)
|
||||
{
|
||||
RemoveWindowSubclass(options.Hwnd, legacyDragBarProc, DragBarSubclassId);
|
||||
legacyDragBarProc = null;
|
||||
legacyDragBarProc = default!;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -94,7 +96,7 @@ internal sealed class WindowSubclass : IDisposable
|
||||
{
|
||||
if (window is IMinMaxInfoHandler handler)
|
||||
{
|
||||
handler.HandleMinMaxInfo(ref *(MINMAXINFO*)lParam.Value, options.GetRasterizationScale());
|
||||
handler.HandleMinMaxInfo(ref *(MINMAXINFO*)lParam, options.GetRasterizationScale());
|
||||
}
|
||||
|
||||
break;
|
||||
@@ -116,7 +118,7 @@ internal sealed class WindowSubclass : IDisposable
|
||||
{
|
||||
if (window.SystemBackdrop is IBackdropNeedEraseBackground)
|
||||
{
|
||||
return (LRESULT)(int)(BOOL)true;
|
||||
return (LRESULT)(int)BOOL.TRUE;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Snap.Hutao.Core.Database;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Text;
|
||||
@@ -113,6 +114,13 @@ internal static partial class EnumerableExtension
|
||||
return new ObservableCollection<T>(source);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static ObservableReorderableDbCollection<T> ToObservableReorderableDbCollection<T>(this IEnumerable<T> source, IServiceProvider serviceProvider)
|
||||
where T : class, IReorderable
|
||||
{
|
||||
return new ObservableReorderableDbCollection<T>([.. source], serviceProvider);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Concatenates each element from the collection into single string.
|
||||
/// </summary>
|
||||
|
||||
@@ -27,18 +27,4 @@ internal static class StringExtension
|
||||
{
|
||||
return source.AsSpan().TrimEnd(value).ToString();
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool EqualsAny(this string source, in ReadOnlySpan<string> values, StringComparison comparisonType)
|
||||
{
|
||||
foreach (ref readonly string value in values)
|
||||
{
|
||||
if (source.Equals(value, comparisonType))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -3,13 +3,14 @@
|
||||
|
||||
using Snap.Hutao.Core.IO;
|
||||
using Snap.Hutao.Core.LifeCycle;
|
||||
using Snap.Hutao.Win32.Foundation;
|
||||
using Snap.Hutao.Win32.System.Com;
|
||||
using Snap.Hutao.Win32.UI.Shell;
|
||||
using Snap.Hutao.Win32.UI.Shell.Common;
|
||||
using System.Runtime.InteropServices;
|
||||
using Windows.Win32;
|
||||
using Windows.Win32.Foundation;
|
||||
using Windows.Win32.System.Com;
|
||||
using Windows.Win32.UI.Shell;
|
||||
using Windows.Win32.UI.Shell.Common;
|
||||
using static Windows.Win32.PInvoke;
|
||||
using static Snap.Hutao.Win32.Macros;
|
||||
using static Snap.Hutao.Win32.Ole32;
|
||||
using static Snap.Hutao.Win32.Shell32;
|
||||
|
||||
namespace Snap.Hutao.Factory.Picker;
|
||||
|
||||
@@ -21,32 +22,33 @@ internal sealed partial class FileSystemPickerInteraction : IFileSystemPickerInt
|
||||
|
||||
public unsafe ValueResult<bool, ValueFile> PickFile(string? title, string? defaultFileName, (string Name, string Type)[]? filters)
|
||||
{
|
||||
CoCreateInstance<FileOpenDialog, IFileOpenDialog>(default, CLSCTX.CLSCTX_INPROC_SERVER, out IFileOpenDialog dialog).ThrowOnFailure();
|
||||
HRESULT hr = CoCreateInstance(in FileOpenDialog.CLSID, default, CLSCTX.CLSCTX_INPROC_SERVER, in IFileDialog.IID, out IFileDialog* pFileDialog);
|
||||
Marshal.ThrowExceptionForHR(hr);
|
||||
|
||||
FILEOPENDIALOGOPTIONS options =
|
||||
FILEOPENDIALOGOPTIONS.FOS_NOTESTFILECREATE |
|
||||
FILEOPENDIALOGOPTIONS.FOS_FORCEFILESYSTEM |
|
||||
FILEOPENDIALOGOPTIONS.FOS_NOCHANGEDIR;
|
||||
|
||||
dialog.SetOptions(options);
|
||||
SetDesktopAsStartupFolder(dialog);
|
||||
|
||||
if (!string.IsNullOrEmpty(title))
|
||||
{
|
||||
dialog.SetTitle(title);
|
||||
}
|
||||
pFileDialog->SetOptions(options);
|
||||
SetDesktopAsStartupFolder(pFileDialog);
|
||||
|
||||
if (!string.IsNullOrEmpty(defaultFileName))
|
||||
{
|
||||
dialog.SetFileName(defaultFileName);
|
||||
pFileDialog->SetFileName(defaultFileName);
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(title))
|
||||
{
|
||||
pFileDialog->SetTitle(title);
|
||||
}
|
||||
|
||||
if (filters is { Length: > 0 })
|
||||
{
|
||||
SetFileTypes(dialog, filters);
|
||||
SetFileTypes(pFileDialog, filters);
|
||||
}
|
||||
|
||||
HRESULT res = dialog.Show(currentWindowReference.GetWindowHandle());
|
||||
HRESULT res = pFileDialog->Show(currentWindowReference.GetWindowHandle());
|
||||
if (res == HRESULT_FROM_WIN32(WIN32_ERROR.ERROR_CANCELLED))
|
||||
{
|
||||
return new(false, default);
|
||||
@@ -56,18 +58,18 @@ internal sealed partial class FileSystemPickerInteraction : IFileSystemPickerInt
|
||||
Marshal.ThrowExceptionForHR(res);
|
||||
}
|
||||
|
||||
dialog.GetResult(out IShellItem item);
|
||||
HRESULT t = pFileDialog->GetResult(out IShellItem* pShellItem);
|
||||
|
||||
PWSTR displayName = default;
|
||||
string file;
|
||||
try
|
||||
{
|
||||
item.GetDisplayName(SIGDN.SIGDN_FILESYSPATH, out displayName);
|
||||
file = new((char*)displayName);
|
||||
pShellItem->GetDisplayName(SIGDN.SIGDN_FILESYSPATH, out displayName);
|
||||
file = new(displayName);
|
||||
}
|
||||
finally
|
||||
{
|
||||
Marshal.FreeCoTaskMem((nint)displayName.Value);
|
||||
CoTaskMemFree(displayName);
|
||||
}
|
||||
|
||||
return new(true, file);
|
||||
@@ -75,7 +77,8 @@ internal sealed partial class FileSystemPickerInteraction : IFileSystemPickerInt
|
||||
|
||||
public unsafe ValueResult<bool, ValueFile> SaveFile(string? title, string? defaultFileName, (string Name, string Type)[]? filters)
|
||||
{
|
||||
CoCreateInstance<FileSaveDialog, IFileSaveDialog>(default, CLSCTX.CLSCTX_INPROC_SERVER, out IFileSaveDialog dialog).ThrowOnFailure();
|
||||
HRESULT hr = CoCreateInstance(in FileSaveDialog.CLSID, default, CLSCTX.CLSCTX_INPROC_SERVER, in IFileDialog.IID, out IFileDialog* pFileDialog);
|
||||
Marshal.ThrowExceptionForHR(hr);
|
||||
|
||||
FILEOPENDIALOGOPTIONS options =
|
||||
FILEOPENDIALOGOPTIONS.FOS_NOTESTFILECREATE |
|
||||
@@ -83,25 +86,25 @@ internal sealed partial class FileSystemPickerInteraction : IFileSystemPickerInt
|
||||
FILEOPENDIALOGOPTIONS.FOS_STRICTFILETYPES |
|
||||
FILEOPENDIALOGOPTIONS.FOS_NOCHANGEDIR;
|
||||
|
||||
dialog.SetOptions(options);
|
||||
SetDesktopAsStartupFolder(dialog);
|
||||
|
||||
if (!string.IsNullOrEmpty(title))
|
||||
{
|
||||
dialog.SetTitle(title);
|
||||
}
|
||||
pFileDialog->SetOptions(options);
|
||||
SetDesktopAsStartupFolder(pFileDialog);
|
||||
|
||||
if (!string.IsNullOrEmpty(defaultFileName))
|
||||
{
|
||||
dialog.SetFileName(defaultFileName);
|
||||
pFileDialog->SetFileName(defaultFileName);
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(title))
|
||||
{
|
||||
pFileDialog->SetTitle(title);
|
||||
}
|
||||
|
||||
if (filters is { Length: > 0 })
|
||||
{
|
||||
SetFileTypes(dialog, filters);
|
||||
SetFileTypes(pFileDialog, filters);
|
||||
}
|
||||
|
||||
HRESULT res = dialog.Show(currentWindowReference.GetWindowHandle());
|
||||
HRESULT res = pFileDialog->Show(currentWindowReference.GetWindowHandle());
|
||||
if (res == HRESULT_FROM_WIN32(WIN32_ERROR.ERROR_CANCELLED))
|
||||
{
|
||||
return new(false, default);
|
||||
@@ -111,18 +114,18 @@ internal sealed partial class FileSystemPickerInteraction : IFileSystemPickerInt
|
||||
Marshal.ThrowExceptionForHR(res);
|
||||
}
|
||||
|
||||
dialog.GetResult(out IShellItem item);
|
||||
pFileDialog->GetResult(out IShellItem* pShellItem);
|
||||
|
||||
PWSTR displayName = default;
|
||||
string file;
|
||||
try
|
||||
{
|
||||
item.GetDisplayName(SIGDN.SIGDN_FILESYSPATH, out displayName);
|
||||
file = new((char*)displayName);
|
||||
pShellItem->GetDisplayName(SIGDN.SIGDN_FILESYSPATH, out displayName);
|
||||
file = new(displayName);
|
||||
}
|
||||
finally
|
||||
{
|
||||
Marshal.FreeCoTaskMem((nint)displayName.Value);
|
||||
CoTaskMemFree(displayName);
|
||||
}
|
||||
|
||||
return new(true, file);
|
||||
@@ -130,7 +133,8 @@ internal sealed partial class FileSystemPickerInteraction : IFileSystemPickerInt
|
||||
|
||||
public unsafe ValueResult<bool, string> PickFolder(string? title)
|
||||
{
|
||||
CoCreateInstance<FileOpenDialog, IFileOpenDialog>(default, CLSCTX.CLSCTX_INPROC_SERVER, out IFileOpenDialog dialog).ThrowOnFailure();
|
||||
HRESULT hr = CoCreateInstance(in FileOpenDialog.CLSID, default, CLSCTX.CLSCTX_INPROC_SERVER, in IFileDialog.IID, out IFileDialog* pFileDialog);
|
||||
Marshal.ThrowExceptionForHR(hr);
|
||||
|
||||
FILEOPENDIALOGOPTIONS options =
|
||||
FILEOPENDIALOGOPTIONS.FOS_NOTESTFILECREATE |
|
||||
@@ -138,15 +142,15 @@ internal sealed partial class FileSystemPickerInteraction : IFileSystemPickerInt
|
||||
FILEOPENDIALOGOPTIONS.FOS_PICKFOLDERS |
|
||||
FILEOPENDIALOGOPTIONS.FOS_NOCHANGEDIR;
|
||||
|
||||
dialog.SetOptions(options);
|
||||
SetDesktopAsStartupFolder(dialog);
|
||||
pFileDialog->SetOptions(options);
|
||||
SetDesktopAsStartupFolder(pFileDialog);
|
||||
|
||||
if (!string.IsNullOrEmpty(title))
|
||||
{
|
||||
dialog.SetTitle(title);
|
||||
pFileDialog->SetTitle(title);
|
||||
}
|
||||
|
||||
HRESULT res = dialog.Show(currentWindowReference.GetWindowHandle());
|
||||
HRESULT res = pFileDialog->Show(currentWindowReference.GetWindowHandle());
|
||||
if (res == HRESULT_FROM_WIN32(WIN32_ERROR.ERROR_CANCELLED))
|
||||
{
|
||||
return new(false, default!);
|
||||
@@ -156,25 +160,24 @@ internal sealed partial class FileSystemPickerInteraction : IFileSystemPickerInt
|
||||
Marshal.ThrowExceptionForHR(res);
|
||||
}
|
||||
|
||||
dialog.GetResult(out IShellItem item);
|
||||
pFileDialog->GetResult(out IShellItem* pShellItem);
|
||||
|
||||
PWSTR displayName = default;
|
||||
string file;
|
||||
try
|
||||
{
|
||||
item.GetDisplayName(SIGDN.SIGDN_FILESYSPATH, out displayName);
|
||||
pShellItem->GetDisplayName(SIGDN.SIGDN_FILESYSPATH, out displayName);
|
||||
file = new((char*)displayName);
|
||||
}
|
||||
finally
|
||||
{
|
||||
Marshal.FreeCoTaskMem((nint)displayName.Value);
|
||||
CoTaskMemFree(displayName);
|
||||
}
|
||||
|
||||
return new(true, file);
|
||||
}
|
||||
|
||||
private static unsafe void SetFileTypes<TDialog>(TDialog dialog, (string Name, string Type)[] filters)
|
||||
where TDialog : IFileDialog
|
||||
private static unsafe void SetFileTypes(IFileDialog* pFileDialog, (string Name, string Type)[] filters)
|
||||
{
|
||||
List<nint> unmanagedStringPtrs = new(filters.Length * 2);
|
||||
List<COMDLG_FILTERSPEC> filterSpecs = new(filters.Length);
|
||||
@@ -190,10 +193,7 @@ internal sealed partial class FileSystemPickerInteraction : IFileSystemPickerInt
|
||||
filterSpecs.Add(spec);
|
||||
}
|
||||
|
||||
fixed (COMDLG_FILTERSPEC* ptr = CollectionsMarshal.AsSpan(filterSpecs))
|
||||
{
|
||||
dialog.SetFileTypes((uint)filterSpecs.Count, ptr);
|
||||
}
|
||||
pFileDialog->SetFileTypes(CollectionsMarshal.AsSpan(filterSpecs));
|
||||
|
||||
foreach (ref readonly nint ptr in CollectionsMarshal.AsSpan(unmanagedStringPtrs))
|
||||
{
|
||||
@@ -201,11 +201,11 @@ internal sealed partial class FileSystemPickerInteraction : IFileSystemPickerInt
|
||||
}
|
||||
}
|
||||
|
||||
private static unsafe void SetDesktopAsStartupFolder<TDialog>(TDialog dialog)
|
||||
where TDialog : IFileDialog
|
||||
private static unsafe void SetDesktopAsStartupFolder(IFileDialog* pFileDialog)
|
||||
{
|
||||
string desktopPath = Environment.GetFolderPath(Environment.SpecialFolder.Desktop);
|
||||
SHCreateItemFromParsingName(desktopPath, default, typeof(IShellItem).GUID, out object shellItem).ThrowOnFailure();
|
||||
dialog.SetFolder((IShellItem)shellItem);
|
||||
HRESULT hr = SHCreateItemFromParsingName(desktopPath, default, in IShellItem.IID, out IShellItem* pShellItem);
|
||||
Marshal.ThrowExceptionForHR(hr);
|
||||
pFileDialog->SetFolder(pShellItem);
|
||||
}
|
||||
}
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
using Microsoft.UI.Xaml;
|
||||
using Snap.Hutao.Core.Windowing;
|
||||
using Windows.Win32.UI.WindowsAndMessaging;
|
||||
using Snap.Hutao.Win32.UI.WindowsAndMessaging;
|
||||
|
||||
namespace Snap.Hutao;
|
||||
|
||||
@@ -32,9 +32,9 @@ internal sealed partial class GuideWindow : Window, IWindowOptionsSource, IMinMa
|
||||
|
||||
public unsafe void HandleMinMaxInfo(ref MINMAXINFO info, double scalingFactor)
|
||||
{
|
||||
info.ptMinTrackSize.X = (int)Math.Max(MinWidth * scalingFactor, info.ptMinTrackSize.X);
|
||||
info.ptMinTrackSize.Y = (int)Math.Max(MinHeight * scalingFactor, info.ptMinTrackSize.Y);
|
||||
info.ptMaxTrackSize.X = (int)Math.Min(MaxWidth * scalingFactor, info.ptMaxTrackSize.X);
|
||||
info.ptMaxTrackSize.Y = (int)Math.Min(MaxHeight * scalingFactor, info.ptMaxTrackSize.Y);
|
||||
info.ptMinTrackSize.x = (int)Math.Max(MinWidth * scalingFactor, info.ptMinTrackSize.x);
|
||||
info.ptMinTrackSize.y = (int)Math.Max(MinHeight * scalingFactor, info.ptMinTrackSize.y);
|
||||
info.ptMaxTrackSize.x = (int)Math.Min(MaxWidth * scalingFactor, info.ptMaxTrackSize.x);
|
||||
info.ptMaxTrackSize.y = (int)Math.Min(MaxHeight * scalingFactor, info.ptMaxTrackSize.y);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
using Microsoft.UI.Windowing;
|
||||
using Microsoft.UI.Xaml;
|
||||
using Snap.Hutao.Core;
|
||||
using Snap.Hutao.Win32;
|
||||
using Windows.Graphics;
|
||||
|
||||
namespace Snap.Hutao;
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
using Microsoft.UI.Xaml;
|
||||
using Snap.Hutao.Core.Windowing;
|
||||
using Snap.Hutao.ViewModel.Game;
|
||||
using Windows.Win32.UI.WindowsAndMessaging;
|
||||
using Snap.Hutao.Win32.UI.WindowsAndMessaging;
|
||||
|
||||
namespace Snap.Hutao;
|
||||
|
||||
@@ -50,9 +50,9 @@ internal sealed partial class LaunchGameWindow : Window, IDisposable, IWindowOpt
|
||||
/// <inheritdoc/>
|
||||
public unsafe void HandleMinMaxInfo(ref MINMAXINFO info, double scalingFactor)
|
||||
{
|
||||
info.ptMinTrackSize.X = (int)Math.Max(MinWidth * scalingFactor, info.ptMinTrackSize.X);
|
||||
info.ptMinTrackSize.Y = (int)Math.Max(MinHeight * scalingFactor, info.ptMinTrackSize.Y);
|
||||
info.ptMaxTrackSize.X = (int)Math.Min(MaxWidth * scalingFactor, info.ptMaxTrackSize.X);
|
||||
info.ptMaxTrackSize.Y = (int)Math.Min(MaxHeight * scalingFactor, info.ptMaxTrackSize.Y);
|
||||
info.ptMinTrackSize.x = (int)Math.Max(MinWidth * scalingFactor, info.ptMinTrackSize.x);
|
||||
info.ptMinTrackSize.y = (int)Math.Max(MinHeight * scalingFactor, info.ptMinTrackSize.y);
|
||||
info.ptMaxTrackSize.x = (int)Math.Min(MaxWidth * scalingFactor, info.ptMaxTrackSize.x);
|
||||
info.ptMaxTrackSize.y = (int)Math.Min(MaxHeight * scalingFactor, info.ptMaxTrackSize.y);
|
||||
}
|
||||
}
|
||||
@@ -2,18 +2,18 @@
|
||||
x:Class="Snap.Hutao.MainWindow"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:cwuc="using:CommunityToolkit.WinUI.UI.Controls"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:shv="using:Snap.Hutao.View"
|
||||
mc:Ignorable="d">
|
||||
|
||||
<Grid>
|
||||
<shv:MainView/>
|
||||
|
||||
<shv:TitleView
|
||||
x:Name="TitleBarView"
|
||||
Height="44"
|
||||
Margin="48,0,0,0"/>
|
||||
Margin="44,0,0,0"/>
|
||||
|
||||
<shv:MainView/>
|
||||
</Grid>
|
||||
</Window>
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
using Microsoft.UI.Xaml;
|
||||
using Snap.Hutao.Core.Windowing;
|
||||
using Windows.Win32.UI.WindowsAndMessaging;
|
||||
using Snap.Hutao.Win32.UI.WindowsAndMessaging;
|
||||
|
||||
namespace Snap.Hutao;
|
||||
|
||||
@@ -37,7 +37,7 @@ internal sealed partial class MainWindow : Window, IWindowOptionsSource, IMinMax
|
||||
/// <inheritdoc/>
|
||||
public unsafe void HandleMinMaxInfo(ref MINMAXINFO pInfo, double scalingFactor)
|
||||
{
|
||||
pInfo.ptMinTrackSize.X = (int)Math.Max(MinWidth * scalingFactor, pInfo.ptMinTrackSize.X);
|
||||
pInfo.ptMinTrackSize.Y = (int)Math.Max(MinHeight * scalingFactor, pInfo.ptMinTrackSize.Y);
|
||||
pInfo.ptMinTrackSize.x = (int)Math.Max(MinWidth * scalingFactor, pInfo.ptMinTrackSize.x);
|
||||
pInfo.ptMinTrackSize.y = (int)Math.Max(MinHeight * scalingFactor, pInfo.ptMinTrackSize.y);
|
||||
}
|
||||
}
|
||||
624
src/Snap.Hutao/Snap.Hutao/Migrations/20240202015857_ImplReorderableOnUserAndGameAccount.Designer.cs
generated
Normal file
624
src/Snap.Hutao/Snap.Hutao/Migrations/20240202015857_ImplReorderableOnUserAndGameAccount.Designer.cs
generated
Normal file
@@ -0,0 +1,624 @@
|
||||
// <auto-generated />
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
using Snap.Hutao.Model.Entity.Database;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Snap.Hutao.Migrations
|
||||
{
|
||||
[DbContext(typeof(AppDbContext))]
|
||||
[Migration("20240202015857_ImplReorderableOnUserAndGameAccount")]
|
||||
partial class ImplReorderableOnUserAndGameAccount
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder.HasAnnotation("ProductVersion", "8.0.1");
|
||||
|
||||
modelBuilder.Entity("Snap.Hutao.Model.Entity.Achievement", b =>
|
||||
{
|
||||
b.Property<Guid>("InnerId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<Guid>("ArchiveId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<uint>("Current")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<uint>("Id")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("Status")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<DateTimeOffset>("Time")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("InnerId");
|
||||
|
||||
b.HasIndex("ArchiveId");
|
||||
|
||||
b.ToTable("achievements");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Snap.Hutao.Model.Entity.AchievementArchive", b =>
|
||||
{
|
||||
b.Property<Guid>("InnerId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<bool>("IsSelected")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("InnerId");
|
||||
|
||||
b.ToTable("achievement_archives");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Snap.Hutao.Model.Entity.AvatarInfo", b =>
|
||||
{
|
||||
b.Property<Guid>("InnerId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTimeOffset>("CalculatorRefreshTime")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTimeOffset>("GameRecordRefreshTime")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Info")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTimeOffset>("ShowcaseRefreshTime")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Uid")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("InnerId");
|
||||
|
||||
b.ToTable("avatar_infos");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Snap.Hutao.Model.Entity.CultivateEntry", b =>
|
||||
{
|
||||
b.Property<Guid>("InnerId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<uint>("Id")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<Guid>("ProjectId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("Type")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasKey("InnerId");
|
||||
|
||||
b.HasIndex("ProjectId");
|
||||
|
||||
b.ToTable("cultivate_entries");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Snap.Hutao.Model.Entity.CultivateEntryLevelInformation", b =>
|
||||
{
|
||||
b.Property<Guid>("InnerId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<uint>("AvatarLevelFrom")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<uint>("AvatarLevelTo")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<Guid>("EntryId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<uint>("SkillALevelFrom")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<uint>("SkillALevelTo")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<uint>("SkillELevelFrom")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<uint>("SkillELevelTo")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<uint>("SkillQLevelFrom")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<uint>("SkillQLevelTo")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<uint>("WeaponLevelFrom")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<uint>("WeaponLevelTo")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasKey("InnerId");
|
||||
|
||||
b.HasIndex("EntryId")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("cultivate_entry_level_informations");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Snap.Hutao.Model.Entity.CultivateItem", b =>
|
||||
{
|
||||
b.Property<Guid>("InnerId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<uint>("Count")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<Guid>("EntryId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<bool>("IsFinished")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<uint>("ItemId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasKey("InnerId");
|
||||
|
||||
b.HasIndex("EntryId");
|
||||
|
||||
b.ToTable("cultivate_items");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Snap.Hutao.Model.Entity.CultivateProject", b =>
|
||||
{
|
||||
b.Property<Guid>("InnerId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("AttachedUid")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<bool>("IsSelected")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("InnerId");
|
||||
|
||||
b.ToTable("cultivate_projects");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Snap.Hutao.Model.Entity.DailyNoteEntry", b =>
|
||||
{
|
||||
b.Property<Guid>("InnerId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("DailyNote")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<bool>("DailyTaskNotify")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<bool>("DailyTaskNotifySuppressed")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<bool>("ExpeditionNotify")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<bool>("ExpeditionNotifySuppressed")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<bool>("HomeCoinNotifySuppressed")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("HomeCoinNotifyThreshold")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<DateTimeOffset>("RefreshTime")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<bool>("ResinNotifySuppressed")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("ResinNotifyThreshold")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<bool>("TransformerNotify")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<bool>("TransformerNotifySuppressed")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Uid")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<Guid>("UserId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("InnerId");
|
||||
|
||||
b.HasIndex("UserId");
|
||||
|
||||
b.ToTable("daily_notes");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Snap.Hutao.Model.Entity.GachaArchive", b =>
|
||||
{
|
||||
b.Property<Guid>("InnerId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<bool>("IsSelected")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Uid")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("InnerId");
|
||||
|
||||
b.ToTable("gacha_archives");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Snap.Hutao.Model.Entity.GachaItem", b =>
|
||||
{
|
||||
b.Property<Guid>("InnerId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<Guid>("ArchiveId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("GachaType")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<long>("Id")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<uint>("ItemId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("QueryType")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<DateTimeOffset>("Time")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("InnerId");
|
||||
|
||||
b.HasIndex("ArchiveId");
|
||||
|
||||
b.ToTable("gacha_items");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Snap.Hutao.Model.Entity.GameAccount", b =>
|
||||
{
|
||||
b.Property<Guid>("InnerId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("AttachUid")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("Index")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("MihoyoSDK")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("Type")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasKey("InnerId");
|
||||
|
||||
b.ToTable("game_accounts");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Snap.Hutao.Model.Entity.InventoryItem", b =>
|
||||
{
|
||||
b.Property<Guid>("InnerId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<uint>("Count")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<uint>("ItemId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<Guid>("ProjectId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("InnerId");
|
||||
|
||||
b.HasIndex("ProjectId");
|
||||
|
||||
b.ToTable("inventory_items");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Snap.Hutao.Model.Entity.InventoryReliquary", b =>
|
||||
{
|
||||
b.Property<Guid>("InnerId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("AppendPropIdList")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("ItemId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("Level")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("MainPropId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<Guid>("ProjectId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("InnerId");
|
||||
|
||||
b.HasIndex("ProjectId");
|
||||
|
||||
b.ToTable("inventory_reliquaries");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Snap.Hutao.Model.Entity.InventoryWeapon", b =>
|
||||
{
|
||||
b.Property<Guid>("InnerId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("ItemId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("Level")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<Guid>("ProjectId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("PromoteLevel")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasKey("InnerId");
|
||||
|
||||
b.HasIndex("ProjectId");
|
||||
|
||||
b.ToTable("inventory_weapons");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Snap.Hutao.Model.Entity.ObjectCacheEntry", b =>
|
||||
{
|
||||
b.Property<string>("Key")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTimeOffset>("ExpireTime")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Value")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Key");
|
||||
|
||||
b.ToTable("object_cache");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Snap.Hutao.Model.Entity.SettingEntry", b =>
|
||||
{
|
||||
b.Property<string>("Key")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Value")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Key");
|
||||
|
||||
b.ToTable("settings");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Snap.Hutao.Model.Entity.SpiralAbyssEntry", b =>
|
||||
{
|
||||
b.Property<Guid>("InnerId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<uint>("ScheduleId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("SpiralAbyss")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Uid")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("InnerId");
|
||||
|
||||
b.ToTable("spiral_abysses");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Snap.Hutao.Model.Entity.User", b =>
|
||||
{
|
||||
b.Property<Guid>("InnerId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Aid")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("CookieToken")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTimeOffset>("CookieTokenLastUpdateTime")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Fingerprint")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTimeOffset>("FingerprintLastUpdateTime")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("Index")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<bool>("IsOversea")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<bool>("IsSelected")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("LToken")
|
||||
.HasColumnType("TEXT")
|
||||
.HasColumnName("Ltoken");
|
||||
|
||||
b.Property<string>("Mid")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("SToken")
|
||||
.HasColumnType("TEXT")
|
||||
.HasColumnName("Stoken");
|
||||
|
||||
b.HasKey("InnerId");
|
||||
|
||||
b.ToTable("users");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Snap.Hutao.Model.Entity.Achievement", b =>
|
||||
{
|
||||
b.HasOne("Snap.Hutao.Model.Entity.AchievementArchive", "Archive")
|
||||
.WithMany()
|
||||
.HasForeignKey("ArchiveId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Archive");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Snap.Hutao.Model.Entity.CultivateEntry", b =>
|
||||
{
|
||||
b.HasOne("Snap.Hutao.Model.Entity.CultivateProject", "Project")
|
||||
.WithMany()
|
||||
.HasForeignKey("ProjectId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Project");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Snap.Hutao.Model.Entity.CultivateEntryLevelInformation", b =>
|
||||
{
|
||||
b.HasOne("Snap.Hutao.Model.Entity.CultivateEntry", "Entry")
|
||||
.WithOne("LevelInformation")
|
||||
.HasForeignKey("Snap.Hutao.Model.Entity.CultivateEntryLevelInformation", "EntryId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Entry");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Snap.Hutao.Model.Entity.CultivateItem", b =>
|
||||
{
|
||||
b.HasOne("Snap.Hutao.Model.Entity.CultivateEntry", "Entry")
|
||||
.WithMany()
|
||||
.HasForeignKey("EntryId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Entry");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Snap.Hutao.Model.Entity.DailyNoteEntry", b =>
|
||||
{
|
||||
b.HasOne("Snap.Hutao.Model.Entity.User", "User")
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("User");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Snap.Hutao.Model.Entity.GachaItem", b =>
|
||||
{
|
||||
b.HasOne("Snap.Hutao.Model.Entity.GachaArchive", "Archive")
|
||||
.WithMany()
|
||||
.HasForeignKey("ArchiveId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Archive");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Snap.Hutao.Model.Entity.InventoryItem", b =>
|
||||
{
|
||||
b.HasOne("Snap.Hutao.Model.Entity.CultivateProject", "Project")
|
||||
.WithMany()
|
||||
.HasForeignKey("ProjectId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Project");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Snap.Hutao.Model.Entity.InventoryReliquary", b =>
|
||||
{
|
||||
b.HasOne("Snap.Hutao.Model.Entity.CultivateProject", "Project")
|
||||
.WithMany()
|
||||
.HasForeignKey("ProjectId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Project");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Snap.Hutao.Model.Entity.InventoryWeapon", b =>
|
||||
{
|
||||
b.HasOne("Snap.Hutao.Model.Entity.CultivateProject", "Project")
|
||||
.WithMany()
|
||||
.HasForeignKey("ProjectId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Project");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Snap.Hutao.Model.Entity.CultivateEntry", b =>
|
||||
{
|
||||
b.Navigation("LevelInformation");
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
// <auto-generated />
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Snap.Hutao.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class ImplReorderableOnUserAndGameAccount : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropIndex(
|
||||
name: "IX_cultivate_entry_level_informations_EntryId",
|
||||
table: "cultivate_entry_level_informations");
|
||||
|
||||
migrationBuilder.AddColumn<int>(
|
||||
name: "Index",
|
||||
table: "users",
|
||||
type: "INTEGER",
|
||||
nullable: false,
|
||||
defaultValue: 0);
|
||||
|
||||
migrationBuilder.AddColumn<int>(
|
||||
name: "Index",
|
||||
table: "game_accounts",
|
||||
type: "INTEGER",
|
||||
nullable: false,
|
||||
defaultValue: 0);
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_cultivate_entry_level_informations_EntryId",
|
||||
table: "cultivate_entry_level_informations",
|
||||
column: "EntryId",
|
||||
unique: true);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropIndex(
|
||||
name: "IX_cultivate_entry_level_informations_EntryId",
|
||||
table: "cultivate_entry_level_informations");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "Index",
|
||||
table: "users");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "Index",
|
||||
table: "game_accounts");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_cultivate_entry_level_informations_EntryId",
|
||||
table: "cultivate_entry_level_informations",
|
||||
column: "EntryId");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -15,7 +15,7 @@ namespace Snap.Hutao.Migrations
|
||||
protected override void BuildModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder.HasAnnotation("ProductVersion", "8.0.0");
|
||||
modelBuilder.HasAnnotation("ProductVersion", "8.0.1");
|
||||
|
||||
modelBuilder.Entity("Snap.Hutao.Model.Entity.Achievement", b =>
|
||||
{
|
||||
@@ -42,7 +42,7 @@ namespace Snap.Hutao.Migrations
|
||||
|
||||
b.HasIndex("ArchiveId");
|
||||
|
||||
b.ToTable("achievements", (string)null);
|
||||
b.ToTable("achievements");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Snap.Hutao.Model.Entity.AchievementArchive", b =>
|
||||
@@ -60,7 +60,7 @@ namespace Snap.Hutao.Migrations
|
||||
|
||||
b.HasKey("InnerId");
|
||||
|
||||
b.ToTable("achievement_archives", (string)null);
|
||||
b.ToTable("achievement_archives");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Snap.Hutao.Model.Entity.AvatarInfo", b =>
|
||||
@@ -88,7 +88,7 @@ namespace Snap.Hutao.Migrations
|
||||
|
||||
b.HasKey("InnerId");
|
||||
|
||||
b.ToTable("avatar_infos", (string)null);
|
||||
b.ToTable("avatar_infos");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Snap.Hutao.Model.Entity.CultivateEntry", b =>
|
||||
@@ -110,7 +110,7 @@ namespace Snap.Hutao.Migrations
|
||||
|
||||
b.HasIndex("ProjectId");
|
||||
|
||||
b.ToTable("cultivate_entries", (string)null);
|
||||
b.ToTable("cultivate_entries");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Snap.Hutao.Model.Entity.CultivateEntryLevelInformation", b =>
|
||||
@@ -154,9 +154,10 @@ namespace Snap.Hutao.Migrations
|
||||
|
||||
b.HasKey("InnerId");
|
||||
|
||||
b.HasIndex("EntryId");
|
||||
b.HasIndex("EntryId")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("cultivate_entry_level_informations", (string)null);
|
||||
b.ToTable("cultivate_entry_level_informations");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Snap.Hutao.Model.Entity.CultivateItem", b =>
|
||||
@@ -181,7 +182,7 @@ namespace Snap.Hutao.Migrations
|
||||
|
||||
b.HasIndex("EntryId");
|
||||
|
||||
b.ToTable("cultivate_items", (string)null);
|
||||
b.ToTable("cultivate_items");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Snap.Hutao.Model.Entity.CultivateProject", b =>
|
||||
@@ -202,7 +203,7 @@ namespace Snap.Hutao.Migrations
|
||||
|
||||
b.HasKey("InnerId");
|
||||
|
||||
b.ToTable("cultivate_projects", (string)null);
|
||||
b.ToTable("cultivate_projects");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Snap.Hutao.Model.Entity.DailyNoteEntry", b =>
|
||||
@@ -258,7 +259,7 @@ namespace Snap.Hutao.Migrations
|
||||
|
||||
b.HasIndex("UserId");
|
||||
|
||||
b.ToTable("daily_notes", (string)null);
|
||||
b.ToTable("daily_notes");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Snap.Hutao.Model.Entity.GachaArchive", b =>
|
||||
@@ -276,7 +277,7 @@ namespace Snap.Hutao.Migrations
|
||||
|
||||
b.HasKey("InnerId");
|
||||
|
||||
b.ToTable("gacha_archives", (string)null);
|
||||
b.ToTable("gacha_archives");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Snap.Hutao.Model.Entity.GachaItem", b =>
|
||||
@@ -307,7 +308,7 @@ namespace Snap.Hutao.Migrations
|
||||
|
||||
b.HasIndex("ArchiveId");
|
||||
|
||||
b.ToTable("gacha_items", (string)null);
|
||||
b.ToTable("gacha_items");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Snap.Hutao.Model.Entity.GameAccount", b =>
|
||||
@@ -319,6 +320,9 @@ namespace Snap.Hutao.Migrations
|
||||
b.Property<string>("AttachUid")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("Index")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("MihoyoSDK")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
@@ -332,7 +336,7 @@ namespace Snap.Hutao.Migrations
|
||||
|
||||
b.HasKey("InnerId");
|
||||
|
||||
b.ToTable("game_accounts", (string)null);
|
||||
b.ToTable("game_accounts");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Snap.Hutao.Model.Entity.InventoryItem", b =>
|
||||
@@ -354,7 +358,7 @@ namespace Snap.Hutao.Migrations
|
||||
|
||||
b.HasIndex("ProjectId");
|
||||
|
||||
b.ToTable("inventory_items", (string)null);
|
||||
b.ToTable("inventory_items");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Snap.Hutao.Model.Entity.InventoryReliquary", b =>
|
||||
@@ -383,7 +387,7 @@ namespace Snap.Hutao.Migrations
|
||||
|
||||
b.HasIndex("ProjectId");
|
||||
|
||||
b.ToTable("inventory_reliquaries", (string)null);
|
||||
b.ToTable("inventory_reliquaries");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Snap.Hutao.Model.Entity.InventoryWeapon", b =>
|
||||
@@ -408,7 +412,7 @@ namespace Snap.Hutao.Migrations
|
||||
|
||||
b.HasIndex("ProjectId");
|
||||
|
||||
b.ToTable("inventory_weapons", (string)null);
|
||||
b.ToTable("inventory_weapons");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Snap.Hutao.Model.Entity.ObjectCacheEntry", b =>
|
||||
@@ -424,7 +428,7 @@ namespace Snap.Hutao.Migrations
|
||||
|
||||
b.HasKey("Key");
|
||||
|
||||
b.ToTable("object_cache", (string)null);
|
||||
b.ToTable("object_cache");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Snap.Hutao.Model.Entity.SettingEntry", b =>
|
||||
@@ -437,7 +441,7 @@ namespace Snap.Hutao.Migrations
|
||||
|
||||
b.HasKey("Key");
|
||||
|
||||
b.ToTable("settings", (string)null);
|
||||
b.ToTable("settings");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Snap.Hutao.Model.Entity.SpiralAbyssEntry", b =>
|
||||
@@ -459,7 +463,7 @@ namespace Snap.Hutao.Migrations
|
||||
|
||||
b.HasKey("InnerId");
|
||||
|
||||
b.ToTable("spiral_abysses", (string)null);
|
||||
b.ToTable("spiral_abysses");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Snap.Hutao.Model.Entity.User", b =>
|
||||
@@ -483,6 +487,9 @@ namespace Snap.Hutao.Migrations
|
||||
b.Property<DateTimeOffset>("FingerprintLastUpdateTime")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("Index")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<bool>("IsOversea")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
@@ -502,7 +509,7 @@ namespace Snap.Hutao.Migrations
|
||||
|
||||
b.HasKey("InnerId");
|
||||
|
||||
b.ToTable("users", (string)null);
|
||||
b.ToTable("users");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Snap.Hutao.Model.Entity.Achievement", b =>
|
||||
@@ -530,8 +537,8 @@ namespace Snap.Hutao.Migrations
|
||||
modelBuilder.Entity("Snap.Hutao.Model.Entity.CultivateEntryLevelInformation", b =>
|
||||
{
|
||||
b.HasOne("Snap.Hutao.Model.Entity.CultivateEntry", "Entry")
|
||||
.WithMany()
|
||||
.HasForeignKey("EntryId")
|
||||
.WithOne("LevelInformation")
|
||||
.HasForeignKey("Snap.Hutao.Model.Entity.CultivateEntryLevelInformation", "EntryId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
@@ -603,6 +610,11 @@ namespace Snap.Hutao.Migrations
|
||||
|
||||
b.Navigation("Project");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Snap.Hutao.Model.Entity.CultivateEntry", b =>
|
||||
{
|
||||
b.Navigation("LevelInformation");
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using Snap.Hutao.Core.Abstraction;
|
||||
using Snap.Hutao.Core.Database;
|
||||
using Snap.Hutao.Model.Entity.Primitive;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
@@ -14,7 +15,7 @@ namespace Snap.Hutao.Model.Entity;
|
||||
/// </summary>
|
||||
[HighQuality]
|
||||
[Table("game_accounts")]
|
||||
internal sealed class GameAccount : ObservableObject, IMappingFrom<GameAccount, string, string, SchemeType>
|
||||
internal sealed class GameAccount : ObservableObject, IReorderable, IMappingFrom<GameAccount, string, string, SchemeType>
|
||||
{
|
||||
/// <summary>
|
||||
/// 内部Id
|
||||
@@ -44,6 +45,8 @@ internal sealed class GameAccount : ObservableObject, IMappingFrom<GameAccount,
|
||||
/// </summary>
|
||||
public string MihoyoSDK { get; set; } = default!;
|
||||
|
||||
public int Index { get; set; }
|
||||
|
||||
public static GameAccount From(string name, string sdk, SchemeType type)
|
||||
{
|
||||
return new()
|
||||
|
||||
@@ -14,7 +14,7 @@ namespace Snap.Hutao.Model.Entity;
|
||||
/// </summary>
|
||||
[HighQuality]
|
||||
[Table("users")]
|
||||
internal sealed class User : ISelectable, IMappingFrom<User, Cookie, bool>
|
||||
internal sealed class User : ISelectable, IReorderable, IMappingFrom<User, Cookie, bool>
|
||||
{
|
||||
/// <summary>
|
||||
/// 内部Id
|
||||
@@ -69,6 +69,8 @@ internal sealed class User : ISelectable, IMappingFrom<User, Cookie, bool>
|
||||
|
||||
public DateTimeOffset CookieTokenLastUpdateTime { get; set; }
|
||||
|
||||
public int Index { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 创建一个新的用户
|
||||
/// </summary>
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
using Snap.Hutao.Core;
|
||||
using Snap.Hutao.Core.Abstraction;
|
||||
using Snap.Hutao.Service;
|
||||
using Snap.Hutao.Service.Metadata;
|
||||
using Snap.Hutao.Web.Hoyolab;
|
||||
|
||||
namespace Snap.Hutao.Model.InterChange.GachaLog;
|
||||
|
||||
@@ -2,7 +2,9 @@
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Snap.Hutao.Control;
|
||||
using Snap.Hutao.Control.Text;
|
||||
using Snap.Hutao.Core.ExceptionService;
|
||||
using Snap.Hutao.Metadata;
|
||||
using Snap.Hutao.Model.Metadata.Avatar;
|
||||
using Snap.Hutao.Model.Primitive;
|
||||
using System.Globalization;
|
||||
@@ -50,8 +52,20 @@ internal sealed partial class DescriptionsParametersDescriptor : ValueConverter<
|
||||
{
|
||||
if (desc.AsSpan().TrySplitIntoTwo('|', out ReadOnlySpan<char> description, out ReadOnlySpan<char> format))
|
||||
{
|
||||
string resultFormatted = ParamRegex().Replace(format.ToString(), match => ReplaceParamInMatch(match, paramList));
|
||||
results.Add(new ParameterDescription { Description = description.ToString(), Parameter = resultFormatted });
|
||||
if (description[0] is not '#')
|
||||
{
|
||||
// Fast path
|
||||
string resultFormatted = ParamRegex().Replace(format.ToString(), match => ReplaceParamInMatch(match, paramList));
|
||||
results.Add(new ParameterDescription { Description = description.ToString(), Parameter = resultFormatted });
|
||||
}
|
||||
else
|
||||
{
|
||||
string descriptionString = SpecialNameHandler.Handle(description.ToString());
|
||||
string formatString = SpecialNameHandler.Handle(format.ToString());
|
||||
|
||||
string resultFormatted = ParamRegex().Replace(formatString, match => ReplaceParamInMatch(match, paramList));
|
||||
results.Add(new ParameterDescription { Description = descriptionString, Parameter = resultFormatted });
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
|
||||
using Microsoft.UI;
|
||||
using Snap.Hutao.Control;
|
||||
using Snap.Hutao.Core;
|
||||
using Snap.Hutao.Model.Intrinsic;
|
||||
using Snap.Hutao.Win32;
|
||||
using Windows.UI;
|
||||
|
||||
namespace Snap.Hutao.Model.Metadata.Converter;
|
||||
|
||||
@@ -13,43 +13,118 @@ internal static class Materials
|
||||
{
|
||||
public static FrozenSet<MaterialId> MondayThursdayItems { get; } = FrozenSet.ToFrozenSet<MaterialId>(
|
||||
[
|
||||
104301U, 104302U, 104303U, // 「自由」
|
||||
104310U, 104311U, 104312U, // 「繁荣」
|
||||
104320U, 104321U, 104322U, // 「浮世」
|
||||
104329U, 104330U, 104331U, // 「诤言」
|
||||
104338U, 104339U, 104340U, // 「公平」
|
||||
114001U, 114002U, 114003U, 114004U, // 高塔孤王
|
||||
114013U, 114014U, 114015U, 114016U, // 孤云寒林
|
||||
114025U, 114026U, 114027U, 114028U, // 远海夷地
|
||||
114037U, 114038U, 114039U, 114040U, // 谧林涓露
|
||||
114049U, 114050U, 114051U, 114052U, // 悠古弦音
|
||||
104301U,
|
||||
104302U,
|
||||
104303U, // 「自由」
|
||||
104310U,
|
||||
104311U,
|
||||
104312U, // 「繁荣」
|
||||
104320U,
|
||||
104321U,
|
||||
104322U, // 「浮世」
|
||||
104329U,
|
||||
104330U,
|
||||
104331U, // 「诤言」
|
||||
104338U,
|
||||
104339U,
|
||||
104340U, // 「公平」
|
||||
114001U,
|
||||
114002U,
|
||||
114003U,
|
||||
114004U, // 高塔孤王
|
||||
114013U,
|
||||
114014U,
|
||||
114015U,
|
||||
114016U, // 孤云寒林
|
||||
114025U,
|
||||
114026U,
|
||||
114027U,
|
||||
114028U, // 远海夷地
|
||||
114037U,
|
||||
114038U,
|
||||
114039U,
|
||||
114040U, // 谧林涓露
|
||||
114049U,
|
||||
114050U,
|
||||
114051U,
|
||||
114052U, // 悠古弦音
|
||||
]);
|
||||
|
||||
public static FrozenSet<MaterialId> TuesdayFridayItems { get; } = FrozenSet.ToFrozenSet<MaterialId>(
|
||||
[
|
||||
104304U, 104305U, 104306U, // 「抗争」
|
||||
104313U, 104314U, 104315U, // 「勤劳」
|
||||
104323U, 104324U, 104325U, // 「风雅」
|
||||
104332U, 104333U, 104334U, // 「巧思」
|
||||
104341U, 104342U, 104343U, // 「正义」
|
||||
114005U, 114006U, 114007U, 114008U, // 凛风奔狼
|
||||
114017U, 114018U, 114019U, 114020U, // 雾海云间
|
||||
114029U, 114030U, 114031U, 114032U, // 鸣神御灵
|
||||
114041U, 114042U, 114043U, 114044U, // 绿洲花园
|
||||
114053U, 114054U, 114055U, 114056U, // 纯圣露滴
|
||||
104304U,
|
||||
104305U,
|
||||
104306U, // 「抗争」
|
||||
104313U,
|
||||
104314U,
|
||||
104315U, // 「勤劳」
|
||||
104323U,
|
||||
104324U,
|
||||
104325U, // 「风雅」
|
||||
104332U,
|
||||
104333U,
|
||||
104334U, // 「巧思」
|
||||
104341U,
|
||||
104342U,
|
||||
104343U, // 「正义」
|
||||
114005U,
|
||||
114006U,
|
||||
114007U,
|
||||
114008U, // 凛风奔狼
|
||||
114017U,
|
||||
114018U,
|
||||
114019U,
|
||||
114020U, // 雾海云间
|
||||
114029U,
|
||||
114030U,
|
||||
114031U,
|
||||
114032U, // 鸣神御灵
|
||||
114041U,
|
||||
114042U,
|
||||
114043U,
|
||||
114044U, // 绿洲花园
|
||||
114053U,
|
||||
114054U,
|
||||
114055U,
|
||||
114056U, // 纯圣露滴
|
||||
]);
|
||||
|
||||
public static FrozenSet<MaterialId> WednesdaySaturdayItems { get; } = FrozenSet.ToFrozenSet<MaterialId>(
|
||||
[
|
||||
104307U, 104308U, 104309U, // 「诗文」
|
||||
104316U, 104317U, 104318U, // 「黄金」
|
||||
104326U, 104327U, 104328U, // 「天光」
|
||||
104335U, 104336U, 104337U, // 「笃行」
|
||||
104344U, 104345U, 104346U, // 「秩序」
|
||||
114009U, 114010U, 114011U, 114012U, // 狮牙斗士
|
||||
114021U, 114022U, 114023U, 114024U, // 漆黑陨铁
|
||||
114033U, 114034U, 114035U, 114036U, // 今昔剧画
|
||||
114045U, 114046U, 114047U, 114048U, // 谧林涓露
|
||||
114057U, 114058U, 114059U, 114060U, // 无垢之海
|
||||
104307U,
|
||||
104308U,
|
||||
104309U, // 「诗文」
|
||||
104316U,
|
||||
104317U,
|
||||
104318U, // 「黄金」
|
||||
104326U,
|
||||
104327U,
|
||||
104328U, // 「天光」
|
||||
104335U,
|
||||
104336U,
|
||||
104337U, // 「笃行」
|
||||
104344U,
|
||||
104345U,
|
||||
104346U, // 「秩序」
|
||||
114009U,
|
||||
114010U,
|
||||
114011U,
|
||||
114012U, // 狮牙斗士
|
||||
114021U,
|
||||
114022U,
|
||||
114023U,
|
||||
114024U, // 漆黑陨铁
|
||||
114033U,
|
||||
114034U,
|
||||
114035U,
|
||||
114036U, // 今昔剧画
|
||||
114045U,
|
||||
114046U,
|
||||
114047U,
|
||||
114048U, // 谧林涓露
|
||||
114057U,
|
||||
114058U,
|
||||
114059U,
|
||||
114060U, // 无垢之海
|
||||
]);
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace Snap.Hutao.Metadata;
|
||||
|
||||
internal static partial class SpecialNameHandler
|
||||
{
|
||||
// Use this regex to query special names in metadata
|
||||
// "#(?!.*(?:F#|M#|NON_BREAK_SPACE|REALNAME\[ID\(1\)(\|HOSTONLY\(true\)|)\]|\{LAYOUT_MOBILE#.+?\}\{LAYOUT_PC#.+?\}\{LAYOUT_PS#.+?\})).*
|
||||
public static string Handle(string input)
|
||||
{
|
||||
if (input.AsSpan()[0] is not '#')
|
||||
{
|
||||
return input;
|
||||
}
|
||||
|
||||
StringBuilder resultBuilder = new(input);
|
||||
resultBuilder
|
||||
.Replace("{MATEAVATAR#SEXPRO[INFO_MALE_PRONOUN_BOYFIRST|INFO_FEMALE_PRONOUN_GIRLFIRST]}", SH.MetadataSpecialNameMetaAvatarSexProInfoPronounBoyGirlFirst)
|
||||
.Replace("{MATEAVATAR#SEXPRO[INFO_MALE_PRONOUN_BOYD|INFO_FEMALE_PRONOUN_GIRLD]}", SH.MetadataSpecialNameMetaAvatarSexProInfoPronounBoyGirlD)
|
||||
.Replace("{PLAYERAVATAR#SEXPRO[INFO_MALE_PRONOUN_HE|INFO_FEMALE_PRONOUN_SHE]}", SH.MetadataSpecialNamePlayerAvatarSexProInfoPronounHeShe)
|
||||
.Replace("{REALNAME[ID(1)|HOSTONLY(true)]}", SH.MetadataSpecialNameRealNameId1)
|
||||
.Replace("{REALNAME[ID(1)]}", SH.MetadataSpecialNameRealNameId1)
|
||||
.Replace("{NICKNAME}", SH.MetadataSpecialNameNickname)
|
||||
.Replace("{NON_BREAK_SPACE}", "\u00A0");
|
||||
|
||||
input = resultBuilder.ToString();
|
||||
|
||||
input = MaleFemaleRegex().Replace(input, "<color=#1E90FF>$1</color>/<color=#FFB6C1>$2</color>");
|
||||
input = FemaleMaleRegex().Replace(input, "<color=#FFB6C1>$1</color>/<color=#1E90FF>$2</color>");
|
||||
input = LayoutRegex().Replace(input, "$2");
|
||||
|
||||
return input[1..];
|
||||
}
|
||||
|
||||
[GeneratedRegex("\\{M#(.*?)\\}\\{F#(.*?)\\}")]
|
||||
private static partial Regex MaleFemaleRegex();
|
||||
|
||||
[GeneratedRegex("\\{F#(.*?)\\}\\{M#(.*?)\\}")]
|
||||
private static partial Regex FemaleMaleRegex();
|
||||
|
||||
[GeneratedRegex("\\{LAYOUT_MOBILE#(.+?)\\}\\{LAYOUT_PC#(.+?)\\}\\{LAYOUT_PS#(.+?)\\}")]
|
||||
private static partial Regex LayoutRegex();
|
||||
}
|
||||
@@ -82,4 +82,7 @@ internal enum WaveType
|
||||
|
||||
[LocalizationKey(nameof(SH.ModelMetadataTowerWaveTypeWave1Additional))]
|
||||
Wave1Additional = 1999,
|
||||
|
||||
[LocalizationKey(nameof(SH.ModelMetadataTowerWaveTypeWave99999))]
|
||||
Wave99999 = 99999,
|
||||
}
|
||||
@@ -13,7 +13,7 @@
|
||||
<Identity
|
||||
Name="60568DGPStudio.SnapHutao"
|
||||
Publisher="CN=35C8E923-85DF-49A7-9172-B39DC6312C52"
|
||||
Version="1.9.4.0" />
|
||||
Version="1.9.5.0" />
|
||||
|
||||
<Properties>
|
||||
<DisplayName>Snap Hutao</DisplayName>
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
<Identity
|
||||
Name="60568DGPStudio.SnapHutaoDev"
|
||||
Publisher="CN=35C8E923-85DF-49A7-9172-B39DC6312C52"
|
||||
Version="1.9.4.0" />
|
||||
Version="1.9.5.0" />
|
||||
|
||||
<Properties>
|
||||
<DisplayName>Snap Hutao Dev</DisplayName>
|
||||
|
||||
@@ -19,6 +19,22 @@ public static partial class Program
|
||||
{
|
||||
private static readonly ApplicationInitializationCallback AppInitializationCallback = InitializeApp;
|
||||
|
||||
[ModuleInitializer]
|
||||
internal static void InitializeModule()
|
||||
{
|
||||
// Set base directory env var for PublishSingleFile support (referenced by SxS redirection)
|
||||
Environment.SetEnvironmentVariable("MICROSOFT_WINDOWSAPPRUNTIME_BASE_DIRECTORY", AppContext.BaseDirectory);
|
||||
|
||||
// No error handling needed as the target function does nothing (just {return S_OK}).
|
||||
// It's the act of calling the function causing the DllImport to load the DLL that
|
||||
// matters. This provides the moral equivalent of a native DLL's Import Address
|
||||
// Table (IAT) have an entry that's resolved when this module is loaded.
|
||||
_ = WindowsAppRuntimeEnsureIsLoaded();
|
||||
}
|
||||
|
||||
[LibraryImport("Microsoft.WindowsAppRuntime.dll", EntryPoint = "WindowsAppRuntime_EnsureIsLoaded")]
|
||||
private static partial int WindowsAppRuntimeEnsureIsLoaded();
|
||||
|
||||
[LibraryImport("Microsoft.ui.xaml.dll")]
|
||||
private static partial void XamlCheckProcessRequirements();
|
||||
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<root>
|
||||
<!--
|
||||
Microsoft ResX Schema
|
||||
|
||||
<!--
|
||||
Microsoft ResX Schema
|
||||
|
||||
Version 2.0
|
||||
|
||||
The primary goals of this format is to allow a simple XML format
|
||||
that is mostly human readable. The generation and parsing of the
|
||||
various data types are done through the TypeConverter classes
|
||||
|
||||
The primary goals of this format is to allow a simple XML format
|
||||
that is mostly human readable. The generation and parsing of the
|
||||
various data types are done through the TypeConverter classes
|
||||
associated with the data types.
|
||||
|
||||
|
||||
Example:
|
||||
|
||||
|
||||
... ado.net/XML headers & schema ...
|
||||
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||
<resheader name="version">2.0</resheader>
|
||||
@@ -26,36 +26,36 @@
|
||||
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||
<comment>This is a comment</comment>
|
||||
</data>
|
||||
|
||||
There are any number of "resheader" rows that contain simple
|
||||
|
||||
There are any number of "resheader" rows that contain simple
|
||||
name/value pairs.
|
||||
|
||||
Each data row contains a name, and value. The row also contains a
|
||||
type or mimetype. Type corresponds to a .NET class that support
|
||||
text/value conversion through the TypeConverter architecture.
|
||||
Classes that don't support this are serialized and stored with the
|
||||
|
||||
Each data row contains a name, and value. The row also contains a
|
||||
type or mimetype. Type corresponds to a .NET class that support
|
||||
text/value conversion through the TypeConverter architecture.
|
||||
Classes that don't support this are serialized and stored with the
|
||||
mimetype set.
|
||||
|
||||
The mimetype is used for serialized objects, and tells the
|
||||
ResXResourceReader how to depersist the object. This is currently not
|
||||
|
||||
The mimetype is used for serialized objects, and tells the
|
||||
ResXResourceReader how to depersist the object. This is currently not
|
||||
extensible. For a given mimetype the value must be set accordingly:
|
||||
|
||||
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||
that the ResXResourceWriter will generate, however the reader can
|
||||
|
||||
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||
that the ResXResourceWriter will generate, however the reader can
|
||||
read any of the formats listed below.
|
||||
|
||||
|
||||
mimetype: application/x-microsoft.net.object.binary.base64
|
||||
value : The object must be serialized with
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
|
||||
mimetype: application/x-microsoft.net.object.soap.base64
|
||||
value : The object must be serialized with
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||
value : The object must be serialized into a byte array
|
||||
value : The object must be serialized into a byte array
|
||||
: using a System.ComponentModel.TypeConverter
|
||||
: and then encoded with base64 encoding.
|
||||
-->
|
||||
@@ -738,10 +738,10 @@
|
||||
<value>Avatar Calculator: {0:MM-dd HH:mm}</value>
|
||||
</data>
|
||||
<data name="ServiceAvatarInfoSummaryGameRecordNotRefreshed" xml:space="preserve">
|
||||
<value>My Character: Not Refreshed</value>
|
||||
<value>Battle Chronicle: Not Refreshed</value>
|
||||
</data>
|
||||
<data name="ServiceAvatarInfoSummaryGameRecordRefreshTimeFormat" xml:space="preserve">
|
||||
<value>My Characters: {0:MM-dd HH:mm}</value>
|
||||
<value>Battle Chronicle: {0:MM-dd HH:mm}</value>
|
||||
</data>
|
||||
<data name="ServiceAvatarInfoSummaryShowcaseNotRefreshed" xml:space="preserve">
|
||||
<value>Enka: N/A</value>
|
||||
@@ -1170,7 +1170,7 @@
|
||||
<value>Realm Currency Alarm Threshold</value>
|
||||
</data>
|
||||
<data name="ViewDialogDailyNoteNotificationResinNotifyThreshold" xml:space="preserve">
|
||||
<value>Realm Currency Alarm Threshold</value>
|
||||
<value>Original Resin Alarm Threshold</value>
|
||||
</data>
|
||||
<data name="ViewDialogDailyNoteNotificationShowInHomeWidget" xml:space="preserve">
|
||||
<value>Show widget on Home page</value>
|
||||
@@ -1187,6 +1187,12 @@
|
||||
<data name="ViewDialogDailyNoteWebhookUrlTitle" xml:space="preserve">
|
||||
<value>Realtime Note Webhook URL</value>
|
||||
</data>
|
||||
<data name="ViewDialogFeedbackEnableLoopbackContent" xml:space="preserve">
|
||||
<value>You need other tools to restore the loopback after it is exempted</value>
|
||||
</data>
|
||||
<data name="ViewDialogFeedbackEnableLoopbackTitle" xml:space="preserve">
|
||||
<value>Confirm to Config Loopback Exemption</value>
|
||||
</data>
|
||||
<data name="ViewDialogGachaLogImportTitle" xml:space="preserve">
|
||||
<value>Import wish history</value>
|
||||
</data>
|
||||
@@ -1451,6 +1457,9 @@
|
||||
<data name="ViewModelDailyNoteHoyolabVerificationUnsupported" xml:space="preserve">
|
||||
<value>HoYoLab account does not support Realtime Notes verification</value>
|
||||
</data>
|
||||
<data name="ViewModelDailyNoteModifyTaskFail" xml:space="preserve">
|
||||
<value>Failed to modify Scheduled Task</value>
|
||||
</data>
|
||||
<data name="ViewModelDailyNoteRefreshTime30" xml:space="preserve">
|
||||
<value>30 min | 3.75 Resin</value>
|
||||
</data>
|
||||
@@ -1466,14 +1475,11 @@
|
||||
<data name="ViewModelDailyNoteRefreshTime8" xml:space="preserve">
|
||||
<value>8 min | 1 Resin</value>
|
||||
</data>
|
||||
<data name="ViewModelDailyNoteRegisterTaskFail" xml:space="preserve">
|
||||
<value>Failed to add Scheduled Task, please try again in administrator mode</value>
|
||||
</data>
|
||||
<data name="ViewModelDailyNoteRequestProgressTitle" xml:space="preserve">
|
||||
<value>Fetching Realtime Notes data, please wait</value>
|
||||
</data>
|
||||
<data name="ViewModelExportSuccessMessage" xml:space="preserve">
|
||||
<value>Successfully saved to selected patch</value>
|
||||
<value>Successfully saved to selected path</value>
|
||||
</data>
|
||||
<data name="ViewModelExportSuccessTitle" xml:space="preserve">
|
||||
<value>Export successfully</value>
|
||||
@@ -1512,7 +1518,7 @@
|
||||
<value>Failed to fetch wish history</value>
|
||||
</data>
|
||||
<data name="ViewModelGachaLogRefreshOperationCancel" xml:space="preserve">
|
||||
<value>Wish history refresh is canceled unexpectedly</value>
|
||||
<value>Wish history refresh was canceled unexpectedly</value>
|
||||
</data>
|
||||
<data name="ViewModelGachaLogRemoveArchiveDescription" xml:space="preserve">
|
||||
<value>This operation is irreversible. The wish history archive will be lost.</value>
|
||||
@@ -1728,7 +1734,7 @@
|
||||
<value>Sync character talents data</value>
|
||||
</data>
|
||||
<data name="ViewPageAvatarPropertyRefreshFromHoyolabGameRecord" xml:space="preserve">
|
||||
<value>Sync from MiHoYo BBS My Character</value>
|
||||
<value>Sync from MiHoYo BBS Battle Chronicle</value>
|
||||
</data>
|
||||
<data name="ViewPageAvatarPropertyRefreshFromHoyolabGameRecordDescription" xml:space="preserve">
|
||||
<value>Sync most data other than character talent</value>
|
||||
@@ -1862,6 +1868,18 @@
|
||||
<data name="ViewPageFeedbackCommonLinksHeader" xml:space="preserve">
|
||||
<value>Useful Links</value>
|
||||
</data>
|
||||
<data name="ViewPageFeedbackCurrentProxyHeader" xml:space="preserve">
|
||||
<value>Current Proxy</value>
|
||||
</data>
|
||||
<data name="ViewPageFeedbackCurrentProxyNoProxyDescription" xml:space="preserve">
|
||||
<value>No Proxy</value>
|
||||
</data>
|
||||
<data name="ViewPageFeedbackEnableLoopbackEnabledDescription" xml:space="preserve">
|
||||
<value>Exempted</value>
|
||||
</data>
|
||||
<data name="ViewPageFeedbackEnableLoopbackHeader" xml:space="preserve">
|
||||
<value>Config Loopback Exemption</value>
|
||||
</data>
|
||||
<data name="ViewPageFeedbackEngageWithUsDescription" xml:space="preserve">
|
||||
<value>Keep in touch with us</value>
|
||||
</data>
|
||||
@@ -2475,7 +2493,7 @@
|
||||
<value>After a full reading of the Genshin Impact and Snap Hutao user agreements, I choose to enable「Game Launcher - Advanced Features」.</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingIsAdvancedLaunchOptionsEnabledHeader" xml:space="preserve">
|
||||
<value>Enable Advanced Features</value>
|
||||
<value>Advanced Features</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingKeyShortcutAutoClickingDescription" xml:space="preserve">
|
||||
<value>Change Auto Click Shortcut</value>
|
||||
@@ -2586,7 +2604,7 @@
|
||||
<value>Quotes</value>
|
||||
</data>
|
||||
<data name="ViewPageWiKiAvatarSpecialFoodTitle" xml:space="preserve">
|
||||
<value>Special Food</value>
|
||||
<value>Special Dish</value>
|
||||
</data>
|
||||
<data name="ViewPageWiKiAvatarStoriesHeader" xml:space="preserve">
|
||||
<value>Stories</value>
|
||||
@@ -2925,7 +2943,7 @@
|
||||
<value>{0} Seconds</value>
|
||||
</data>
|
||||
<data name="WebDailyNoteVerificationFailed" xml:space="preserve">
|
||||
<value>Verification failed. Please check MiHoYo BBS - My Character - Realtime Notes manually</value>
|
||||
<value>Verification failed. Please check MiHoYo BBS - Toolbox - Realtime Notes manually</value>
|
||||
</data>
|
||||
<data name="WebEnkaResponseStatusCode400" xml:space="preserve">
|
||||
<value>Wrong UID format</value>
|
||||
@@ -2994,7 +3012,7 @@
|
||||
<value>Snap Hutao server is under maintenance</value>
|
||||
</data>
|
||||
<data name="WebIndexOrSpiralAbyssVerificationFailed" xml:space="preserve">
|
||||
<value>Verification failed. Please verify manually or check MiHoYo BBS - My Characters page</value>
|
||||
<value>Verification failed. Please verify manually or check MiHoYo BBS - Battle Chronicle page</value>
|
||||
</data>
|
||||
<data name="WebResponseFormat" xml:space="preserve">
|
||||
<value>Return Code: {0} | Message: {1}</value>
|
||||
@@ -3008,4 +3026,4 @@
|
||||
<data name="WindowIdentifyMonitorHeader" xml:space="preserve">
|
||||
<value>Monitor ID</value>
|
||||
</data>
|
||||
</root>
|
||||
</root>
|
||||
|
||||
@@ -738,10 +738,10 @@
|
||||
<value>Kalkulator Avatar: {0:MM-dd HH:mm}</value>
|
||||
</data>
|
||||
<data name="ServiceAvatarInfoSummaryGameRecordNotRefreshed" xml:space="preserve">
|
||||
<value>原神战绩:尚未刷新</value>
|
||||
<value>Battle Chronicle: Belum Disegarkan</value>
|
||||
</data>
|
||||
<data name="ServiceAvatarInfoSummaryGameRecordRefreshTimeFormat" xml:space="preserve">
|
||||
<value>原神战绩:{0:MM-dd HH:mm}</value>
|
||||
<value>Battle Chronicle: {0:MM-dd HH:mm}</value>
|
||||
</data>
|
||||
<data name="ServiceAvatarInfoSummaryShowcaseNotRefreshed" xml:space="preserve">
|
||||
<value>Enka: N/A</value>
|
||||
@@ -1187,6 +1187,12 @@
|
||||
<data name="ViewDialogDailyNoteWebhookUrlTitle" xml:space="preserve">
|
||||
<value>Catatan Realtime Webhook URL</value>
|
||||
</data>
|
||||
<data name="ViewDialogFeedbackEnableLoopbackContent" xml:space="preserve">
|
||||
<value>解除限制后需要使用其他工具恢复限制</value>
|
||||
</data>
|
||||
<data name="ViewDialogFeedbackEnableLoopbackTitle" xml:space="preserve">
|
||||
<value>是否解除 Loopback 限制</value>
|
||||
</data>
|
||||
<data name="ViewDialogGachaLogImportTitle" xml:space="preserve">
|
||||
<value>Mengimpor riwayat wish</value>
|
||||
</data>
|
||||
@@ -1284,7 +1290,7 @@
|
||||
<value>Pindai kode QR dengan Aplikasi MiHoYo BBS</value>
|
||||
</data>
|
||||
<data name="ViewDialogReconfirmTextHeader" xml:space="preserve">
|
||||
<value>为防止你在无意间启用,请输入正在启用的功能开关的<b>标题名称</b></value>
|
||||
<value>Untuk menghindari mengaktifkan secara keliru, harap masukkan <b>nama judul</b> dari fitur yang Anda aktifkan</value>
|
||||
</data>
|
||||
<data name="ViewDialogReconfirmTitle" xml:space="preserve">
|
||||
<value>Anda Mengaktifkan Fitur Berbahaya</value>
|
||||
@@ -1451,6 +1457,9 @@
|
||||
<data name="ViewModelDailyNoteHoyolabVerificationUnsupported" xml:space="preserve">
|
||||
<value>Akun HoYoLab tidak mendukung verifikasi Catatan Realtime.</value>
|
||||
</data>
|
||||
<data name="ViewModelDailyNoteModifyTaskFail" xml:space="preserve">
|
||||
<value>Gagal mengubah tugas terjadwal</value>
|
||||
</data>
|
||||
<data name="ViewModelDailyNoteRefreshTime30" xml:space="preserve">
|
||||
<value>30 min | 3.75 Resin</value>
|
||||
</data>
|
||||
@@ -1466,9 +1475,6 @@
|
||||
<data name="ViewModelDailyNoteRefreshTime8" xml:space="preserve">
|
||||
<value>8 min | 1 Resin</value>
|
||||
</data>
|
||||
<data name="ViewModelDailyNoteRegisterTaskFail" xml:space="preserve">
|
||||
<value>Gagal menambahkan Tugas Terjadwal, silakan coba lagi dalam mode administrator.</value>
|
||||
</data>
|
||||
<data name="ViewModelDailyNoteRequestProgressTitle" xml:space="preserve">
|
||||
<value>Mengambil data Catatan Realtime, mohon tunggu.</value>
|
||||
</data>
|
||||
@@ -1728,7 +1734,7 @@
|
||||
<value>Sinkronisasi Data Talenta Karakter</value>
|
||||
</data>
|
||||
<data name="ViewPageAvatarPropertyRefreshFromHoyolabGameRecord" xml:space="preserve">
|
||||
<value>从米游社原神战绩同步</value>
|
||||
<value>Sinkronisasi dari MiHoYo BBS Battle Chronicle</value>
|
||||
</data>
|
||||
<data name="ViewPageAvatarPropertyRefreshFromHoyolabGameRecordDescription" xml:space="preserve">
|
||||
<value>Sinkronkan sebagian besar data kecuali talent karakter.</value>
|
||||
@@ -1862,6 +1868,18 @@
|
||||
<data name="ViewPageFeedbackCommonLinksHeader" xml:space="preserve">
|
||||
<value>Tautan Berguna</value>
|
||||
</data>
|
||||
<data name="ViewPageFeedbackCurrentProxyHeader" xml:space="preserve">
|
||||
<value>当前代理</value>
|
||||
</data>
|
||||
<data name="ViewPageFeedbackCurrentProxyNoProxyDescription" xml:space="preserve">
|
||||
<value>无代理</value>
|
||||
</data>
|
||||
<data name="ViewPageFeedbackEnableLoopbackEnabledDescription" xml:space="preserve">
|
||||
<value>已解除</value>
|
||||
</data>
|
||||
<data name="ViewPageFeedbackEnableLoopbackHeader" xml:space="preserve">
|
||||
<value>解除 Loopback 限制</value>
|
||||
</data>
|
||||
<data name="ViewPageFeedbackEngageWithUsDescription" xml:space="preserve">
|
||||
<value>Tetap berhubungan dengan kami</value>
|
||||
</data>
|
||||
@@ -1869,13 +1887,13 @@
|
||||
<value>Dokumen Fitur</value>
|
||||
</data>
|
||||
<data name="ViewPageFeedbackGithubIssuesDescription" xml:space="preserve">
|
||||
<value>我们总是优先处理 GitHub 上的问题</value>
|
||||
<value>Kami selalu memberikan prioritas pada masalah yang dilaporkan di GitHub.</value>
|
||||
</data>
|
||||
<data name="ViewPageFeedbackRoadmapDescription" xml:space="preserve">
|
||||
<value>Roadmap Pengembangan</value>
|
||||
</data>
|
||||
<data name="ViewPageFeedbackSearchResultPlaceholderTitle" xml:space="preserve">
|
||||
<value>暂无搜索结果</value>
|
||||
<value>Tidak ada hasil</value>
|
||||
</data>
|
||||
<data name="ViewPageFeedbackServerStatusDescription" xml:space="preserve">
|
||||
<value>Pemantau Ketersediaan Layanan Snap Hutao</value>
|
||||
@@ -2202,7 +2220,7 @@
|
||||
<value>Pengunduhan Sumber Daya</value>
|
||||
</data>
|
||||
<data name="ViewPageLaunchGameResourceLatestHeader" xml:space="preserve">
|
||||
<value>Klien</value>
|
||||
<value>完整包</value>
|
||||
</data>
|
||||
<data name="ViewPageLaunchGameResourcePreDownloadHeader" xml:space="preserve">
|
||||
<value>Prapengunduhan</value>
|
||||
@@ -2925,7 +2943,7 @@
|
||||
<value>{0} detik</value>
|
||||
</data>
|
||||
<data name="WebDailyNoteVerificationFailed" xml:space="preserve">
|
||||
<value>验证失败,请手动验证或前往「米游社-旅行工具-原神战绩-实时便笺」页面查看</value>
|
||||
<value>Verifikasi gagal. Harap periksa MiHoYo BBS - Toolbox - Catatan Real-time secara manual.</value>
|
||||
</data>
|
||||
<data name="WebEnkaResponseStatusCode400" xml:space="preserve">
|
||||
<value>Format UID Salah</value>
|
||||
@@ -2994,7 +3012,7 @@
|
||||
<value>Server Snap Hutao sedang dalam perbaikan</value>
|
||||
</data>
|
||||
<data name="WebIndexOrSpiralAbyssVerificationFailed" xml:space="preserve">
|
||||
<value>验证失败,请手动验证或前往「米游社-旅行工具-原神战绩」页面查看</value>
|
||||
<value>Verifikasi gagal. Harap verifikasi secara manual atau periksa halaman MiHoYo BBS - Battle Chronicle.</value>
|
||||
</data>
|
||||
<data name="WebResponseFormat" xml:space="preserve">
|
||||
<value>Kode Kembali: {0} | Pesan: {1}</value>
|
||||
|
||||
@@ -121,7 +121,7 @@
|
||||
<value>胡桃 Dev {0}</value>
|
||||
</data>
|
||||
<data name="AppElevatedDevNameAndVersion" xml:space="preserve">
|
||||
<value>胡桃Dev {0} [管理者]</value>
|
||||
<value>胡桃 Dev {0} [管理者]</value>
|
||||
</data>
|
||||
<data name="AppElevatedNameAndVersion" xml:space="preserve">
|
||||
<value>胡桃 {0} [管理者]</value>
|
||||
@@ -738,10 +738,10 @@
|
||||
<value>育成計算: {0:MM-dd HH:mm}</value>
|
||||
</data>
|
||||
<data name="ServiceAvatarInfoSummaryGameRecordNotRefreshed" xml:space="preserve">
|
||||
<value>原神战绩:尚未刷新</value>
|
||||
<value>原神の記録: 更新していません</value>
|
||||
</data>
|
||||
<data name="ServiceAvatarInfoSummaryGameRecordRefreshTimeFormat" xml:space="preserve">
|
||||
<value>原神战绩:{0:MM-dd HH:mm}</value>
|
||||
<value>原神の記録:{0:MM-dd HH:mm}</value>
|
||||
</data>
|
||||
<data name="ServiceAvatarInfoSummaryShowcaseNotRefreshed" xml:space="preserve">
|
||||
<value>キャラクターラインナップ:未更新</value>
|
||||
@@ -870,22 +870,22 @@
|
||||
<value>ファイル書き込みの権限が無いため、サーバー変換機能を使用できません</value>
|
||||
</data>
|
||||
<data name="ServiceGameEnsureGameResourceQueryResourceInformation" xml:space="preserve">
|
||||
<value>下载游戏资源索引</value>
|
||||
<value>ゲームリソースのインデックスをダウンロード</value>
|
||||
</data>
|
||||
<data name="ServiceGameFileOperationExceptionMessage" xml:space="preserve">
|
||||
<value>ゲームファイルの操作に失敗しました。: {0}</value>
|
||||
</data>
|
||||
<data name="ServiceGameLaunchExecutionGameFpsUnlockFailed" xml:space="preserve">
|
||||
<value>解锁帧率上限失败</value>
|
||||
<value>FPS上限の解放に失敗しました</value>
|
||||
</data>
|
||||
<data name="ServiceGameLaunchExecutionGameIsRunning" xml:space="preserve">
|
||||
<value>游戏进程运行中</value>
|
||||
<value>ゲームは実行中です</value>
|
||||
</data>
|
||||
<data name="ServiceGameLaunchExecutionGamePathNotValid" xml:space="preserve">
|
||||
<value>请选择游戏路径</value>
|
||||
<value>ゲームのパスを選択してください</value>
|
||||
</data>
|
||||
<data name="ServiceGameLaunchExecutionGameResourceQueryIndexFailed" xml:space="preserve">
|
||||
<value>下载游戏资源索引失败: {0}</value>
|
||||
<value>ゲームリソースのインデックスのダウンロードに失敗しました: {0}</value>
|
||||
</data>
|
||||
<data name="ServiceGameLaunchPhaseProcessExited" xml:space="preserve">
|
||||
<value>ゲームプロセスが終了しました</value>
|
||||
@@ -1187,6 +1187,12 @@
|
||||
<data name="ViewDialogDailyNoteWebhookUrlTitle" xml:space="preserve">
|
||||
<value>リアルタイムノートのWebhook URL</value>
|
||||
</data>
|
||||
<data name="ViewDialogFeedbackEnableLoopbackContent" xml:space="preserve">
|
||||
<value>解除限制后需要使用其他工具恢复限制</value>
|
||||
</data>
|
||||
<data name="ViewDialogFeedbackEnableLoopbackTitle" xml:space="preserve">
|
||||
<value>是否解除 Loopback 限制</value>
|
||||
</data>
|
||||
<data name="ViewDialogGachaLogImportTitle" xml:space="preserve">
|
||||
<value>祈願記録をインポート</value>
|
||||
</data>
|
||||
@@ -1284,10 +1290,10 @@
|
||||
<value>MiHoYo BBS を使用して QR コードをスキャンします</value>
|
||||
</data>
|
||||
<data name="ViewDialogReconfirmTextHeader" xml:space="preserve">
|
||||
<value>为防止你在无意间启用,请输入正在启用的功能开关的<b>标题名称</b></value>
|
||||
<value>謝って有効にしないようにするためには、有効にする機能の<b>タイトル名</b>を入力してください</value>
|
||||
</data>
|
||||
<data name="ViewDialogReconfirmTitle" xml:space="preserve">
|
||||
<value>你正在启用一个危险功能</value>
|
||||
<value>危険な機能を有効にしています</value>
|
||||
</data>
|
||||
<data name="ViewDialogSettingDeleteUserDataContent" xml:space="preserve">
|
||||
<value>この操作は取り消せません。すべてのユーザーのログイン状態が解除されます。</value>
|
||||
@@ -1311,7 +1317,7 @@
|
||||
<value>クッキーを設定</value>
|
||||
</data>
|
||||
<data name="ViewFeedbackHeader" xml:space="preserve">
|
||||
<value>反馈中心</value>
|
||||
<value>フィードバック センター</value>
|
||||
</data>
|
||||
<data name="ViewGachaLogHeader" xml:space="preserve">
|
||||
<value>祈願履歴</value>
|
||||
@@ -1451,6 +1457,9 @@
|
||||
<data name="ViewModelDailyNoteHoyolabVerificationUnsupported" xml:space="preserve">
|
||||
<value>HoYoLabユーザーのリアルタイムノートはサポートしていません。</value>
|
||||
</data>
|
||||
<data name="ViewModelDailyNoteModifyTaskFail" xml:space="preserve">
|
||||
<value>スケジュールされたタスクの変更に失敗しました</value>
|
||||
</data>
|
||||
<data name="ViewModelDailyNoteRefreshTime30" xml:space="preserve">
|
||||
<value>30 分 | 3.75樹脂</value>
|
||||
</data>
|
||||
@@ -1466,9 +1475,6 @@
|
||||
<data name="ViewModelDailyNoteRefreshTime8" xml:space="preserve">
|
||||
<value>8 分 | 1樹脂</value>
|
||||
</data>
|
||||
<data name="ViewModelDailyNoteRegisterTaskFail" xml:space="preserve">
|
||||
<value>タスク スケジューラーへの追加に失敗しました。管理者モードで再度お試しください。</value>
|
||||
</data>
|
||||
<data name="ViewModelDailyNoteRequestProgressTitle" xml:space="preserve">
|
||||
<value>リアルタイムノートの情報を取得中、しばらくお待ちください</value>
|
||||
</data>
|
||||
@@ -1563,7 +1569,7 @@
|
||||
<value>サーバーの切り替えができませんでした</value>
|
||||
</data>
|
||||
<data name="ViewModelLaunchGameIdentifyMonitorsAction" xml:space="preserve">
|
||||
<value>识别显示器</value>
|
||||
<value>モニターの識別</value>
|
||||
</data>
|
||||
<data name="ViewModelLaunchGameMultiChannelReadFail" xml:space="preserve">
|
||||
<value>ゲーム設定ファイル {0} の読み込みに失敗しました。ファイルが無いか、権限が不足している可能性があります。</value>
|
||||
@@ -1599,10 +1605,10 @@
|
||||
<value>デスクトップへのショートカット作成に失敗しました</value>
|
||||
</data>
|
||||
<data name="ViewModelSettingDeleteServerCacheFolderContent" xml:space="preserve">
|
||||
<value>后续转换会重新下载所需的文件,确定要删除吗?</value>
|
||||
<value>必要なファイルを再度ダウンロードする必要があります、削除してもよろしいですか?</value>
|
||||
</data>
|
||||
<data name="ViewModelSettingDeleteServerCacheFolderTitle" xml:space="preserve">
|
||||
<value>删除转换服务器游戏客户端缓存</value>
|
||||
<value>サーバー変換用キャッシュファイルの削除</value>
|
||||
</data>
|
||||
<data name="ViewModelSettingFolderSizeDescription" xml:space="preserve">
|
||||
<value>使用済みディスク容量: {0}</value>
|
||||
@@ -1728,7 +1734,7 @@
|
||||
<value>キャラクターの天賦情報を同期</value>
|
||||
</data>
|
||||
<data name="ViewPageAvatarPropertyRefreshFromHoyolabGameRecord" xml:space="preserve">
|
||||
<value>从米游社原神战绩同步</value>
|
||||
<value>MiHoYo BBSから原神の記録を同期</value>
|
||||
</data>
|
||||
<data name="ViewPageAvatarPropertyRefreshFromHoyolabGameRecordDescription" xml:space="preserve">
|
||||
<value>キャラ天賦以外の情報を概ね同期</value>
|
||||
@@ -1854,34 +1860,46 @@
|
||||
<value>現在のユーザーとUIDを確認する</value>
|
||||
</data>
|
||||
<data name="ViewPageFeedbackAutoSuggestBoxPlaceholder" xml:space="preserve">
|
||||
<value>搜索问题与建议</value>
|
||||
<value>質問や提案を検索</value>
|
||||
</data>
|
||||
<data name="ViewPageFeedBackBasicInformation" xml:space="preserve">
|
||||
<value>基本信息</value>
|
||||
<value>基本情報</value>
|
||||
</data>
|
||||
<data name="ViewPageFeedbackCommonLinksHeader" xml:space="preserve">
|
||||
<value>常用链接</value>
|
||||
<value>よく使われるリンク</value>
|
||||
</data>
|
||||
<data name="ViewPageFeedbackCurrentProxyHeader" xml:space="preserve">
|
||||
<value>当前代理</value>
|
||||
</data>
|
||||
<data name="ViewPageFeedbackCurrentProxyNoProxyDescription" xml:space="preserve">
|
||||
<value>无代理</value>
|
||||
</data>
|
||||
<data name="ViewPageFeedbackEnableLoopbackEnabledDescription" xml:space="preserve">
|
||||
<value>已解除</value>
|
||||
</data>
|
||||
<data name="ViewPageFeedbackEnableLoopbackHeader" xml:space="preserve">
|
||||
<value>解除 Loopback 限制</value>
|
||||
</data>
|
||||
<data name="ViewPageFeedbackEngageWithUsDescription" xml:space="preserve">
|
||||
<value>与我们密切联系</value>
|
||||
<value>引き続き連絡をしてください</value>
|
||||
</data>
|
||||
<data name="ViewPageFeedbackFeatureGuideHeader" xml:space="preserve">
|
||||
<value>功能指南</value>
|
||||
<value>機能ガイド</value>
|
||||
</data>
|
||||
<data name="ViewPageFeedbackGithubIssuesDescription" xml:space="preserve">
|
||||
<value>我们总是优先处理 GitHub 上的问题</value>
|
||||
<value>Githubでは報告された問題を常に優先しています</value>
|
||||
</data>
|
||||
<data name="ViewPageFeedbackRoadmapDescription" xml:space="preserve">
|
||||
<value>开发路线规划</value>
|
||||
<value>開発ロードマップ</value>
|
||||
</data>
|
||||
<data name="ViewPageFeedbackSearchResultPlaceholderTitle" xml:space="preserve">
|
||||
<value>暂无搜索结果</value>
|
||||
<value>検索結果はありません</value>
|
||||
</data>
|
||||
<data name="ViewPageFeedbackServerStatusDescription" xml:space="preserve">
|
||||
<value>胡桃服务可用性监控</value>
|
||||
<value>胡桃のサービス状態</value>
|
||||
</data>
|
||||
<data name="ViewPageFeedbackServerStatusHeader" xml:space="preserve">
|
||||
<value>胡桃服务</value>
|
||||
<value>胡桃サービス</value>
|
||||
</data>
|
||||
<data name="ViewPageGachaLogAggressiveRefresh" xml:space="preserve">
|
||||
<value>すべて更新</value>
|
||||
@@ -2202,7 +2220,7 @@
|
||||
<value>リソースダウンロード</value>
|
||||
</data>
|
||||
<data name="ViewPageLaunchGameResourceLatestHeader" xml:space="preserve">
|
||||
<value>クライアント</value>
|
||||
<value>完整包</value>
|
||||
</data>
|
||||
<data name="ViewPageLaunchGameResourcePreDownloadHeader" xml:space="preserve">
|
||||
<value>事前ダウンロード</value>
|
||||
@@ -2271,7 +2289,7 @@
|
||||
<value>スクリーンショットフォルダを開く</value>
|
||||
</data>
|
||||
<data name="ViewPageResetGamePathAction" xml:space="preserve">
|
||||
<value>ゲームのフォルダを選択してください。</value>
|
||||
<value>ゲームフォルダを選択</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingAboutHeader" xml:space="preserve">
|
||||
<value>胡桃について</value>
|
||||
@@ -2631,16 +2649,16 @@
|
||||
<value>デバッグコンソール</value>
|
||||
</data>
|
||||
<data name="ViewSettingDeleteServerCacheFolderDescription" xml:space="preserve">
|
||||
<value>在启动游戏中转换服务器后会产生对应的游戏客户端文件用作缓存</value>
|
||||
<value>サーバー変換を使用すると、対応するサーバーのゲームクライアントのキャッシュファイルが生成されます</value>
|
||||
</data>
|
||||
<data name="ViewSettingDeleteServerCacheFolderHeader" xml:space="preserve">
|
||||
<value>删除转换服务器缓存</value>
|
||||
<value>サーバー変換のキャッシュを削除</value>
|
||||
</data>
|
||||
<data name="ViewSettingFolderViewOpenFolderAction" xml:space="preserve">
|
||||
<value>フォルダを開く</value>
|
||||
</data>
|
||||
<data name="ViewSettingHeader" xml:space="preserve">
|
||||
<value>设置</value>
|
||||
<value>設定</value>
|
||||
</data>
|
||||
<data name="ViewSpiralAbyssAvatarAppearanceRankDescription" xml:space="preserve">
|
||||
<value>キャラクター出場率 = この階層における出場回数(最初の一回のみカウント) / この階層のアップロード総数</value>
|
||||
@@ -2823,7 +2841,7 @@
|
||||
<value>{0} 時間後に終了</value>
|
||||
</data>
|
||||
<data name="WebBridgeShareCopyToClipboardFailed" xml:space="preserve">
|
||||
<value>打开剪贴板失败</value>
|
||||
<value>クリップボードを開けませんでした</value>
|
||||
</data>
|
||||
<data name="WebBridgeShareCopyToClipboardSuccess" xml:space="preserve">
|
||||
<value>クリップボードにコピーしました。</value>
|
||||
@@ -2925,7 +2943,7 @@
|
||||
<value>{0} 秒</value>
|
||||
</data>
|
||||
<data name="WebDailyNoteVerificationFailed" xml:space="preserve">
|
||||
<value>验证失败,请手动验证或前往「米游社-旅行工具-原神战绩-实时便笺」页面查看</value>
|
||||
<value>認証に失敗しました。「MiHoYo BBS - 戦績ツール - リアルタイムノート」で確認してください</value>
|
||||
</data>
|
||||
<data name="WebEnkaResponseStatusCode400" xml:space="preserve">
|
||||
<value>UIDは正しくありません</value>
|
||||
@@ -2994,7 +3012,7 @@
|
||||
<value>胡桃サーバがメンテナンス中です</value>
|
||||
</data>
|
||||
<data name="WebIndexOrSpiralAbyssVerificationFailed" xml:space="preserve">
|
||||
<value>验证失败,请手动验证或前往「米游社-旅行工具-原神战绩」页面查看</value>
|
||||
<value>認証に失敗しました。 手動で認証するか、MiHoYo BBS - 戦績 - マイ キャラクター を確認してください。</value>
|
||||
</data>
|
||||
<data name="WebResponseFormat" xml:space="preserve">
|
||||
<value>リターンコード:{0} | メッセージ:{1}</value>
|
||||
@@ -3006,6 +3024,6 @@
|
||||
<value>[{0}] の[{1}] のリクエストにエラーが発生、時間をおいてから試してください</value>
|
||||
</data>
|
||||
<data name="WindowIdentifyMonitorHeader" xml:space="preserve">
|
||||
<value>显示器编号</value>
|
||||
<value>モニターID</value>
|
||||
</data>
|
||||
</root>
|
||||
@@ -1187,6 +1187,12 @@
|
||||
<data name="ViewDialogDailyNoteWebhookUrlTitle" xml:space="preserve">
|
||||
<value>实时便笺 Webhook Url</value>
|
||||
</data>
|
||||
<data name="ViewDialogFeedbackEnableLoopbackContent" xml:space="preserve">
|
||||
<value>解除限制后需要使用其他工具恢复限制</value>
|
||||
</data>
|
||||
<data name="ViewDialogFeedbackEnableLoopbackTitle" xml:space="preserve">
|
||||
<value>是否解除 Loopback 限制</value>
|
||||
</data>
|
||||
<data name="ViewDialogGachaLogImportTitle" xml:space="preserve">
|
||||
<value>기원 기록 가져오기</value>
|
||||
</data>
|
||||
@@ -1451,6 +1457,9 @@
|
||||
<data name="ViewModelDailyNoteHoyolabVerificationUnsupported" xml:space="preserve">
|
||||
<value>HoYoLab 계정은 실시간 메모 확인 기능을 지원하지 않습니다</value>
|
||||
</data>
|
||||
<data name="ViewModelDailyNoteModifyTaskFail" xml:space="preserve">
|
||||
<value>예약된 작업을 수정하지 못했습니다</value>
|
||||
</data>
|
||||
<data name="ViewModelDailyNoteRefreshTime30" xml:space="preserve">
|
||||
<value>30분 | 3.75 레진</value>
|
||||
</data>
|
||||
@@ -1466,9 +1475,6 @@
|
||||
<data name="ViewModelDailyNoteRefreshTime8" xml:space="preserve">
|
||||
<value>8분 | 1 레진</value>
|
||||
</data>
|
||||
<data name="ViewModelDailyNoteRegisterTaskFail" xml:space="preserve">
|
||||
<value>작업 등록에 실패했습니다. 관리자 모드로 실행하세요</value>
|
||||
</data>
|
||||
<data name="ViewModelDailyNoteRequestProgressTitle" xml:space="preserve">
|
||||
<value>正在获取实时便笺信息,请稍候</value>
|
||||
</data>
|
||||
@@ -1862,6 +1868,18 @@
|
||||
<data name="ViewPageFeedbackCommonLinksHeader" xml:space="preserve">
|
||||
<value>常用链接</value>
|
||||
</data>
|
||||
<data name="ViewPageFeedbackCurrentProxyHeader" xml:space="preserve">
|
||||
<value>当前代理</value>
|
||||
</data>
|
||||
<data name="ViewPageFeedbackCurrentProxyNoProxyDescription" xml:space="preserve">
|
||||
<value>无代理</value>
|
||||
</data>
|
||||
<data name="ViewPageFeedbackEnableLoopbackEnabledDescription" xml:space="preserve">
|
||||
<value>已解除</value>
|
||||
</data>
|
||||
<data name="ViewPageFeedbackEnableLoopbackHeader" xml:space="preserve">
|
||||
<value>解除 Loopback 限制</value>
|
||||
</data>
|
||||
<data name="ViewPageFeedbackEngageWithUsDescription" xml:space="preserve">
|
||||
<value>与我们密切联系</value>
|
||||
</data>
|
||||
@@ -2202,7 +2220,7 @@
|
||||
<value>리소스 다운로드</value>
|
||||
</data>
|
||||
<data name="ViewPageLaunchGameResourceLatestHeader" xml:space="preserve">
|
||||
<value>클라이언트</value>
|
||||
<value>完整包</value>
|
||||
</data>
|
||||
<data name="ViewPageLaunchGameResourcePreDownloadHeader" xml:space="preserve">
|
||||
<value>사전 다운로드</value>
|
||||
|
||||
3029
src/Snap.Hutao/Snap.Hutao/Resource/Localization/SH.pt.resx
Normal file
3029
src/Snap.Hutao/Snap.Hutao/Resource/Localization/SH.pt.resx
Normal file
File diff suppressed because it is too large
Load Diff
@@ -198,6 +198,21 @@
|
||||
<data name="LaunchGameTitle" xml:space="preserve">
|
||||
<value>选择账号并启动</value>
|
||||
</data>
|
||||
<data name="MetadataSpecialNameMetaAvatarSexProInfoPronounBoyGirlD" xml:space="preserve">
|
||||
<value><color=#1E90FF>王子</color>/<color=#FFB6C1>公主</color></value>
|
||||
</data>
|
||||
<data name="MetadataSpecialNameMetaAvatarSexProInfoPronounBoyGirlFirst" xml:space="preserve">
|
||||
<value><color=#1E90FF>我</color>/<color=#FFB6C1>我</color></value>
|
||||
</data>
|
||||
<data name="MetadataSpecialNameNickname" xml:space="preserve">
|
||||
<value>旅行者</value>
|
||||
</data>
|
||||
<data name="MetadataSpecialNamePlayerAvatarSexProInfoPronounHeShe" xml:space="preserve">
|
||||
<value><color=#1E90FF>他</color>/<color=#FFB6C1>她</color></value>
|
||||
</data>
|
||||
<data name="MetadataSpecialNameRealNameId1" xml:space="preserve">
|
||||
<value>流浪者</value>
|
||||
</data>
|
||||
<data name="ModelBindingAvatarPropertyWeaponAffixFormat" xml:space="preserve">
|
||||
<value>精炼 {0}</value>
|
||||
</data>
|
||||
@@ -497,6 +512,9 @@
|
||||
<data name="ModelMetadataTowerWaveTypeWave4" xml:space="preserve">
|
||||
<value>第四波:击败所有怪物,下一波才会出现</value>
|
||||
</data>
|
||||
<data name="ModelMetadataTowerWaveTypeWave99999" xml:space="preserve">
|
||||
<value>维持场上 4 个盗宝团怪物,击杀后立即替换,总数 12 个</value>
|
||||
</data>
|
||||
<data name="ModelNameValueDefaultDescription" xml:space="preserve">
|
||||
<value>请更新角色橱窗数据</value>
|
||||
</data>
|
||||
@@ -1187,6 +1205,12 @@
|
||||
<data name="ViewDialogDailyNoteWebhookUrlTitle" xml:space="preserve">
|
||||
<value>实时便笺 Webhook Url</value>
|
||||
</data>
|
||||
<data name="ViewDialogFeedbackEnableLoopbackContent" xml:space="preserve">
|
||||
<value>解除限制后需要使用其他工具恢复限制</value>
|
||||
</data>
|
||||
<data name="ViewDialogFeedbackEnableLoopbackTitle" xml:space="preserve">
|
||||
<value>是否解除 Loopback 限制</value>
|
||||
</data>
|
||||
<data name="ViewDialogGachaLogImportTitle" xml:space="preserve">
|
||||
<value>导入祈愿记录</value>
|
||||
</data>
|
||||
@@ -1451,6 +1475,9 @@
|
||||
<data name="ViewModelDailyNoteHoyolabVerificationUnsupported" xml:space="preserve">
|
||||
<value>HoYoLab 账号不支持验证实时便笺</value>
|
||||
</data>
|
||||
<data name="ViewModelDailyNoteModifyTaskFail" xml:space="preserve">
|
||||
<value>修改计划任务失败</value>
|
||||
</data>
|
||||
<data name="ViewModelDailyNoteRefreshTime30" xml:space="preserve">
|
||||
<value>30 分钟 | 3.75 树脂</value>
|
||||
</data>
|
||||
@@ -1466,9 +1493,6 @@
|
||||
<data name="ViewModelDailyNoteRefreshTime8" xml:space="preserve">
|
||||
<value>8 分钟 | 1 树脂</value>
|
||||
</data>
|
||||
<data name="ViewModelDailyNoteRegisterTaskFail" xml:space="preserve">
|
||||
<value>注册计划任务失败,请使用管理员模式重试</value>
|
||||
</data>
|
||||
<data name="ViewModelDailyNoteRequestProgressTitle" xml:space="preserve">
|
||||
<value>正在获取实时便笺信息,请稍候</value>
|
||||
</data>
|
||||
@@ -1862,6 +1886,18 @@
|
||||
<data name="ViewPageFeedbackCommonLinksHeader" xml:space="preserve">
|
||||
<value>常用链接</value>
|
||||
</data>
|
||||
<data name="ViewPageFeedbackCurrentProxyHeader" xml:space="preserve">
|
||||
<value>当前代理</value>
|
||||
</data>
|
||||
<data name="ViewPageFeedbackCurrentProxyNoProxyDescription" xml:space="preserve">
|
||||
<value>无代理</value>
|
||||
</data>
|
||||
<data name="ViewPageFeedbackEnableLoopbackEnabledDescription" xml:space="preserve">
|
||||
<value>已解除</value>
|
||||
</data>
|
||||
<data name="ViewPageFeedbackEnableLoopbackHeader" xml:space="preserve">
|
||||
<value>解除 Loopback 限制</value>
|
||||
</data>
|
||||
<data name="ViewPageFeedbackEngageWithUsDescription" xml:space="preserve">
|
||||
<value>与我们密切联系</value>
|
||||
</data>
|
||||
@@ -1974,7 +2010,7 @@
|
||||
<value>总览</value>
|
||||
</data>
|
||||
<data name="ViewPageGahcaLogPivotStatistics" xml:space="preserve">
|
||||
<value>统计</value>
|
||||
<value>全球祈愿统计</value>
|
||||
</data>
|
||||
<data name="ViewPageGahcaLogPivotWeapon" xml:space="preserve">
|
||||
<value>武器</value>
|
||||
@@ -2202,7 +2238,7 @@
|
||||
<value>资源下载</value>
|
||||
</data>
|
||||
<data name="ViewPageLaunchGameResourceLatestHeader" xml:space="preserve">
|
||||
<value>客户端</value>
|
||||
<value>完整包</value>
|
||||
</data>
|
||||
<data name="ViewPageLaunchGameResourcePreDownloadHeader" xml:space="preserve">
|
||||
<value>预下载</value>
|
||||
|
||||
@@ -738,10 +738,10 @@
|
||||
<value>Калькулятор аватара: {0:MM-dd HH:mm}</value>
|
||||
</data>
|
||||
<data name="ServiceAvatarInfoSummaryGameRecordNotRefreshed" xml:space="preserve">
|
||||
<value>原神战绩:尚未刷新</value>
|
||||
<value>Запись Genshin Impact: еще не обновлены</value>
|
||||
</data>
|
||||
<data name="ServiceAvatarInfoSummaryGameRecordRefreshTimeFormat" xml:space="preserve">
|
||||
<value>原神战绩:{0:MM-dd HH:mm}</value>
|
||||
<value>Боевые достижения в Genshin Impact:{0:MM-dd HH:mm}</value>
|
||||
</data>
|
||||
<data name="ServiceAvatarInfoSummaryShowcaseNotRefreshed" xml:space="preserve">
|
||||
<value>Enka: N/A</value>
|
||||
@@ -783,16 +783,16 @@
|
||||
<value>Realm Currency</value>
|
||||
</data>
|
||||
<data name="ServiceDailyNoteNotifierHomeCoinCurrent" xml:space="preserve">
|
||||
<value>当前洞天宝钱:{0}</value>
|
||||
<value>Текущий баланс сокровищ в Дунтянь: {0}</value>
|
||||
</data>
|
||||
<data name="ServiceDailyNoteNotifierMultiValueReached" xml:space="preserve">
|
||||
<value>多个提醒项达到设定值</value>
|
||||
<value>Несколько напоминаний достигли установленного значения</value>
|
||||
</data>
|
||||
<data name="ServiceDailyNoteNotifierResin" xml:space="preserve">
|
||||
<value>原粹树脂</value>
|
||||
<value>Оригинальная смола</value>
|
||||
</data>
|
||||
<data name="ServiceDailyNoteNotifierResinCurrent" xml:space="preserve">
|
||||
<value>当前原粹树脂:{0}</value>
|
||||
<value>Текущая оригинальная смола:{0}</value>
|
||||
</data>
|
||||
<data name="ServiceDailyNoteNotifierTitle" xml:space="preserve">
|
||||
<value>实时便笺提醒</value>
|
||||
@@ -801,7 +801,7 @@
|
||||
<value>参量质变仪</value>
|
||||
</data>
|
||||
<data name="ServiceDailyNoteNotifierTransformerAdaptiveHint" xml:space="preserve">
|
||||
<value>准备完成</value>
|
||||
<value>Готово</value>
|
||||
</data>
|
||||
<data name="ServiceDailyNoteNotifierTransformerHint" xml:space="preserve">
|
||||
<value>参量质变仪已准备完成</value>
|
||||
@@ -810,7 +810,7 @@
|
||||
<value>正在提瓦特大陆中探索</value>
|
||||
</data>
|
||||
<data name="ServiceDiscordGameLaunchedBy" xml:space="preserve">
|
||||
<value>由 {0} 启动</value>
|
||||
<value>Started by {0}</value>
|
||||
</data>
|
||||
<data name="ServiceGachaLogArchiveCollectionUserdataCorruptedMessage" xml:space="preserve">
|
||||
<value>无法获取祈愿记录:{0}</value>
|
||||
@@ -852,16 +852,16 @@
|
||||
<value>提供的 Url 无效</value>
|
||||
</data>
|
||||
<data name="ServiceGachaLogUrlProviderStokenUnsupported" xml:space="preserve">
|
||||
<value>HoYoLab 账号不支持使用 SToken 刷新祈愿记录</value>
|
||||
<value>SToken refresh не поддерживает учетную запись HoYoLab</value>
|
||||
</data>
|
||||
<data name="ServiceGachaLogUrlProviderUrlLanguageNotMatchCurrentLocale" xml:space="preserve">
|
||||
<value>Url 中的语言:{0} 与胡桃的语言:{1} 不匹配,请切换到对应语言重试</value>
|
||||
<value>Язык в URL: {0} не соответствует языку Walnut: {1}, пожалуйста, переключитесь на соответствующий язык и повторите попытку</value>
|
||||
</data>
|
||||
<data name="ServiceGachaStatisticsFactoryItemIdInvalid" xml:space="preserve">
|
||||
<value>不支持的 Item Id:{0}</value>
|
||||
<value>Неподдерживаемый Item ID: {0}</value>
|
||||
</data>
|
||||
<data name="ServiceGachaUIGFImportLanguageNotMatch" xml:space="preserve">
|
||||
<value>UIGF 文件的语言:{0} 与胡桃的语言:{1} 不匹配,请切换到对应语言重试</value>
|
||||
<value>Язык файла UIGF: {0} не соответствует языку Snap Hutao: {1}, пожалуйста, повторите попытку после переключения языка клиента на целевой язык файла UIGF</value>
|
||||
</data>
|
||||
<data name="ServiceGameDetectGameAccountMultiMatched" xml:space="preserve">
|
||||
<value>存在多个匹配账号,请删除重复的账号</value>
|
||||
@@ -894,7 +894,7 @@
|
||||
<value>正在初始化游戏进程</value>
|
||||
</data>
|
||||
<data name="ServiceGameLaunchPhaseProcessStarted" xml:space="preserve">
|
||||
<value>游戏进程已启动</value>
|
||||
<value>Игра запускается...</value>
|
||||
</data>
|
||||
<data name="ServiceGameLaunchPhaseUnlockFpsFailed" xml:space="preserve">
|
||||
<value>解锁帧率上限失败,正在结束游戏进程</value>
|
||||
@@ -912,22 +912,22 @@
|
||||
<value>选择游戏本体</value>
|
||||
</data>
|
||||
<data name="ServiceGameLocatorPickerFilterText" xml:space="preserve">
|
||||
<value>游戏本体</value>
|
||||
<value>Игровой клиент</value>
|
||||
</data>
|
||||
<data name="ServiceGameLocatorUnityLogFileNotFound" xml:space="preserve">
|
||||
<value>找不到 Unity 日志文件</value>
|
||||
</data>
|
||||
<data name="ServiceGameLocatorUnityLogGamePathNotFound" xml:space="preserve">
|
||||
<value>在 Unity 日志文件中找不到游戏路径</value>
|
||||
<value>Путь к игре не найден в файле журнала Unity</value>
|
||||
</data>
|
||||
<data name="ServiceGamePackageConvertMoveFileBackupFormat" xml:space="preserve">
|
||||
<value>备份:{0}</value>
|
||||
<value>Резервная копия:{0}</value>
|
||||
</data>
|
||||
<data name="ServiceGamePackageConvertMoveFileRenameFormat" xml:space="preserve">
|
||||
<value>重命名:{0} 到:{1}</value>
|
||||
<value>Переименовать: {0} в:{1}</value>
|
||||
</data>
|
||||
<data name="ServiceGamePackageConvertMoveFileRestoreFormat" xml:space="preserve">
|
||||
<value>替换:{0}</value>
|
||||
<value>Изменить: {0}</value>
|
||||
</data>
|
||||
<data name="ServiceGamePackageRenameDataFolderFailed" xml:space="preserve">
|
||||
<value>重命名数据文件夹名称失败</value>
|
||||
@@ -969,148 +969,148 @@
|
||||
<value>在读取游戏进程内存时遇到问题:无法读取到指定地址的有效值</value>
|
||||
</data>
|
||||
<data name="ServiceHutaoUserGachaLogExpiredAt" xml:space="preserve">
|
||||
<value>祈愿记录上传服务有效期至\n{0:yyyy.MM.dd HH:mm:ss}</value>
|
||||
<value>Запись молитв доступна до \n{0:yyyy.MM.dd HH:mm:ss}</value>
|
||||
</data>
|
||||
<data name="ServiceMetadataFileNotFound" xml:space="preserve">
|
||||
<value>无法找到缓存的元数据文件</value>
|
||||
</data>
|
||||
<data name="ServiceMetadataHttpRequestFailed" xml:space="preserve">
|
||||
<value>HTTP {0} | Error:{1}:元数据校验文件下载失败</value>
|
||||
<value>HTTP {0} | Ошибка {1}: Не удалось загрузить файл проверки метаданных</value>
|
||||
</data>
|
||||
<data name="ServiceMetadataNotInitialized" xml:space="preserve">
|
||||
<value>元数据服务尚未初始化,或初始化失败</value>
|
||||
<value>Служба метаданных не была инициализирована или инициализация завершилась неудачно</value>
|
||||
</data>
|
||||
<data name="ServiceMetadataParseFailed" xml:space="preserve">
|
||||
<value>元数据校验文件解析失败</value>
|
||||
<value>Не удалось прочитать файл проверки метаданных.</value>
|
||||
</data>
|
||||
<data name="ServiceMetadataRequestFailed" xml:space="preserve">
|
||||
<value>元数据校验文件下载失败</value>
|
||||
<value>Не удалось загрузить файл проверки метаданных.</value>
|
||||
</data>
|
||||
<data name="ServiceMetadataVersionNotSupported" xml:space="preserve">
|
||||
<value>你的胡桃版本过低,请尽快升级</value>
|
||||
<value>Ваша версия Snap Hutao устарела. Пожалуйста, обновите приложение</value>
|
||||
</data>
|
||||
<data name="ServiceSignInClaimRewardFailedFormat" xml:space="preserve">
|
||||
<value>签到失败,{0}</value>
|
||||
<value>Ошибка регистрации, {0}</value>
|
||||
</data>
|
||||
<data name="ServiceSignInInfoRequestFailed" xml:space="preserve">
|
||||
<value>获取签到次数失败</value>
|
||||
<value>Не удалось получить количество дней регистрации</value>
|
||||
</data>
|
||||
<data name="ServiceSignInRewardListRequestFailed" xml:space="preserve">
|
||||
<value>获取奖励列表失败</value>
|
||||
<value>Не удалось получить список наград за регистрацию.</value>
|
||||
</data>
|
||||
<data name="ServiceSignInRiskVerificationFailed" xml:space="preserve">
|
||||
<value>验证失败,请前往米游社原神签到页面自行领取奖励</value>
|
||||
<value>Проверка не удалась. Пожалуйста, перейдите на страницу регистрации HoYoLab в Genshin Impact, чтобы вручную получить награду.</value>
|
||||
</data>
|
||||
<data name="ServiceSignInSuccessRewardFormat" xml:space="preserve">
|
||||
<value>签到成功,{0}×{1}</value>
|
||||
<value>Перемещение успешно, {0} × {1}.</value>
|
||||
</data>
|
||||
<data name="ServiceUIGFImportUnsupportedVersion" xml:space="preserve">
|
||||
<value>不支持的 UIGF 版本</value>
|
||||
<value>Неподдерживаемая версия UIGF.</value>
|
||||
</data>
|
||||
<data name="ServiceUpdateStatusVersionDescription" xml:space="preserve">
|
||||
<value>发现新版本 {0}</value>
|
||||
<value>Доступна новая версия {0}.</value>
|
||||
</data>
|
||||
<data name="ServiceUserCurrentMultiMatched" xml:space="preserve">
|
||||
<value>多个用户记录为选中状态</value>
|
||||
<value>Зафиксирован выбор нескольких записей пользователей.</value>
|
||||
</data>
|
||||
<data name="ServiceUserCurrentUpdateAndSaveFailed" xml:space="preserve">
|
||||
<value>用户 {0} 状态保存失败</value>
|
||||
<value>Не удалось сохранить состояние пользователя {0}.</value>
|
||||
</data>
|
||||
<data name="ServiceUserProcessCookieNoMid" xml:space="preserve">
|
||||
<value>输入的 Cookie 必须包含 Mid</value>
|
||||
<value>Введенные Cookie должны включать Mid.</value>
|
||||
</data>
|
||||
<data name="ServiceUserProcessCookieNoSToken" xml:space="preserve">
|
||||
<value>输入的 Cookie 必须包含 SToken</value>
|
||||
<value>Введенные Cookie должны включать SToken.</value>
|
||||
</data>
|
||||
<data name="ServiceUserProcessCookieRequestUserInfoFailed" xml:space="preserve">
|
||||
<value>输入的 Cookie 无法获取用户信息</value>
|
||||
<value>Предоставленные Cookie не позволяют получить информацию о пользователе.</value>
|
||||
</data>
|
||||
<data name="ViewAchievementHeader" xml:space="preserve">
|
||||
<value>成就管理</value>
|
||||
<value>Достижения</value>
|
||||
</data>
|
||||
<data name="ViewAnnouncementHeader" xml:space="preserve">
|
||||
<value>主页</value>
|
||||
<value>Главная</value>
|
||||
</data>
|
||||
<data name="ViewAvatarPropertyHeader" xml:space="preserve">
|
||||
<value>我的角色</value>
|
||||
<value>Мои персонажи</value>
|
||||
</data>
|
||||
<data name="ViewAvatarPropertySyncDataButtonLabel" xml:space="preserve">
|
||||
<value>同步角色信息</value>
|
||||
<value>Синхронизация данных персонажа.</value>
|
||||
</data>
|
||||
<data name="ViewCardAchievementStatisticsTitle" xml:space="preserve">
|
||||
<value>成就统计</value>
|
||||
<value>Статистика достижений.</value>
|
||||
</data>
|
||||
<data name="ViewCardGachaStatisticsTitle" xml:space="preserve">
|
||||
<value>保底计数</value>
|
||||
<value>Pity Count</value>
|
||||
</data>
|
||||
<data name="ViewCardLaunchGameSelectAccountPlaceholder" xml:space="preserve">
|
||||
<value>选择账号或直接启动</value>
|
||||
<value>Выберите аккаунт для начала</value>
|
||||
</data>
|
||||
<data name="ViewControlBaseValueSliderLevel" xml:space="preserve">
|
||||
<value>等级</value>
|
||||
<value>Уровень</value>
|
||||
</data>
|
||||
<data name="ViewControlBaseValueSliderPromoted" xml:space="preserve">
|
||||
<value>突破后</value>
|
||||
<value>After Ascension</value>
|
||||
</data>
|
||||
<data name="ViewControlElevationText" xml:space="preserve">
|
||||
<value>需要管理员权限</value>
|
||||
</data>
|
||||
<data name="ViewControlLoadingText" xml:space="preserve">
|
||||
<value>加载中,请稍候</value>
|
||||
<value>Загрузка, пожалуйста подождите</value>
|
||||
</data>
|
||||
<data name="ViewControlStatisticsCardBlueText" xml:space="preserve">
|
||||
<value>三星</value>
|
||||
<value>3-зв.</value>
|
||||
</data>
|
||||
<data name="ViewControlStatisticsCardGuaranteeText" xml:space="preserve">
|
||||
<value>保底</value>
|
||||
<value>Гарант</value>
|
||||
</data>
|
||||
<data name="ViewControlStatisticsCardOrangeAveragePullText" xml:space="preserve">
|
||||
<value>五星平均抽数</value>
|
||||
</data>
|
||||
<data name="ViewControlStatisticsCardOrangeText" xml:space="preserve">
|
||||
<value>五星</value>
|
||||
<value>5-зв.</value>
|
||||
</data>
|
||||
<data name="ViewControlStatisticsCardPullText" xml:space="preserve">
|
||||
<value>抽</value>
|
||||
<value>Pulls</value>
|
||||
</data>
|
||||
<data name="ViewControlStatisticsCardPurpleText" xml:space="preserve">
|
||||
<value>四星</value>
|
||||
<value>4 зв.</value>
|
||||
</data>
|
||||
<data name="ViewControlStatisticsCardToLastOrangeText" xml:space="preserve">
|
||||
<value>距上个五星</value>
|
||||
<value>Последний 5-зв.</value>
|
||||
</data>
|
||||
<data name="ViewControlStatisticsCardToLastPurpleText" xml:space="preserve">
|
||||
<value>距上个四星</value>
|
||||
<value>Последний 4-зв.</value>
|
||||
</data>
|
||||
<data name="ViewControlStatisticsCardUpAveragePullText" xml:space="preserve">
|
||||
<value>UP 平均抽数</value>
|
||||
<value>Средняя удача выпадения</value>
|
||||
</data>
|
||||
<data name="ViewControlStatisticsCardUpText" xml:space="preserve">
|
||||
<value>UP</value>
|
||||
</data>
|
||||
<data name="ViewControlStatisticsSegmentedItemContentPrediction" xml:space="preserve">
|
||||
<value>预测</value>
|
||||
<value>Prediction</value>
|
||||
</data>
|
||||
<data name="ViewControlStatisticsSegmentedItemContentProportion" xml:space="preserve">
|
||||
<value>比例</value>
|
||||
<value>Масштаб</value>
|
||||
</data>
|
||||
<data name="ViewControlStatisticsSegmentedItemContentStatistics" xml:space="preserve">
|
||||
<value>统计</value>
|
||||
<value>Статистика</value>
|
||||
</data>
|
||||
<data name="ViewControlWebViewerCoreWebView2ProfileQueryInterfaceFailed" xml:space="preserve">
|
||||
<value>当前 WebView2 版本不支持管理配置,继续使用可能会导致异常,请尽快升级</value>
|
||||
</data>
|
||||
<data name="ViewCultivationHeader" xml:space="preserve">
|
||||
<value>养成计划</value>
|
||||
<value>План разработки</value>
|
||||
</data>
|
||||
<data name="ViewDailyNoteHeader" xml:space="preserve">
|
||||
<value>实时便笺</value>
|
||||
</data>
|
||||
<data name="ViewDataHeader" xml:space="preserve">
|
||||
<value>数据</value>
|
||||
<value>Data</value>
|
||||
</data>
|
||||
<data name="ViewDialogAchievementArchiveCreateInputPlaceholder" xml:space="preserve">
|
||||
<value>在此处输入</value>
|
||||
<value>Enter here</value>
|
||||
</data>
|
||||
<data name="ViewDialogAchievementArchiveCreateTitle" xml:space="preserve">
|
||||
<value>设置成就存档的名称</value>
|
||||
@@ -1187,6 +1187,12 @@
|
||||
<data name="ViewDialogDailyNoteWebhookUrlTitle" xml:space="preserve">
|
||||
<value>实时便笺 Webhook Url</value>
|
||||
</data>
|
||||
<data name="ViewDialogFeedbackEnableLoopbackContent" xml:space="preserve">
|
||||
<value>解除限制后需要使用其他工具恢复限制</value>
|
||||
</data>
|
||||
<data name="ViewDialogFeedbackEnableLoopbackTitle" xml:space="preserve">
|
||||
<value>是否解除 Loopback 限制</value>
|
||||
</data>
|
||||
<data name="ViewDialogGachaLogImportTitle" xml:space="preserve">
|
||||
<value>导入祈愿记录</value>
|
||||
</data>
|
||||
@@ -1332,10 +1338,10 @@
|
||||
<value>用户使用协议与法律声明</value>
|
||||
</data>
|
||||
<data name="ViewGuideStepDocument" xml:space="preserve">
|
||||
<value>文档</value>
|
||||
<value>Документация</value>
|
||||
</data>
|
||||
<data name="ViewGuideStepEnvironment" xml:space="preserve">
|
||||
<value>环境</value>
|
||||
<value>Environments</value>
|
||||
</data>
|
||||
<data name="ViewGuideStepEnvironmentAfterInstallDescription" xml:space="preserve">
|
||||
<value>安装完成后重启胡桃以查看是否正常生效</value>
|
||||
@@ -1353,10 +1359,10 @@
|
||||
<value>下载并自行安装运行时</value>
|
||||
</data>
|
||||
<data name="ViewGuideStepLanguage" xml:space="preserve">
|
||||
<value>语言</value>
|
||||
<value>Язык</value>
|
||||
</data>
|
||||
<data name="ViewGuideStepStaticResource" xml:space="preserve">
|
||||
<value>资源</value>
|
||||
<value>Assets</value>
|
||||
</data>
|
||||
<data name="ViewHutaoDatabaseHeader" xml:space="preserve">
|
||||
<value>深渊统计</value>
|
||||
@@ -1451,6 +1457,9 @@
|
||||
<data name="ViewModelDailyNoteHoyolabVerificationUnsupported" xml:space="preserve">
|
||||
<value>HoYoLab 账号不支持验证实时便笺</value>
|
||||
</data>
|
||||
<data name="ViewModelDailyNoteModifyTaskFail" xml:space="preserve">
|
||||
<value>修改计划任务失败</value>
|
||||
</data>
|
||||
<data name="ViewModelDailyNoteRefreshTime30" xml:space="preserve">
|
||||
<value>30 分钟 | 3.75 树脂</value>
|
||||
</data>
|
||||
@@ -1466,9 +1475,6 @@
|
||||
<data name="ViewModelDailyNoteRefreshTime8" xml:space="preserve">
|
||||
<value>8 分钟 | 1 树脂</value>
|
||||
</data>
|
||||
<data name="ViewModelDailyNoteRegisterTaskFail" xml:space="preserve">
|
||||
<value>注册计划任务失败,请使用管理员模式重试</value>
|
||||
</data>
|
||||
<data name="ViewModelDailyNoteRequestProgressTitle" xml:space="preserve">
|
||||
<value>正在获取实时便笺信息,请稍候</value>
|
||||
</data>
|
||||
@@ -1536,10 +1542,10 @@
|
||||
<value>我已阅读并同意上方的条款</value>
|
||||
</data>
|
||||
<data name="ViewModelGuideActionComplete" xml:space="preserve">
|
||||
<value>完成</value>
|
||||
<value>Завершено</value>
|
||||
</data>
|
||||
<data name="ViewModelGuideActionNext" xml:space="preserve">
|
||||
<value>下一步</value>
|
||||
<value>Далее</value>
|
||||
</data>
|
||||
<data name="ViewModelGuideActionStaticResourceBegin" xml:space="preserve">
|
||||
<value>下载资源文件中,请稍候</value>
|
||||
@@ -1581,7 +1587,7 @@
|
||||
<value>无法选择UID [{0}] 对应的账号 [{1}],该账号不属于当前服务器</value>
|
||||
</data>
|
||||
<data name="ViewModelSettingActionComplete" xml:space="preserve">
|
||||
<value>操作完成</value>
|
||||
<value>Действие завершено</value>
|
||||
</data>
|
||||
<data name="ViewModelSettingClearWebCacheFail" xml:space="preserve">
|
||||
<value>清除失败,文件目录权限不足,请使用管理员模式重试</value>
|
||||
@@ -1590,10 +1596,10 @@
|
||||
<value>清除失败,找不到目录:{0}</value>
|
||||
</data>
|
||||
<data name="ViewModelSettingClearWebCacheSuccess" xml:space="preserve">
|
||||
<value>清除完成</value>
|
||||
<value>Очистка завершена</value>
|
||||
</data>
|
||||
<data name="ViewModelSettingCopyDeviceIdSuccess" xml:space="preserve">
|
||||
<value>复制成功</value>
|
||||
<value>Успешно скопировано</value>
|
||||
</data>
|
||||
<data name="ViewModelSettingCreateDesktopShortcutFailed" xml:space="preserve">
|
||||
<value>创建桌面快捷方式失败</value>
|
||||
@@ -1614,7 +1620,7 @@
|
||||
<value>设置数据目录成功,重启以应用更改</value>
|
||||
</data>
|
||||
<data name="ViewModelSettingSetGamePathDatabaseFailedTitle" xml:space="preserve">
|
||||
<value>保存游戏路径失败</value>
|
||||
<value>Ошибка сохранения игрового пути</value>
|
||||
</data>
|
||||
<data name="ViewModelUserAdded" xml:space="preserve">
|
||||
<value>用户 [{0}] 添加成功</value>
|
||||
@@ -1641,34 +1647,34 @@
|
||||
<value>现在可以开始使用胡桃了</value>
|
||||
</data>
|
||||
<data name="ViewModelWelcomeDownloadCompleteTitle" xml:space="preserve">
|
||||
<value>下载完成</value>
|
||||
<value>Загрузка завершена</value>
|
||||
</data>
|
||||
<data name="ViewModelWelcomeDownloadSummaryComplete" xml:space="preserve">
|
||||
<value>完成</value>
|
||||
<value>Завершено</value>
|
||||
</data>
|
||||
<data name="ViewModelWelcomeDownloadSummaryDefault" xml:space="preserve">
|
||||
<value>等待中</value>
|
||||
<value>В процессе</value>
|
||||
</data>
|
||||
<data name="ViewModelWelcomeDownloadSummaryException" xml:space="preserve">
|
||||
<value>文件下载异常</value>
|
||||
</data>
|
||||
<data name="ViewPageAchievementAddArchive" xml:space="preserve">
|
||||
<value>创建新存档</value>
|
||||
<value>Создать новый архив</value>
|
||||
</data>
|
||||
<data name="ViewPageAchievementAddArchiveHint" xml:space="preserve">
|
||||
<value>创建新存档以继续</value>
|
||||
</data>
|
||||
<data name="ViewPageAchievementExportLabel" xml:space="preserve">
|
||||
<value>导出</value>
|
||||
<value>Экспортировать</value>
|
||||
</data>
|
||||
<data name="ViewPageAchievementImportFromClipboard" xml:space="preserve">
|
||||
<value>从剪贴板导入</value>
|
||||
<value>Вставить из буфера обмена</value>
|
||||
</data>
|
||||
<data name="ViewPageAchievementImportFromFile" xml:space="preserve">
|
||||
<value>从 UIAF 文件导入</value>
|
||||
</data>
|
||||
<data name="ViewPageAchievementImportLabel" xml:space="preserve">
|
||||
<value>导入</value>
|
||||
<value>Импорт</value>
|
||||
</data>
|
||||
<data name="ViewPageAchievementRemoveArchive" xml:space="preserve">
|
||||
<value>删除当前存档</value>
|
||||
@@ -1683,10 +1689,10 @@
|
||||
<value>活动公告</value>
|
||||
</data>
|
||||
<data name="ViewPageAnnouncementGame" xml:space="preserve">
|
||||
<value>游戏公告</value>
|
||||
<value>Игровое Объявление</value>
|
||||
</data>
|
||||
<data name="ViewPageAnnouncementViewDetails" xml:space="preserve">
|
||||
<value>查看详情</value>
|
||||
<value>Показать детали</value>
|
||||
</data>
|
||||
<data name="ViewPageAvatarPropertyArtifactScore" xml:space="preserve">
|
||||
<value>圣遗物评分</value>
|
||||
@@ -1698,7 +1704,7 @@
|
||||
<value>当前角色与武器</value>
|
||||
</data>
|
||||
<data name="ViewPageAvatarPropertyCritScore" xml:space="preserve">
|
||||
<value>双暴评分</value>
|
||||
<value>Крит урон</value>
|
||||
</data>
|
||||
<data name="ViewPageAvatarPropertyDefaultDescription" xml:space="preserve">
|
||||
<value>尚未获取任何角色信息</value>
|
||||
@@ -1773,10 +1779,10 @@
|
||||
<value>材料统计</value>
|
||||
</data>
|
||||
<data name="ViewPageCultivationNavigateAction" xml:space="preserve">
|
||||
<value>前往</value>
|
||||
<value>Перейти</value>
|
||||
</data>
|
||||
<data name="ViewPageCultivationRemoveEntry" xml:space="preserve">
|
||||
<value>删除清单</value>
|
||||
<value>Удалить список</value>
|
||||
</data>
|
||||
<data name="ViewPageCultivationRemoveProject" xml:space="preserve">
|
||||
<value>删除当前计划</value>
|
||||
@@ -1788,13 +1794,13 @@
|
||||
<value>添加任意武器到养成计划</value>
|
||||
</data>
|
||||
<data name="ViewPageDailyNoteAddEntry" xml:space="preserve">
|
||||
<value>添加角色</value>
|
||||
<value>Добавить роль</value>
|
||||
</data>
|
||||
<data name="ViewPageDailyNoteAddEntryHint" xml:space="preserve">
|
||||
<value>添加角色以定时刷新</value>
|
||||
</data>
|
||||
<data name="ViewPageDailyNoteAddEntryToolTip" xml:space="preserve">
|
||||
<value>添加</value>
|
||||
<value>Добавить</value>
|
||||
</data>
|
||||
<data name="ViewPageDailyNoteAttendanceStatusInfo" xml:space="preserve">
|
||||
<value>历练点获取详情</value>
|
||||
@@ -1803,19 +1809,19 @@
|
||||
<value>在实时便笺刷新后推送到指定的 Webhook</value>
|
||||
</data>
|
||||
<data name="ViewPageDailyNoteConfigWebhookHeader" xml:space="preserve">
|
||||
<value>配置 Webhook</value>
|
||||
<value>Настройка вебхуков</value>
|
||||
</data>
|
||||
<data name="ViewPageDailyNoteDataInteropHeader" xml:space="preserve">
|
||||
<value>数据互操作</value>
|
||||
</data>
|
||||
<data name="ViewPageDailyNoteNotificationHeader" xml:space="preserve">
|
||||
<value>通知</value>
|
||||
<value>Уведомления</value>
|
||||
</data>
|
||||
<data name="ViewPageDailyNoteNotificationSetting" xml:space="preserve">
|
||||
<value>通知设置</value>
|
||||
<value>Настройки уведомлений</value>
|
||||
</data>
|
||||
<data name="ViewPageDailyNoteRefresh" xml:space="preserve">
|
||||
<value>立即刷新</value>
|
||||
<value>Обновить</value>
|
||||
</data>
|
||||
<data name="ViewPageDailyNoteRefreshTime" xml:space="preserve">
|
||||
<value>刷新间隔时间</value>
|
||||
@@ -1827,13 +1833,13 @@
|
||||
<value>提醒通知</value>
|
||||
</data>
|
||||
<data name="ViewPageDailyNoteRemoveToolTip" xml:space="preserve">
|
||||
<value>移除角色</value>
|
||||
<value>Удалить роли</value>
|
||||
</data>
|
||||
<data name="ViewPageDailyNoteResinDiscountUsed" xml:space="preserve">
|
||||
<value>本周已消耗减半次数</value>
|
||||
</data>
|
||||
<data name="ViewPageDailyNoteSettingAutoRefresh" xml:space="preserve">
|
||||
<value>自动刷新</value>
|
||||
<value>Автообновление</value>
|
||||
</data>
|
||||
<data name="ViewPageDailyNoteSettingAutoRefreshDescription" xml:space="preserve">
|
||||
<value>间隔选定的时间后刷新添加的实时便笺</value>
|
||||
@@ -1842,7 +1848,7 @@
|
||||
<value>这些选项仅允许在非管理员模式下更改</value>
|
||||
</data>
|
||||
<data name="ViewPageDailyNoteSettingRefreshHeader" xml:space="preserve">
|
||||
<value>刷新</value>
|
||||
<value>Обновить</value>
|
||||
</data>
|
||||
<data name="ViewPageDailyNoteSlientModeDescription" xml:space="preserve">
|
||||
<value>在我游玩原神时不通知我</value>
|
||||
@@ -1857,16 +1863,28 @@
|
||||
<value>搜索问题与建议</value>
|
||||
</data>
|
||||
<data name="ViewPageFeedBackBasicInformation" xml:space="preserve">
|
||||
<value>基本信息</value>
|
||||
<value>Основная информация</value>
|
||||
</data>
|
||||
<data name="ViewPageFeedbackCommonLinksHeader" xml:space="preserve">
|
||||
<value>常用链接</value>
|
||||
<value>Полезные ссылки</value>
|
||||
</data>
|
||||
<data name="ViewPageFeedbackCurrentProxyHeader" xml:space="preserve">
|
||||
<value>当前代理</value>
|
||||
</data>
|
||||
<data name="ViewPageFeedbackCurrentProxyNoProxyDescription" xml:space="preserve">
|
||||
<value>无代理</value>
|
||||
</data>
|
||||
<data name="ViewPageFeedbackEnableLoopbackEnabledDescription" xml:space="preserve">
|
||||
<value>已解除</value>
|
||||
</data>
|
||||
<data name="ViewPageFeedbackEnableLoopbackHeader" xml:space="preserve">
|
||||
<value>解除 Loopback 限制</value>
|
||||
</data>
|
||||
<data name="ViewPageFeedbackEngageWithUsDescription" xml:space="preserve">
|
||||
<value>与我们密切联系</value>
|
||||
</data>
|
||||
<data name="ViewPageFeedbackFeatureGuideHeader" xml:space="preserve">
|
||||
<value>功能指南</value>
|
||||
<value>Гайд по функциям</value>
|
||||
</data>
|
||||
<data name="ViewPageFeedbackGithubIssuesDescription" xml:space="preserve">
|
||||
<value>我们总是优先处理 GitHub 上的问题</value>
|
||||
@@ -1875,7 +1893,7 @@
|
||||
<value>开发路线规划</value>
|
||||
</data>
|
||||
<data name="ViewPageFeedbackSearchResultPlaceholderTitle" xml:space="preserve">
|
||||
<value>暂无搜索结果</value>
|
||||
<value>Не найдено</value>
|
||||
</data>
|
||||
<data name="ViewPageFeedbackServerStatusDescription" xml:space="preserve">
|
||||
<value>胡桃服务可用性监控</value>
|
||||
@@ -1887,13 +1905,13 @@
|
||||
<value>全量刷新</value>
|
||||
</data>
|
||||
<data name="ViewPageGachaLogExportAction" xml:space="preserve">
|
||||
<value>导出</value>
|
||||
<value>Экспортировать</value>
|
||||
</data>
|
||||
<data name="ViewPageGachaLogHint" xml:space="preserve">
|
||||
<value>尚未获取任何祈愿记录</value>
|
||||
</data>
|
||||
<data name="ViewPageGachaLogHutaoCloud" xml:space="preserve">
|
||||
<value>胡桃云</value>
|
||||
<value>Snap Hutao Cloud</value>
|
||||
</data>
|
||||
<data name="ViewPageGachaLogHutaoCloudAfdianPurchaseDescription" xml:space="preserve">
|
||||
<value>前往爱发电购买相关服务</value>
|
||||
@@ -1974,10 +1992,10 @@
|
||||
<value>总览</value>
|
||||
</data>
|
||||
<data name="ViewPageGahcaLogPivotStatistics" xml:space="preserve">
|
||||
<value>统计</value>
|
||||
<value>Статистика</value>
|
||||
</data>
|
||||
<data name="ViewPageGahcaLogPivotWeapon" xml:space="preserve">
|
||||
<value>武器</value>
|
||||
<value>Оружие</value>
|
||||
</data>
|
||||
<data name="ViewPageHomeGreetingTextCommon1" xml:space="preserve">
|
||||
<value>胡桃已经为你启动了 {0} 次游戏</value>
|
||||
@@ -1986,7 +2004,7 @@
|
||||
<value>你已经启动了胡桃 {0} 次</value>
|
||||
</data>
|
||||
<data name="ViewPageHomeGreetingTextDefault" xml:space="preserve">
|
||||
<value>旅行者,欢迎来到提瓦特大陆!</value>
|
||||
<value>Добро пожаловать в Тейват, Путешественник!</value>
|
||||
</data>
|
||||
<data name="ViewPageHomeGreetingTextEasterEgg" xml:space="preserve">
|
||||
<value>你说的对,但是《胡桃》是由 DGP Studio 自主研发的一款...</value>
|
||||
@@ -1995,10 +2013,10 @@
|
||||
<value>呐,旅行者,这是你来到提瓦特大陆的第 {0} 天哦</value>
|
||||
</data>
|
||||
<data name="ViewPageHomeLaunchGameSettingAction" xml:space="preserve">
|
||||
<value>设置</value>
|
||||
<value>Настройки</value>
|
||||
</data>
|
||||
<data name="ViewPageHutaoDatabaseOverview" xml:space="preserve">
|
||||
<value>详情</value>
|
||||
<value>Детали</value>
|
||||
</data>
|
||||
<data name="ViewPageHutaoDatabaseOverviewAvatarAppearanceRank" xml:space="preserve">
|
||||
<value>角色出场</value>
|
||||
@@ -2010,28 +2028,28 @@
|
||||
<value>角色使用</value>
|
||||
</data>
|
||||
<data name="ViewPageHutaoDatabaseOverviewConstellation0" xml:space="preserve">
|
||||
<value>0 命</value>
|
||||
<value>C 0</value>
|
||||
</data>
|
||||
<data name="ViewPageHutaoDatabaseOverviewConstellation1" xml:space="preserve">
|
||||
<value>1 命</value>
|
||||
<value>C 1</value>
|
||||
</data>
|
||||
<data name="ViewPageHutaoDatabaseOverviewConstellation2" xml:space="preserve">
|
||||
<value>2 命</value>
|
||||
<value>C 2</value>
|
||||
</data>
|
||||
<data name="ViewPageHutaoDatabaseOverviewConstellation3" xml:space="preserve">
|
||||
<value>3 命</value>
|
||||
<value>C 3</value>
|
||||
</data>
|
||||
<data name="ViewPageHutaoDatabaseOverviewConstellation4" xml:space="preserve">
|
||||
<value>4 命</value>
|
||||
<value>C 4</value>
|
||||
</data>
|
||||
<data name="ViewPageHutaoDatabaseOverviewConstellation5" xml:space="preserve">
|
||||
<value>5 命</value>
|
||||
<value>C 5</value>
|
||||
</data>
|
||||
<data name="ViewPageHutaoDatabaseOverviewConstellation6" xml:space="preserve">
|
||||
<value>6 命</value>
|
||||
<value>C 6</value>
|
||||
</data>
|
||||
<data name="ViewPageHutaoDatabaseOverviewConstellationAvatar" xml:space="preserve">
|
||||
<value>角色</value>
|
||||
<value>Персонаж</value>
|
||||
</data>
|
||||
<data name="ViewPageHutaoDatabaseOverviewConstellationHolding" xml:space="preserve">
|
||||
<value>持有</value>
|
||||
@@ -2064,22 +2082,22 @@
|
||||
<value>总计深渊记录</value>
|
||||
</data>
|
||||
<data name="ViewPageHutaoDatabaseOverviewTeamAppearance" xml:space="preserve">
|
||||
<value>队伍出场</value>
|
||||
<value>Teams</value>
|
||||
</data>
|
||||
<data name="ViewPageHutaoPassportLoginHeader" xml:space="preserve">
|
||||
<value>登录</value>
|
||||
<value>Войти</value>
|
||||
</data>
|
||||
<data name="ViewPageHutaoPassportPasswordHint" xml:space="preserve">
|
||||
<value>请输入密码</value>
|
||||
<value>Введите пароль</value>
|
||||
</data>
|
||||
<data name="ViewPageHutaoPassportPasswordRequirementHint" xml:space="preserve">
|
||||
<value>至少需要 8 个字符</value>
|
||||
<value>Требуется не менее 8 символов</value>
|
||||
</data>
|
||||
<data name="ViewPageHutaoPassportRegisterHeader" xml:space="preserve">
|
||||
<value>注册</value>
|
||||
<value>Регистрация</value>
|
||||
</data>
|
||||
<data name="ViewPageHutaoPassportResetPasswordHeader" xml:space="preserve">
|
||||
<value>重置密码</value>
|
||||
<value>Сбросить пароль</value>
|
||||
</data>
|
||||
<data name="ViewPageHutaoPassportResetPasswordHint" xml:space="preserve">
|
||||
<value>注销账号的数据将永远丢失,无法恢复</value>
|
||||
@@ -2202,7 +2220,7 @@
|
||||
<value>资源下载</value>
|
||||
</data>
|
||||
<data name="ViewPageLaunchGameResourceLatestHeader" xml:space="preserve">
|
||||
<value>客户端</value>
|
||||
<value>完整包</value>
|
||||
</data>
|
||||
<data name="ViewPageLaunchGameResourcePreDownloadHeader" xml:space="preserve">
|
||||
<value>预下载</value>
|
||||
@@ -2538,7 +2556,7 @@
|
||||
<value>前往商店</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingWebview2Header" xml:space="preserve">
|
||||
<value>Webview2 运行时</value>
|
||||
<value>Webview2 Runtime</value>
|
||||
</data>
|
||||
<data name="ViewPageWiKiAvatarArtifactSetCombinationHeader" xml:space="preserve">
|
||||
<value>搭配圣遗物</value>
|
||||
@@ -2637,10 +2655,10 @@
|
||||
<value>删除转换服务器缓存</value>
|
||||
</data>
|
||||
<data name="ViewSettingFolderViewOpenFolderAction" xml:space="preserve">
|
||||
<value>打开文件夹</value>
|
||||
<value>Открыть папку</value>
|
||||
</data>
|
||||
<data name="ViewSettingHeader" xml:space="preserve">
|
||||
<value>设置</value>
|
||||
<value>Настройки</value>
|
||||
</data>
|
||||
<data name="ViewSpiralAbyssAvatarAppearanceRankDescription" xml:space="preserve">
|
||||
<value>角色出场率 = 本层上阵该角色次数(层内重复出现只记一次)/ 深渊记录总数</value>
|
||||
@@ -2649,13 +2667,13 @@
|
||||
<value>角色使用率 = 本层上阵该角色次数(层内重复出现只记一次)/ 持有该角色的深渊记录总数</value>
|
||||
</data>
|
||||
<data name="ViewSpiralAbyssBattleHeader" xml:space="preserve">
|
||||
<value>战斗数据</value>
|
||||
<value>Результаты боя</value>
|
||||
</data>
|
||||
<data name="ViewSpiralAbyssBattleTimes" xml:space="preserve">
|
||||
<value>战斗次数</value>
|
||||
<value>Количество сражений</value>
|
||||
</data>
|
||||
<data name="ViewSpiralAbyssDamage" xml:space="preserve">
|
||||
<value>最强一击</value>
|
||||
<value>Максимальный урон</value>
|
||||
</data>
|
||||
<data name="ViewSpiralAbyssDefaultDescription" xml:space="preserve">
|
||||
<value>尚未获取任何挑战记录</value>
|
||||
@@ -2667,19 +2685,19 @@
|
||||
<value>分期详情</value>
|
||||
</data>
|
||||
<data name="ViewSpiralAbyssEnergySkill" xml:space="preserve">
|
||||
<value>元素爆发</value>
|
||||
<value>Элементарная атака</value>
|
||||
</data>
|
||||
<data name="ViewSpiralAbyssHeader" xml:space="preserve">
|
||||
<value>深境螺旋</value>
|
||||
<value>Витая Бездна</value>
|
||||
</data>
|
||||
<data name="ViewSpiralAbyssHutaoStatistics" xml:space="preserve">
|
||||
<value>本期统计</value>
|
||||
<value>Статистика</value>
|
||||
</data>
|
||||
<data name="ViewSpiralAbyssMaxFloor" xml:space="preserve">
|
||||
<value>最深抵达</value>
|
||||
<value>Максимальный этаж Бездны</value>
|
||||
</data>
|
||||
<data name="ViewSpiralAbyssNormalSkill" xml:space="preserve">
|
||||
<value>元素战技</value>
|
||||
<value>Elemental Skills Cast</value>
|
||||
</data>
|
||||
<data name="ViewSpiralAbyssRecordBattleAvatars" xml:space="preserve">
|
||||
<value>上场角色</value>
|
||||
@@ -2688,7 +2706,7 @@
|
||||
<value>攻击地脉镇石</value>
|
||||
</data>
|
||||
<data name="ViewSpiralAbyssRefresh" xml:space="preserve">
|
||||
<value>刷新数据</value>
|
||||
<value>Обновить данные</value>
|
||||
</data>
|
||||
<data name="ViewSpiralAbyssRefreshDescription" xml:space="preserve">
|
||||
<value>同步米游社的深渊挑战记录</value>
|
||||
@@ -2697,79 +2715,79 @@
|
||||
<value>出战次数</value>
|
||||
</data>
|
||||
<data name="ViewSpiralAbyssStatistics" xml:space="preserve">
|
||||
<value>统计数据</value>
|
||||
<value>Статистика</value>
|
||||
</data>
|
||||
<data name="ViewSpiralAbyssTakeDamage" xml:space="preserve">
|
||||
<value>最多承伤</value>
|
||||
</data>
|
||||
<data name="ViewSpiralAbyssTotalStar" xml:space="preserve">
|
||||
<value>获得渊星</value>
|
||||
<value>Stars Earned</value>
|
||||
</data>
|
||||
<data name="ViewSpiralAbyssUploadRecord" xml:space="preserve">
|
||||
<value>上传数据</value>
|
||||
<value>Загрузить данные</value>
|
||||
</data>
|
||||
<data name="ViewTitileUpdatePackageReadyContent" xml:space="preserve">
|
||||
<value>是否立即安装?</value>
|
||||
<value>Установить сейчас?</value>
|
||||
</data>
|
||||
<data name="ViewTitileUpdatePackageReadyTitle" xml:space="preserve">
|
||||
<value>胡桃 {0} 版本已准备就绪</value>
|
||||
<value>Версия Snap Hutao {0} готова</value>
|
||||
</data>
|
||||
<data name="ViewTitleAutoClicking" xml:space="preserve">
|
||||
<value>自动连点</value>
|
||||
<value>Авто Клики</value>
|
||||
</data>
|
||||
<data name="ViewToolHeader" xml:space="preserve">
|
||||
<value>工具</value>
|
||||
<value>Инструменты</value>
|
||||
</data>
|
||||
<data name="ViewUserCookieOperation" xml:space="preserve">
|
||||
<value>米游社</value>
|
||||
<value>MiYouShe</value>
|
||||
</data>
|
||||
<data name="ViewUserCookieOperation2" xml:space="preserve">
|
||||
<value>HoYoLAB</value>
|
||||
</data>
|
||||
<data name="ViewUserCookieOperation3" xml:space="preserve">
|
||||
<value>当前用户</value>
|
||||
<value>Текущий пользователь</value>
|
||||
</data>
|
||||
<data name="ViewUserCookieOperationGameRecordIndexAction" xml:space="preserve">
|
||||
<value>旅行工具</value>
|
||||
<value>Официальные инструменты</value>
|
||||
</data>
|
||||
<data name="ViewUserCookieOperationLoginMihoyoUserAction" xml:space="preserve">
|
||||
<value>网页登录</value>
|
||||
<value>Вход через браузер</value>
|
||||
</data>
|
||||
<data name="ViewUserCookieOperationLoginQRCodeAction" xml:space="preserve">
|
||||
<value>扫码登录</value>
|
||||
<value>Вход по QR-коду</value>
|
||||
</data>
|
||||
<data name="ViewUserCookieOperationManualInputAction" xml:space="preserve">
|
||||
<value>手动输入</value>
|
||||
<value>Ввести вручную</value>
|
||||
</data>
|
||||
<data name="ViewUserCookieOperationRefreshCookieAction" xml:space="preserve">
|
||||
<value>刷新 Cookie</value>
|
||||
<value>Обновить Cookie</value>
|
||||
</data>
|
||||
<data name="ViewUserCookieOperationSignInRewardAction" xml:space="preserve">
|
||||
<value>领取签到奖励</value>
|
||||
<value>Забрать награду за регистрацию</value>
|
||||
</data>
|
||||
<data name="ViewUserCopyCookieAction" xml:space="preserve">
|
||||
<value>复制 Cookie</value>
|
||||
<value>Скопировать Cookie</value>
|
||||
</data>
|
||||
<data name="ViewUserDefaultDescription" xml:space="preserve">
|
||||
<value>请先登录</value>
|
||||
<value>Пожалуйста, сначала войдите в приложение</value>
|
||||
</data>
|
||||
<data name="ViewUserDocumentationHeader" xml:space="preserve">
|
||||
<value>文档</value>
|
||||
<value>Документация</value>
|
||||
</data>
|
||||
<data name="ViewUserNoUserHint" xml:space="preserve">
|
||||
<value>尚未登录</value>
|
||||
<value>Вы не вошли в приложение</value>
|
||||
</data>
|
||||
<data name="ViewUserRefreshCookieTokenSuccess" xml:space="preserve">
|
||||
<value>刷新 CookieToken 成功</value>
|
||||
<value>Обновление токена Cookie выполнено успешно.</value>
|
||||
</data>
|
||||
<data name="ViewUserRefreshCookieTokenWarning" xml:space="preserve">
|
||||
<value>刷新 CookieToken 失败</value>
|
||||
<value>Не удалось обновить токен Cookie.</value>
|
||||
</data>
|
||||
<data name="ViewUserRemoveAction" xml:space="preserve">
|
||||
<value>移除用户</value>
|
||||
<value>Удалить пользователя</value>
|
||||
</data>
|
||||
<data name="ViewUserRole" xml:space="preserve">
|
||||
<value>角色</value>
|
||||
<value>Персонаж</value>
|
||||
</data>
|
||||
<data name="ViewUserUser" xml:space="preserve">
|
||||
<value>用户</value>
|
||||
|
||||
@@ -738,10 +738,10 @@
|
||||
<value>養成計算:{0:MM-dd HH:mm}</value>
|
||||
</data>
|
||||
<data name="ServiceAvatarInfoSummaryGameRecordNotRefreshed" xml:space="preserve">
|
||||
<value>原神战绩:尚未刷新</value>
|
||||
<value>原神戰績:尚未更新</value>
|
||||
</data>
|
||||
<data name="ServiceAvatarInfoSummaryGameRecordRefreshTimeFormat" xml:space="preserve">
|
||||
<value>原神战绩:{0:MM-dd HH:mm}</value>
|
||||
<value>原神戰績:{0:MM-dd HH:mm}</value>
|
||||
</data>
|
||||
<data name="ServiceAvatarInfoSummaryShowcaseNotRefreshed" xml:space="preserve">
|
||||
<value>角色櫥窗:尚未刷新</value>
|
||||
@@ -1187,6 +1187,12 @@
|
||||
<data name="ViewDialogDailyNoteWebhookUrlTitle" xml:space="preserve">
|
||||
<value>即時便箋 Webhook Url</value>
|
||||
</data>
|
||||
<data name="ViewDialogFeedbackEnableLoopbackContent" xml:space="preserve">
|
||||
<value>解除限制后需要使用其他工具恢复限制</value>
|
||||
</data>
|
||||
<data name="ViewDialogFeedbackEnableLoopbackTitle" xml:space="preserve">
|
||||
<value>是否解除 Loopback 限制</value>
|
||||
</data>
|
||||
<data name="ViewDialogGachaLogImportTitle" xml:space="preserve">
|
||||
<value>匯入祈願記錄</value>
|
||||
</data>
|
||||
@@ -1284,7 +1290,7 @@
|
||||
<value>使用米遊社掃描 QR 碼</value>
|
||||
</data>
|
||||
<data name="ViewDialogReconfirmTextHeader" xml:space="preserve">
|
||||
<value>为防止你在无意间启用,请输入正在启用的功能开关的<b>标题名称</b></value>
|
||||
<value>為防止你在無意間啟用,請輸入正在啟用的功能開關的<b>標題名稱</b></value>
|
||||
</data>
|
||||
<data name="ViewDialogReconfirmTitle" xml:space="preserve">
|
||||
<value>你正在啟用一個危險功能</value>
|
||||
@@ -1451,6 +1457,9 @@
|
||||
<data name="ViewModelDailyNoteHoyolabVerificationUnsupported" xml:space="preserve">
|
||||
<value>HoYoLAB 賬號不支持驗證實时便箋</value>
|
||||
</data>
|
||||
<data name="ViewModelDailyNoteModifyTaskFail" xml:space="preserve">
|
||||
<value>修改計劃任務失敗</value>
|
||||
</data>
|
||||
<data name="ViewModelDailyNoteRefreshTime30" xml:space="preserve">
|
||||
<value>30 分鐘 | 3.75 樹脂</value>
|
||||
</data>
|
||||
@@ -1466,9 +1475,6 @@
|
||||
<data name="ViewModelDailyNoteRefreshTime8" xml:space="preserve">
|
||||
<value>8 分鐘 | 1 樹脂</value>
|
||||
</data>
|
||||
<data name="ViewModelDailyNoteRegisterTaskFail" xml:space="preserve">
|
||||
<value>注冊計劃任務失敗,請以管理員模式重試</value>
|
||||
</data>
|
||||
<data name="ViewModelDailyNoteRequestProgressTitle" xml:space="preserve">
|
||||
<value>正在獲取實時便箋信息,請稍候</value>
|
||||
</data>
|
||||
@@ -1728,7 +1734,7 @@
|
||||
<value>同步角色天賦信息</value>
|
||||
</data>
|
||||
<data name="ViewPageAvatarPropertyRefreshFromHoyolabGameRecord" xml:space="preserve">
|
||||
<value>从米游社原神战绩同步</value>
|
||||
<value>從米遊社原神戰績同步</value>
|
||||
</data>
|
||||
<data name="ViewPageAvatarPropertyRefreshFromHoyolabGameRecordDescription" xml:space="preserve">
|
||||
<value>同步角色天賦外的大部分信息</value>
|
||||
@@ -1860,28 +1866,40 @@
|
||||
<value>基本資訊</value>
|
||||
</data>
|
||||
<data name="ViewPageFeedbackCommonLinksHeader" xml:space="preserve">
|
||||
<value>常用链接</value>
|
||||
<value>常用連結</value>
|
||||
</data>
|
||||
<data name="ViewPageFeedbackCurrentProxyHeader" xml:space="preserve">
|
||||
<value>当前代理</value>
|
||||
</data>
|
||||
<data name="ViewPageFeedbackCurrentProxyNoProxyDescription" xml:space="preserve">
|
||||
<value>无代理</value>
|
||||
</data>
|
||||
<data name="ViewPageFeedbackEnableLoopbackEnabledDescription" xml:space="preserve">
|
||||
<value>已解除</value>
|
||||
</data>
|
||||
<data name="ViewPageFeedbackEnableLoopbackHeader" xml:space="preserve">
|
||||
<value>解除 Loopback 限制</value>
|
||||
</data>
|
||||
<data name="ViewPageFeedbackEngageWithUsDescription" xml:space="preserve">
|
||||
<value>与我们密切联系</value>
|
||||
<value>與我們密切聯繫</value>
|
||||
</data>
|
||||
<data name="ViewPageFeedbackFeatureGuideHeader" xml:space="preserve">
|
||||
<value>功能指南</value>
|
||||
</data>
|
||||
<data name="ViewPageFeedbackGithubIssuesDescription" xml:space="preserve">
|
||||
<value>我们总是优先处理 GitHub 上的问题</value>
|
||||
<value>我們總是優先處理 GitHub 上的問題</value>
|
||||
</data>
|
||||
<data name="ViewPageFeedbackRoadmapDescription" xml:space="preserve">
|
||||
<value>开发路线规划</value>
|
||||
<value>開發路線規劃</value>
|
||||
</data>
|
||||
<data name="ViewPageFeedbackSearchResultPlaceholderTitle" xml:space="preserve">
|
||||
<value>暂无搜索结果</value>
|
||||
<value>暫無搜尋結果</value>
|
||||
</data>
|
||||
<data name="ViewPageFeedbackServerStatusDescription" xml:space="preserve">
|
||||
<value>胡桃服务可用性监控</value>
|
||||
<value>胡桃服務可用性監控</value>
|
||||
</data>
|
||||
<data name="ViewPageFeedbackServerStatusHeader" xml:space="preserve">
|
||||
<value>胡桃服务</value>
|
||||
<value>胡桃服務</value>
|
||||
</data>
|
||||
<data name="ViewPageGachaLogAggressiveRefresh" xml:space="preserve">
|
||||
<value>全量式重整</value>
|
||||
@@ -2202,7 +2220,7 @@
|
||||
<value>資源下載</value>
|
||||
</data>
|
||||
<data name="ViewPageLaunchGameResourceLatestHeader" xml:space="preserve">
|
||||
<value>用戶端</value>
|
||||
<value>完整包</value>
|
||||
</data>
|
||||
<data name="ViewPageLaunchGameResourcePreDownloadHeader" xml:space="preserve">
|
||||
<value>預下載</value>
|
||||
@@ -2925,7 +2943,7 @@
|
||||
<value>{0} 秒</value>
|
||||
</data>
|
||||
<data name="WebDailyNoteVerificationFailed" xml:space="preserve">
|
||||
<value>验证失败,请手动验证或前往「米游社-旅行工具-原神战绩-实时便笺」页面查看</value>
|
||||
<value>驗證失敗,請手動進行驗證或前往「米遊社-旅行工具-原神戰績-實時便箋」頁面查看。</value>
|
||||
</data>
|
||||
<data name="WebEnkaResponseStatusCode400" xml:space="preserve">
|
||||
<value>錯誤的 UID 格式</value>
|
||||
@@ -2994,7 +3012,7 @@
|
||||
<value>胡桃服務維護中</value>
|
||||
</data>
|
||||
<data name="WebIndexOrSpiralAbyssVerificationFailed" xml:space="preserve">
|
||||
<value>验证失败,请手动验证或前往「米游社-旅行工具-原神战绩」页面查看</value>
|
||||
<value>驗證失敗,請手動進行驗證或前往「米遊社-旅行工具-原神戰績」頁面查看。</value>
|
||||
</data>
|
||||
<data name="WebResponseFormat" xml:space="preserve">
|
||||
<value>狀態:{0} | 信息:{1}</value>
|
||||
|
||||
BIN
src/Snap.Hutao/Snap.Hutao/Resource/TestBackground.jpg
Normal file
BIN
src/Snap.Hutao/Snap.Hutao/Resource/TestBackground.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.8 MiB |
@@ -60,14 +60,16 @@ internal sealed partial class DailyNoteOptions : DbStoreOptions
|
||||
{
|
||||
if (!scheduleTaskInterop.RegisterForDailyNoteRefresh(SelectedRefreshTime.Value))
|
||||
{
|
||||
serviceProvider.GetRequiredService<IInfoBarService>().Warning(SH.ViewModelDailyNoteRegisterTaskFail);
|
||||
return;
|
||||
serviceProvider.GetRequiredService<IInfoBarService>().Warning(SH.ViewModelDailyNoteModifyTaskFail);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
scheduleTaskInterop.UnregisterForDailyNoteRefresh();
|
||||
if (!scheduleTaskInterop.UnregisterForDailyNoteRefresh())
|
||||
{
|
||||
serviceProvider.GetRequiredService<IInfoBarService>().Warning(SH.ViewModelDailyNoteModifyTaskFail);
|
||||
}
|
||||
}
|
||||
|
||||
OnPropertyChanged();
|
||||
@@ -106,7 +108,7 @@ internal sealed partial class DailyNoteOptions : DbStoreOptions
|
||||
}
|
||||
else
|
||||
{
|
||||
serviceProvider.GetRequiredService<IInfoBarService>().Warning(SH.ViewModelDailyNoteRegisterTaskFail);
|
||||
serviceProvider.GetRequiredService<IInfoBarService>().Warning(SH.ViewModelDailyNoteModifyTaskFail);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user