mirror of
https://jihulab.com/DGP-Studio/Snap.Hutao.git
synced 2025-11-19 21:02:53 +08:00
Compare commits
49 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
94a5e71130 | ||
|
|
07cdfcea28 | ||
|
|
d93a9f41f3 | ||
|
|
910f099c6d | ||
|
|
e68449ec0c | ||
|
|
e484fbed21 | ||
|
|
88af6d28a9 | ||
|
|
3ab34f0992 | ||
|
|
5e875a7f18 | ||
|
|
89d98748e8 | ||
|
|
d33cd894b9 | ||
|
|
f0c19b419e | ||
|
|
f1d9787e45 | ||
|
|
5f9b4a7cb2 | ||
|
|
8710150897 | ||
|
|
92c1b12764 | ||
|
|
d73bd557f3 | ||
|
|
777d7d1056 | ||
|
|
1a944dae9c | ||
|
|
a26c52ba97 | ||
|
|
5fab03d57e | ||
|
|
e8a459cb41 | ||
|
|
04df5a7bf1 | ||
|
|
a93eb505d6 | ||
|
|
1ebcc2fc89 | ||
|
|
e9917e788d | ||
|
|
9665876d52 | ||
|
|
8921816873 | ||
|
|
2698761594 | ||
|
|
3ae4210ca0 | ||
|
|
2f5e0cbe39 | ||
|
|
d3444a9435 | ||
|
|
8b6f95c3d9 | ||
|
|
88b8335e5b | ||
|
|
061aba715b | ||
|
|
da80631b72 | ||
|
|
97acf872bc | ||
|
|
addaf1a9e3 | ||
|
|
76183901da | ||
|
|
87ee81e7fa | ||
|
|
f2f858de15 | ||
|
|
c434521004 | ||
|
|
27ed2cefc1 | ||
|
|
6dc1e664b0 | ||
|
|
aa46b6531b | ||
|
|
b36399f572 | ||
|
|
51c3dde24b | ||
|
|
2d497faaa5 | ||
|
|
4783934b92 |
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
|
||||
|
||||
@@ -4,13 +4,15 @@
|
||||
|
||||
### Setup Snap.Hutao Project
|
||||
|
||||
1. Download and install [Visual Studio 2022 Community](https://visualstudio.microsoft.com/downloads/)
|
||||
2. Open Visual Studio Installer to complete Visual Studio installation
|
||||
- You need to install `.NET desktop development`, `Desktop development with C++` and `Universal Windows Platform development` components
|
||||
3. Install `Single-project MSIX Packaging Tools for VS 2022` provided by Microsoft in Visual Studio marketplace
|
||||
4. Use git to clone the project `https://github.com/DGP-Studio/Snap.Hutao.git` to your local device
|
||||
5. Switch git branch to `develop`
|
||||
6. Open project solution with your Visual Studio and then you are ready to go
|
||||
1. Download and install [Visual Studio 2022 Community](https://visualstudio.microsoft.com/downloads/).
|
||||
- No need to select workloads; Visual Studio will handle it automatically.
|
||||
- Close Visual Studio Installer to ensure a smooth installation experience for workloads.
|
||||
- If using Visual Studio 2022 17.9 preview, skip step 5, as automatic extension installation is supported in this version.
|
||||
2. Use git to clone the project `https://github.com/DGP-Studio/Snap.Hutao.git` to your local device.
|
||||
3. Switch to the`develop` branch using git.
|
||||
4. Open the project solution with your Visual Studio. Visual Studio will prompt you to install the necessary workloads, closing and reopening automatically.
|
||||
5. (For Visual Studio 2022 17.8) Install the [Single-project MSIX Packaging Tools for VS 2022](https://marketplace.visualstudio.com/items?itemName=ProjectReunion.MicrosoftSingleProjectMSIXPackagingToolsDev17) provided by Microsoft in Visual Studio marketplace.
|
||||
6. Open the project solution with your Visual Studio, and you are ready to go.
|
||||
|
||||
### Start Pull Request
|
||||
|
||||
|
||||
@@ -124,9 +124,6 @@ dotnet_diagnostic.SA1623.severity = none
|
||||
# SA1636: File header copyright text should match
|
||||
dotnet_diagnostic.SA1636.severity = none
|
||||
|
||||
# SA1414: Tuple types in signatures should have element names
|
||||
dotnet_diagnostic.SA1414.severity = none
|
||||
|
||||
# SA0001: XML comment analysis disabled
|
||||
dotnet_diagnostic.SA0001.severity = none
|
||||
csharp_style_prefer_parameter_null_checking = true:suggestion
|
||||
@@ -325,7 +322,6 @@ dotnet_diagnostic.CA2227.severity = suggestion
|
||||
# CA2251: 使用 “string.Equals”
|
||||
dotnet_diagnostic.CA2251.severity = suggestion
|
||||
csharp_style_prefer_primary_constructors = true:suggestion
|
||||
dotnet_diagnostic.SA1010.severity = none
|
||||
|
||||
[*.vb]
|
||||
#### 命名样式 ####
|
||||
|
||||
11
src/Snap.Hutao/.vsconfig
Normal file
11
src/Snap.Hutao/.vsconfig
Normal file
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"version": "1.0",
|
||||
"components": [
|
||||
"Microsoft.VisualStudio.Workload.ManagedDesktop",
|
||||
"Microsoft.VisualStudio.Workload.NativeDesktop",
|
||||
"Microsoft.VisualStudio.Workload.Universal"
|
||||
],
|
||||
"extensions": [
|
||||
"https://marketplace.visualstudio.com/items?itemName=ProjectReunion.MicrosoftSingleProjectMSIXPackagingToolsDev17"
|
||||
]
|
||||
}
|
||||
@@ -1,4 +1,9 @@
|
||||
// COMCTL32
|
||||
// ADVAPI32
|
||||
RegCloseKey
|
||||
RegOpenKeyExW
|
||||
RegNotifyChangeKeyValue
|
||||
|
||||
// COMCTL32
|
||||
DefSubclassProc
|
||||
RemoveWindowSubclass
|
||||
SetWindowSubclass
|
||||
@@ -47,12 +52,14 @@ GetCursorPos
|
||||
GetDC
|
||||
GetDpiForWindow
|
||||
GetForegroundWindow
|
||||
GetWindowLongPtrW
|
||||
GetWindowPlacement
|
||||
GetWindowThreadProcessId
|
||||
ReleaseDC
|
||||
RegisterHotKey
|
||||
SendInput
|
||||
SetForegroundWindow
|
||||
SetWindowLongPtrW
|
||||
UnregisterHotKey
|
||||
|
||||
// COM
|
||||
@@ -69,26 +76,42 @@ SHELL_LINK_DATA_FLAGS
|
||||
// WinRT
|
||||
IMemoryBufferByteAccess
|
||||
|
||||
// Macro
|
||||
HRESULT_FROM_WIN32
|
||||
|
||||
// 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
|
||||
// HKEY
|
||||
HKEY_CLASSES_ROOT
|
||||
HKEY_CURRENT_CONFIG
|
||||
HKEY_CURRENT_USER
|
||||
HKEY_LOCAL_MACHINE
|
||||
HKEY_USERS
|
||||
|
||||
// HRESULT
|
||||
E_FAIL
|
||||
RPC_E_WRONG_THREAD
|
||||
|
||||
// System.Com
|
||||
CWMO_FLAGS
|
||||
|
||||
// System.Registry
|
||||
REG_NOTIFY_FILTER
|
||||
|
||||
// System.Threading
|
||||
LPTHREAD_START_ROUTINE
|
||||
|
||||
// UI.Shell
|
||||
SLGP_FLAGS
|
||||
|
||||
// UI.WindowsAndMessaging
|
||||
MINMAXINFO
|
||||
|
||||
// System.Com
|
||||
CWMO_FLAGS
|
||||
WINDOW_EX_STYLE
|
||||
@@ -1,9 +1,10 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<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>
|
||||
|
||||
@@ -1,72 +0,0 @@
|
||||
using System;
|
||||
using System.Reflection;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Windows.Win32.CsWin32.InteropServices;
|
||||
|
||||
internal class WinRTCustomMarshaler : ICustomMarshaler
|
||||
{
|
||||
private static readonly string? AssemblyFullName = typeof(Windows.Foundation.IMemoryBuffer).Assembly.FullName;
|
||||
|
||||
private readonly string className;
|
||||
private bool lookedForFromAbi;
|
||||
private MethodInfo? fromAbiMethod;
|
||||
|
||||
private WinRTCustomMarshaler(string className)
|
||||
{
|
||||
this.className = className;
|
||||
}
|
||||
|
||||
public static ICustomMarshaler GetInstance(string cookie)
|
||||
{
|
||||
return new WinRTCustomMarshaler(cookie);
|
||||
}
|
||||
|
||||
public void CleanUpManagedData(object ManagedObj)
|
||||
{
|
||||
}
|
||||
|
||||
public void CleanUpNativeData(nint pNativeData)
|
||||
{
|
||||
Marshal.Release(pNativeData);
|
||||
}
|
||||
|
||||
public int GetNativeDataSize()
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
public nint MarshalManagedToNative(object ManagedObj)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
public object MarshalNativeToManaged(nint thisPtr)
|
||||
{
|
||||
return className switch
|
||||
{
|
||||
"Windows.System.DispatcherQueueController" => Windows.System.DispatcherQueueController.FromAbi(thisPtr),
|
||||
_ => MarshalNativeToManagedSlow(thisPtr),
|
||||
};
|
||||
}
|
||||
|
||||
private object MarshalNativeToManagedSlow(nint pNativeData)
|
||||
{
|
||||
if (!lookedForFromAbi)
|
||||
{
|
||||
Type? type = Type.GetType($"{className}, {AssemblyFullName}");
|
||||
|
||||
fromAbiMethod = type?.GetMethod("FromAbi");
|
||||
lookedForFromAbi = true;
|
||||
}
|
||||
|
||||
if (fromAbiMethod is not null)
|
||||
{
|
||||
return fromAbiMethod.Invoke(default, new object[] { pNativeData })!;
|
||||
}
|
||||
else
|
||||
{
|
||||
return Marshal.GetObjectForIUnknown(pNativeData);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -8,6 +8,7 @@ EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{9A95A964-04B1-477A-BDE7-505525B3CAD8}"
|
||||
ProjectSection(SolutionItems) = preProject
|
||||
.editorconfig = .editorconfig
|
||||
.vsconfig = .vsconfig
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Snap.Hutao.Test", "Snap.Hutao.Test\Snap.Hutao.Test.csproj", "{D691BA9F-904C-4229-87A5-E14F2EFF2F64}"
|
||||
@@ -87,11 +88,11 @@ Global
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
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"]}
|
||||
RESX_ShowErrorsInErrorList = False
|
||||
RESX_SortFileContentOnSave = True
|
||||
SolutionGuid = {E4449B1C-0E6A-4D19-955E-1CA491656ABA}
|
||||
RESX_NeutralResourcesLanguage = zh-CN
|
||||
RESX_AutoApplyExistingTranslations = False
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
|
||||
@@ -7,6 +7,7 @@ namespace Snap.Hutao.Control;
|
||||
|
||||
[TemplateVisualState(Name = "LoadingIn", GroupName = "CommonStates")]
|
||||
[TemplateVisualState(Name = "LoadingOut", GroupName = "CommonStates")]
|
||||
[TemplatePart(Name = "ContentGrid", Type = typeof(FrameworkElement))]
|
||||
internal class Loading : Microsoft.UI.Xaml.Controls.ContentControl
|
||||
{
|
||||
public static readonly DependencyProperty IsLoadingProperty = DependencyProperty.Register(nameof(IsLoading), typeof(bool), typeof(Loading), new PropertyMetadata(default(bool), IsLoadingPropertyChanged));
|
||||
|
||||
@@ -3,7 +3,9 @@
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:shc="using:Snap.Hutao.Control">
|
||||
|
||||
<Style TargetType="shc:Loading">
|
||||
<Style BasedOn="{StaticResource DefaultLoadingStyle}" TargetType="shc:Loading"/>
|
||||
|
||||
<Style x:Key="DefaultLoadingStyle" TargetType="shc:Loading">
|
||||
<Setter Property="HorizontalContentAlignment" Value="Center"/>
|
||||
<Setter Property="VerticalContentAlignment" Value="Center"/>
|
||||
<Setter Property="HorizontalAlignment" Value="Stretch"/>
|
||||
|
||||
@@ -18,12 +18,15 @@ internal sealed class FontIconExtension : MarkupExtension
|
||||
/// </summary>
|
||||
public string Glyph { get; set; } = default!;
|
||||
|
||||
public double FontSize { get; set; } = 12;
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override object ProvideValue()
|
||||
{
|
||||
return new FontIcon()
|
||||
{
|
||||
Glyph = Glyph,
|
||||
FontSize = FontSize,
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -156,7 +156,7 @@ internal sealed partial class HtmlDescriptionTextBlock : ContentControl
|
||||
text.Inlines.Add(new Run
|
||||
{
|
||||
Text = slice.ToString(),
|
||||
FontWeight = FontWeights.Bold,
|
||||
FontWeight = FontWeights.SemiBold,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -20,6 +20,9 @@
|
||||
<ItemsPanelTemplate x:Key="StackPanelSpacing4Template">
|
||||
<StackPanel Spacing="4"/>
|
||||
</ItemsPanelTemplate>
|
||||
<ItemsPanelTemplate x:Key="StackPanelSpacing8Template">
|
||||
<StackPanel Spacing="8"/>
|
||||
</ItemsPanelTemplate>
|
||||
<ItemsPanelTemplate x:Key="UniformGridColumns2Spacing2Template">
|
||||
<cwcont:UniformGrid
|
||||
ColumnSpacing="2"
|
||||
|
||||
@@ -6,6 +6,12 @@
|
||||
<x:Double x:Key="SettingsCardWrapNoIconThreshold">0</x:Double>
|
||||
|
||||
<x:Double x:Key="SettingsCardContentControlMinWidth">120</x:Double>
|
||||
<x:Double x:Key="SettingsCardContentControlMinWidth2">160</x:Double>
|
||||
|
||||
<x:Double x:Key="SettingsCardContentControlSpacing">10</x:Double>
|
||||
|
||||
<Thickness x:Key="SettingsCardAlignSettingsExpanderPadding">16,16,44,16</Thickness>
|
||||
<Thickness x:Key="SettingsExpanderItemHasIconPadding">16,8,16,8</Thickness>
|
||||
|
||||
<Style
|
||||
x:Key="SettingsSectionHeaderTextBlockStyle"
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
|
||||
<!-- EmotionIcon -->
|
||||
<x:String x:Key="UI_EmotionIcon25">https://api.snapgenshin.com/static/raw/EmotionIcon/UI_EmotionIcon25.png</x:String>
|
||||
<x:String x:Key="UI_EmotionIcon52">https://api.snapgenshin.com/static/raw/EmotionIcon/UI_EmotionIcon52.png</x:String>
|
||||
<x:String x:Key="UI_EmotionIcon71">https://api.snapgenshin.com/static/raw/EmotionIcon/UI_EmotionIcon71.png</x:String>
|
||||
<x:String x:Key="UI_EmotionIcon250">https://api.snapgenshin.com/static/raw/EmotionIcon/UI_EmotionIcon250.png</x:String>
|
||||
<x:String x:Key="UI_EmotionIcon271">https://api.snapgenshin.com/static/raw/EmotionIcon/UI_EmotionIcon271.png</x:String>
|
||||
|
||||
@@ -2,9 +2,11 @@
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using CommunityToolkit.Mvvm.Messaging;
|
||||
using Snap.Hutao.Core.IO.Http.DynamicProxy;
|
||||
using Snap.Hutao.Core.Logging;
|
||||
using Snap.Hutao.Service;
|
||||
using System.Globalization;
|
||||
using System.Net.Http;
|
||||
using System.Runtime.CompilerServices;
|
||||
using Windows.Globalization;
|
||||
|
||||
@@ -41,6 +43,7 @@ internal static class DependencyInjection
|
||||
|
||||
serviceProvider.InitializeConsoleWindow();
|
||||
serviceProvider.InitializeCulture();
|
||||
serviceProvider.InitializedDynamicHttpProxy();
|
||||
|
||||
return serviceProvider;
|
||||
}
|
||||
@@ -48,10 +51,10 @@ internal static class DependencyInjection
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static void InitializeCulture(this IServiceProvider serviceProvider)
|
||||
{
|
||||
AppOptions appOptions = serviceProvider.GetRequiredService<AppOptions>();
|
||||
appOptions.PreviousCulture = CultureInfo.CurrentCulture;
|
||||
CultureOptions cultureOptions = serviceProvider.GetRequiredService<CultureOptions>();
|
||||
cultureOptions.SystemCulture = CultureInfo.CurrentCulture;
|
||||
|
||||
CultureInfo cultureInfo = appOptions.CurrentCulture;
|
||||
CultureInfo cultureInfo = cultureOptions.CurrentCulture;
|
||||
|
||||
CultureInfo.DefaultThreadCurrentCulture = cultureInfo;
|
||||
CultureInfo.DefaultThreadCurrentUICulture = cultureInfo;
|
||||
@@ -67,4 +70,9 @@ internal static class DependencyInjection
|
||||
{
|
||||
_ = serviceProvider.GetRequiredService<ConsoleWindowLifeTime>();
|
||||
}
|
||||
|
||||
private static void InitializedDynamicHttpProxy(this IServiceProvider serviceProvider)
|
||||
{
|
||||
HttpClient.DefaultProxy = serviceProvider.GetRequiredService<DynamicHttpProxy>();
|
||||
}
|
||||
}
|
||||
@@ -38,8 +38,8 @@ internal static class IocConfiguration
|
||||
|
||||
private static void AddDbContextCore(IServiceProvider provider, DbContextOptionsBuilder builder)
|
||||
{
|
||||
RuntimeOptions hutaoOptions = provider.GetRequiredService<RuntimeOptions>();
|
||||
string dbFile = System.IO.Path.Combine(hutaoOptions.DataFolder, "Userdata.db");
|
||||
RuntimeOptions runtimeOptions = provider.GetRequiredService<RuntimeOptions>();
|
||||
string dbFile = System.IO.Path.Combine(runtimeOptions.DataFolder, "Userdata.db");
|
||||
string sqlConnectionString = $"Data Source={dbFile}";
|
||||
|
||||
// Temporarily create a context
|
||||
|
||||
@@ -29,10 +29,10 @@ internal static partial class IocHttpClientConfiguration
|
||||
/// <param name="client">配置后的客户端</param>
|
||||
private static void DefaultConfiguration(IServiceProvider serviceProvider, HttpClient client)
|
||||
{
|
||||
RuntimeOptions hutaoOptions = serviceProvider.GetRequiredService<RuntimeOptions>();
|
||||
RuntimeOptions runtimeOptions = serviceProvider.GetRequiredService<RuntimeOptions>();
|
||||
|
||||
client.Timeout = Timeout.InfiniteTimeSpan;
|
||||
client.DefaultRequestHeaders.UserAgent.ParseAdd(hutaoOptions.UserAgent);
|
||||
client.DefaultRequestHeaders.UserAgent.ParseAdd(runtimeOptions.UserAgent);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -0,0 +1,87 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Snap.Hutao.Win32.Registry;
|
||||
using System.Net;
|
||||
using System.Reflection;
|
||||
|
||||
namespace Snap.Hutao.Core.IO.Http.DynamicProxy;
|
||||
|
||||
[Injection(InjectAs.Singleton)]
|
||||
internal sealed partial class DynamicHttpProxy : IWebProxy, IDisposable
|
||||
{
|
||||
private const string ProxySettingPath = @"HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Internet Settings\Connections";
|
||||
|
||||
private static readonly MethodInfo ConstructSystemProxyMethod;
|
||||
|
||||
private readonly RegistryWatcher watcher;
|
||||
|
||||
private IWebProxy innerProxy = default!;
|
||||
|
||||
static DynamicHttpProxy()
|
||||
{
|
||||
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);
|
||||
ConstructSystemProxyMethod = constructSystemProxyMethod;
|
||||
}
|
||||
|
||||
public DynamicHttpProxy()
|
||||
{
|
||||
UpdateProxy();
|
||||
|
||||
watcher = new(ProxySettingPath, UpdateProxy);
|
||||
watcher.Start();
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public ICredentials? Credentials
|
||||
{
|
||||
get => InnerProxy.Credentials;
|
||||
set => InnerProxy.Credentials = value;
|
||||
}
|
||||
|
||||
private IWebProxy InnerProxy
|
||||
{
|
||||
get => innerProxy;
|
||||
|
||||
[MemberNotNull(nameof(innerProxy))]
|
||||
set
|
||||
{
|
||||
if (ReferenceEquals(innerProxy, value))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
(innerProxy as IDisposable)?.Dispose();
|
||||
innerProxy = value;
|
||||
}
|
||||
}
|
||||
|
||||
[MemberNotNull(nameof(innerProxy))]
|
||||
public void UpdateProxy()
|
||||
{
|
||||
IWebProxy? proxy = ConstructSystemProxyMethod.Invoke(default, default) as IWebProxy;
|
||||
ArgumentNullException.ThrowIfNull(proxy);
|
||||
|
||||
InnerProxy = proxy;
|
||||
}
|
||||
|
||||
public Uri? GetProxy(Uri destination)
|
||||
{
|
||||
return InnerProxy.GetProxy(destination);
|
||||
}
|
||||
|
||||
public bool IsBypassed(Uri host)
|
||||
{
|
||||
return InnerProxy.IsBypassed(host);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
(innerProxy as IDisposable)?.Dispose();
|
||||
watcher.Dispose();
|
||||
}
|
||||
}
|
||||
@@ -11,11 +11,14 @@ namespace Snap.Hutao.Core.IO.Ini;
|
||||
[HighQuality]
|
||||
internal static class IniSerializer
|
||||
{
|
||||
/// <summary>
|
||||
/// 反序列化
|
||||
/// </summary>
|
||||
/// <param name="fileStream">文件流</param>
|
||||
/// <returns>Ini 元素集合</returns>
|
||||
public static List<IniElement> DeserializeFromFile(string filePath)
|
||||
{
|
||||
using (FileStream readStream = File.OpenRead(filePath))
|
||||
{
|
||||
return Deserialize(readStream);
|
||||
}
|
||||
}
|
||||
|
||||
public static List<IniElement> Deserialize(FileStream fileStream)
|
||||
{
|
||||
List<IniElement> results = [];
|
||||
@@ -50,11 +53,14 @@ internal static class IniSerializer
|
||||
return results;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 序列化
|
||||
/// </summary>
|
||||
/// <param name="fileStream">写入的流</param>
|
||||
/// <param name="elements">元素</param>
|
||||
public static void SerializeToFile(string filePath, IEnumerable<IniElement> elements)
|
||||
{
|
||||
using (FileStream writeStream = File.Create(filePath))
|
||||
{
|
||||
Serialize(writeStream, elements);
|
||||
}
|
||||
}
|
||||
|
||||
public static void Serialize(FileStream fileStream, IEnumerable<IniElement> elements)
|
||||
{
|
||||
using (StreamWriter writer = new(fileStream))
|
||||
|
||||
@@ -190,7 +190,7 @@ internal sealed partial class Activation : IActivation
|
||||
|
||||
serviceProvider
|
||||
.GetRequiredService<IDiscordService>()
|
||||
.SetNormalActivity()
|
||||
.SetNormalActivityAsync()
|
||||
.SafeForget();
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -57,4 +57,9 @@ internal static class SettingKeys
|
||||
public const string OverrideElevationRequirement = "OverrideElevationRequirement";
|
||||
public const string OverrideUpdateVersionComparison = "OverrideUpdateVersionComparison";
|
||||
#endregion
|
||||
|
||||
#region Obsolete
|
||||
[Obsolete("重置调试控制台开关")]
|
||||
public const string IsAllocConsoleDebugModeEnabledLegacy1 = "IsAllocConsoleDebugModeEnabled";
|
||||
#endregion
|
||||
}
|
||||
@@ -39,6 +39,11 @@ internal sealed class ScheduleTaskInterop : IScheduleTaskInterop
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
if (WScriptExists(DailyNoteRefreshScriptName, out string fullPath))
|
||||
{
|
||||
File.Delete(fullPath);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -69,4 +69,9 @@ internal static class StructMarshal
|
||||
{
|
||||
return new(point.X, point.Y, size.Width, size.Height);
|
||||
}
|
||||
|
||||
public static SizeInt32 SizeInt32(RectInt32 rect)
|
||||
{
|
||||
return new(rect.Width, rect.Height);
|
||||
}
|
||||
}
|
||||
@@ -8,12 +8,12 @@ internal readonly struct Delay
|
||||
/// <summary>
|
||||
/// 随机延迟
|
||||
/// </summary>
|
||||
/// <param name="minMilliSeconds">最小,闭</param>
|
||||
/// <param name="maxMilliSeconds">最小,开</param>
|
||||
/// <param name="min">最小,闭</param>
|
||||
/// <param name="max">最小,开</param>
|
||||
/// <returns>任务</returns>
|
||||
public static ValueTask Random(int minMilliSeconds, int maxMilliSeconds)
|
||||
public static ValueTask RandomMilliSeconds(int min, int max)
|
||||
{
|
||||
return Task.Delay((int)(System.Random.Shared.NextDouble() * (maxMilliSeconds - minMilliSeconds)) + minMilliSeconds).AsValueTask();
|
||||
return Task.Delay((int)(System.Random.Shared.NextDouble() * (max - min)) + min).AsValueTask();
|
||||
}
|
||||
|
||||
public static ValueTask FromSeconds(int seconds)
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Microsoft.UI;
|
||||
using Microsoft.UI.Composition;
|
||||
using Microsoft.UI.Xaml;
|
||||
using Microsoft.UI.Xaml.Media;
|
||||
using Windows.UI;
|
||||
|
||||
namespace Snap.Hutao.Core.Windowing.Backdrop;
|
||||
|
||||
internal sealed class TransparentBackdrop : SystemBackdrop, IDisposable, IBackdropNeedEraseBackground
|
||||
{
|
||||
private readonly object compositorLock = new();
|
||||
|
||||
private Color tintColor;
|
||||
private Windows.UI.Composition.CompositionColorBrush? brush;
|
||||
private Windows.UI.Composition.Compositor? compositor;
|
||||
|
||||
public TransparentBackdrop()
|
||||
: this(Colors.Transparent)
|
||||
{
|
||||
}
|
||||
|
||||
public TransparentBackdrop(Color tintColor)
|
||||
{
|
||||
this.tintColor = tintColor;
|
||||
}
|
||||
|
||||
internal Windows.UI.Composition.Compositor Compositor
|
||||
{
|
||||
get
|
||||
{
|
||||
if (compositor is null)
|
||||
{
|
||||
lock (compositorLock)
|
||||
{
|
||||
if (compositor is null)
|
||||
{
|
||||
DispatcherQueue.EnsureSystemDispatcherQueue();
|
||||
compositor = new Windows.UI.Composition.Compositor();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return compositor;
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
compositor?.Dispose();
|
||||
}
|
||||
|
||||
protected override void OnTargetConnected(ICompositionSupportsSystemBackdrop connectedTarget, XamlRoot xamlRoot)
|
||||
{
|
||||
brush ??= Compositor.CreateColorBrush(tintColor);
|
||||
connectedTarget.SystemBackdrop = brush;
|
||||
}
|
||||
|
||||
protected override void OnTargetDisconnected(ICompositionSupportsSystemBackdrop disconnectedTarget)
|
||||
{
|
||||
disconnectedTarget.SystemBackdrop = null;
|
||||
}
|
||||
}
|
||||
|
||||
internal interface IBackdropNeedEraseBackground;
|
||||
@@ -9,6 +9,8 @@ namespace Snap.Hutao.Core.Windowing;
|
||||
[HighQuality]
|
||||
internal enum BackdropType
|
||||
{
|
||||
Transparent = -1,
|
||||
|
||||
/// <summary>
|
||||
/// 无
|
||||
/// </summary>
|
||||
|
||||
@@ -53,10 +53,10 @@ internal sealed class WindowController
|
||||
|
||||
private void InitializeCore()
|
||||
{
|
||||
RuntimeOptions hutaoOptions = serviceProvider.GetRequiredService<RuntimeOptions>();
|
||||
RuntimeOptions runtimeOptions = serviceProvider.GetRequiredService<RuntimeOptions>();
|
||||
|
||||
window.AppWindow.Title = SH.FormatAppNameAndVersion(hutaoOptions.Version);
|
||||
window.AppWindow.SetIcon(Path.Combine(hutaoOptions.InstalledLocation, "Assets/Logo.ico"));
|
||||
window.AppWindow.Title = SH.FormatAppNameAndVersion(runtimeOptions.Version);
|
||||
window.AppWindow.SetIcon(Path.Combine(runtimeOptions.InstalledLocation, "Assets/Logo.ico"));
|
||||
ExtendsContentIntoTitleBar();
|
||||
|
||||
RecoverOrInitWindowSize();
|
||||
@@ -157,6 +157,7 @@ internal sealed class WindowController
|
||||
{
|
||||
window.SystemBackdrop = backdropType switch
|
||||
{
|
||||
BackdropType.Transparent => new Backdrop.TransparentBackdrop(),
|
||||
BackdropType.MicaAlt => new MicaBackdrop() { Kind = MicaKind.BaseAlt },
|
||||
BackdropType.Mica => new MicaBackdrop() { Kind = MicaKind.Base },
|
||||
BackdropType.Acrylic => new DesktopAcrylicBackdrop(),
|
||||
|
||||
@@ -3,6 +3,10 @@
|
||||
|
||||
using Microsoft.UI.Xaml;
|
||||
using System.Runtime.CompilerServices;
|
||||
using Windows.Win32.Foundation;
|
||||
using Windows.Win32.UI.WindowsAndMessaging;
|
||||
using WinRT.Interop;
|
||||
using static Windows.Win32.PInvoke;
|
||||
|
||||
namespace Snap.Hutao.Core.Windowing;
|
||||
|
||||
@@ -16,4 +20,12 @@ internal static class WindowExtension
|
||||
WindowController windowController = new(window, window.WindowOptions, serviceProvider);
|
||||
WindowControllers.Add(window, windowController);
|
||||
}
|
||||
|
||||
public static void SetLayeredWindow(this Window window)
|
||||
{
|
||||
HWND hwnd = (HWND)WindowNative.GetWindowHandle(window);
|
||||
nint style = GetWindowLongPtr(hwnd, WINDOW_LONG_PTR_INDEX.GWL_EXSTYLE);
|
||||
style |= (nint)WINDOW_EX_STYLE.WS_EX_LAYERED;
|
||||
SetWindowLongPtr(hwnd, WINDOW_LONG_PTR_INDEX.GWL_EXSTYLE, style);
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,7 @@
|
||||
// Licensed under the MIT license.
|
||||
|
||||
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;
|
||||
@@ -110,6 +111,16 @@ internal sealed class WindowSubclass : IDisposable
|
||||
hotKeyController.OnHotKeyPressed(*(HotKeyParameter*)&lParam);
|
||||
break;
|
||||
}
|
||||
|
||||
case WM_ERASEBKGND:
|
||||
{
|
||||
if (window.SystemBackdrop is IBackdropNeedEraseBackground)
|
||||
{
|
||||
return (LRESULT)(int)(BOOL)true;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return DefSubclassProc(hwnd, uMsg, wParam, lParam);
|
||||
|
||||
19
src/Snap.Hutao/Snap.Hutao/IdentifyMonitorWindow.xaml
Normal file
19
src/Snap.Hutao/Snap.Hutao/IdentifyMonitorWindow.xaml
Normal file
@@ -0,0 +1,19 @@
|
||||
<Window
|
||||
x:Class="Snap.Hutao.IdentifyMonitorWindow"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:shcm="using:Snap.Hutao.Control.Markup"
|
||||
mc:Ignorable="d">
|
||||
<StackPanel
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
Spacing="3">
|
||||
<TextBlock Text="{shcm:ResourceString Name=WindowIdentifyMonitorHeader}"/>
|
||||
<TextBlock
|
||||
Style="{StaticResource SubtitleTextBlockStyle}"
|
||||
Text="{x:Bind Monitor}"
|
||||
TextAlignment="Center"/>
|
||||
</StackPanel>
|
||||
</Window>
|
||||
30
src/Snap.Hutao/Snap.Hutao/IdentifyMonitorWindow.xaml.cs
Normal file
30
src/Snap.Hutao/Snap.Hutao/IdentifyMonitorWindow.xaml.cs
Normal file
@@ -0,0 +1,30 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Microsoft.UI.Windowing;
|
||||
using Microsoft.UI.Xaml;
|
||||
using Snap.Hutao.Core;
|
||||
using Windows.Graphics;
|
||||
|
||||
namespace Snap.Hutao;
|
||||
|
||||
internal sealed partial class IdentifyMonitorWindow : Window
|
||||
{
|
||||
public IdentifyMonitorWindow(DisplayArea displayArea, int index)
|
||||
{
|
||||
InitializeComponent();
|
||||
Monitor = $"{displayArea.DisplayId.Value:X8}:{index}";
|
||||
|
||||
OverlappedPresenter presenter = OverlappedPresenter.Create();
|
||||
presenter.SetBorderAndTitleBar(false, false);
|
||||
presenter.IsAlwaysOnTop = true;
|
||||
presenter.IsResizable = false;
|
||||
AppWindow.SetPresenter(presenter);
|
||||
|
||||
PointInt32 point = new(40, 32);
|
||||
SizeInt32 size = StructMarshal.SizeInt32(displayArea.WorkArea).Scale(0.1);
|
||||
AppWindow.MoveAndResize(StructMarshal.RectInt32(point, size), displayArea);
|
||||
}
|
||||
|
||||
public string Monitor { get; private set; }
|
||||
}
|
||||
@@ -11,6 +11,12 @@ internal static class CollectionsNameValue
|
||||
return [.. Enum.GetValues<TEnum>().Select(x => new NameValue<TEnum>(x.ToString(), x))];
|
||||
}
|
||||
|
||||
public static List<NameValue<TEnum>> FromEnum<TEnum>(Func<TEnum, bool> codiction)
|
||||
where TEnum : struct, Enum
|
||||
{
|
||||
return [.. Enum.GetValues<TEnum>().Where(codiction).Select(x => new NameValue<TEnum>(x.ToString(), x))];
|
||||
}
|
||||
|
||||
public static List<NameValue<TSource>> From<TSource>(IEnumerable<TSource> sources, Func<TSource, string> nameSelector)
|
||||
{
|
||||
return [.. sources.Select(x => new NameValue<TSource>(nameSelector(x), x))];
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
using Snap.Hutao.Core;
|
||||
using Snap.Hutao.Core.Abstraction;
|
||||
using Snap.Hutao.Service.Metadata;
|
||||
using Snap.Hutao.Service;
|
||||
using Snap.Hutao.Web.Hoyolab;
|
||||
|
||||
namespace Snap.Hutao.Model.InterChange.GachaLog;
|
||||
@@ -12,7 +12,7 @@ namespace Snap.Hutao.Model.InterChange.GachaLog;
|
||||
/// UIGF格式的信息
|
||||
/// </summary>
|
||||
[HighQuality]
|
||||
internal sealed class UIGFInfo : IMappingFrom<UIGFInfo, RuntimeOptions, MetadataOptions, string>
|
||||
internal sealed class UIGFInfo : IMappingFrom<UIGFInfo, RuntimeOptions, CultureOptions, string>
|
||||
{
|
||||
/// <summary>
|
||||
/// 用户Uid
|
||||
@@ -65,12 +65,12 @@ internal sealed class UIGFInfo : IMappingFrom<UIGFInfo, RuntimeOptions, Metadata
|
||||
[JsonPropertyName("region_time_zone")]
|
||||
public int? RegionTimeZone { get; set; } = default!;
|
||||
|
||||
public static UIGFInfo From(RuntimeOptions runtimeOptions, MetadataOptions metadataOptions, string uid)
|
||||
public static UIGFInfo From(RuntimeOptions runtimeOptions, CultureOptions cultureOptions, string uid)
|
||||
{
|
||||
return new()
|
||||
{
|
||||
Uid = uid,
|
||||
Language = metadataOptions.LanguageCode,
|
||||
Language = cultureOptions.LanguageCode,
|
||||
ExportTimestamp = DateTimeOffset.UtcNow.ToUnixTimeSeconds(),
|
||||
ExportApp = SH.AppName,
|
||||
ExportAppVersion = runtimeOptions.Version.ToString(),
|
||||
|
||||
@@ -64,14 +64,14 @@ internal sealed class UIIFInfo
|
||||
/// <returns>专用 UIGF 信息</returns>
|
||||
public static UIIFInfo From(IServiceProvider serviceProvider, string uid)
|
||||
{
|
||||
RuntimeOptions hutaoOptions = serviceProvider.GetRequiredService<RuntimeOptions>();
|
||||
RuntimeOptions runtimeOptions = serviceProvider.GetRequiredService<RuntimeOptions>();
|
||||
|
||||
return new()
|
||||
{
|
||||
Uid = uid,
|
||||
ExportTimestamp = DateTimeOffset.UtcNow.ToUnixTimeSeconds(),
|
||||
ExportApp = SH.AppName,
|
||||
ExportAppVersion = hutaoOptions.Version.ToString(),
|
||||
ExportAppVersion = runtimeOptions.Version.ToString(),
|
||||
UIIFVersion = UIIF.CurrentVersion,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -247,7 +247,7 @@
|
||||
<value>Not refreshed</value>
|
||||
</data>
|
||||
<data name="ModelEntityDailyNoteRefreshTimeFormat" xml:space="preserve">
|
||||
<value>Refresh at {0:MM.dd HH:mm:ss}</value>
|
||||
<value>Refreshed at {0:MM.dd HH:mm:ss}</value>
|
||||
</data>
|
||||
<data name="ModelEntitySpiralAbyssScheduleFormat" xml:space="preserve">
|
||||
<value>Schedule {0}</value>
|
||||
@@ -738,10 +738,10 @@
|
||||
<value>Avatar Calculator: {0:MM-dd HH:mm}</value>
|
||||
</data>
|
||||
<data name="ServiceAvatarInfoSummaryGameRecordNotRefreshed" xml:space="preserve">
|
||||
<value>My Characters: N/A</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>
|
||||
@@ -870,11 +870,23 @@
|
||||
<value>No writing permission in file system, unable to start the server conversion.</value>
|
||||
</data>
|
||||
<data name="ServiceGameEnsureGameResourceQueryResourceInformation" xml:space="preserve">
|
||||
<value>Querying Game Resource Information</value>
|
||||
<value>Download game resource index</value>
|
||||
</data>
|
||||
<data name="ServiceGameFileOperationExceptionMessage" xml:space="preserve">
|
||||
<value>Failed to operate on game file: {0}</value>
|
||||
</data>
|
||||
<data name="ServiceGameLaunchExecutionGameFpsUnlockFailed" xml:space="preserve">
|
||||
<value>Failed to unlock frame rate limit</value>
|
||||
</data>
|
||||
<data name="ServiceGameLaunchExecutionGameIsRunning" xml:space="preserve">
|
||||
<value>Game in process</value>
|
||||
</data>
|
||||
<data name="ServiceGameLaunchExecutionGamePathNotValid" xml:space="preserve">
|
||||
<value>Select Game Path</value>
|
||||
</data>
|
||||
<data name="ServiceGameLaunchExecutionGameResourceQueryIndexFailed" xml:space="preserve">
|
||||
<value>Failed to download game resource index: {0}</value>
|
||||
</data>
|
||||
<data name="ServiceGameLaunchPhaseProcessExited" xml:space="preserve">
|
||||
<value>Game process closed</value>
|
||||
</data>
|
||||
@@ -1271,6 +1283,12 @@
|
||||
<data name="ViewDialogQRCodeTitle" xml:space="preserve">
|
||||
<value>Scan the QR code with MiHoYo BBS App</value>
|
||||
</data>
|
||||
<data name="ViewDialogReconfirmTextHeader" xml:space="preserve">
|
||||
<value>To avoid enable in a mistake, please input <b>title name</b> of feature you are enabling</value>
|
||||
</data>
|
||||
<data name="ViewDialogReconfirmTitle" xml:space="preserve">
|
||||
<value>You are Enabling a Dangerous Feature</value>
|
||||
</data>
|
||||
<data name="ViewDialogSettingDeleteUserDataContent" xml:space="preserve">
|
||||
<value>This action is irreversible, and all user login status will be lost</value>
|
||||
</data>
|
||||
@@ -1292,6 +1310,9 @@
|
||||
<data name="ViewDialogUserTitle" xml:space="preserve">
|
||||
<value>Set Cookie</value>
|
||||
</data>
|
||||
<data name="ViewFeedbackHeader" xml:space="preserve">
|
||||
<value>Feedback Center</value>
|
||||
</data>
|
||||
<data name="ViewGachaLogHeader" xml:space="preserve">
|
||||
<value>Wish History</value>
|
||||
</data>
|
||||
@@ -1368,7 +1389,7 @@
|
||||
<value>This operation is irreversible. The achievement archive will be lost.</value>
|
||||
</data>
|
||||
<data name="ViewModelAchievementRemoveArchiveTitle" xml:space="preserve">
|
||||
<value>Are you sure you want to delete archive {0}?</value>
|
||||
<value>Are you sure to delete archive {0}?</value>
|
||||
</data>
|
||||
<data name="ViewModelAchievementUIAFExportPickerTitle" xml:space="preserve">
|
||||
<value>Export UIAF Json file to the selected path</value>
|
||||
@@ -1541,6 +1562,9 @@
|
||||
<data name="ViewModelLaunchGameEnsureGameResourceFail" xml:space="preserve">
|
||||
<value>Convert server failed</value>
|
||||
</data>
|
||||
<data name="ViewModelLaunchGameIdentifyMonitorsAction" xml:space="preserve">
|
||||
<value>Identify Monitors</value>
|
||||
</data>
|
||||
<data name="ViewModelLaunchGameMultiChannelReadFail" xml:space="preserve">
|
||||
<value>Unable to read game config file: {0}, file may be not exist not lack of user permission</value>
|
||||
</data>
|
||||
@@ -1574,6 +1598,12 @@
|
||||
<data name="ViewModelSettingCreateDesktopShortcutFailed" xml:space="preserve">
|
||||
<value>Failed to create desktop shortcut</value>
|
||||
</data>
|
||||
<data name="ViewModelSettingDeleteServerCacheFolderContent" xml:space="preserve">
|
||||
<value>You will need to re-download needed files, are you sure to delete?</value>
|
||||
</data>
|
||||
<data name="ViewModelSettingDeleteServerCacheFolderTitle" xml:space="preserve">
|
||||
<value>Delete Sever Conversion Client Cache</value>
|
||||
</data>
|
||||
<data name="ViewModelSettingFolderSizeDescription" xml:space="preserve">
|
||||
<value>Used disk space: {0}</value>
|
||||
</data>
|
||||
@@ -1626,7 +1656,7 @@
|
||||
<value>Create New Archive</value>
|
||||
</data>
|
||||
<data name="ViewPageAchievementAddArchiveHint" xml:space="preserve">
|
||||
<value>Create new archive to continue</value>
|
||||
<value>Create New Archive to Continue</value>
|
||||
</data>
|
||||
<data name="ViewPageAchievementExportLabel" xml:space="preserve">
|
||||
<value>Export</value>
|
||||
@@ -1647,7 +1677,7 @@
|
||||
<value>Name, description, version or ID</value>
|
||||
</data>
|
||||
<data name="ViewPageAchievementSortIncompletedItemsFirst" xml:space="preserve">
|
||||
<value>Prefer incomplete</value>
|
||||
<value>Prefer Incomplete</value>
|
||||
</data>
|
||||
<data name="ViewPageAnnouncementActivity" xml:space="preserve">
|
||||
<value>Event Notice</value>
|
||||
@@ -1671,7 +1701,7 @@
|
||||
<value>CRIT Rating</value>
|
||||
</data>
|
||||
<data name="ViewPageAvatarPropertyDefaultDescription" xml:space="preserve">
|
||||
<value>No character data fetched</value>
|
||||
<value>No Character Data Fetched</value>
|
||||
</data>
|
||||
<data name="ViewPageAvatarPropertyExportAsImage" xml:space="preserve">
|
||||
<value>Export as Image</value>
|
||||
@@ -1698,7 +1728,7 @@
|
||||
<value>Sync character talents data</value>
|
||||
</data>
|
||||
<data name="ViewPageAvatarPropertyRefreshFromHoyolabGameRecord" xml:space="preserve">
|
||||
<value>Sync from MiHoYo BBS My Characters</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>
|
||||
@@ -1722,7 +1752,7 @@
|
||||
<value>Create</value>
|
||||
</data>
|
||||
<data name="ViewPageCultivationAddProjectContinue" xml:space="preserve">
|
||||
<value>Create plan to continue</value>
|
||||
<value>Create Plan to Continue</value>
|
||||
</data>
|
||||
<data name="ViewPageCultivationAddProjectDescription" xml:space="preserve">
|
||||
<value>You can add development plan items later from other pages</value>
|
||||
@@ -1823,6 +1853,36 @@
|
||||
<data name="ViewPageDailyNoteVerify" xml:space="preserve">
|
||||
<value>Verify Current User and Role</value>
|
||||
</data>
|
||||
<data name="ViewPageFeedbackAutoSuggestBoxPlaceholder" xml:space="preserve">
|
||||
<value>Search for questions and suggestions</value>
|
||||
</data>
|
||||
<data name="ViewPageFeedBackBasicInformation" xml:space="preserve">
|
||||
<value>Basic Information</value>
|
||||
</data>
|
||||
<data name="ViewPageFeedbackCommonLinksHeader" xml:space="preserve">
|
||||
<value>Useful Links</value>
|
||||
</data>
|
||||
<data name="ViewPageFeedbackEngageWithUsDescription" xml:space="preserve">
|
||||
<value>Keep in touch with us</value>
|
||||
</data>
|
||||
<data name="ViewPageFeedbackFeatureGuideHeader" xml:space="preserve">
|
||||
<value>Feature Documents</value>
|
||||
</data>
|
||||
<data name="ViewPageFeedbackGithubIssuesDescription" xml:space="preserve">
|
||||
<value>We always prioritize issues reported on GitHub</value>
|
||||
</data>
|
||||
<data name="ViewPageFeedbackRoadmapDescription" xml:space="preserve">
|
||||
<value>Development Roadmap</value>
|
||||
</data>
|
||||
<data name="ViewPageFeedbackSearchResultPlaceholderTitle" xml:space="preserve">
|
||||
<value>No Result</value>
|
||||
</data>
|
||||
<data name="ViewPageFeedbackServerStatusDescription" xml:space="preserve">
|
||||
<value>Snap Hutao Service Availability Monitor</value>
|
||||
</data>
|
||||
<data name="ViewPageFeedbackServerStatusHeader" xml:space="preserve">
|
||||
<value>Snap Hutao Services</value>
|
||||
</data>
|
||||
<data name="ViewPageGachaLogAggressiveRefresh" xml:space="preserve">
|
||||
<value>Full Refresh</value>
|
||||
</data>
|
||||
@@ -1875,7 +1935,7 @@
|
||||
<value>Input</value>
|
||||
</data>
|
||||
<data name="ViewPageGachaLogRecoverFromHutaoCloudDescription" xml:space="preserve">
|
||||
<value>Recover Wish Record from Snap Hutao Cloud</value>
|
||||
<value>Recover Wish Records from Snap Hutao Cloud</value>
|
||||
</data>
|
||||
<data name="ViewPageGachaLogRefresh" xml:space="preserve">
|
||||
<value>Refresh</value>
|
||||
@@ -2055,10 +2115,10 @@
|
||||
<value>Borderless</value>
|
||||
</data>
|
||||
<data name="ViewPageLaunchGameAppearanceCloudThirdPartyMobileDescription" xml:space="preserve">
|
||||
<value>Enable touchscreen layout, but the keyboard & mouse are no longer usable.</value>
|
||||
<value>Enable touchscreen layout, but the keyboard & mouse are no longer usable</value>
|
||||
</data>
|
||||
<data name="ViewPageLaunchGameAppearanceExclusiveDescription" xml:space="preserve">
|
||||
<value>Incompatible with embedded browsers; operations like switching window may cause the game to crash.</value>
|
||||
<value>Incompatible with embedded browsers; operations like switching window may cause the game to crash</value>
|
||||
</data>
|
||||
<data name="ViewPageLaunchGameAppearanceExclusiveHeader" xml:space="preserve">
|
||||
<value>Exclusive Fullscreen</value>
|
||||
@@ -2088,7 +2148,7 @@
|
||||
<value>Modify its default behavior at game startup</value>
|
||||
</data>
|
||||
<data name="ViewPageLaunchGameArgumentsHeader" xml:space="preserve">
|
||||
<value>Start-up Arguments</value>
|
||||
<value>Launch Parameters</value>
|
||||
</data>
|
||||
<data name="ViewPageLaunchGameCommonHeader" xml:space="preserve">
|
||||
<value>General</value>
|
||||
@@ -2142,7 +2202,7 @@
|
||||
<value>Resource Download</value>
|
||||
</data>
|
||||
<data name="ViewPageLaunchGameResourceLatestHeader" xml:space="preserve">
|
||||
<value>Client</value>
|
||||
<value>Full Package</value>
|
||||
</data>
|
||||
<data name="ViewPageLaunchGameResourcePreDownloadHeader" xml:space="preserve">
|
||||
<value>Pre-download</value>
|
||||
@@ -2192,6 +2252,12 @@
|
||||
<data name="ViewPageLaunchGameUnlockFpsOn" xml:space="preserve">
|
||||
<value>Enabled</value>
|
||||
</data>
|
||||
<data name="ViewPageLaunchGameWindowsHDRDescription" xml:space="preserve">
|
||||
<value>Take advantage of displays that support HDR for brighter, more vivid, and more detailed pictures</value>
|
||||
</data>
|
||||
<data name="ViewPageLaunchGameWindowsHDRHeader" xml:space="preserve">
|
||||
<value>Windows HDR</value>
|
||||
</data>
|
||||
<data name="ViewPageLoginHoyoverseUserHint" xml:space="preserve">
|
||||
<value>Enter your HoYoLab UID</value>
|
||||
</data>
|
||||
@@ -2205,7 +2271,7 @@
|
||||
<value>Open Screenshots Folder</value>
|
||||
</data>
|
||||
<data name="ViewPageResetGamePathAction" xml:space="preserve">
|
||||
<value>Select game path</value>
|
||||
<value>Select Game Path</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingAboutHeader" xml:space="preserve">
|
||||
<value>About Snap Hutao</value>
|
||||
@@ -2409,7 +2475,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>
|
||||
@@ -2564,9 +2630,18 @@
|
||||
<data name="ViewSettingAllocConsoleHeader" xml:space="preserve">
|
||||
<value>Debug Console</value>
|
||||
</data>
|
||||
<data name="ViewSettingDeleteServerCacheFolderDescription" xml:space="preserve">
|
||||
<value>Cache file are downloaded for server conversion in Game Launcher</value>
|
||||
</data>
|
||||
<data name="ViewSettingDeleteServerCacheFolderHeader" xml:space="preserve">
|
||||
<value>Delete Server Conversion Cache</value>
|
||||
</data>
|
||||
<data name="ViewSettingFolderViewOpenFolderAction" xml:space="preserve">
|
||||
<value>Open Folder</value>
|
||||
</data>
|
||||
<data name="ViewSettingHeader" xml:space="preserve">
|
||||
<value>Settings</value>
|
||||
</data>
|
||||
<data name="ViewSpiralAbyssAvatarAppearanceRankDescription" xml:space="preserve">
|
||||
<value>Character Appearance Rate = Character Appearance in this Floor (only count for 1 if repeated) / Total Number of Abyss Record of this Floor</value>
|
||||
</data>
|
||||
@@ -2661,7 +2736,7 @@
|
||||
<value>Web Login</value>
|
||||
</data>
|
||||
<data name="ViewUserCookieOperationLoginQRCodeAction" xml:space="preserve">
|
||||
<value>Login via scanning QR code</value>
|
||||
<value>Login via QR code</value>
|
||||
</data>
|
||||
<data name="ViewUserCookieOperationManualInputAction" xml:space="preserve">
|
||||
<value>Input Manually</value>
|
||||
@@ -2747,6 +2822,9 @@
|
||||
<data name="WebAnnouncementTimeHoursEndFormat" xml:space="preserve">
|
||||
<value>End in {0} hours</value>
|
||||
</data>
|
||||
<data name="WebBridgeShareCopyToClipboardFailed" xml:space="preserve">
|
||||
<value>Failed to open clipboard</value>
|
||||
</data>
|
||||
<data name="WebBridgeShareCopyToClipboardSuccess" xml:space="preserve">
|
||||
<value>Copied to clipboard</value>
|
||||
</data>
|
||||
@@ -2811,10 +2889,10 @@
|
||||
<value>{0} day</value>
|
||||
</data>
|
||||
<data name="WebDailyNoteResinRecoveryCompleted" xml:space="preserve">
|
||||
<value>Original Resin is full</value>
|
||||
<value>Original Resin is Full</value>
|
||||
</data>
|
||||
<data name="WebDailyNoteResinRecoveryFormat" xml:space="preserve">
|
||||
<value>Will be replenished in {0} {1:HH:mm}</value>
|
||||
<value>Will be Replenished in {0} {1:HH:mm}</value>
|
||||
</data>
|
||||
<data name="WebDailyNoteTransformerAppend" xml:space="preserve">
|
||||
<value>Ready for use again after</value>
|
||||
@@ -2847,7 +2925,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>
|
||||
@@ -2916,7 +2994,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>
|
||||
@@ -2927,4 +3005,7 @@
|
||||
<data name="WebResponseRequestExceptionFormat" xml:space="preserve">
|
||||
<value>[{1}] network request exception in [{0}] please try again later</value>
|
||||
</data>
|
||||
<data name="WindowIdentifyMonitorHeader" xml:space="preserve">
|
||||
<value>Monitor ID</value>
|
||||
</data>
|
||||
</root>
|
||||
@@ -121,7 +121,7 @@
|
||||
<value>Snap Hutao Dev {0}</value>
|
||||
</data>
|
||||
<data name="AppElevatedDevNameAndVersion" xml:space="preserve">
|
||||
<value>Snap Hutao Dev {0} [Administrator]</value>
|
||||
<value>胡桃Dev {0} [管理员]</value>
|
||||
</data>
|
||||
<data name="AppElevatedNameAndVersion" xml:space="preserve">
|
||||
<value>Snap Hutao {0} [Administrator]</value>
|
||||
@@ -738,10 +738,10 @@
|
||||
<value>Kalkulator Avatar: {0:MM-dd HH:mm}</value>
|
||||
</data>
|
||||
<data name="ServiceAvatarInfoSummaryGameRecordNotRefreshed" xml:space="preserve">
|
||||
<value>Karakter Saya: N/A</value>
|
||||
<value>原神战绩:尚未刷新</value>
|
||||
</data>
|
||||
<data name="ServiceAvatarInfoSummaryGameRecordRefreshTimeFormat" xml:space="preserve">
|
||||
<value>Karakter Saya: {0:MM-dd HH:mm}</value>
|
||||
<value>原神战绩:{0:MM-dd HH:mm}</value>
|
||||
</data>
|
||||
<data name="ServiceAvatarInfoSummaryShowcaseNotRefreshed" xml:space="preserve">
|
||||
<value>Enka: N/A</value>
|
||||
@@ -870,11 +870,23 @@
|
||||
<value>Tidak ada izin menulis dalam sistem berkas, tidak dapat memulai konversi server.</value>
|
||||
</data>
|
||||
<data name="ServiceGameEnsureGameResourceQueryResourceInformation" xml:space="preserve">
|
||||
<value>Mencari Informasi Sumber Daya Game</value>
|
||||
<value>Unduh indeks sumber daya game</value>
|
||||
</data>
|
||||
<data name="ServiceGameFileOperationExceptionMessage" xml:space="preserve">
|
||||
<value>Gagal melakukan operasi pada file game: {0}</value>
|
||||
</data>
|
||||
<data name="ServiceGameLaunchExecutionGameFpsUnlockFailed" xml:space="preserve">
|
||||
<value>Gagal membuka batas frame rate</value>
|
||||
</data>
|
||||
<data name="ServiceGameLaunchExecutionGameIsRunning" xml:space="preserve">
|
||||
<value>Proses game sedang berjalan</value>
|
||||
</data>
|
||||
<data name="ServiceGameLaunchExecutionGamePathNotValid" xml:space="preserve">
|
||||
<value>Mohon pilih path game</value>
|
||||
</data>
|
||||
<data name="ServiceGameLaunchExecutionGameResourceQueryIndexFailed" xml:space="preserve">
|
||||
<value>Gagal mengunduh indeks sumber daya game: {0}</value>
|
||||
</data>
|
||||
<data name="ServiceGameLaunchPhaseProcessExited" xml:space="preserve">
|
||||
<value>Proses game ditutup</value>
|
||||
</data>
|
||||
@@ -1271,6 +1283,12 @@
|
||||
<data name="ViewDialogQRCodeTitle" xml:space="preserve">
|
||||
<value>Pindai kode QR dengan Aplikasi MiHoYo BBS</value>
|
||||
</data>
|
||||
<data name="ViewDialogReconfirmTextHeader" xml:space="preserve">
|
||||
<value>为防止你在无意间启用,请输入正在启用的功能开关的<b>标题名称</b></value>
|
||||
</data>
|
||||
<data name="ViewDialogReconfirmTitle" xml:space="preserve">
|
||||
<value>Anda Mengaktifkan Fitur Berbahaya</value>
|
||||
</data>
|
||||
<data name="ViewDialogSettingDeleteUserDataContent" xml:space="preserve">
|
||||
<value>Tindakan ini tidak dapat dibatalkan, dan semua status login pengguna akan hilang.</value>
|
||||
</data>
|
||||
@@ -1292,6 +1310,9 @@
|
||||
<data name="ViewDialogUserTitle" xml:space="preserve">
|
||||
<value>Setel Cookie</value>
|
||||
</data>
|
||||
<data name="ViewFeedbackHeader" xml:space="preserve">
|
||||
<value>Pusat Umpan Balik</value>
|
||||
</data>
|
||||
<data name="ViewGachaLogHeader" xml:space="preserve">
|
||||
<value>Riwayat Wish</value>
|
||||
</data>
|
||||
@@ -1541,6 +1562,9 @@
|
||||
<data name="ViewModelLaunchGameEnsureGameResourceFail" xml:space="preserve">
|
||||
<value>Konversi server gagal</value>
|
||||
</data>
|
||||
<data name="ViewModelLaunchGameIdentifyMonitorsAction" xml:space="preserve">
|
||||
<value>Identifikasi Monitor</value>
|
||||
</data>
|
||||
<data name="ViewModelLaunchGameMultiChannelReadFail" xml:space="preserve">
|
||||
<value>Tidak dapat membaca file konfigurasi game: {0}, file mungkin tidak ada atau kurang izin pengguna</value>
|
||||
</data>
|
||||
@@ -1574,6 +1598,12 @@
|
||||
<data name="ViewModelSettingCreateDesktopShortcutFailed" xml:space="preserve">
|
||||
<value>Gagal membuat shortcut di desktop</value>
|
||||
</data>
|
||||
<data name="ViewModelSettingDeleteServerCacheFolderContent" xml:space="preserve">
|
||||
<value>Anda akan diminta mengunduh file yang diperlukan, Yakin ingin menghapus?</value>
|
||||
</data>
|
||||
<data name="ViewModelSettingDeleteServerCacheFolderTitle" xml:space="preserve">
|
||||
<value>Menghapus Cache Server Konfersi</value>
|
||||
</data>
|
||||
<data name="ViewModelSettingFolderSizeDescription" xml:space="preserve">
|
||||
<value>Ruang disk yang digunakan: {0}</value>
|
||||
</data>
|
||||
@@ -1698,7 +1728,7 @@
|
||||
<value>Sinkronisasi Data Talenta Karakter</value>
|
||||
</data>
|
||||
<data name="ViewPageAvatarPropertyRefreshFromHoyolabGameRecord" xml:space="preserve">
|
||||
<value>Sinkronkan dari Karakter Saya MiHoYo BBS</value>
|
||||
<value>从米游社原神战绩同步</value>
|
||||
</data>
|
||||
<data name="ViewPageAvatarPropertyRefreshFromHoyolabGameRecordDescription" xml:space="preserve">
|
||||
<value>Sinkronkan sebagian besar data kecuali talent karakter.</value>
|
||||
@@ -1823,6 +1853,36 @@
|
||||
<data name="ViewPageDailyNoteVerify" xml:space="preserve">
|
||||
<value>Verifikasi Pengguna dan Role Saat Ini</value>
|
||||
</data>
|
||||
<data name="ViewPageFeedbackAutoSuggestBoxPlaceholder" xml:space="preserve">
|
||||
<value>Cari pertanyaan dan saran</value>
|
||||
</data>
|
||||
<data name="ViewPageFeedBackBasicInformation" xml:space="preserve">
|
||||
<value>Informasi Dasar</value>
|
||||
</data>
|
||||
<data name="ViewPageFeedbackCommonLinksHeader" xml:space="preserve">
|
||||
<value>Tautan Berguna</value>
|
||||
</data>
|
||||
<data name="ViewPageFeedbackEngageWithUsDescription" xml:space="preserve">
|
||||
<value>Tetap berhubungan dengan kami</value>
|
||||
</data>
|
||||
<data name="ViewPageFeedbackFeatureGuideHeader" xml:space="preserve">
|
||||
<value>Dokumen Fitur</value>
|
||||
</data>
|
||||
<data name="ViewPageFeedbackGithubIssuesDescription" xml:space="preserve">
|
||||
<value>我们总是优先处理 GitHub 上的问题</value>
|
||||
</data>
|
||||
<data name="ViewPageFeedbackRoadmapDescription" xml:space="preserve">
|
||||
<value>Roadmap Pengembangan</value>
|
||||
</data>
|
||||
<data name="ViewPageFeedbackSearchResultPlaceholderTitle" xml:space="preserve">
|
||||
<value>暂无搜索结果</value>
|
||||
</data>
|
||||
<data name="ViewPageFeedbackServerStatusDescription" xml:space="preserve">
|
||||
<value>Pemantau Ketersediaan Layanan Snap Hutao</value>
|
||||
</data>
|
||||
<data name="ViewPageFeedbackServerStatusHeader" xml:space="preserve">
|
||||
<value>Servis Snap Hutao</value>
|
||||
</data>
|
||||
<data name="ViewPageGachaLogAggressiveRefresh" xml:space="preserve">
|
||||
<value>Segarkan Seutuhnya</value>
|
||||
</data>
|
||||
@@ -2192,6 +2252,12 @@
|
||||
<data name="ViewPageLaunchGameUnlockFpsOn" xml:space="preserve">
|
||||
<value>Aktifkan</value>
|
||||
</data>
|
||||
<data name="ViewPageLaunchGameWindowsHDRDescription" xml:space="preserve">
|
||||
<value>Manfaatkan tampilan yang mendukung rentang dinamis tinggi untuk gambar yang lebih terang, lebih jelas, dan lebih detail</value>
|
||||
</data>
|
||||
<data name="ViewPageLaunchGameWindowsHDRHeader" xml:space="preserve">
|
||||
<value>Windows HDR</value>
|
||||
</data>
|
||||
<data name="ViewPageLoginHoyoverseUserHint" xml:space="preserve">
|
||||
<value>Masukkan UID HoYoLab Anda</value>
|
||||
</data>
|
||||
@@ -2564,9 +2630,18 @@
|
||||
<data name="ViewSettingAllocConsoleHeader" xml:space="preserve">
|
||||
<value>Konsol debug</value>
|
||||
</data>
|
||||
<data name="ViewSettingDeleteServerCacheFolderDescription" xml:space="preserve">
|
||||
<value>File Cache diunduh untuk Server Konfersi pada Game Launcher</value>
|
||||
</data>
|
||||
<data name="ViewSettingDeleteServerCacheFolderHeader" xml:space="preserve">
|
||||
<value>Menghapus Cache Server Konfersi</value>
|
||||
</data>
|
||||
<data name="ViewSettingFolderViewOpenFolderAction" xml:space="preserve">
|
||||
<value>Buka berkas</value>
|
||||
</data>
|
||||
<data name="ViewSettingHeader" xml:space="preserve">
|
||||
<value>Pengaturan</value>
|
||||
</data>
|
||||
<data name="ViewSpiralAbyssAvatarAppearanceRankDescription" xml:space="preserve">
|
||||
<value>Rasio Penampilan Karakter = Penampilan Karakter di Lantai Ini (hanya dihitung sekali jika berulang) / Total Jumlah Rekor Abyss di Lantai Ini</value>
|
||||
</data>
|
||||
@@ -2747,6 +2822,9 @@
|
||||
<data name="WebAnnouncementTimeHoursEndFormat" xml:space="preserve">
|
||||
<value>Selesai {0} jam</value>
|
||||
</data>
|
||||
<data name="WebBridgeShareCopyToClipboardFailed" xml:space="preserve">
|
||||
<value>Gagal membuka clipboard</value>
|
||||
</data>
|
||||
<data name="WebBridgeShareCopyToClipboardSuccess" xml:space="preserve">
|
||||
<value>Disalin ke clipboard</value>
|
||||
</data>
|
||||
@@ -2847,7 +2925,7 @@
|
||||
<value>{0} detik</value>
|
||||
</data>
|
||||
<data name="WebDailyNoteVerificationFailed" xml:space="preserve">
|
||||
<value>Verifikasi gagal. Silakan periksa MiHoYo BBS - Karakter Saya - Catatan Realtime secara manual.</value>
|
||||
<value>验证失败,请手动验证或前往「米游社-旅行工具-原神战绩-实时便笺」页面查看</value>
|
||||
</data>
|
||||
<data name="WebEnkaResponseStatusCode400" xml:space="preserve">
|
||||
<value>Format UID Salah</value>
|
||||
@@ -2916,7 +2994,7 @@
|
||||
<value>Server Snap Hutao sedang dalam perbaikan</value>
|
||||
</data>
|
||||
<data name="WebIndexOrSpiralAbyssVerificationFailed" xml:space="preserve">
|
||||
<value>Verifikasi gagal. Harap verifikasi secara manual atau periksa halaman Karakter Saya di MiHoYo BBS.</value>
|
||||
<value>验证失败,请手动验证或前往「米游社-旅行工具-原神战绩」页面查看</value>
|
||||
</data>
|
||||
<data name="WebResponseFormat" xml:space="preserve">
|
||||
<value>Kode Kembali: {0} | Pesan: {1}</value>
|
||||
@@ -2927,4 +3005,7 @@
|
||||
<data name="WebResponseRequestExceptionFormat" xml:space="preserve">
|
||||
<value>[{1}] pengecualian permintaan jaringan di [{0}], harap coba lagi nanti</value>
|
||||
</data>
|
||||
<data name="WindowIdentifyMonitorHeader" xml:space="preserve">
|
||||
<value>ID Monitor</value>
|
||||
</data>
|
||||
</root>
|
||||
@@ -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,11 +870,23 @@
|
||||
<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>FPS上限の解放に失敗しました</value>
|
||||
</data>
|
||||
<data name="ServiceGameLaunchExecutionGameIsRunning" xml:space="preserve">
|
||||
<value>ゲームは実行中です</value>
|
||||
</data>
|
||||
<data name="ServiceGameLaunchExecutionGamePathNotValid" xml:space="preserve">
|
||||
<value>ゲームのパスを選択してください</value>
|
||||
</data>
|
||||
<data name="ServiceGameLaunchExecutionGameResourceQueryIndexFailed" xml:space="preserve">
|
||||
<value>ゲームリソースのインデックスのダウンロードに失敗しました: {0}</value>
|
||||
</data>
|
||||
<data name="ServiceGameLaunchPhaseProcessExited" xml:space="preserve">
|
||||
<value>ゲームプロセスが終了しました</value>
|
||||
</data>
|
||||
@@ -1271,6 +1283,12 @@
|
||||
<data name="ViewDialogQRCodeTitle" xml:space="preserve">
|
||||
<value>MiHoYo BBS を使用して QR コードをスキャンします</value>
|
||||
</data>
|
||||
<data name="ViewDialogReconfirmTextHeader" xml:space="preserve">
|
||||
<value>謝って有効にしないようにするためには、有効にする機能の<b>タイトル名</b>を入力してください</value>
|
||||
</data>
|
||||
<data name="ViewDialogReconfirmTitle" xml:space="preserve">
|
||||
<value>危険な機能を有効にしています</value>
|
||||
</data>
|
||||
<data name="ViewDialogSettingDeleteUserDataContent" xml:space="preserve">
|
||||
<value>この操作は取り消せません。すべてのユーザーのログイン状態が解除されます。</value>
|
||||
</data>
|
||||
@@ -1292,6 +1310,9 @@
|
||||
<data name="ViewDialogUserTitle" xml:space="preserve">
|
||||
<value>クッキーを設定</value>
|
||||
</data>
|
||||
<data name="ViewFeedbackHeader" xml:space="preserve">
|
||||
<value>フィードバック センター</value>
|
||||
</data>
|
||||
<data name="ViewGachaLogHeader" xml:space="preserve">
|
||||
<value>祈願履歴</value>
|
||||
</data>
|
||||
@@ -1541,6 +1562,9 @@
|
||||
<data name="ViewModelLaunchGameEnsureGameResourceFail" xml:space="preserve">
|
||||
<value>サーバーの切り替えができませんでした</value>
|
||||
</data>
|
||||
<data name="ViewModelLaunchGameIdentifyMonitorsAction" xml:space="preserve">
|
||||
<value>モニターの識別</value>
|
||||
</data>
|
||||
<data name="ViewModelLaunchGameMultiChannelReadFail" xml:space="preserve">
|
||||
<value>ゲーム設定ファイル {0} の読み込みに失敗しました。ファイルが無いか、権限が不足している可能性があります。</value>
|
||||
</data>
|
||||
@@ -1574,6 +1598,12 @@
|
||||
<data name="ViewModelSettingCreateDesktopShortcutFailed" xml:space="preserve">
|
||||
<value>デスクトップへのショートカット作成に失敗しました</value>
|
||||
</data>
|
||||
<data name="ViewModelSettingDeleteServerCacheFolderContent" xml:space="preserve">
|
||||
<value>必要なファイルを再度ダウンロードする必要があります、削除してもよろしいですか?</value>
|
||||
</data>
|
||||
<data name="ViewModelSettingDeleteServerCacheFolderTitle" xml:space="preserve">
|
||||
<value>サーバー変換用キャッシュファイルの削除</value>
|
||||
</data>
|
||||
<data name="ViewModelSettingFolderSizeDescription" xml:space="preserve">
|
||||
<value>使用済みディスク容量: {0}</value>
|
||||
</data>
|
||||
@@ -1698,7 +1728,7 @@
|
||||
<value>キャラクターの天賦情報を同期</value>
|
||||
</data>
|
||||
<data name="ViewPageAvatarPropertyRefreshFromHoyolabGameRecord" xml:space="preserve">
|
||||
<value>MiHoYo BBSから所持キャラを同期</value>
|
||||
<value>MiHoYo BBSから原神の記録を同期</value>
|
||||
</data>
|
||||
<data name="ViewPageAvatarPropertyRefreshFromHoyolabGameRecordDescription" xml:space="preserve">
|
||||
<value>キャラ天賦以外の情報を概ね同期</value>
|
||||
@@ -1823,6 +1853,36 @@
|
||||
<data name="ViewPageDailyNoteVerify" xml:space="preserve">
|
||||
<value>現在のユーザーとUIDを確認する</value>
|
||||
</data>
|
||||
<data name="ViewPageFeedbackAutoSuggestBoxPlaceholder" xml:space="preserve">
|
||||
<value>質問や提案を検索</value>
|
||||
</data>
|
||||
<data name="ViewPageFeedBackBasicInformation" xml:space="preserve">
|
||||
<value>基本情報</value>
|
||||
</data>
|
||||
<data name="ViewPageFeedbackCommonLinksHeader" xml:space="preserve">
|
||||
<value>よく使われるリンク</value>
|
||||
</data>
|
||||
<data name="ViewPageFeedbackEngageWithUsDescription" xml:space="preserve">
|
||||
<value>引き続き連絡をしてください</value>
|
||||
</data>
|
||||
<data name="ViewPageFeedbackFeatureGuideHeader" xml:space="preserve">
|
||||
<value>機能ガイド</value>
|
||||
</data>
|
||||
<data name="ViewPageFeedbackGithubIssuesDescription" xml:space="preserve">
|
||||
<value>Githubでは報告された問題を常に優先しています</value>
|
||||
</data>
|
||||
<data name="ViewPageFeedbackRoadmapDescription" xml:space="preserve">
|
||||
<value>開発ロードマップ</value>
|
||||
</data>
|
||||
<data name="ViewPageFeedbackSearchResultPlaceholderTitle" xml:space="preserve">
|
||||
<value>検索結果はありません</value>
|
||||
</data>
|
||||
<data name="ViewPageFeedbackServerStatusDescription" xml:space="preserve">
|
||||
<value>胡桃のサービス状態</value>
|
||||
</data>
|
||||
<data name="ViewPageFeedbackServerStatusHeader" xml:space="preserve">
|
||||
<value>胡桃サービス</value>
|
||||
</data>
|
||||
<data name="ViewPageGachaLogAggressiveRefresh" xml:space="preserve">
|
||||
<value>すべて更新</value>
|
||||
</data>
|
||||
@@ -2192,6 +2252,12 @@
|
||||
<data name="ViewPageLaunchGameUnlockFpsOn" xml:space="preserve">
|
||||
<value>有効</value>
|
||||
</data>
|
||||
<data name="ViewPageLaunchGameWindowsHDRDescription" xml:space="preserve">
|
||||
<value>HDRをサポートするディスプレイを活用して、より明るく鮮やかなグラフィックを実現します。</value>
|
||||
</data>
|
||||
<data name="ViewPageLaunchGameWindowsHDRHeader" xml:space="preserve">
|
||||
<value>Windows HDR</value>
|
||||
</data>
|
||||
<data name="ViewPageLoginHoyoverseUserHint" xml:space="preserve">
|
||||
<value>HoYoLab UIDを入力してください</value>
|
||||
</data>
|
||||
@@ -2564,9 +2630,18 @@
|
||||
<data name="ViewSettingAllocConsoleHeader" xml:space="preserve">
|
||||
<value>デバッグコンソール</value>
|
||||
</data>
|
||||
<data name="ViewSettingDeleteServerCacheFolderDescription" xml:space="preserve">
|
||||
<value>サーバー変換を使用すると、対応するサーバーのゲームクライアントのキャッシュファイルが生成されます</value>
|
||||
</data>
|
||||
<data name="ViewSettingDeleteServerCacheFolderHeader" xml:space="preserve">
|
||||
<value>サーバー変換のキャッシュを削除</value>
|
||||
</data>
|
||||
<data name="ViewSettingFolderViewOpenFolderAction" xml:space="preserve">
|
||||
<value>フォルダを開く</value>
|
||||
</data>
|
||||
<data name="ViewSettingHeader" xml:space="preserve">
|
||||
<value>設定</value>
|
||||
</data>
|
||||
<data name="ViewSpiralAbyssAvatarAppearanceRankDescription" xml:space="preserve">
|
||||
<value>キャラクター出場率 = この階層における出場回数(最初の一回のみカウント) / この階層のアップロード総数</value>
|
||||
</data>
|
||||
@@ -2747,6 +2822,9 @@
|
||||
<data name="WebAnnouncementTimeHoursEndFormat" xml:space="preserve">
|
||||
<value>{0} 時間後に終了</value>
|
||||
</data>
|
||||
<data name="WebBridgeShareCopyToClipboardFailed" xml:space="preserve">
|
||||
<value>クリップボードを開けませんでした</value>
|
||||
</data>
|
||||
<data name="WebBridgeShareCopyToClipboardSuccess" xml:space="preserve">
|
||||
<value>クリップボードにコピーしました。</value>
|
||||
</data>
|
||||
@@ -2847,7 +2925,7 @@
|
||||
<value>{0} 秒</value>
|
||||
</data>
|
||||
<data name="WebDailyNoteVerificationFailed" xml:space="preserve">
|
||||
<value>認証に失敗しました。「MiHoYo BBS - 戦績ツール - リアルタイムノート」で確認し、認証を行ってください</value>
|
||||
<value>認証に失敗しました。「MiHoYo BBS - 戦績ツール - リアルタイムノート」で確認してください</value>
|
||||
</data>
|
||||
<data name="WebEnkaResponseStatusCode400" xml:space="preserve">
|
||||
<value>UIDは正しくありません</value>
|
||||
@@ -2916,7 +2994,7 @@
|
||||
<value>胡桃サーバがメンテナンス中です</value>
|
||||
</data>
|
||||
<data name="WebIndexOrSpiralAbyssVerificationFailed" xml:space="preserve">
|
||||
<value>認証に失敗しました。 手動で認証するか、MiHoYo BBS - 戦績 を確認してください。</value>
|
||||
<value>認証に失敗しました。 手動で認証するか、MiHoYo BBS - 戦績 - マイ キャラクター を確認してください。</value>
|
||||
</data>
|
||||
<data name="WebResponseFormat" xml:space="preserve">
|
||||
<value>リターンコード:{0} | メッセージ:{1}</value>
|
||||
@@ -2927,4 +3005,7 @@
|
||||
<data name="WebResponseRequestExceptionFormat" xml:space="preserve">
|
||||
<value>[{0}] の[{1}] のリクエストにエラーが発生、時間をおいてから試してください</value>
|
||||
</data>
|
||||
<data name="WindowIdentifyMonitorHeader" xml:space="preserve">
|
||||
<value>モニターID</value>
|
||||
</data>
|
||||
</root>
|
||||
@@ -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,11 +870,23 @@
|
||||
<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>
|
||||
</data>
|
||||
<data name="ServiceGameLaunchExecutionGameIsRunning" xml:space="preserve">
|
||||
<value>游戏进程运行中</value>
|
||||
</data>
|
||||
<data name="ServiceGameLaunchExecutionGamePathNotValid" xml:space="preserve">
|
||||
<value>请选择游戏路径</value>
|
||||
</data>
|
||||
<data name="ServiceGameLaunchExecutionGameResourceQueryIndexFailed" xml:space="preserve">
|
||||
<value>下载游戏资源索引失败: {0}</value>
|
||||
</data>
|
||||
<data name="ServiceGameLaunchPhaseProcessExited" xml:space="preserve">
|
||||
<value>游戏进程已退出</value>
|
||||
</data>
|
||||
@@ -1271,6 +1283,12 @@
|
||||
<data name="ViewDialogQRCodeTitle" xml:space="preserve">
|
||||
<value>使用米游社扫描二维码</value>
|
||||
</data>
|
||||
<data name="ViewDialogReconfirmTextHeader" xml:space="preserve">
|
||||
<value>为防止你在无意间启用,请输入正在启用的功能开关的<b>标题名称</b></value>
|
||||
</data>
|
||||
<data name="ViewDialogReconfirmTitle" xml:space="preserve">
|
||||
<value>你正在启用一个危险功能</value>
|
||||
</data>
|
||||
<data name="ViewDialogSettingDeleteUserDataContent" xml:space="preserve">
|
||||
<value>이 작업은 되돌릴 수 없으며, 모든 사용자 로그인 상태가 해제됩니다</value>
|
||||
</data>
|
||||
@@ -1292,6 +1310,9 @@
|
||||
<data name="ViewDialogUserTitle" xml:space="preserve">
|
||||
<value>쿠키 설정</value>
|
||||
</data>
|
||||
<data name="ViewFeedbackHeader" xml:space="preserve">
|
||||
<value>反馈中心</value>
|
||||
</data>
|
||||
<data name="ViewGachaLogHeader" xml:space="preserve">
|
||||
<value>기원 기록</value>
|
||||
</data>
|
||||
@@ -1541,6 +1562,9 @@
|
||||
<data name="ViewModelLaunchGameEnsureGameResourceFail" xml:space="preserve">
|
||||
<value>서버 변경 실패</value>
|
||||
</data>
|
||||
<data name="ViewModelLaunchGameIdentifyMonitorsAction" xml:space="preserve">
|
||||
<value>识别显示器</value>
|
||||
</data>
|
||||
<data name="ViewModelLaunchGameMultiChannelReadFail" xml:space="preserve">
|
||||
<value>无法读取游戏配置文件: {0},可能是文件不存在或权限不足</value>
|
||||
</data>
|
||||
@@ -1574,6 +1598,12 @@
|
||||
<data name="ViewModelSettingCreateDesktopShortcutFailed" xml:space="preserve">
|
||||
<value>创建桌面快捷方式失败</value>
|
||||
</data>
|
||||
<data name="ViewModelSettingDeleteServerCacheFolderContent" xml:space="preserve">
|
||||
<value>后续转换会重新下载所需的文件,确定要删除吗?</value>
|
||||
</data>
|
||||
<data name="ViewModelSettingDeleteServerCacheFolderTitle" xml:space="preserve">
|
||||
<value>删除转换服务器游戏客户端缓存</value>
|
||||
</data>
|
||||
<data name="ViewModelSettingFolderSizeDescription" xml:space="preserve">
|
||||
<value>已使用磁盘空间:{0}</value>
|
||||
</data>
|
||||
@@ -1698,7 +1728,7 @@
|
||||
<value>캐릭터 특성 정보 동기화</value>
|
||||
</data>
|
||||
<data name="ViewPageAvatarPropertyRefreshFromHoyolabGameRecord" xml:space="preserve">
|
||||
<value>HoYoLAB에서 내 캐릭터 동기화</value>
|
||||
<value>从米游社原神战绩同步</value>
|
||||
</data>
|
||||
<data name="ViewPageAvatarPropertyRefreshFromHoyolabGameRecordDescription" xml:space="preserve">
|
||||
<value>캐릭터 특성을 제외한 대부분의 정보 동기화</value>
|
||||
@@ -1823,6 +1853,36 @@
|
||||
<data name="ViewPageDailyNoteVerify" xml:space="preserve">
|
||||
<value>현재 사용자와 캐릭터 확인</value>
|
||||
</data>
|
||||
<data name="ViewPageFeedbackAutoSuggestBoxPlaceholder" xml:space="preserve">
|
||||
<value>搜索问题与建议</value>
|
||||
</data>
|
||||
<data name="ViewPageFeedBackBasicInformation" xml:space="preserve">
|
||||
<value>基本信息</value>
|
||||
</data>
|
||||
<data name="ViewPageFeedbackCommonLinksHeader" xml:space="preserve">
|
||||
<value>常用链接</value>
|
||||
</data>
|
||||
<data name="ViewPageFeedbackEngageWithUsDescription" xml:space="preserve">
|
||||
<value>与我们密切联系</value>
|
||||
</data>
|
||||
<data name="ViewPageFeedbackFeatureGuideHeader" xml:space="preserve">
|
||||
<value>功能指南</value>
|
||||
</data>
|
||||
<data name="ViewPageFeedbackGithubIssuesDescription" xml:space="preserve">
|
||||
<value>我们总是优先处理 GitHub 上的问题</value>
|
||||
</data>
|
||||
<data name="ViewPageFeedbackRoadmapDescription" xml:space="preserve">
|
||||
<value>开发路线规划</value>
|
||||
</data>
|
||||
<data name="ViewPageFeedbackSearchResultPlaceholderTitle" xml:space="preserve">
|
||||
<value>暂无搜索结果</value>
|
||||
</data>
|
||||
<data name="ViewPageFeedbackServerStatusDescription" xml:space="preserve">
|
||||
<value>胡桃服务可用性监控</value>
|
||||
</data>
|
||||
<data name="ViewPageFeedbackServerStatusHeader" xml:space="preserve">
|
||||
<value>胡桃服务</value>
|
||||
</data>
|
||||
<data name="ViewPageGachaLogAggressiveRefresh" xml:space="preserve">
|
||||
<value>전체 동기화</value>
|
||||
</data>
|
||||
@@ -2192,6 +2252,12 @@
|
||||
<data name="ViewPageLaunchGameUnlockFpsOn" xml:space="preserve">
|
||||
<value>활성화</value>
|
||||
</data>
|
||||
<data name="ViewPageLaunchGameWindowsHDRDescription" xml:space="preserve">
|
||||
<value>充分利用支持高动态范围的显示器获得更亮、更生动、更精细的画面</value>
|
||||
</data>
|
||||
<data name="ViewPageLaunchGameWindowsHDRHeader" xml:space="preserve">
|
||||
<value>Windows HDR</value>
|
||||
</data>
|
||||
<data name="ViewPageLoginHoyoverseUserHint" xml:space="preserve">
|
||||
<value>HoYoLab Uid를 입력하세요</value>
|
||||
</data>
|
||||
@@ -2564,9 +2630,18 @@
|
||||
<data name="ViewSettingAllocConsoleHeader" xml:space="preserve">
|
||||
<value>调试控制台</value>
|
||||
</data>
|
||||
<data name="ViewSettingDeleteServerCacheFolderDescription" xml:space="preserve">
|
||||
<value>在启动游戏中转换服务器后会产生对应的游戏客户端文件用作缓存</value>
|
||||
</data>
|
||||
<data name="ViewSettingDeleteServerCacheFolderHeader" xml:space="preserve">
|
||||
<value>删除转换服务器缓存</value>
|
||||
</data>
|
||||
<data name="ViewSettingFolderViewOpenFolderAction" xml:space="preserve">
|
||||
<value>打开文件夹</value>
|
||||
</data>
|
||||
<data name="ViewSettingHeader" xml:space="preserve">
|
||||
<value>设置</value>
|
||||
</data>
|
||||
<data name="ViewSpiralAbyssAvatarAppearanceRankDescription" xml:space="preserve">
|
||||
<value>角色出场率 = 本层上阵该角色次数(层内重复出现只记一次)/ 深渊记录总数</value>
|
||||
</data>
|
||||
@@ -2747,6 +2822,9 @@
|
||||
<data name="WebAnnouncementTimeHoursEndFormat" xml:space="preserve">
|
||||
<value>{0}시간 후 종료</value>
|
||||
</data>
|
||||
<data name="WebBridgeShareCopyToClipboardFailed" xml:space="preserve">
|
||||
<value>打开剪贴板失败</value>
|
||||
</data>
|
||||
<data name="WebBridgeShareCopyToClipboardSuccess" xml:space="preserve">
|
||||
<value>已复制到剪贴板</value>
|
||||
</data>
|
||||
@@ -2847,7 +2925,7 @@
|
||||
<value>{0}초</value>
|
||||
</data>
|
||||
<data name="WebDailyNoteVerificationFailed" xml:space="preserve">
|
||||
<value>인증에 실패했습니다. 수동으로 인증하거나 'HoYoLAB-전적-실시간 메모' 페이지에서 확인하시기 바랍니다.</value>
|
||||
<value>验证失败,请手动验证或前往「米游社-旅行工具-原神战绩-实时便笺」页面查看</value>
|
||||
</data>
|
||||
<data name="WebEnkaResponseStatusCode400" xml:space="preserve">
|
||||
<value>错误的 UID 格式</value>
|
||||
@@ -2916,7 +2994,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>
|
||||
@@ -2927,4 +3005,7 @@
|
||||
<data name="WebResponseRequestExceptionFormat" xml:space="preserve">
|
||||
<value>[{0}] 中的 [{1}] 网络请求异常,请稍后再试</value>
|
||||
</data>
|
||||
<data name="WindowIdentifyMonitorHeader" xml:space="preserve">
|
||||
<value>显示器编号</value>
|
||||
</data>
|
||||
</root>
|
||||
@@ -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,11 +870,23 @@
|
||||
<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>
|
||||
</data>
|
||||
<data name="ServiceGameLaunchExecutionGameIsRunning" xml:space="preserve">
|
||||
<value>游戏进程运行中</value>
|
||||
</data>
|
||||
<data name="ServiceGameLaunchExecutionGamePathNotValid" xml:space="preserve">
|
||||
<value>请选择游戏路径</value>
|
||||
</data>
|
||||
<data name="ServiceGameLaunchExecutionGameResourceQueryIndexFailed" xml:space="preserve">
|
||||
<value>下载游戏资源索引失败: {0}</value>
|
||||
</data>
|
||||
<data name="ServiceGameLaunchPhaseProcessExited" xml:space="preserve">
|
||||
<value>游戏进程已退出</value>
|
||||
</data>
|
||||
@@ -1272,7 +1284,7 @@
|
||||
<value>使用米游社扫描二维码</value>
|
||||
</data>
|
||||
<data name="ViewDialogReconfirmTextHeader" xml:space="preserve">
|
||||
<value>请输入你正在启用的功能标题</value>
|
||||
<value>为防止你在无意间启用,请输入正在启用的功能开关的<b>标题名称</b></value>
|
||||
</data>
|
||||
<data name="ViewDialogReconfirmTitle" xml:space="preserve">
|
||||
<value>你正在启用一个危险功能</value>
|
||||
@@ -1298,6 +1310,9 @@
|
||||
<data name="ViewDialogUserTitle" xml:space="preserve">
|
||||
<value>设置 Cookie</value>
|
||||
</data>
|
||||
<data name="ViewFeedbackHeader" xml:space="preserve">
|
||||
<value>反馈中心</value>
|
||||
</data>
|
||||
<data name="ViewGachaLogHeader" xml:space="preserve">
|
||||
<value>祈愿记录</value>
|
||||
</data>
|
||||
@@ -1547,6 +1562,9 @@
|
||||
<data name="ViewModelLaunchGameEnsureGameResourceFail" xml:space="preserve">
|
||||
<value>切换服务器失败</value>
|
||||
</data>
|
||||
<data name="ViewModelLaunchGameIdentifyMonitorsAction" xml:space="preserve">
|
||||
<value>识别显示器</value>
|
||||
</data>
|
||||
<data name="ViewModelLaunchGameMultiChannelReadFail" xml:space="preserve">
|
||||
<value>无法读取游戏配置文件: {0},可能是文件不存在或权限不足</value>
|
||||
</data>
|
||||
@@ -1710,7 +1728,7 @@
|
||||
<value>同步角色天赋信息</value>
|
||||
</data>
|
||||
<data name="ViewPageAvatarPropertyRefreshFromHoyolabGameRecord" xml:space="preserve">
|
||||
<value>从米游社我的角色同步</value>
|
||||
<value>从米游社原神战绩同步</value>
|
||||
</data>
|
||||
<data name="ViewPageAvatarPropertyRefreshFromHoyolabGameRecordDescription" xml:space="preserve">
|
||||
<value>同步角色天赋外的大部分信息</value>
|
||||
@@ -1835,6 +1853,36 @@
|
||||
<data name="ViewPageDailyNoteVerify" xml:space="preserve">
|
||||
<value>验证当前用户与角色</value>
|
||||
</data>
|
||||
<data name="ViewPageFeedbackAutoSuggestBoxPlaceholder" xml:space="preserve">
|
||||
<value>搜索问题与建议</value>
|
||||
</data>
|
||||
<data name="ViewPageFeedBackBasicInformation" xml:space="preserve">
|
||||
<value>基本信息</value>
|
||||
</data>
|
||||
<data name="ViewPageFeedbackCommonLinksHeader" xml:space="preserve">
|
||||
<value>常用链接</value>
|
||||
</data>
|
||||
<data name="ViewPageFeedbackEngageWithUsDescription" xml:space="preserve">
|
||||
<value>与我们密切联系</value>
|
||||
</data>
|
||||
<data name="ViewPageFeedbackFeatureGuideHeader" xml:space="preserve">
|
||||
<value>功能指南</value>
|
||||
</data>
|
||||
<data name="ViewPageFeedbackGithubIssuesDescription" xml:space="preserve">
|
||||
<value>我们总是优先处理 GitHub 上的问题</value>
|
||||
</data>
|
||||
<data name="ViewPageFeedbackRoadmapDescription" xml:space="preserve">
|
||||
<value>开发路线规划</value>
|
||||
</data>
|
||||
<data name="ViewPageFeedbackSearchResultPlaceholderTitle" xml:space="preserve">
|
||||
<value>暂无搜索结果</value>
|
||||
</data>
|
||||
<data name="ViewPageFeedbackServerStatusDescription" xml:space="preserve">
|
||||
<value>胡桃服务可用性监控</value>
|
||||
</data>
|
||||
<data name="ViewPageFeedbackServerStatusHeader" xml:space="preserve">
|
||||
<value>胡桃服务</value>
|
||||
</data>
|
||||
<data name="ViewPageGachaLogAggressiveRefresh" xml:space="preserve">
|
||||
<value>全量刷新</value>
|
||||
</data>
|
||||
@@ -2591,6 +2639,9 @@
|
||||
<data name="ViewSettingFolderViewOpenFolderAction" xml:space="preserve">
|
||||
<value>打开文件夹</value>
|
||||
</data>
|
||||
<data name="ViewSettingHeader" xml:space="preserve">
|
||||
<value>设置</value>
|
||||
</data>
|
||||
<data name="ViewSpiralAbyssAvatarAppearanceRankDescription" xml:space="preserve">
|
||||
<value>角色出场率 = 本层上阵该角色次数(层内重复出现只记一次)/ 深渊记录总数</value>
|
||||
</data>
|
||||
@@ -2874,7 +2925,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>
|
||||
@@ -2943,7 +2994,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>
|
||||
@@ -2954,4 +3005,7 @@
|
||||
<data name="WebResponseRequestExceptionFormat" xml:space="preserve">
|
||||
<value>[{0}] 中的 [{1}] 网络请求异常,请稍后再试</value>
|
||||
</data>
|
||||
<data name="WindowIdentifyMonitorHeader" xml:space="preserve">
|
||||
<value>显示器编号</value>
|
||||
</data>
|
||||
</root>
|
||||
@@ -510,141 +510,141 @@
|
||||
<value>Необходимо войти в учетную запись miHoYo/HoYoLAB и выбрать пользователя с персонажем</value>
|
||||
</data>
|
||||
<data name="ServerGachaLogServiceDeleteEntrySucceed" xml:space="preserve">
|
||||
<value>删除了 Uid:{0} 的 {1} 条祈愿记录</value>
|
||||
<value>Удален Uid: {0} из {1} записи пожеланий</value>
|
||||
</data>
|
||||
<data name="ServerGachaLogServiceInsufficientRecordSlot" xml:space="preserve">
|
||||
<value>胡桃云保存的祈愿记录存档数已达当前账号上限</value>
|
||||
<value>Достигнуто максимально допустимое количество архивов истории желаний в облаке Snap Hutao</value>
|
||||
</data>
|
||||
<data name="ServerGachaLogServiceInsufficientTime" xml:space="preserve">
|
||||
<value>未开通祈愿记录上传服务或已到期</value>
|
||||
<value>Сервис загрузки записей пожеланий не активирован или истек срок его действия</value>
|
||||
</data>
|
||||
<data name="ServerGachaLogServiceInvalidGachaLogData" xml:space="preserve">
|
||||
<value>祈愿数据存在无效的物品,无法保存至胡桃云</value>
|
||||
<value>Данные о желаниях содержат недопустимые предметы и не могут быть сохранены в Ху Тао облако</value>
|
||||
</data>
|
||||
<data name="ServerGachaLogServiceServerDatabaseError" xml:space="preserve">
|
||||
<value>数据异常,无法保存至云端,请勿跨账号上传或尝试删除云端数据后重试</value>
|
||||
<value>Данные некорректны, не удается сохранить в облако. Пожалуйста, не загружайте через другие аккаунты или попробуйте удалить данные в облаке и повторите попытку</value>
|
||||
</data>
|
||||
<data name="ServerGachaLogServiceUploadEntrySucceed" xml:space="preserve">
|
||||
<value>上传了 Uid:{0} 的 {1} 条祈愿记录,存储了 {2} 条</value>
|
||||
<value>Загружено Uid: {0} из {1} записи желаний, Сохранено {2} записей</value>
|
||||
</data>
|
||||
<data name="ServerPassportLoginRequired" xml:space="preserve">
|
||||
<value>请先登录或注册胡桃账号</value>
|
||||
<value>Пожалуйста, сначала войдите или зарегистрируйтесь Snap Hutao аккаунт</value>
|
||||
</data>
|
||||
<data name="ServerPassportLoginSucceed" xml:space="preserve">
|
||||
<value>登录成功</value>
|
||||
<value>Успешный вход</value>
|
||||
</data>
|
||||
<data name="ServerPassportRegisterSucceed" xml:space="preserve">
|
||||
<value>注册成功</value>
|
||||
<value>Регистрация успешна</value>
|
||||
</data>
|
||||
<data name="ServerPassportResetPasswordSucceed" xml:space="preserve">
|
||||
<value>新密码设置成功</value>
|
||||
<value>Новый пароль установлен успешно</value>
|
||||
</data>
|
||||
<data name="ServerPassportServiceEmailHasNotRegistered" xml:space="preserve">
|
||||
<value>当前邮箱尚未注册</value>
|
||||
<value>Текущий адрес электронной почты еще не зарегистрирован</value>
|
||||
</data>
|
||||
<data name="ServerPassportServiceEmailHasRegistered" xml:space="preserve">
|
||||
<value>当前邮箱已被注册</value>
|
||||
<value>Текущий адрес электронной почты уже зарегистрирован</value>
|
||||
</data>
|
||||
<data name="ServerPassportServiceInternalException" xml:space="preserve">
|
||||
<value>注册失败,服务器异常,请尽快联系开发者解决</value>
|
||||
<value>Ошибка регистрации, проблемы с сервером. Пожалуйста, свяжитесь с разработчиком для решения проблемы</value>
|
||||
</data>
|
||||
<data name="ServerPassportServiceUnregisterFailed" xml:space="preserve">
|
||||
<value>用户不存在,注销失败</value>
|
||||
<value>Пользователь не существует, отмена не удалась</value>
|
||||
</data>
|
||||
<data name="ServerPassportUnregisterSucceed" xml:space="preserve">
|
||||
<value>用户注销成功</value>
|
||||
<value>Пользователь успешно отменен</value>
|
||||
</data>
|
||||
<data name="ServerPassportUserInfoNotExist" xml:space="preserve">
|
||||
<value>用户不存在,获取用户信息失败</value>
|
||||
<value>Пользователь не существует, не удалось получить информацию о пользователе</value>
|
||||
</data>
|
||||
<data name="ServerPassportUserNameOrPasswordIncorrect" xml:space="preserve">
|
||||
<value>用户名或密码错误</value>
|
||||
<value>Неверное имя пользователя или пароль</value>
|
||||
</data>
|
||||
<data name="ServerPassportVerifyFailed" xml:space="preserve">
|
||||
<value>验证失败</value>
|
||||
<value>Проверка не удалась</value>
|
||||
</data>
|
||||
<data name="ServerPassportVerifyRequestNotCurrentUser" xml:space="preserve">
|
||||
<value>验证请求失败,不是当前登录的账号</value>
|
||||
<value>Ошибка проверки запроса, это не текущий вход в учетную запись</value>
|
||||
</data>
|
||||
<data name="ServerPassportVerifyRequestSuccess" xml:space="preserve">
|
||||
<value>验证码已发送至邮箱</value>
|
||||
<value>Код подтверждения отправлен на электронную почту</value>
|
||||
</data>
|
||||
<data name="ServerPassportVerifyRequestUserAlreadyExisted" xml:space="preserve">
|
||||
<value>验证请求失败,当前邮箱已被注册</value>
|
||||
<value>Ошибка проверки запроса, текущий адрес электронной почты уже зарегистрирован</value>
|
||||
</data>
|
||||
<data name="ServerPassportVerifyTooFrequent" xml:space="preserve">
|
||||
<value>验证请求过快,请 1 分钟后再试</value>
|
||||
<value>Слишком частые запросы проверки, пожалуйста, повторите через 1 минуту</value>
|
||||
</data>
|
||||
<data name="ServerRecordBannedUid" xml:space="preserve">
|
||||
<value>上传深渊记录失败,当前 Uid 已被胡桃数据库封禁</value>
|
||||
<value>Не удалось загрузить записи из Бездны, текущий Uid заблокирован в базе данных Walnut</value>
|
||||
</data>
|
||||
<data name="ServerRecordComputingStatistics" xml:space="preserve">
|
||||
<value>上传深渊记录失败,正在计算统计数据</value>
|
||||
<value>Не удалось загрузить записи из Бездны, выполняется подсчет статистики</value>
|
||||
</data>
|
||||
<data name="ServerRecordComputingStatistics2" xml:space="preserve">
|
||||
<value>获取数据失败,正在计算统计数据</value>
|
||||
<value>Не удалось получить данные, выполняется подсчет статистики</value>
|
||||
</data>
|
||||
<data name="ServerRecordInternalException" xml:space="preserve">
|
||||
<value>上传深渊记录失败,服务器异常,请尽快联系开发者解决</value>
|
||||
<value>Не удалось загрузить записи из Бездны, Сервер недоступен, пожалуйста, свяжитесь с разработчиком для решения проблемы</value>
|
||||
</data>
|
||||
<data name="ServerRecordInvalidData" xml:space="preserve">
|
||||
<value>上传深渊记录失败,存在无效的数据</value>
|
||||
<value>Не удалось загрузить записи из Бездны, обнаружены недопустимые данные</value>
|
||||
</data>
|
||||
<data name="ServerRecordInvalidUid" xml:space="preserve">
|
||||
<value>无效的 Uid</value>
|
||||
<value>Недопустимый Uid</value>
|
||||
</data>
|
||||
<data name="ServerRecordNotCurrentSchedule" xml:space="preserve">
|
||||
<value>上传深渊记录失败,不是本期数据</value>
|
||||
<value>Не удалось загрузить записи из Бездны, это не текущие данные</value>
|
||||
</data>
|
||||
<data name="ServerRecordPreviousRequestNotCompleted" xml:space="preserve">
|
||||
<value>上传深渊记录失败,当前 Uid 的记录仍在处理中,请勿重复操作</value>
|
||||
<value>Не удалось загрузить записи из Бездны, записи для текущего Uid все еще обрабатываются. Пожалуйста, не повторяйте операцию</value>
|
||||
</data>
|
||||
<data name="ServerRecordUploadSuccessAndGachaLogServiceTimeExtended" xml:space="preserve">
|
||||
<value>上传深渊记录成功,获赠祈愿记录上传服务时长</value>
|
||||
<value>Загрузка записей из Бездны успешна, выдан срок обслуживания загрузки записей о желаниях</value>
|
||||
</data>
|
||||
<data name="ServerRecordUploadSuccessButNoPassport" xml:space="preserve">
|
||||
<value>上传深渊记录成功,但未登录胡桃通行证,无法获赠祈愿记录上传服务时长</value>
|
||||
<value>Загрузка записей из Бездны успешна, Но без входа в учетную Snap Hutao пропускной билет, Невозможно получить бесплатный период обслуживания для загрузки записей о желаниях</value>
|
||||
</data>
|
||||
<data name="ServerRecordUploadSuccessButNoSuchUser" xml:space="preserve">
|
||||
<value>上传深渊记录成功,但无法找到用户,无法获赠祈愿记录上传服务时长</value>
|
||||
<value>Загрузка записей из Бездны успешна, Но не удается найти пользователя, Невозможно получить бесплатный период обслуживания для загрузки записей о желаниях</value>
|
||||
</data>
|
||||
<data name="ServerRecordUploadSuccessButNotFirstTimeAtCurrentSchedule" xml:space="preserve">
|
||||
<value>上传深渊记录成功,但不是本期首次提交,无法获赠祈愿记录上传服务时长</value>
|
||||
<value>Загрузка записей из Бездны успешна, Но это не первая подача в текущем периоде, Невозможно получить бесплатный период обслуживания для загрузки записей о желаниях</value>
|
||||
</data>
|
||||
<data name="ServiceAchievementImportResultFormat" xml:space="preserve">
|
||||
<value>新增:{0} 个成就 | 更新:{1} 个成就 | 删除:{2} 个成就</value>
|
||||
<value>Добавить:{0} достижения | Обновление: {1} достижение | Удаление: {2} достижения</value>
|
||||
</data>
|
||||
<data name="ServiceAchievementUIAFImportPickerFilterText" xml:space="preserve">
|
||||
<value>UIAF Json 文件</value>
|
||||
<value>UIAF Json файл</value>
|
||||
</data>
|
||||
<data name="ServiceAchievementUIAFImportPickerTitile" xml:space="preserve">
|
||||
<value>打开 UIAF Json 文件</value>
|
||||
<value>Открыть UIAF Json файл</value>
|
||||
</data>
|
||||
<data name="ServiceAchievementUserdataCorruptedInnerIdNotUnique" xml:space="preserve">
|
||||
<value>单个成就存档内发现多个相同的成就 Id</value>
|
||||
<value>В одном архиве достижений обнаружено несколько одинаковых идентификаторов достижений</value>
|
||||
</data>
|
||||
<data name="ServiceAvatarInfoPropertyAtk" xml:space="preserve">
|
||||
<value>攻击力</value>
|
||||
<value>Сила атаки</value>
|
||||
<comment>Need EXACT same string in game</comment>
|
||||
</data>
|
||||
<data name="ServiceAvatarInfoPropertyBaseAtk" xml:space="preserve">
|
||||
<value>基础攻击力</value>
|
||||
<value>Базовая сила атаки</value>
|
||||
<comment>Need EXACT same string in game</comment>
|
||||
</data>
|
||||
<data name="ServiceAvatarInfoPropertyBaseDef" xml:space="preserve">
|
||||
<value>基础防御力</value>
|
||||
<value>Основная защита</value>
|
||||
<comment>Need EXACT same string in game</comment>
|
||||
</data>
|
||||
<data name="ServiceAvatarInfoPropertyBaseHp" xml:space="preserve">
|
||||
<value>基础生命值</value>
|
||||
<value>Базовое здоровье</value>
|
||||
<comment>Need EXACT same string in game</comment>
|
||||
</data>
|
||||
<data name="ServiceAvatarInfoPropertyCDmg" xml:space="preserve">
|
||||
<value>暴击伤害</value>
|
||||
<value>Урон при критическом ударе</value>
|
||||
<comment>Need EXACT same string in game</comment>
|
||||
</data>
|
||||
<data name="ServiceAvatarInfoPropertyCE" xml:space="preserve">
|
||||
<value>元素充能效率</value>
|
||||
<value>Эффективность заряда элемента</value>
|
||||
<comment>Need EXACT same string in game</comment>
|
||||
</data>
|
||||
<data name="ServiceAvatarInfoPropertyCR" xml:space="preserve">
|
||||
@@ -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>Enka: N/A</value>
|
||||
@@ -870,11 +870,23 @@
|
||||
<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>
|
||||
</data>
|
||||
<data name="ServiceGameLaunchExecutionGameIsRunning" xml:space="preserve">
|
||||
<value>游戏进程运行中</value>
|
||||
</data>
|
||||
<data name="ServiceGameLaunchExecutionGamePathNotValid" xml:space="preserve">
|
||||
<value>请选择游戏路径</value>
|
||||
</data>
|
||||
<data name="ServiceGameLaunchExecutionGameResourceQueryIndexFailed" xml:space="preserve">
|
||||
<value>下载游戏资源索引失败: {0}</value>
|
||||
</data>
|
||||
<data name="ServiceGameLaunchPhaseProcessExited" xml:space="preserve">
|
||||
<value>游戏进程已退出</value>
|
||||
</data>
|
||||
@@ -1271,6 +1283,12 @@
|
||||
<data name="ViewDialogQRCodeTitle" xml:space="preserve">
|
||||
<value>使用米游社扫描二维码</value>
|
||||
</data>
|
||||
<data name="ViewDialogReconfirmTextHeader" xml:space="preserve">
|
||||
<value>为防止你在无意间启用,请输入正在启用的功能开关的<b>标题名称</b></value>
|
||||
</data>
|
||||
<data name="ViewDialogReconfirmTitle" xml:space="preserve">
|
||||
<value>你正在启用一个危险功能</value>
|
||||
</data>
|
||||
<data name="ViewDialogSettingDeleteUserDataContent" xml:space="preserve">
|
||||
<value>该操作是不可逆的,所有用户登录状态会丢失</value>
|
||||
</data>
|
||||
@@ -1292,6 +1310,9 @@
|
||||
<data name="ViewDialogUserTitle" xml:space="preserve">
|
||||
<value>设置 Cookie</value>
|
||||
</data>
|
||||
<data name="ViewFeedbackHeader" xml:space="preserve">
|
||||
<value>反馈中心</value>
|
||||
</data>
|
||||
<data name="ViewGachaLogHeader" xml:space="preserve">
|
||||
<value>祈愿记录</value>
|
||||
</data>
|
||||
@@ -1541,6 +1562,9 @@
|
||||
<data name="ViewModelLaunchGameEnsureGameResourceFail" xml:space="preserve">
|
||||
<value>切换服务器失败</value>
|
||||
</data>
|
||||
<data name="ViewModelLaunchGameIdentifyMonitorsAction" xml:space="preserve">
|
||||
<value>识别显示器</value>
|
||||
</data>
|
||||
<data name="ViewModelLaunchGameMultiChannelReadFail" xml:space="preserve">
|
||||
<value>无法读取游戏配置文件: {0},可能是文件不存在或权限不足</value>
|
||||
</data>
|
||||
@@ -1574,6 +1598,12 @@
|
||||
<data name="ViewModelSettingCreateDesktopShortcutFailed" xml:space="preserve">
|
||||
<value>创建桌面快捷方式失败</value>
|
||||
</data>
|
||||
<data name="ViewModelSettingDeleteServerCacheFolderContent" xml:space="preserve">
|
||||
<value>后续转换会重新下载所需的文件,确定要删除吗?</value>
|
||||
</data>
|
||||
<data name="ViewModelSettingDeleteServerCacheFolderTitle" xml:space="preserve">
|
||||
<value>删除转换服务器游戏客户端缓存</value>
|
||||
</data>
|
||||
<data name="ViewModelSettingFolderSizeDescription" xml:space="preserve">
|
||||
<value>已使用磁盘空间:{0}</value>
|
||||
</data>
|
||||
@@ -1698,7 +1728,7 @@
|
||||
<value>同步角色天赋信息</value>
|
||||
</data>
|
||||
<data name="ViewPageAvatarPropertyRefreshFromHoyolabGameRecord" xml:space="preserve">
|
||||
<value>从米游社我的角色同步</value>
|
||||
<value>从米游社原神战绩同步</value>
|
||||
</data>
|
||||
<data name="ViewPageAvatarPropertyRefreshFromHoyolabGameRecordDescription" xml:space="preserve">
|
||||
<value>同步角色天赋外的大部分信息</value>
|
||||
@@ -1823,6 +1853,36 @@
|
||||
<data name="ViewPageDailyNoteVerify" xml:space="preserve">
|
||||
<value>验证当前用户与角色</value>
|
||||
</data>
|
||||
<data name="ViewPageFeedbackAutoSuggestBoxPlaceholder" xml:space="preserve">
|
||||
<value>搜索问题与建议</value>
|
||||
</data>
|
||||
<data name="ViewPageFeedBackBasicInformation" xml:space="preserve">
|
||||
<value>基本信息</value>
|
||||
</data>
|
||||
<data name="ViewPageFeedbackCommonLinksHeader" xml:space="preserve">
|
||||
<value>常用链接</value>
|
||||
</data>
|
||||
<data name="ViewPageFeedbackEngageWithUsDescription" xml:space="preserve">
|
||||
<value>与我们密切联系</value>
|
||||
</data>
|
||||
<data name="ViewPageFeedbackFeatureGuideHeader" xml:space="preserve">
|
||||
<value>功能指南</value>
|
||||
</data>
|
||||
<data name="ViewPageFeedbackGithubIssuesDescription" xml:space="preserve">
|
||||
<value>我们总是优先处理 GitHub 上的问题</value>
|
||||
</data>
|
||||
<data name="ViewPageFeedbackRoadmapDescription" xml:space="preserve">
|
||||
<value>开发路线规划</value>
|
||||
</data>
|
||||
<data name="ViewPageFeedbackSearchResultPlaceholderTitle" xml:space="preserve">
|
||||
<value>暂无搜索结果</value>
|
||||
</data>
|
||||
<data name="ViewPageFeedbackServerStatusDescription" xml:space="preserve">
|
||||
<value>胡桃服务可用性监控</value>
|
||||
</data>
|
||||
<data name="ViewPageFeedbackServerStatusHeader" xml:space="preserve">
|
||||
<value>胡桃服务</value>
|
||||
</data>
|
||||
<data name="ViewPageGachaLogAggressiveRefresh" xml:space="preserve">
|
||||
<value>全量刷新</value>
|
||||
</data>
|
||||
@@ -2192,6 +2252,12 @@
|
||||
<data name="ViewPageLaunchGameUnlockFpsOn" xml:space="preserve">
|
||||
<value>启用</value>
|
||||
</data>
|
||||
<data name="ViewPageLaunchGameWindowsHDRDescription" xml:space="preserve">
|
||||
<value>充分利用支持高动态范围的显示器获得更亮、更生动、更精细的画面</value>
|
||||
</data>
|
||||
<data name="ViewPageLaunchGameWindowsHDRHeader" xml:space="preserve">
|
||||
<value>Windows HDR</value>
|
||||
</data>
|
||||
<data name="ViewPageLoginHoyoverseUserHint" xml:space="preserve">
|
||||
<value>请输入你的 HoYoLab Uid</value>
|
||||
</data>
|
||||
@@ -2564,9 +2630,18 @@
|
||||
<data name="ViewSettingAllocConsoleHeader" xml:space="preserve">
|
||||
<value>调试控制台</value>
|
||||
</data>
|
||||
<data name="ViewSettingDeleteServerCacheFolderDescription" xml:space="preserve">
|
||||
<value>在启动游戏中转换服务器后会产生对应的游戏客户端文件用作缓存</value>
|
||||
</data>
|
||||
<data name="ViewSettingDeleteServerCacheFolderHeader" xml:space="preserve">
|
||||
<value>删除转换服务器缓存</value>
|
||||
</data>
|
||||
<data name="ViewSettingFolderViewOpenFolderAction" xml:space="preserve">
|
||||
<value>打开文件夹</value>
|
||||
</data>
|
||||
<data name="ViewSettingHeader" xml:space="preserve">
|
||||
<value>设置</value>
|
||||
</data>
|
||||
<data name="ViewSpiralAbyssAvatarAppearanceRankDescription" xml:space="preserve">
|
||||
<value>角色出场率 = 本层上阵该角色次数(层内重复出现只记一次)/ 深渊记录总数</value>
|
||||
</data>
|
||||
@@ -2733,198 +2808,204 @@
|
||||
<value>〓更新时间〓.+?&lt;t class=\"t_(?:gl|lc)\".*?&gt;(.*?)&lt;/t&gt;</value>
|
||||
</data>
|
||||
<data name="WebAnnouncementMatchVersionUpdateTitle" xml:space="preserve">
|
||||
<value>\d\.\d版本更新说明</value>
|
||||
<value>Версия \d\.\d Подробности обновления</value>
|
||||
</data>
|
||||
<data name="WebAnnouncementTimeDaysBeginFormat" xml:space="preserve">
|
||||
<value>{0} 天后开始</value>
|
||||
<value>Начало через {0} дней</value>
|
||||
</data>
|
||||
<data name="WebAnnouncementTimeDaysEndFormat" xml:space="preserve">
|
||||
<value>{0} 天后结束</value>
|
||||
<value>Закончится через {0} дней</value>
|
||||
</data>
|
||||
<data name="WebAnnouncementTimeHoursBeginFormat" xml:space="preserve">
|
||||
<value>{0} 小时后开始</value>
|
||||
<value>Начинается через {0} часов</value>
|
||||
</data>
|
||||
<data name="WebAnnouncementTimeHoursEndFormat" xml:space="preserve">
|
||||
<value>{0} 小时后结束</value>
|
||||
<value>Закончится через {0} часов</value>
|
||||
</data>
|
||||
<data name="WebBridgeShareCopyToClipboardFailed" xml:space="preserve">
|
||||
<value>Не удалось открыть буфер обмена</value>
|
||||
</data>
|
||||
<data name="WebBridgeShareCopyToClipboardSuccess" xml:space="preserve">
|
||||
<value>已复制到剪贴板</value>
|
||||
<value>Скопировано в буфер обмена</value>
|
||||
</data>
|
||||
<data name="WebDailyNoteArchonQuestStatusFinished" xml:space="preserve">
|
||||
<value>全部完成</value>
|
||||
<value>Завершенно</value>
|
||||
</data>
|
||||
<data name="WebDailyNoteArchonQuestStatusNotOpen" xml:space="preserve">
|
||||
<value>尚未开启</value>
|
||||
<value>Недоступно</value>
|
||||
</data>
|
||||
<data name="WebDailyNoteArchonQuestStatusOngoing" xml:space="preserve">
|
||||
<value>进行中</value>
|
||||
<value>В процессе</value>
|
||||
</data>
|
||||
<data name="WebDailyNoteAttendanceRewardStatusFinishedNonReward" xml:space="preserve">
|
||||
<value>已完成</value>
|
||||
<value>Выполнено</value>
|
||||
</data>
|
||||
<data name="WebDailyNoteAttendanceRewardStatusForbid" xml:space="preserve">
|
||||
<value>禁止领取</value>
|
||||
<value>Forbid to Claim</value>
|
||||
</data>
|
||||
<data name="WebDailyNoteAttendanceRewardStatusInvalid" xml:space="preserve">
|
||||
<value>无效</value>
|
||||
<value>Недействительно</value>
|
||||
</data>
|
||||
<data name="WebDailyNoteAttendanceRewardStatusTakenAward" xml:space="preserve">
|
||||
<value>已领取</value>
|
||||
<value>Получено</value>
|
||||
</data>
|
||||
<data name="WebDailyNoteAttendanceRewardStatusUnfinished" xml:space="preserve">
|
||||
<value>尚未完成</value>
|
||||
<value>Незаконченно</value>
|
||||
</data>
|
||||
<data name="WebDailyNoteAttendanceRewardStatusWaitTaken" xml:space="preserve">
|
||||
<value>等待领取</value>
|
||||
<value>Ready to claim</value>
|
||||
</data>
|
||||
<data name="WebDailyNoteExpeditionRemainHoursFormat" xml:space="preserve">
|
||||
<value>{0} 时</value>
|
||||
<value>{0} часов</value>
|
||||
</data>
|
||||
<data name="WebDailyNoteExpeditionRemainMinutesFormat" xml:space="preserve">
|
||||
<value>{0} 分</value>
|
||||
<value>{0} минут</value>
|
||||
</data>
|
||||
<data name="WebDailyNoteExtraTaskRewardNotAllowed" xml:space="preserve">
|
||||
<value>今日完成委托数量不足</value>
|
||||
<value>Incomplete Daily Commissions</value>
|
||||
</data>
|
||||
<data name="WebDailyNoteExtraTaskRewardNotTaken" xml:space="preserve">
|
||||
<value>「每日委托」奖励未领取</value>
|
||||
<value>Ежедневная награда не собрана</value>
|
||||
</data>
|
||||
<data name="WebDailyNoteExtraTaskRewardReceived" xml:space="preserve">
|
||||
<value>「每日委托」奖励已领取</value>
|
||||
<value>Ежедневная награда собрана</value>
|
||||
</data>
|
||||
<data name="WebDailyNoteHomeCoinRecoveryFormat" xml:space="preserve">
|
||||
<value>预计 {0} {1:HH:mm} 达到存储上限</value>
|
||||
<value>Будет заполнен через {0} {1:HH:mm}</value>
|
||||
</data>
|
||||
<data name="WebDailyNoteHomeLocked" xml:space="preserve">
|
||||
<value>尚未解锁洞天</value>
|
||||
<value>Serenitea Pot not Unlocked</value>
|
||||
</data>
|
||||
<data name="WebDailyNoteRecoveryTimeDay0" xml:space="preserve">
|
||||
<value>今天</value>
|
||||
<value>Сегодня</value>
|
||||
</data>
|
||||
<data name="WebDailyNoteRecoveryTimeDay1" xml:space="preserve">
|
||||
<value>明天</value>
|
||||
<value>Завтра</value>
|
||||
</data>
|
||||
<data name="WebDailyNoteRecoveryTimeDay2" xml:space="preserve">
|
||||
<value>后天</value>
|
||||
<value>Послезавтра</value>
|
||||
</data>
|
||||
<data name="WebDailyNoteRecoveryTimeDayFormat" xml:space="preserve">
|
||||
<value>{0} 天</value>
|
||||
<value>{0} день</value>
|
||||
</data>
|
||||
<data name="WebDailyNoteResinRecoveryCompleted" xml:space="preserve">
|
||||
<value>原粹树脂已完全恢复</value>
|
||||
<value>Смола заполнена</value>
|
||||
</data>
|
||||
<data name="WebDailyNoteResinRecoveryFormat" xml:space="preserve">
|
||||
<value>将于 {0} {1:HH:mm} 后全部恢复</value>
|
||||
<value>Будет пополнен через {0} {1:HH:mm}</value>
|
||||
</data>
|
||||
<data name="WebDailyNoteTransformerAppend" xml:space="preserve">
|
||||
<value>后可再次使用</value>
|
||||
<value>Снова готов к использованию после</value>
|
||||
</data>
|
||||
<data name="WebDailyNoteTransformerDaysFormat" xml:space="preserve">
|
||||
<value>{0} 天</value>
|
||||
<value>{0} дней</value>
|
||||
</data>
|
||||
<data name="WebDailyNoteTransformerHoursFormat" xml:space="preserve">
|
||||
<value>{0} 时</value>
|
||||
<value>{0} часов</value>
|
||||
</data>
|
||||
<data name="WebDailyNoteTransformerMinutesFormat" xml:space="preserve">
|
||||
<value>{0} 分</value>
|
||||
<value>{0} минут</value>
|
||||
</data>
|
||||
<data name="WebDailyNoteTransformerNotObtained" xml:space="preserve">
|
||||
<value>尚未获得</value>
|
||||
<value>Не получен</value>
|
||||
</data>
|
||||
<data name="WebDailyNoteTransformerNotObtainedDetail" xml:space="preserve">
|
||||
<value>尚未获得参量质变仪</value>
|
||||
<value>Transformer not obtained</value>
|
||||
</data>
|
||||
<data name="WebDailyNoteTransformerNotReached" xml:space="preserve">
|
||||
<value>冷却中</value>
|
||||
<value>Перезарядка</value>
|
||||
</data>
|
||||
<data name="WebDailyNoteTransformerReached" xml:space="preserve">
|
||||
<value>可使用</value>
|
||||
<value>Разрешить</value>
|
||||
</data>
|
||||
<data name="WebDailyNoteTransformerReady" xml:space="preserve">
|
||||
<value>已准备完成</value>
|
||||
<value>Готов</value>
|
||||
</data>
|
||||
<data name="WebDailyNoteTransformerSecondsFormat" xml:space="preserve">
|
||||
<value>{0} 秒</value>
|
||||
<value>{0} Секунд</value>
|
||||
</data>
|
||||
<data name="WebDailyNoteVerificationFailed" xml:space="preserve">
|
||||
<value>验证失败,请手动验证或前往「米游社-我的角色-实时便笺」页面查看</value>
|
||||
<value>验证失败,请手动验证或前往「米游社-旅行工具-原神战绩-实时便笺」页面查看</value>
|
||||
</data>
|
||||
<data name="WebEnkaResponseStatusCode400" xml:space="preserve">
|
||||
<value>错误的 UID 格式</value>
|
||||
<value>Неправильный формат UID</value>
|
||||
</data>
|
||||
<data name="WebEnkaResponseStatusCode404" xml:space="preserve">
|
||||
<value>角色 UID 不存在,请稍候再试</value>
|
||||
<value>Идентификатор UID не существует, пожалуйста, повторите попытку позже</value>
|
||||
</data>
|
||||
<data name="WebEnkaResponseStatusCode424" xml:space="preserve">
|
||||
<value>游戏维护中</value>
|
||||
<value>Игра на обновлении</value>
|
||||
</data>
|
||||
<data name="WebEnkaResponseStatusCode429" xml:space="preserve">
|
||||
<value>请求过快,请稍后再试</value>
|
||||
<value>Слишком много запросов, пожалуйста, повторите попытку позже</value>
|
||||
</data>
|
||||
<data name="WebEnkaResponseStatusCode500" xml:space="preserve">
|
||||
<value>服务器偶发错误</value>
|
||||
<value>Случайная ошибка сервера</value>
|
||||
</data>
|
||||
<data name="WebEnkaResponseStatusCode503" xml:space="preserve">
|
||||
<value>服务器严重错误</value>
|
||||
<value>Критическая ошибка сервера.</value>
|
||||
</data>
|
||||
<data name="WebEnkaResponseStatusCodeUnknown" xml:space="preserve">
|
||||
<value>未知的服务器错误</value>
|
||||
<value>Неизвестная ошибка сервера</value>
|
||||
</data>
|
||||
<data name="WebGachaConfigTypeAvatarEventWish" xml:space="preserve">
|
||||
<value>角色活动祈愿</value>
|
||||
<value>Молитва события персонажа</value>
|
||||
</data>
|
||||
<data name="WebGachaConfigTypeAvatarEventWish2" xml:space="preserve">
|
||||
<value>角色活动祈愿-2</value>
|
||||
<value>Молитва события персонажа - 2</value>
|
||||
</data>
|
||||
<data name="WebGachaConfigTypeNoviceWish" xml:space="preserve">
|
||||
<value>新手祈愿</value>
|
||||
<value>Молитва новичка</value>
|
||||
</data>
|
||||
<data name="WebGachaConfigTypePermanentWish" xml:space="preserve">
|
||||
<value>常驻祈愿</value>
|
||||
<value>Стандартная молитва</value>
|
||||
</data>
|
||||
<data name="WebGachaConfigTypeWeaponEventWish" xml:space="preserve">
|
||||
<value>武器活动祈愿</value>
|
||||
<value>Молитва события оружия</value>
|
||||
</data>
|
||||
<data name="WebGameResourcePathCopySucceed" xml:space="preserve">
|
||||
<value>下载链接复制成功</value>
|
||||
<value>Ссылка успешно скопирована</value>
|
||||
</data>
|
||||
<data name="WebHoyolabInvalidRegion" xml:space="preserve">
|
||||
<value>无效的服务器</value>
|
||||
<value>Ошибка сервера</value>
|
||||
</data>
|
||||
<data name="WebHoyolabInvalidUid" xml:space="preserve">
|
||||
<value>无效的 UID</value>
|
||||
<value>Invalid UID</value>
|
||||
</data>
|
||||
<data name="WebHoyolabRegionCNGF01" xml:space="preserve">
|
||||
<value>国服 官方服</value>
|
||||
<value>CN Server: Official</value>
|
||||
</data>
|
||||
<data name="WebHoyolabRegionCNQD01" xml:space="preserve">
|
||||
<value>国服 渠道服</value>
|
||||
<value>CN Server: bilibili</value>
|
||||
</data>
|
||||
<data name="WebHoyolabRegionOSASIA" xml:space="preserve">
|
||||
<value>国际服 亚服</value>
|
||||
<value>Oversea Server: Asian</value>
|
||||
</data>
|
||||
<data name="WebHoyolabRegionOSCHT" xml:space="preserve">
|
||||
<value>国际服 港澳台服</value>
|
||||
<value>Oversea Server: TW/HK/MU server</value>
|
||||
</data>
|
||||
<data name="WebHoyolabRegionOSEURO" xml:space="preserve">
|
||||
<value>国际服 欧服</value>
|
||||
<value>Oversea Server: EU</value>
|
||||
</data>
|
||||
<data name="WebHoyolabRegionOSUSA" xml:space="preserve">
|
||||
<value>国际服 美服</value>
|
||||
<value>Oversea Server: NA</value>
|
||||
</data>
|
||||
<data name="WebHutaoServiceUnAvailable" xml:space="preserve">
|
||||
<value>胡桃服务维护中</value>
|
||||
<value>Сервер Snap Hutao находится на техническом обслуживании</value>
|
||||
</data>
|
||||
<data name="WebIndexOrSpiralAbyssVerificationFailed" xml:space="preserve">
|
||||
<value>验证失败,请手动验证或前往「米游社-我的角色」页面查看</value>
|
||||
<value>验证失败,请手动验证或前往「米游社-旅行工具-原神战绩」页面查看</value>
|
||||
</data>
|
||||
<data name="WebResponseFormat" xml:space="preserve">
|
||||
<value>状态:{0} | 信息:{1}</value>
|
||||
<value>Код возврата: {0} | Сообщение: {1}</value>
|
||||
</data>
|
||||
<data name="WebResponseRefreshCookieHintFormat" xml:space="preserve">
|
||||
<value>请刷新 Cookie,原始消息:{0}</value>
|
||||
<value>Пожалуйста, обновите файл cookie, необработанное сообщение: {0}</value>
|
||||
</data>
|
||||
<data name="WebResponseRequestExceptionFormat" xml:space="preserve">
|
||||
<value>[{0}] 中的 [{1}] 网络请求异常,请稍后再试</value>
|
||||
<value>[{1}] исключение сетевого запроса в [{0}] пожалуйста, повторите попытку позже</value>
|
||||
</data>
|
||||
<data name="WindowIdentifyMonitorHeader" xml:space="preserve">
|
||||
<value>显示器编号</value>
|
||||
</data>
|
||||
</root>
|
||||
@@ -124,7 +124,7 @@
|
||||
<value>胡桃Dev {0} [管理员]</value>
|
||||
</data>
|
||||
<data name="AppElevatedNameAndVersion" xml:space="preserve">
|
||||
<value>胡桃 {0} [管理员]</value>
|
||||
<value>胡桃 {0} [系統管理員]</value>
|
||||
</data>
|
||||
<data name="AppName" xml:space="preserve">
|
||||
<value>胡桃</value>
|
||||
@@ -148,7 +148,7 @@
|
||||
<value>無效的 Uri</value>
|
||||
</data>
|
||||
<data name="ControlImageCompositionImageHttpRequest" xml:space="preserve">
|
||||
<value>获取HTTP{0}</value>
|
||||
<value>HTTP GET {0}</value>
|
||||
</data>
|
||||
<data name="ControlImageCompositionImageSystemException" xml:space="preserve">
|
||||
<value>應用 CompositionImage 的源時發生異常</value>
|
||||
@@ -196,7 +196,7 @@
|
||||
<value>歡迎使用胡桃</value>
|
||||
</data>
|
||||
<data name="LaunchGameTitle" xml:space="preserve">
|
||||
<value>選擇賬號並啓動</value>
|
||||
<value>選擇帳號並啟動</value>
|
||||
</data>
|
||||
<data name="ModelBindingAvatarPropertyWeaponAffixFormat" 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,11 +870,23 @@
|
||||
<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>解鎖 FPS 上限失敗</value>
|
||||
</data>
|
||||
<data name="ServiceGameLaunchExecutionGameIsRunning" xml:space="preserve">
|
||||
<value>遊戲進程運行中</value>
|
||||
</data>
|
||||
<data name="ServiceGameLaunchExecutionGamePathNotValid" xml:space="preserve">
|
||||
<value>請選擇遊戲路徑</value>
|
||||
</data>
|
||||
<data name="ServiceGameLaunchExecutionGameResourceQueryIndexFailed" xml:space="preserve">
|
||||
<value>下载游戏资源索引失败: {0}</value>
|
||||
</data>
|
||||
<data name="ServiceGameLaunchPhaseProcessExited" xml:space="preserve">
|
||||
<value>遊戲進程已退出</value>
|
||||
</data>
|
||||
@@ -885,13 +897,13 @@
|
||||
<value>遊戲進程已啟動</value>
|
||||
</data>
|
||||
<data name="ServiceGameLaunchPhaseUnlockFpsFailed" xml:space="preserve">
|
||||
<value>解鎖幀率上限失敗,正在結束遊戲進程</value>
|
||||
<value>解鎖 FPS 上限失敗,正在結束遊戲行程</value>
|
||||
</data>
|
||||
<data name="ServiceGameLaunchPhaseUnlockFpsSucceed" xml:space="preserve">
|
||||
<value>解鎖幀率上限成功</value>
|
||||
<value>解鎖 FPS 上限成功</value>
|
||||
</data>
|
||||
<data name="ServiceGameLaunchPhaseUnlockingFps" xml:space="preserve">
|
||||
<value>正在嘗試解鎖幀率上限</value>
|
||||
<value>正在嘗試解鎖 FPS 上限</value>
|
||||
</data>
|
||||
<data name="ServiceGameLaunchPhaseWaitingProcessExit" xml:space="preserve">
|
||||
<value>等待遊戲進程退出</value>
|
||||
@@ -1271,6 +1283,12 @@
|
||||
<data name="ViewDialogQRCodeTitle" xml:space="preserve">
|
||||
<value>使用米遊社掃描 QR 碼</value>
|
||||
</data>
|
||||
<data name="ViewDialogReconfirmTextHeader" xml:space="preserve">
|
||||
<value>為防止你在無意間啟用,請輸入正在啟用的功能開關的<b>標題名稱</b></value>
|
||||
</data>
|
||||
<data name="ViewDialogReconfirmTitle" xml:space="preserve">
|
||||
<value>你正在啟用一個危險功能</value>
|
||||
</data>
|
||||
<data name="ViewDialogSettingDeleteUserDataContent" xml:space="preserve">
|
||||
<value>該操作是不可逆,所有用戶登錄狀態會遺失</value>
|
||||
</data>
|
||||
@@ -1292,6 +1310,9 @@
|
||||
<data name="ViewDialogUserTitle" xml:space="preserve">
|
||||
<value>設定 Cookie</value>
|
||||
</data>
|
||||
<data name="ViewFeedbackHeader" xml:space="preserve">
|
||||
<value>回饋中心</value>
|
||||
</data>
|
||||
<data name="ViewGachaLogHeader" xml:space="preserve">
|
||||
<value>祈願記錄</value>
|
||||
</data>
|
||||
@@ -1541,6 +1562,9 @@
|
||||
<data name="ViewModelLaunchGameEnsureGameResourceFail" xml:space="preserve">
|
||||
<value>切換伺服器失敗</value>
|
||||
</data>
|
||||
<data name="ViewModelLaunchGameIdentifyMonitorsAction" xml:space="preserve">
|
||||
<value>識別顯示器</value>
|
||||
</data>
|
||||
<data name="ViewModelLaunchGameMultiChannelReadFail" xml:space="preserve">
|
||||
<value>無法讀取遊戲設定檔案: {0},可能是檔案不存在或權限不足</value>
|
||||
</data>
|
||||
@@ -1554,7 +1578,7 @@
|
||||
<value>切換帳號失敗</value>
|
||||
</data>
|
||||
<data name="ViewModelLaunchGameUnableToSwitchUidAttachedGameAccount" xml:space="preserve">
|
||||
<value>无法选择UID [{0}] 对应的账号 [{1}],该账号不属于当前服务器</value>
|
||||
<value>無法選擇 UID [{0}] 對應的帳號 [{1}],該帳號不屬於當前伺服器</value>
|
||||
</data>
|
||||
<data name="ViewModelSettingActionComplete" xml:space="preserve">
|
||||
<value>操作完成</value>
|
||||
@@ -1574,6 +1598,12 @@
|
||||
<data name="ViewModelSettingCreateDesktopShortcutFailed" xml:space="preserve">
|
||||
<value>創建桌面捷徑失敗</value>
|
||||
</data>
|
||||
<data name="ViewModelSettingDeleteServerCacheFolderContent" xml:space="preserve">
|
||||
<value>後續轉換會重新下載所需檔案,確定要刪除嗎?</value>
|
||||
</data>
|
||||
<data name="ViewModelSettingDeleteServerCacheFolderTitle" xml:space="preserve">
|
||||
<value>刪除轉換伺服器遊戲用戶端暫存</value>
|
||||
</data>
|
||||
<data name="ViewModelSettingFolderSizeDescription" xml:space="preserve">
|
||||
<value>已使用磁碟空間:{0}</value>
|
||||
</data>
|
||||
@@ -1656,7 +1686,7 @@
|
||||
<value>遊戲公告</value>
|
||||
</data>
|
||||
<data name="ViewPageAnnouncementViewDetails" xml:space="preserve">
|
||||
<value>查看详情</value>
|
||||
<value>檢視詳情</value>
|
||||
</data>
|
||||
<data name="ViewPageAvatarPropertyArtifactScore" xml:space="preserve">
|
||||
<value>聖遺物評分</value>
|
||||
@@ -1698,7 +1728,7 @@
|
||||
<value>同步角色天賦信息</value>
|
||||
</data>
|
||||
<data name="ViewPageAvatarPropertyRefreshFromHoyolabGameRecord" xml:space="preserve">
|
||||
<value>從 HoYoLAB - 戰績中同步</value>
|
||||
<value>从米游社原神战绩同步</value>
|
||||
</data>
|
||||
<data name="ViewPageAvatarPropertyRefreshFromHoyolabGameRecordDescription" xml:space="preserve">
|
||||
<value>同步角色天賦外的大部分信息</value>
|
||||
@@ -1823,6 +1853,36 @@
|
||||
<data name="ViewPageDailyNoteVerify" xml:space="preserve">
|
||||
<value>驗證當前用戶與角色</value>
|
||||
</data>
|
||||
<data name="ViewPageFeedbackAutoSuggestBoxPlaceholder" xml:space="preserve">
|
||||
<value>搜尋問題與建議</value>
|
||||
</data>
|
||||
<data name="ViewPageFeedBackBasicInformation" xml:space="preserve">
|
||||
<value>基本資訊</value>
|
||||
</data>
|
||||
<data name="ViewPageFeedbackCommonLinksHeader" xml:space="preserve">
|
||||
<value>常用連結</value>
|
||||
</data>
|
||||
<data name="ViewPageFeedbackEngageWithUsDescription" xml:space="preserve">
|
||||
<value>與我們密切聯繫</value>
|
||||
</data>
|
||||
<data name="ViewPageFeedbackFeatureGuideHeader" xml:space="preserve">
|
||||
<value>功能指南</value>
|
||||
</data>
|
||||
<data name="ViewPageFeedbackGithubIssuesDescription" xml:space="preserve">
|
||||
<value>我們總是優先處理 GitHub 上的問題</value>
|
||||
</data>
|
||||
<data name="ViewPageFeedbackRoadmapDescription" xml:space="preserve">
|
||||
<value>開發路線規劃</value>
|
||||
</data>
|
||||
<data name="ViewPageFeedbackSearchResultPlaceholderTitle" xml:space="preserve">
|
||||
<value>暫無搜尋結果</value>
|
||||
</data>
|
||||
<data name="ViewPageFeedbackServerStatusDescription" xml:space="preserve">
|
||||
<value>胡桃服务可用性监控</value>
|
||||
</data>
|
||||
<data name="ViewPageFeedbackServerStatusHeader" xml:space="preserve">
|
||||
<value>胡桃服务</value>
|
||||
</data>
|
||||
<data name="ViewPageGachaLogAggressiveRefresh" xml:space="preserve">
|
||||
<value>全量式重整</value>
|
||||
</data>
|
||||
@@ -2016,7 +2076,7 @@
|
||||
<value>至少需要八個字元</value>
|
||||
</data>
|
||||
<data name="ViewPageHutaoPassportRegisterHeader" xml:space="preserve">
|
||||
<value>註冊</value>
|
||||
<value>建立帳號</value>
|
||||
</data>
|
||||
<data name="ViewPageHutaoPassportResetPasswordHeader" xml:space="preserve">
|
||||
<value>重設密碼</value>
|
||||
@@ -2112,10 +2172,10 @@
|
||||
<value>在指定的屏幕上運行</value>
|
||||
</data>
|
||||
<data name="ViewPageLaunchGameMonitorsHeader" xml:space="preserve">
|
||||
<value>螢幕</value>
|
||||
<value>顯示器</value>
|
||||
</data>
|
||||
<data name="ViewPageLaunchGameMultipleInstancesDescription" xml:space="preserve">
|
||||
<value>同時運行多個游戲用戶端</value>
|
||||
<value>同時運行多個遊戲用戶端</value>
|
||||
</data>
|
||||
<data name="ViewPageLaunchGameMultipleInstancesHeader" xml:space="preserve">
|
||||
<value>多用戶端</value>
|
||||
@@ -2148,7 +2208,7 @@
|
||||
<value>預下載</value>
|
||||
</data>
|
||||
<data name="ViewPageLaunchGameSelectGamePath" xml:space="preserve">
|
||||
<value>选择游戏路径</value>
|
||||
<value>選擇遊戲路徑</value>
|
||||
</data>
|
||||
<data name="ViewPageLaunchGameSwitchAccountAttachUidNull" xml:space="preserve">
|
||||
<value>該用戶尚未綁定即時便箋通知 UID</value>
|
||||
@@ -2163,7 +2223,7 @@
|
||||
<value>檢測</value>
|
||||
</data>
|
||||
<data name="ViewPageLaunchGameSwitchAccountHeader" xml:space="preserve">
|
||||
<value>檢測賬號</value>
|
||||
<value>檢測帳號</value>
|
||||
</data>
|
||||
<data name="ViewPageLaunchGameSwitchAccountRemoveToolTip" xml:space="preserve">
|
||||
<value>刪除</value>
|
||||
@@ -2172,7 +2232,7 @@
|
||||
<value>重新命名</value>
|
||||
</data>
|
||||
<data name="ViewPageLaunchGameSwitchSchemeDescription" xml:space="preserve">
|
||||
<value>切換游戲伺服器(國服/渠道服/國際服)</value>
|
||||
<value>切換遊戲伺服器(陸服/渠道服/國際服)</value>
|
||||
</data>
|
||||
<data name="ViewPageLaunchGameSwitchSchemeHeader" xml:space="preserve">
|
||||
<value>伺服器</value>
|
||||
@@ -2192,6 +2252,12 @@
|
||||
<data name="ViewPageLaunchGameUnlockFpsOn" xml:space="preserve">
|
||||
<value>啟用</value>
|
||||
</data>
|
||||
<data name="ViewPageLaunchGameWindowsHDRDescription" xml:space="preserve">
|
||||
<value>充分利用支援 HDR 的顯示器以獲得更亮、更生動、更精細的畫面</value>
|
||||
</data>
|
||||
<data name="ViewPageLaunchGameWindowsHDRHeader" xml:space="preserve">
|
||||
<value>Windows HDR</value>
|
||||
</data>
|
||||
<data name="ViewPageLoginHoyoverseUserHint" xml:space="preserve">
|
||||
<value>請輸入您的 HoYoLAB UID</value>
|
||||
</data>
|
||||
@@ -2214,7 +2280,7 @@
|
||||
<value>外觀</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingApperanceLanguageDescription" xml:space="preserve">
|
||||
<value>設定系統語言</value>
|
||||
<value>設定呈現語言</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingApperanceLanguageHeader" xml:space="preserve">
|
||||
<value>語言</value>
|
||||
@@ -2226,7 +2292,7 @@
|
||||
<value>背景材質</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingCacheFolderDescription" xml:space="preserve">
|
||||
<value>圖片緩存存放在此</value>
|
||||
<value>圖片暫存存放在此</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingCacheFolderHeader" xml:space="preserve">
|
||||
<value>暫存檔案夾</value>
|
||||
@@ -2238,7 +2304,7 @@
|
||||
<value>創建</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingCreateDesktopShortcutDescription" xml:space="preserve">
|
||||
<value>在桌面上創建預設以管理員方式啟動的快捷方式</value>
|
||||
<value>在桌面上創建預設以系統管理員方式啟動的捷徑</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingCreateDesktopShortcutHeader" xml:space="preserve">
|
||||
<value>創建快捷方式</value>
|
||||
@@ -2283,13 +2349,13 @@
|
||||
<value>設備 IP</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingElevatedModeDescription" xml:space="preserve">
|
||||
<value>管理员模式会影响部分功能的可用性与行为</value>
|
||||
<value>系統管理員模式會影響部分功能的可用性與行為</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingElevatedModeHeader" xml:space="preserve">
|
||||
<value>管理员模式</value>
|
||||
<value>系統管理員模式</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingElevatedModeRestartAction" xml:space="preserve">
|
||||
<value>以管理员身份重启</value>
|
||||
<value>以系統管理員身分重啟動</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingEmptyHistoryVisibleDescription" xml:space="preserve">
|
||||
<value>在祈願紀錄頁面顯示或隱藏無記錄的歷史祈願活動</value>
|
||||
@@ -2313,7 +2379,7 @@
|
||||
<value>祈願記錄</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingGameHeader" xml:space="preserve">
|
||||
<value>游戲</value>
|
||||
<value>遊戲</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingGeetestCustomUrlAction" xml:space="preserve">
|
||||
<value>配置</value>
|
||||
@@ -2328,10 +2394,10 @@
|
||||
<value>無感驗證</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingHomeAnnouncementRegionDescription" xml:space="preserve">
|
||||
<value>选择想要获取公告的游戏服务器</value>
|
||||
<value>選擇想要獲取公告的遊戲伺服器</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingHomeAnnouncementRegionHeader" xml:space="preserve">
|
||||
<value>公告所属服务器</value>
|
||||
<value>公告所屬伺服器</value>
|
||||
</data>
|
||||
<data name="ViewpageSettingHomeCardDescription" xml:space="preserve">
|
||||
<value>管理主頁儀表板中的卡片</value>
|
||||
@@ -2361,7 +2427,7 @@
|
||||
<value>主頁</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingHutaoPassportDangerZoneDescription" xml:space="preserve">
|
||||
<value>三思而后行</value>
|
||||
<value>三思而後行</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingHutaoPassportDangerZoneHeader" xml:space="preserve">
|
||||
<value>危險操作</value>
|
||||
@@ -2442,10 +2508,10 @@
|
||||
<value>設置路徑</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingSetGamePathHeader" xml:space="preserve">
|
||||
<value>游戲路徑</value>
|
||||
<value>遊戲路徑</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingSetGamePathHint" xml:space="preserve">
|
||||
<value>設置游戲路徑時,請選擇游戲本體(YuanShen.exe 或 GenshinImpact.exe) 而不是啓動器(launcher.exe)</value>
|
||||
<value>設置遊戲路徑時,請選擇遊戲本體(YuanShen.exe 或 GenshinImpact.exe) 而不是啟動器(launcher.exe)</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingShellExperienceHeader" xml:space="preserve">
|
||||
<value>Shell 體驗</value>
|
||||
@@ -2556,7 +2622,7 @@
|
||||
<value>登入失敗,請重新登入</value>
|
||||
</data>
|
||||
<data name="ViewServiceHutaoUserLoginOrRegisterHint" xml:space="preserve">
|
||||
<value>立即登入或註冊</value>
|
||||
<value>立即登入或建立帳號</value>
|
||||
</data>
|
||||
<data name="ViewSettingAllocConsoleDescription" xml:space="preserve">
|
||||
<value>控制胡桃啟動時是否開啟主控台,重新啟動後生效</value>
|
||||
@@ -2564,9 +2630,18 @@
|
||||
<data name="ViewSettingAllocConsoleHeader" xml:space="preserve">
|
||||
<value>偵錯主控台</value>
|
||||
</data>
|
||||
<data name="ViewSettingDeleteServerCacheFolderDescription" xml:space="preserve">
|
||||
<value>在啟動遊戲中轉換伺服器後會產生對應的遊戲用戶端檔案用作暫存</value>
|
||||
</data>
|
||||
<data name="ViewSettingDeleteServerCacheFolderHeader" xml:space="preserve">
|
||||
<value>刪除轉換伺服器暫存</value>
|
||||
</data>
|
||||
<data name="ViewSettingFolderViewOpenFolderAction" xml:space="preserve">
|
||||
<value>打開檔案夾</value>
|
||||
</data>
|
||||
<data name="ViewSettingHeader" xml:space="preserve">
|
||||
<value>設定</value>
|
||||
</data>
|
||||
<data name="ViewSpiralAbyssAvatarAppearanceRankDescription" xml:space="preserve">
|
||||
<value>角色出場率 = 本層上陣該角色次數(層內重複出現只記一次)/ 深淵記錄總數</value>
|
||||
</data>
|
||||
@@ -2634,10 +2709,10 @@
|
||||
<value>上傳資料</value>
|
||||
</data>
|
||||
<data name="ViewTitileUpdatePackageReadyContent" xml:space="preserve">
|
||||
<value>是否立即安装?</value>
|
||||
<value>是否立即安裝?</value>
|
||||
</data>
|
||||
<data name="ViewTitileUpdatePackageReadyTitle" xml:space="preserve">
|
||||
<value>胡桃 {0} 版本已准备就绪</value>
|
||||
<value>胡桃 {0} 版本已準備就緒</value>
|
||||
</data>
|
||||
<data name="ViewTitleAutoClicking" xml:space="preserve">
|
||||
<value>自動連續點按</value>
|
||||
@@ -2673,10 +2748,10 @@
|
||||
<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>
|
||||
@@ -2685,10 +2760,10 @@
|
||||
<value>尚未登入</value>
|
||||
</data>
|
||||
<data name="ViewUserRefreshCookieTokenSuccess" xml:space="preserve">
|
||||
<value>重整 CookieToken 成功</value>
|
||||
<value>更新 CookieToken 成功</value>
|
||||
</data>
|
||||
<data name="ViewUserRefreshCookieTokenWarning" xml:space="preserve">
|
||||
<value>刷新 CookieToken 失敗</value>
|
||||
<value>更新 CookieToken 失敗</value>
|
||||
</data>
|
||||
<data name="ViewUserRemoveAction" xml:space="preserve">
|
||||
<value>移除用戶</value>
|
||||
@@ -2700,7 +2775,7 @@
|
||||
<value>用戶</value>
|
||||
</data>
|
||||
<data name="ViewWelcomeBase" xml:space="preserve">
|
||||
<value>我們將爲你下載最基本的圖像資源</value>
|
||||
<value>我們將為你下載最基本的圖像資源</value>
|
||||
</data>
|
||||
<data name="ViewWelcomeBody" xml:space="preserve">
|
||||
<value>你可以繼續使用電腦,絲毫不受影響</value>
|
||||
@@ -2747,6 +2822,9 @@
|
||||
<data name="WebAnnouncementTimeHoursEndFormat" xml:space="preserve">
|
||||
<value>{0} 小時後結束</value>
|
||||
</data>
|
||||
<data name="WebBridgeShareCopyToClipboardFailed" xml:space="preserve">
|
||||
<value>打開剪貼簿失敗</value>
|
||||
</data>
|
||||
<data name="WebBridgeShareCopyToClipboardSuccess" xml:space="preserve">
|
||||
<value>已複製到剪貼簿</value>
|
||||
</data>
|
||||
@@ -2754,10 +2832,10 @@
|
||||
<value>全部完成</value>
|
||||
</data>
|
||||
<data name="WebDailyNoteArchonQuestStatusNotOpen" xml:space="preserve">
|
||||
<value>尚未开启</value>
|
||||
<value>尚未開啟</value>
|
||||
</data>
|
||||
<data name="WebDailyNoteArchonQuestStatusOngoing" xml:space="preserve">
|
||||
<value>进行中</value>
|
||||
<value>進行中</value>
|
||||
</data>
|
||||
<data name="WebDailyNoteAttendanceRewardStatusFinishedNonReward" xml:space="preserve">
|
||||
<value>已完成</value>
|
||||
@@ -2847,7 +2925,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>
|
||||
@@ -2889,42 +2967,45 @@
|
||||
<value>下載連結複製成功</value>
|
||||
</data>
|
||||
<data name="WebHoyolabInvalidRegion" xml:space="preserve">
|
||||
<value>无效的服务器</value>
|
||||
<value>無效的伺服器</value>
|
||||
</data>
|
||||
<data name="WebHoyolabInvalidUid" xml:space="preserve">
|
||||
<value>無效的 UID</value>
|
||||
</data>
|
||||
<data name="WebHoyolabRegionCNGF01" xml:space="preserve">
|
||||
<value>国服 官方服</value>
|
||||
<value>陸服 官方服</value>
|
||||
</data>
|
||||
<data name="WebHoyolabRegionCNQD01" xml:space="preserve">
|
||||
<value>国服 渠道服</value>
|
||||
<value>陸服 渠道服</value>
|
||||
</data>
|
||||
<data name="WebHoyolabRegionOSASIA" xml:space="preserve">
|
||||
<value>国际服 亚服</value>
|
||||
<value>國際服 亞服</value>
|
||||
</data>
|
||||
<data name="WebHoyolabRegionOSCHT" xml:space="preserve">
|
||||
<value>国际服 港澳台服</value>
|
||||
<value>國際服 港澳台服</value>
|
||||
</data>
|
||||
<data name="WebHoyolabRegionOSEURO" xml:space="preserve">
|
||||
<value>国际服 欧服</value>
|
||||
<value>國際服 歐服</value>
|
||||
</data>
|
||||
<data name="WebHoyolabRegionOSUSA" xml:space="preserve">
|
||||
<value>国际服 美服</value>
|
||||
<value>國際服 美服</value>
|
||||
</data>
|
||||
<data name="WebHutaoServiceUnAvailable" xml:space="preserve">
|
||||
<value>胡桃服務維護中</value>
|
||||
</data>
|
||||
<data name="WebIndexOrSpiralAbyssVerificationFailed" xml:space="preserve">
|
||||
<value>驗證失敗,請手動驗證或前往「米遊社-我的角色」頁面查看</value>
|
||||
<value>验证失败,请手动验证或前往「米游社-旅行工具-原神战绩」页面查看</value>
|
||||
</data>
|
||||
<data name="WebResponseFormat" xml:space="preserve">
|
||||
<value>狀態:{0} | 信息:{1}</value>
|
||||
</data>
|
||||
<data name="WebResponseRefreshCookieHintFormat" xml:space="preserve">
|
||||
<value>請刷新 Cookie,原始消息:{0}</value>
|
||||
<value>請更新 Cookie,原始消息:{0}</value>
|
||||
</data>
|
||||
<data name="WebResponseRequestExceptionFormat" xml:space="preserve">
|
||||
<value>[{0}] 中的 [{1}] 網路請求異常,請稍後再試</value>
|
||||
</data>
|
||||
<data name="WindowIdentifyMonitorHeader" xml:space="preserve">
|
||||
<value>顯示器編號</value>
|
||||
</data>
|
||||
</root>
|
||||
BIN
src/Snap.Hutao/Snap.Hutao/Resource/Navigation/Feedback.png
Normal file
BIN
src/Snap.Hutao/Snap.Hutao/Resource/Navigation/Feedback.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 4.0 KiB |
@@ -6,7 +6,6 @@ using Snap.Hutao.Model;
|
||||
using Snap.Hutao.Model.Entity;
|
||||
using Snap.Hutao.Service.Abstraction;
|
||||
using Snap.Hutao.Web.Hoyolab;
|
||||
using System.Globalization;
|
||||
|
||||
namespace Snap.Hutao.Service;
|
||||
|
||||
@@ -16,7 +15,6 @@ internal sealed partial class AppOptions : DbStoreOptions
|
||||
{
|
||||
private bool? isEmptyHistoryWishVisible;
|
||||
private BackdropType? backdropType;
|
||||
private CultureInfo? currentCulture;
|
||||
private Region? region;
|
||||
private string? geetestCustomCompositeUrl;
|
||||
|
||||
@@ -26,7 +24,7 @@ internal sealed partial class AppOptions : DbStoreOptions
|
||||
set => SetOption(ref isEmptyHistoryWishVisible, SettingEntry.IsEmptyHistoryWishVisible, value);
|
||||
}
|
||||
|
||||
public List<NameValue<BackdropType>> BackdropTypes { get; } = CollectionsNameValue.FromEnum<BackdropType>();
|
||||
public List<NameValue<BackdropType>> BackdropTypes { get; } = CollectionsNameValue.FromEnum<BackdropType>(type => type >= 0);
|
||||
|
||||
public BackdropType BackdropType
|
||||
{
|
||||
@@ -34,14 +32,6 @@ internal sealed partial class AppOptions : DbStoreOptions
|
||||
set => SetOption(ref backdropType, SettingEntry.SystemBackdropType, value, value => value.ToStringOrEmpty());
|
||||
}
|
||||
|
||||
public List<NameValue<CultureInfo>> Cultures { get; } = SupportedCultures.Get();
|
||||
|
||||
public CultureInfo CurrentCulture
|
||||
{
|
||||
get => GetOption(ref currentCulture, SettingEntry.Culture, CultureInfo.GetCultureInfo, CultureInfo.CurrentCulture);
|
||||
set => SetOption(ref currentCulture, SettingEntry.Culture, value, value => value.Name);
|
||||
}
|
||||
|
||||
public Lazy<List<NameValue<Region>>> LazyRegions { get; } = new(KnownRegions.Get);
|
||||
|
||||
public Region Region
|
||||
@@ -55,6 +45,4 @@ internal sealed partial class AppOptions : DbStoreOptions
|
||||
get => GetOption(ref geetestCustomCompositeUrl, SettingEntry.GeetestCustomCompositeUrl);
|
||||
set => SetOption(ref geetestCustomCompositeUrl, SettingEntry.GeetestCustomCompositeUrl, value);
|
||||
}
|
||||
|
||||
internal CultureInfo PreviousCulture { get; set; } = default!;
|
||||
}
|
||||
@@ -3,17 +3,11 @@
|
||||
|
||||
using Snap.Hutao.Model;
|
||||
using Snap.Hutao.Web.Hoyolab;
|
||||
using System.Globalization;
|
||||
|
||||
namespace Snap.Hutao.Service;
|
||||
|
||||
internal static class AppOptionsExtension
|
||||
{
|
||||
public static NameValue<CultureInfo>? GetCurrentCultureForSelectionOrDefault(this AppOptions appOptions)
|
||||
{
|
||||
return appOptions.Cultures.SingleOrDefault(c => c.Value == appOptions.CurrentCulture);
|
||||
}
|
||||
|
||||
public static NameValue<Region>? GetCurrentRegionForSelectionOrDefault(this AppOptions appOptions)
|
||||
{
|
||||
return appOptions.LazyRegions.Value.SingleOrDefault(c => c.Value.Value == appOptions.Region.Value);
|
||||
|
||||
43
src/Snap.Hutao/Snap.Hutao/Service/CultureOptions.cs
Normal file
43
src/Snap.Hutao/Snap.Hutao/Service/CultureOptions.cs
Normal file
@@ -0,0 +1,43 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Snap.Hutao.Model;
|
||||
using Snap.Hutao.Model.Entity;
|
||||
using Snap.Hutao.Service.Abstraction;
|
||||
using System.Globalization;
|
||||
|
||||
namespace Snap.Hutao.Service;
|
||||
|
||||
[ConstructorGenerated(CallBaseConstructor = true)]
|
||||
[Injection(InjectAs.Singleton)]
|
||||
internal sealed partial class CultureOptions : DbStoreOptions
|
||||
{
|
||||
private CultureInfo? currentCulture;
|
||||
private string? localeName;
|
||||
private string? languageCode;
|
||||
|
||||
public List<NameValue<CultureInfo>> Cultures { get; } = SupportedCultures.Get();
|
||||
|
||||
public CultureInfo CurrentCulture
|
||||
{
|
||||
get => GetOption(ref currentCulture, SettingEntry.Culture, CultureInfo.GetCultureInfo, CultureInfo.CurrentCulture);
|
||||
set => SetOption(ref currentCulture, SettingEntry.Culture, value, value => value.Name);
|
||||
}
|
||||
|
||||
public CultureInfo SystemCulture { get; set; } = default!;
|
||||
|
||||
public string LocaleName { get => localeName ??= CultureOptionsExtension.GetLocaleName(CurrentCulture); }
|
||||
|
||||
public string LanguageCode
|
||||
{
|
||||
get
|
||||
{
|
||||
if (languageCode is null && !LocaleNames.TryGetLanguageCodeFromLocaleName(LocaleName, out languageCode))
|
||||
{
|
||||
throw new KeyNotFoundException($"Invalid localeName: '{LocaleName}'");
|
||||
}
|
||||
|
||||
return languageCode;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,24 +1,19 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Snap.Hutao.Model;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
|
||||
namespace Snap.Hutao.Service.Metadata;
|
||||
namespace Snap.Hutao.Service;
|
||||
|
||||
internal static class MetadataOptionsExtension
|
||||
internal static class CultureOptionsExtension
|
||||
{
|
||||
public static string GetLocalizedLocalFile(this MetadataOptions options, string fileNameWithExtension)
|
||||
public static NameValue<CultureInfo>? GetCurrentCultureForSelectionOrDefault(this CultureOptions options)
|
||||
{
|
||||
return Path.Combine(options.LocalizedDataFolder, fileNameWithExtension);
|
||||
return options.Cultures.SingleOrDefault(c => c.Value == options.CurrentCulture);
|
||||
}
|
||||
|
||||
public static string GetLocalizedRemoteFile(this MetadataOptions options, string fileNameWithExtension)
|
||||
{
|
||||
return Web.HutaoEndpoints.Metadata(options.LocaleName, fileNameWithExtension);
|
||||
}
|
||||
|
||||
public static bool LanguageCodeFitsCurrentLocale(this MetadataOptions options, string? languageCode)
|
||||
public static bool LanguageCodeFitsCurrentLocale(this CultureOptions options, string? languageCode)
|
||||
{
|
||||
if (string.IsNullOrEmpty(languageCode))
|
||||
{
|
||||
@@ -30,6 +25,11 @@ internal static class MetadataOptionsExtension
|
||||
return GetLocaleName(cultureInfo) == options.LocaleName;
|
||||
}
|
||||
|
||||
public static string GetLanguageCodeForDocumentationSearch(this CultureOptions options)
|
||||
{
|
||||
return LocaleNames.GetLanguageCodeForDocumentationSearchFromLocaleName(options.LocaleName);
|
||||
}
|
||||
|
||||
internal static string GetLocaleName(CultureInfo cultureInfo)
|
||||
{
|
||||
while (true)
|
||||
@@ -58,7 +58,11 @@ internal sealed partial class DailyNoteOptions : DbStoreOptions
|
||||
{
|
||||
if (SelectedRefreshTime is not null)
|
||||
{
|
||||
scheduleTaskInterop.RegisterForDailyNoteRefresh(SelectedRefreshTime.Value);
|
||||
if (!scheduleTaskInterop.RegisterForDailyNoteRefresh(SelectedRefreshTime.Value))
|
||||
{
|
||||
serviceProvider.GetRequiredService<IInfoBarService>().Warning(SH.ViewModelDailyNoteRegisterTaskFail);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
|
||||
@@ -172,6 +172,8 @@ internal static class DiscordController
|
||||
|
||||
private static async ValueTask DiscordRunCallbacksAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
int notRunningCounter = 0;
|
||||
|
||||
using (PeriodicTimer timer = new(TimeSpan.FromMilliseconds(500)))
|
||||
{
|
||||
try
|
||||
@@ -190,7 +192,18 @@ internal static class DiscordController
|
||||
DiscordResult result = DiscordCoreRunRunCallbacks();
|
||||
if (result is not DiscordResult.Ok)
|
||||
{
|
||||
System.Diagnostics.Debug.WriteLine($"[Discord.GameSDK ERROR]:{result:D} {result}");
|
||||
if (result is DiscordResult.NotRunning)
|
||||
{
|
||||
if (++notRunningCounter > 20)
|
||||
{
|
||||
Stop();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
notRunningCounter = 0;
|
||||
System.Diagnostics.Debug.WriteLine($"[Discord.GameSDK ERROR]:{result:D} {result}");
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (SEHException ex)
|
||||
|
||||
@@ -11,14 +11,14 @@ internal sealed partial class DiscordService : IDiscordService, IDisposable
|
||||
{
|
||||
private readonly RuntimeOptions runtimeOptions;
|
||||
|
||||
public async ValueTask SetPlayingActivity(bool isOversea)
|
||||
public async ValueTask SetPlayingActivityAsync(bool isOversea)
|
||||
{
|
||||
_ = isOversea
|
||||
? await DiscordController.SetPlayingGenshinImpactAsync().ConfigureAwait(false)
|
||||
: await DiscordController.SetPlayingYuanShenAsync().ConfigureAwait(false);
|
||||
}
|
||||
|
||||
public async ValueTask SetNormalActivity()
|
||||
public async ValueTask SetNormalActivityAsync()
|
||||
{
|
||||
_ = await DiscordController.SetDefaultActivityAsync(runtimeOptions.AppLaunchTime).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ namespace Snap.Hutao.Service.Discord;
|
||||
|
||||
internal interface IDiscordService
|
||||
{
|
||||
ValueTask SetNormalActivity();
|
||||
ValueTask SetNormalActivityAsync();
|
||||
|
||||
ValueTask SetPlayingActivity(bool isOversea);
|
||||
ValueTask SetPlayingActivityAsync(bool isOversea);
|
||||
}
|
||||
@@ -226,7 +226,7 @@ internal sealed partial class GachaLogService : IGachaLogService
|
||||
break;
|
||||
}
|
||||
|
||||
await Delay.Random(1000, 2000).ConfigureAwait(false);
|
||||
await Delay.RandomMilliSeconds(1000, 2000).ConfigureAwait(false);
|
||||
}
|
||||
while (true);
|
||||
|
||||
@@ -238,7 +238,7 @@ internal sealed partial class GachaLogService : IGachaLogService
|
||||
// save items for each queryType
|
||||
token.ThrowIfCancellationRequested();
|
||||
fetchContext.SaveItems();
|
||||
await Delay.Random(1000, 2000).ConfigureAwait(false);
|
||||
await Delay.RandomMilliSeconds(1000, 2000).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
return new(!fetchContext.FetchStatus.AuthKeyTimeout, fetchContext.TargetArchive);
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Snap.Hutao.Factory.ContentDialog;
|
||||
using Snap.Hutao.Service.Metadata;
|
||||
using Snap.Hutao.View.Dialog;
|
||||
using System.Collections.Specialized;
|
||||
using System.Web;
|
||||
@@ -18,7 +17,7 @@ namespace Snap.Hutao.Service.GachaLog.QueryProvider;
|
||||
internal sealed partial class GachaLogQueryManualInputProvider : IGachaLogQueryProvider
|
||||
{
|
||||
private readonly IContentDialogFactory contentDialogFactory;
|
||||
private readonly MetadataOptions metadataOptions;
|
||||
private readonly CultureOptions cultureOptions;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async ValueTask<ValueResult<bool, GachaLogQuery>> GetQueryAsync()
|
||||
@@ -33,13 +32,13 @@ internal sealed partial class GachaLogQueryManualInputProvider : IGachaLogQueryP
|
||||
if (query.TryGetSingleValue("auth_appid", out string? appId) && appId is "webview_gacha")
|
||||
{
|
||||
string? queryLanguageCode = query["lang"];
|
||||
if (metadataOptions.LanguageCodeFitsCurrentLocale(queryLanguageCode))
|
||||
if (cultureOptions.LanguageCodeFitsCurrentLocale(queryLanguageCode))
|
||||
{
|
||||
return new(true, new(queryString));
|
||||
}
|
||||
else
|
||||
{
|
||||
string message = SH.FormatServiceGachaLogUrlProviderUrlLanguageNotMatchCurrentLocale(queryLanguageCode, metadataOptions.LanguageCode);
|
||||
string message = SH.FormatServiceGachaLogUrlProviderUrlLanguageNotMatchCurrentLocale(queryLanguageCode, cultureOptions.LanguageCode);
|
||||
return new(false, message);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Snap.Hutao.Service.Metadata;
|
||||
using Snap.Hutao.Service.User;
|
||||
using Snap.Hutao.ViewModel.User;
|
||||
using Snap.Hutao.Web.Hoyolab.Takumi.Binding;
|
||||
@@ -20,7 +19,7 @@ namespace Snap.Hutao.Service.GachaLog.QueryProvider;
|
||||
internal sealed partial class GachaLogQuerySTokenProvider : IGachaLogQueryProvider
|
||||
{
|
||||
private readonly BindingClient2 bindingClient2;
|
||||
private readonly MetadataOptions metadataOptions;
|
||||
private readonly CultureOptions cultureOptions;
|
||||
private readonly IUserService userService;
|
||||
|
||||
/// <inheritdoc/>
|
||||
@@ -38,7 +37,7 @@ internal sealed partial class GachaLogQuerySTokenProvider : IGachaLogQueryProvid
|
||||
|
||||
if (authkeyResponse.IsOk())
|
||||
{
|
||||
return new(true, new(ComposeQueryString(data, authkeyResponse.Data, metadataOptions.LanguageCode)));
|
||||
return new(true, new(ComposeQueryString(data, authkeyResponse.Data, cultureOptions.LanguageCode)));
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
|
||||
using Snap.Hutao.Core.IO;
|
||||
using Snap.Hutao.Service.Game;
|
||||
using Snap.Hutao.Service.Metadata;
|
||||
using System.Collections.Specialized;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
@@ -22,7 +21,7 @@ namespace Snap.Hutao.Service.GachaLog.QueryProvider;
|
||||
internal sealed partial class GachaLogQueryWebCacheProvider : IGachaLogQueryProvider
|
||||
{
|
||||
private readonly IGameServiceFacade gameService;
|
||||
private readonly MetadataOptions metadataOptions;
|
||||
private readonly CultureOptions cultureOptions;
|
||||
|
||||
/// <summary>
|
||||
/// 获取缓存文件路径
|
||||
@@ -90,12 +89,12 @@ internal sealed partial class GachaLogQueryWebCacheProvider : IGachaLogQueryProv
|
||||
NameValueCollection query = HttpUtility.ParseQueryString(result.TrimEnd("#/log"));
|
||||
string? queryLanguageCode = query["lang"];
|
||||
|
||||
if (metadataOptions.LanguageCodeFitsCurrentLocale(queryLanguageCode))
|
||||
if (cultureOptions.LanguageCodeFitsCurrentLocale(queryLanguageCode))
|
||||
{
|
||||
return new(true, new(result));
|
||||
}
|
||||
|
||||
string message = SH.FormatServiceGachaLogUrlProviderUrlLanguageNotMatchCurrentLocale(queryLanguageCode, metadataOptions.LanguageCode);
|
||||
string message = SH.FormatServiceGachaLogUrlProviderUrlLanguageNotMatchCurrentLocale(queryLanguageCode, cultureOptions.LanguageCode);
|
||||
return new(false, message);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
using Snap.Hutao.Core;
|
||||
using Snap.Hutao.Model.Entity;
|
||||
using Snap.Hutao.Model.InterChange.GachaLog;
|
||||
using Snap.Hutao.Service.Metadata;
|
||||
|
||||
namespace Snap.Hutao.Service.GachaLog;
|
||||
|
||||
@@ -17,7 +16,7 @@ internal sealed partial class UIGFExportService : IUIGFExportService
|
||||
{
|
||||
private readonly IGachaLogDbService gachaLogDbService;
|
||||
private readonly RuntimeOptions runtimeOptions;
|
||||
private readonly MetadataOptions metadataOptions;
|
||||
private readonly CultureOptions cultureOptions;
|
||||
private readonly ITaskContext taskContext;
|
||||
|
||||
/// <inheritdoc/>
|
||||
@@ -31,7 +30,7 @@ internal sealed partial class UIGFExportService : IUIGFExportService
|
||||
|
||||
UIGF uigf = new()
|
||||
{
|
||||
Info = UIGFInfo.From(runtimeOptions, metadataOptions, archive.Uid),
|
||||
Info = UIGFInfo.From(runtimeOptions, cultureOptions, archive.Uid),
|
||||
List = list,
|
||||
};
|
||||
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
using Snap.Hutao.Core.ExceptionService;
|
||||
using Snap.Hutao.Model.Entity;
|
||||
using Snap.Hutao.Model.InterChange.GachaLog;
|
||||
using Snap.Hutao.Service.Metadata;
|
||||
using Snap.Hutao.Web.Hoyolab.Hk4e.Event.GachaInfo;
|
||||
using System.Collections.ObjectModel;
|
||||
|
||||
@@ -18,7 +17,7 @@ namespace Snap.Hutao.Service.GachaLog;
|
||||
internal sealed partial class UIGFImportService : IUIGFImportService
|
||||
{
|
||||
private readonly ILogger<UIGFImportService> logger;
|
||||
private readonly MetadataOptions metadataOptions;
|
||||
private readonly CultureOptions cultureOptions;
|
||||
private readonly IGachaLogDbService gachaLogDbService;
|
||||
private readonly ITaskContext taskContext;
|
||||
|
||||
@@ -37,9 +36,9 @@ internal sealed partial class UIGFImportService : IUIGFImportService
|
||||
// v2.1 only support CHS
|
||||
if (version is UIGFVersion.Major2Minor2OrLower)
|
||||
{
|
||||
if (!metadataOptions.LanguageCodeFitsCurrentLocale(uigf.Info.Language))
|
||||
if (!cultureOptions.LanguageCodeFitsCurrentLocale(uigf.Info.Language))
|
||||
{
|
||||
string message = SH.FormatServiceGachaUIGFImportLanguageNotMatch(uigf.Info.Language, metadataOptions.LanguageCode);
|
||||
string message = SH.FormatServiceGachaUIGFImportLanguageNotMatch(uigf.Info.Language, cultureOptions.LanguageCode);
|
||||
ThrowHelper.InvalidOperation(message);
|
||||
}
|
||||
|
||||
|
||||
@@ -29,10 +29,9 @@ internal readonly struct ChannelOptions
|
||||
/// </summary>
|
||||
public readonly bool IsOversea;
|
||||
|
||||
/// <summary>
|
||||
/// 配置文件路径 当不为 null 时则存在文件读写问题
|
||||
/// </summary>
|
||||
public readonly string? ConfigFilePath;
|
||||
public readonly ChannelOptionsErrorKind ErrorKind;
|
||||
|
||||
public readonly string? FilePath;
|
||||
|
||||
public ChannelOptions(ChannelType channel, SubChannelType subChannel, bool isOversea)
|
||||
{
|
||||
@@ -48,15 +47,20 @@ internal readonly struct ChannelOptions
|
||||
IsOversea = isOversea;
|
||||
}
|
||||
|
||||
private ChannelOptions(bool isOversea, string? configFilePath)
|
||||
private ChannelOptions(ChannelOptionsErrorKind errorKind, string? filePath)
|
||||
{
|
||||
IsOversea = isOversea;
|
||||
ConfigFilePath = configFilePath;
|
||||
ErrorKind = errorKind;
|
||||
FilePath = filePath;
|
||||
}
|
||||
|
||||
public static ChannelOptions FileNotFound(bool isOversea, string configFilePath)
|
||||
public static ChannelOptions ConfigurationFileNotFound(string filePath)
|
||||
{
|
||||
return new(isOversea, configFilePath);
|
||||
return new(ChannelOptionsErrorKind.ConfigurationFileNotFound, filePath);
|
||||
}
|
||||
|
||||
public static ChannelOptions GamePathNullOrEmpty()
|
||||
{
|
||||
return new(ChannelOptionsErrorKind.GamePathNullOrEmpty, string.Empty);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
namespace Snap.Hutao.Service.Game.Configuration;
|
||||
|
||||
internal enum ChannelOptionsErrorKind
|
||||
{
|
||||
None,
|
||||
ConfigurationFileNotFound,
|
||||
GamePathNullOrEmpty,
|
||||
}
|
||||
@@ -1,11 +1,9 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Snap.Hutao.Core.ExceptionService;
|
||||
using Snap.Hutao.Core.IO.Ini;
|
||||
using Snap.Hutao.Service.Game.Scheme;
|
||||
using System.IO;
|
||||
using static Snap.Hutao.Service.Game.GameConstants;
|
||||
|
||||
namespace Snap.Hutao.Service.Game.Configuration;
|
||||
|
||||
@@ -17,84 +15,22 @@ internal sealed partial class GameChannelOptionsService : IGameChannelOptionsSer
|
||||
|
||||
public ChannelOptions GetChannelOptions()
|
||||
{
|
||||
if (!launchOptions.TryGetGamePathAndFilePathByName(ConfigFileName, out string gamePath, out string? configPath))
|
||||
if (!launchOptions.TryGetGameFileSystem(out GameFileSystem? gameFileSystem))
|
||||
{
|
||||
throw ThrowHelper.InvalidOperation($"Invalid game path: {gamePath}");
|
||||
return ChannelOptions.GamePathNullOrEmpty();
|
||||
}
|
||||
|
||||
bool isOversea = LaunchScheme.ExecutableIsOversea(Path.GetFileName(gamePath));
|
||||
bool isOversea = LaunchScheme.ExecutableIsOversea(gameFileSystem.GameFileName);
|
||||
|
||||
if (!File.Exists(configPath))
|
||||
if (!File.Exists(gameFileSystem.GameConfigFilePath))
|
||||
{
|
||||
return ChannelOptions.FileNotFound(isOversea, configPath);
|
||||
return ChannelOptions.ConfigurationFileNotFound(gameFileSystem.GameConfigFilePath);
|
||||
}
|
||||
|
||||
using (FileStream stream = File.OpenRead(configPath))
|
||||
{
|
||||
List<IniParameter> parameters = IniSerializer.Deserialize(stream).OfType<IniParameter>().ToList();
|
||||
string? channel = parameters.FirstOrDefault(p => p.Key == ChannelOptions.ChannelName)?.Value;
|
||||
string? subChannel = parameters.FirstOrDefault(p => p.Key == ChannelOptions.SubChannelName)?.Value;
|
||||
List<IniParameter> parameters = IniSerializer.DeserializeFromFile(gameFileSystem.GameConfigFilePath).OfType<IniParameter>().ToList();
|
||||
string? channel = parameters.FirstOrDefault(p => p.Key == ChannelOptions.ChannelName)?.Value;
|
||||
string? subChannel = parameters.FirstOrDefault(p => p.Key == ChannelOptions.SubChannelName)?.Value;
|
||||
|
||||
return new(channel, subChannel, isOversea);
|
||||
}
|
||||
}
|
||||
|
||||
public bool SetChannelOptions(LaunchScheme scheme)
|
||||
{
|
||||
if (!launchOptions.TryGetGamePathAndFilePathByName(ConfigFileName, out string gamePath, out string? configPath))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
List<IniElement> elements = default!;
|
||||
try
|
||||
{
|
||||
using (FileStream readStream = File.OpenRead(configPath))
|
||||
{
|
||||
elements = [.. IniSerializer.Deserialize(readStream)];
|
||||
}
|
||||
}
|
||||
catch (FileNotFoundException ex)
|
||||
{
|
||||
ThrowHelper.GameFileOperation(SH.FormatServiceGameSetMultiChannelConfigFileNotFound(configPath), ex);
|
||||
}
|
||||
catch (DirectoryNotFoundException ex)
|
||||
{
|
||||
ThrowHelper.GameFileOperation(SH.FormatServiceGameSetMultiChannelConfigFileNotFound(configPath), ex);
|
||||
}
|
||||
catch (UnauthorizedAccessException ex)
|
||||
{
|
||||
ThrowHelper.GameFileOperation(SH.ServiceGameSetMultiChannelUnauthorizedAccess, ex);
|
||||
}
|
||||
|
||||
bool changed = false;
|
||||
|
||||
foreach (IniElement element in elements)
|
||||
{
|
||||
if (element is IniParameter parameter)
|
||||
{
|
||||
if (parameter.Key is ChannelOptions.ChannelName)
|
||||
{
|
||||
changed = parameter.Set(scheme.Channel.ToString("D")) || changed;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (parameter.Key is ChannelOptions.SubChannelName)
|
||||
{
|
||||
changed = parameter.Set(scheme.SubChannel.ToString("D")) || changed;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (changed)
|
||||
{
|
||||
using (FileStream writeStream = File.Create(configPath))
|
||||
{
|
||||
IniSerializer.Serialize(writeStream, elements);
|
||||
}
|
||||
}
|
||||
|
||||
return changed;
|
||||
return new(channel, subChannel, isOversea);
|
||||
}
|
||||
}
|
||||
@@ -1,13 +1,9 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Snap.Hutao.Service.Game.Scheme;
|
||||
|
||||
namespace Snap.Hutao.Service.Game.Configuration;
|
||||
|
||||
internal interface IGameChannelOptionsService
|
||||
{
|
||||
ChannelOptions GetChannelOptions();
|
||||
|
||||
bool SetChannelOptions(LaunchScheme scheme);
|
||||
}
|
||||
@@ -10,6 +10,7 @@ namespace Snap.Hutao.Service.Game;
|
||||
internal static class GameConstants
|
||||
{
|
||||
public const string ConfigFileName = "config.ini";
|
||||
public const string PCGameSDKFilePath = @"YuanShen_Data\Plugins\PCGameSDK.dll";
|
||||
public const string YuanShenFileName = "YuanShen.exe";
|
||||
public const string YuanShenFileNameUpper = "YUANSHEN.EXE";
|
||||
public const string GenshinImpactFileName = "GenshinImpact.exe";
|
||||
|
||||
39
src/Snap.Hutao/Snap.Hutao/Service/Game/GameFileSystem.cs
Normal file
39
src/Snap.Hutao/Snap.Hutao/Service/Game/GameFileSystem.cs
Normal file
@@ -0,0 +1,39 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using System.IO;
|
||||
|
||||
namespace Snap.Hutao.Service.Game;
|
||||
|
||||
internal sealed class GameFileSystem
|
||||
{
|
||||
private readonly string gameFilePath;
|
||||
|
||||
private string? gameFileName;
|
||||
private string? gameDirectory;
|
||||
private string? gameConfigFilePath;
|
||||
private string? pcGameSDKFilePath;
|
||||
|
||||
public GameFileSystem(string gameFilePath)
|
||||
{
|
||||
this.gameFilePath = gameFilePath;
|
||||
}
|
||||
|
||||
public string GameFilePath { get => gameFilePath; }
|
||||
|
||||
public string GameFileName { get => gameFileName ??= Path.GetFileName(gameFilePath); }
|
||||
|
||||
public string GameDirectory
|
||||
{
|
||||
get
|
||||
{
|
||||
gameDirectory ??= Path.GetDirectoryName(gameFilePath);
|
||||
ArgumentException.ThrowIfNullOrEmpty(gameDirectory);
|
||||
return gameDirectory;
|
||||
}
|
||||
}
|
||||
|
||||
public string GameConfigFilePath { get => gameConfigFilePath ??= Path.Combine(GameDirectory, GameConstants.ConfigFileName); }
|
||||
|
||||
public string PCGameSDKFilePath { get => pcGameSDKFilePath ??= Path.Combine(GameDirectory, GameConstants.PCGameSDKFilePath); }
|
||||
}
|
||||
@@ -5,10 +5,8 @@ using Snap.Hutao.Model.Entity;
|
||||
using Snap.Hutao.Model.Entity.Primitive;
|
||||
using Snap.Hutao.Service.Game.Account;
|
||||
using Snap.Hutao.Service.Game.Configuration;
|
||||
using Snap.Hutao.Service.Game.Package;
|
||||
using Snap.Hutao.Service.Game.Launching.Handler;
|
||||
using Snap.Hutao.Service.Game.PathAbstraction;
|
||||
using Snap.Hutao.Service.Game.Process;
|
||||
using Snap.Hutao.Service.Game.Scheme;
|
||||
using System.Collections.ObjectModel;
|
||||
|
||||
namespace Snap.Hutao.Service.Game;
|
||||
@@ -23,8 +21,6 @@ internal sealed partial class GameServiceFacade : IGameServiceFacade
|
||||
{
|
||||
private readonly IGameChannelOptionsService gameChannelOptionsService;
|
||||
private readonly IGameAccountService gameAccountService;
|
||||
private readonly IGameProcessService gameProcessService;
|
||||
private readonly IGamePackageService gamePackageService;
|
||||
private readonly IGamePathService gamePathService;
|
||||
|
||||
/// <inheritdoc/>
|
||||
@@ -45,12 +41,6 @@ internal sealed partial class GameServiceFacade : IGameServiceFacade
|
||||
return gameChannelOptionsService.GetChannelOptions();
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool SetChannelOptions(LaunchScheme scheme)
|
||||
{
|
||||
return gameChannelOptionsService.SetChannelOptions(scheme);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public ValueTask<GameAccount?> DetectGameAccountAsync(SchemeType scheme)
|
||||
{
|
||||
@@ -63,12 +53,6 @@ internal sealed partial class GameServiceFacade : IGameServiceFacade
|
||||
return gameAccountService.DetectCurrentGameAccount(scheme);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool SetGameAccount(GameAccount account)
|
||||
{
|
||||
return gameAccountService.SetGameAccount(account);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void AttachGameAccountToUid(GameAccount gameAccount, string uid)
|
||||
{
|
||||
@@ -90,18 +74,6 @@ internal sealed partial class GameServiceFacade : IGameServiceFacade
|
||||
/// <inheritdoc/>
|
||||
public bool IsGameRunning()
|
||||
{
|
||||
return gameProcessService.IsGameRunning();
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public ValueTask LaunchAsync(IProgress<LaunchStatus> progress)
|
||||
{
|
||||
return gameProcessService.LaunchAsync(progress);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public ValueTask<bool> EnsureGameResourceAsync(LaunchScheme launchScheme, IProgress<PackageReplaceStatus> progress)
|
||||
{
|
||||
return gamePackageService.EnsureGameResourceAsync(launchScheme, progress);
|
||||
return LaunchExecutionEnsureGameNotRunningHandler.IsGameRunning(out _);
|
||||
}
|
||||
}
|
||||
@@ -4,8 +4,6 @@
|
||||
using Snap.Hutao.Model.Entity;
|
||||
using Snap.Hutao.Model.Entity.Primitive;
|
||||
using Snap.Hutao.Service.Game.Configuration;
|
||||
using Snap.Hutao.Service.Game.Package;
|
||||
using Snap.Hutao.Service.Game.Scheme;
|
||||
using System.Collections.ObjectModel;
|
||||
|
||||
namespace Snap.Hutao.Service.Game;
|
||||
@@ -49,8 +47,6 @@ internal interface IGameServiceFacade
|
||||
/// <returns>是否正在运行</returns>
|
||||
bool IsGameRunning();
|
||||
|
||||
ValueTask LaunchAsync(IProgress<LaunchStatus> progress);
|
||||
|
||||
/// <summary>
|
||||
/// 异步修改游戏账号名称
|
||||
/// </summary>
|
||||
@@ -65,27 +61,5 @@ internal interface IGameServiceFacade
|
||||
/// <returns>任务</returns>
|
||||
ValueTask RemoveGameAccountAsync(GameAccount gameAccount);
|
||||
|
||||
/// <summary>
|
||||
/// 替换游戏资源
|
||||
/// </summary>
|
||||
/// <param name="launchScheme">目标启动方案</param>
|
||||
/// <param name="progress">进度</param>
|
||||
/// <returns>是否替换成功</returns>
|
||||
ValueTask<bool> EnsureGameResourceAsync(LaunchScheme launchScheme, IProgress<PackageReplaceStatus> progress);
|
||||
|
||||
/// <summary>
|
||||
/// 修改注册表中的账号信息
|
||||
/// </summary>
|
||||
/// <param name="account">账号</param>
|
||||
/// <returns>是否设置成功</returns>
|
||||
bool SetGameAccount(GameAccount account);
|
||||
|
||||
/// <summary>
|
||||
/// 设置多通道值
|
||||
/// </summary>
|
||||
/// <param name="scheme">方案</param>
|
||||
/// <returns>是否更改了ini文件</returns>
|
||||
bool SetChannelOptions(LaunchScheme scheme);
|
||||
|
||||
GameAccount? DetectCurrentGameAccount(SchemeType scheme);
|
||||
}
|
||||
@@ -218,8 +218,7 @@ internal sealed class LaunchOptions : DbStoreOptions
|
||||
{
|
||||
if (SetProperty(ref selectedAspectRatio, value) && value is AspectRatio aspectRatio)
|
||||
{
|
||||
ScreenWidth = (int)aspectRatio.Width;
|
||||
ScreenHeight = (int)aspectRatio.Height;
|
||||
(ScreenWidth, ScreenHeight) = ((int)aspectRatio.Width, (int)aspectRatio.Height);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,70 +3,25 @@
|
||||
|
||||
using Snap.Hutao.Service.Game.PathAbstraction;
|
||||
using System.Collections.Immutable;
|
||||
using System.IO;
|
||||
|
||||
namespace Snap.Hutao.Service.Game;
|
||||
|
||||
internal static class LaunchOptionsExtension
|
||||
{
|
||||
public static bool TryGetGamePathAndGameDirectory(this LaunchOptions options, out string gamePath, [NotNullWhen(true)] out string? gameDirectory)
|
||||
{
|
||||
gamePath = options.GamePath;
|
||||
|
||||
gameDirectory = Path.GetDirectoryName(gamePath);
|
||||
if (string.IsNullOrEmpty(gameDirectory))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public static bool TryGetGameDirectoryAndGameFileName(this LaunchOptions options, [NotNullWhen(true)] out string? gameDirectory, [NotNullWhen(true)] out string? gameFileName)
|
||||
public static bool TryGetGameFileSystem(this LaunchOptions options, [NotNullWhen(true)] out GameFileSystem? fileSystem)
|
||||
{
|
||||
string gamePath = options.GamePath;
|
||||
|
||||
gameDirectory = Path.GetDirectoryName(gamePath);
|
||||
if (string.IsNullOrEmpty(gameDirectory))
|
||||
{
|
||||
gameFileName = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
gameFileName = Path.GetFileName(gamePath);
|
||||
if (string.IsNullOrEmpty(gameFileName))
|
||||
if (string.IsNullOrEmpty(gamePath))
|
||||
{
|
||||
fileSystem = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
fileSystem = new GameFileSystem(gamePath);
|
||||
return true;
|
||||
}
|
||||
|
||||
public static bool TryGetGamePathAndGameFileName(this LaunchOptions options, out string gamePath, [NotNullWhen(true)] out string? gameFileName)
|
||||
{
|
||||
gamePath = options.GamePath;
|
||||
|
||||
gameFileName = Path.GetFileName(gamePath);
|
||||
if (string.IsNullOrEmpty(gameFileName))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public static bool TryGetGamePathAndFilePathByName(this LaunchOptions options, string fileName, out string gamePath, [NotNullWhen(true)] out string? filePath)
|
||||
{
|
||||
if (options.TryGetGamePathAndGameDirectory(out gamePath, out string? gameDirectory))
|
||||
{
|
||||
filePath = Path.Combine(gameDirectory, fileName);
|
||||
return true;
|
||||
}
|
||||
|
||||
filePath = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
public static ImmutableList<GamePathEntry> GetGamePathEntries(this LaunchOptions options, out GamePathEntry? entry)
|
||||
{
|
||||
string gamePath = options.GamePath;
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
namespace Snap.Hutao.Service.Game.Launching.Handler;
|
||||
|
||||
internal sealed class LaunchExecutionEnsureGameNotRunningHandler : ILaunchExecutionDelegateHandler
|
||||
{
|
||||
public static bool IsGameRunning([NotNullWhen(true)] out System.Diagnostics.Process? runningProcess)
|
||||
{
|
||||
// GetProcesses once and manually loop is O(n)
|
||||
foreach (ref readonly System.Diagnostics.Process process in System.Diagnostics.Process.GetProcesses().AsSpan())
|
||||
{
|
||||
if (string.Equals(process.ProcessName, GameConstants.YuanShenProcessName, StringComparison.OrdinalIgnoreCase) ||
|
||||
string.Equals(process.ProcessName, GameConstants.GenshinImpactProcessName, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
runningProcess = process;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
runningProcess = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
public async ValueTask OnExecutionAsync(LaunchExecutionContext context, LaunchExecutionDelegate next)
|
||||
{
|
||||
if (IsGameRunning(out System.Diagnostics.Process? process))
|
||||
{
|
||||
context.Logger.LogInformation("Game process detected, id: {Id}", process.Id);
|
||||
|
||||
context.Result.Kind = LaunchExecutionResultKind.GameProcessRunning;
|
||||
context.Result.ErrorMessage = SH.ServiceGameLaunchExecutionGameIsRunning;
|
||||
return;
|
||||
}
|
||||
|
||||
await next().ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,160 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Microsoft.Win32.SafeHandles;
|
||||
using Snap.Hutao.Control.Extension;
|
||||
using Snap.Hutao.Factory.ContentDialog;
|
||||
using Snap.Hutao.Factory.Progress;
|
||||
using Snap.Hutao.Model.Intrinsic;
|
||||
using Snap.Hutao.Service.Game.Package;
|
||||
using Snap.Hutao.View.Dialog;
|
||||
using Snap.Hutao.Web.Hoyolab.SdkStatic.Hk4e.Launcher;
|
||||
using Snap.Hutao.Web.Response;
|
||||
using System.IO;
|
||||
|
||||
namespace Snap.Hutao.Service.Game.Launching.Handler;
|
||||
|
||||
internal sealed class LaunchExecutionEnsureGameResourceHandler : ILaunchExecutionDelegateHandler
|
||||
{
|
||||
public async ValueTask OnExecutionAsync(LaunchExecutionContext context, LaunchExecutionDelegate next)
|
||||
{
|
||||
if (!context.TryGetGameFileSystem(out GameFileSystem? gameFileSystem))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (ShouldConvert(context, gameFileSystem))
|
||||
{
|
||||
IServiceProvider serviceProvider = context.ServiceProvider;
|
||||
IContentDialogFactory contentDialogFactory = serviceProvider.GetRequiredService<IContentDialogFactory>();
|
||||
IProgressFactory progressFactory = serviceProvider.GetRequiredService<IProgressFactory>();
|
||||
|
||||
LaunchGamePackageConvertDialog dialog = await contentDialogFactory.CreateInstanceAsync<LaunchGamePackageConvertDialog>().ConfigureAwait(false);
|
||||
IProgress<PackageConvertStatus> convertProgress = progressFactory.CreateForMainThread<PackageConvertStatus>(state => dialog.State = state);
|
||||
|
||||
using (await dialog.BlockAsync(context.TaskContext).ConfigureAwait(false))
|
||||
{
|
||||
if (!await EnsureGameResourceAsync(context, gameFileSystem, convertProgress).ConfigureAwait(false))
|
||||
{
|
||||
// context.Result is set in EnsureGameResourceAsync
|
||||
return;
|
||||
}
|
||||
|
||||
await context.TaskContext.SwitchToMainThreadAsync();
|
||||
context.UpdateGamePathEntry();
|
||||
}
|
||||
}
|
||||
|
||||
await next().ConfigureAwait(false);
|
||||
}
|
||||
|
||||
private static bool ShouldConvert(LaunchExecutionContext context, GameFileSystem gameFileSystem)
|
||||
{
|
||||
// Configuration file changed
|
||||
if (context.ChannelOptionsChanged)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// Executable name not match
|
||||
if (!context.Scheme.ExecutableMatches(gameFileSystem.GameFileName))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!context.Scheme.IsOversea)
|
||||
{
|
||||
// [It's Bilibili channel xor PCGameSDK.dll exists] means we need to convert
|
||||
if (context.Scheme.Channel is ChannelType.Bili ^ File.Exists(gameFileSystem.PCGameSDKFilePath))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static async ValueTask<bool> EnsureGameResourceAsync(LaunchExecutionContext context, GameFileSystem gameFileSystem, IProgress<PackageConvertStatus> progress)
|
||||
{
|
||||
string gameFolder = gameFileSystem.GameDirectory;
|
||||
string gameFileName = gameFileSystem.GameFileName;
|
||||
|
||||
context.Logger.LogInformation("Game folder: {GameFolder}", gameFolder);
|
||||
|
||||
if (!CheckDirectoryPermissions(gameFolder))
|
||||
{
|
||||
context.Result.Kind = LaunchExecutionResultKind.GameDirectoryInsufficientPermissions;
|
||||
context.Result.ErrorMessage = SH.ServiceGameEnsureGameResourceInsufficientDirectoryPermissions;
|
||||
return false;
|
||||
}
|
||||
|
||||
progress.Report(new(SH.ServiceGameEnsureGameResourceQueryResourceInformation));
|
||||
|
||||
ResourceClient resourceClient = context.ServiceProvider.GetRequiredService<ResourceClient>();
|
||||
Response<GameResource> response = await resourceClient.GetResourceAsync(context.Scheme).ConfigureAwait(false);
|
||||
|
||||
if (!response.TryGetDataWithoutUINotification(out GameResource? resource))
|
||||
{
|
||||
context.Result.Kind = LaunchExecutionResultKind.GameResourceIndexQueryInvalidResponse;
|
||||
context.Result.ErrorMessage = SH.FormatServiceGameLaunchExecutionGameResourceQueryIndexFailed(response);
|
||||
return false;
|
||||
}
|
||||
|
||||
PackageConverter packageConverter = context.ServiceProvider.GetRequiredService<PackageConverter>();
|
||||
|
||||
if (!context.Scheme.ExecutableMatches(gameFileName))
|
||||
{
|
||||
if (!await packageConverter.EnsureGameResourceAsync(context.Scheme, resource, gameFolder, progress).ConfigureAwait(false))
|
||||
{
|
||||
context.Result.Kind = LaunchExecutionResultKind.GameResourcePackageConvertInternalError;
|
||||
context.Result.ErrorMessage = SH.ViewModelLaunchGameEnsureGameResourceFail;
|
||||
return false;
|
||||
}
|
||||
|
||||
// We need to change the gamePath if we switched.
|
||||
string executableName = context.Scheme.IsOversea ? GameConstants.GenshinImpactFileName : GameConstants.YuanShenFileName;
|
||||
|
||||
await context.TaskContext.SwitchToMainThreadAsync();
|
||||
context.Options.UpdateGamePathAndRefreshEntries(Path.Combine(gameFolder, executableName));
|
||||
}
|
||||
|
||||
await packageConverter.EnsureDeprecatedFilesAndSdkAsync(resource, gameFolder).ConfigureAwait(false);
|
||||
return true;
|
||||
}
|
||||
|
||||
private static bool CheckDirectoryPermissions(string folder)
|
||||
{
|
||||
// Program Files has special permissions limitation.
|
||||
string programFiles = Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles);
|
||||
if (folder.StartsWith(programFiles, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
string tempFilePath = Path.Combine(folder, $"{Guid.NewGuid():N}.tmp");
|
||||
string tempFilePathMove = Path.Combine(folder, $"{Guid.NewGuid():N}.tmp");
|
||||
|
||||
// Test create file
|
||||
using (SafeFileHandle handle = File.OpenHandle(tempFilePath, FileMode.CreateNew, FileAccess.ReadWrite, FileShare.None, preallocationSize: 32 * 1024))
|
||||
{
|
||||
// Test write file
|
||||
RandomAccess.Write(handle, "SNAP HUTAO DIRECTORY PERMISSION CHECK"u8, 0);
|
||||
RandomAccess.FlushToDisk(handle);
|
||||
}
|
||||
|
||||
// Test move file
|
||||
File.Move(tempFilePath, tempFilePathMove);
|
||||
|
||||
// Test delete file
|
||||
File.Delete(tempFilePathMove);
|
||||
|
||||
return true;
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
namespace Snap.Hutao.Service.Game.Launching.Handler;
|
||||
|
||||
internal sealed class LaunchExecutionEnsureSchemeHandler : ILaunchExecutionDelegateHandler
|
||||
{
|
||||
public async ValueTask OnExecutionAsync(LaunchExecutionContext context, LaunchExecutionDelegate next)
|
||||
{
|
||||
if (context.Scheme is null)
|
||||
{
|
||||
context.Result.Kind = LaunchExecutionResultKind.NoActiveScheme;
|
||||
context.Result.ErrorMessage = SH.ViewModelLaunchGameSchemeNotSelected;
|
||||
return;
|
||||
}
|
||||
|
||||
context.Logger.LogInformation("Scheme [{Scheme}] is selected", context.Scheme.DisplayName);
|
||||
await next().ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
namespace Snap.Hutao.Service.Game.Launching.Handler;
|
||||
|
||||
internal sealed class LaunchExecutionGameProcessExitHandler : ILaunchExecutionDelegateHandler
|
||||
{
|
||||
public async ValueTask OnExecutionAsync(LaunchExecutionContext context, LaunchExecutionDelegate next)
|
||||
{
|
||||
if (!context.Process.HasExited)
|
||||
{
|
||||
context.Progress.Report(new(LaunchPhase.WaitingForExit, SH.ServiceGameLaunchPhaseWaitingProcessExit));
|
||||
await context.Process.WaitForExitAsync().ConfigureAwait(false);
|
||||
}
|
||||
|
||||
context.Logger.LogInformation("Game process exited with code {ExitCode}", context.Process.ExitCode);
|
||||
context.Progress.Report(new(LaunchPhase.ProcessExited, SH.ServiceGameLaunchPhaseProcessExited));
|
||||
await next().ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Snap.Hutao.Core;
|
||||
|
||||
namespace Snap.Hutao.Service.Game.Launching.Handler;
|
||||
|
||||
internal sealed class LaunchExecutionGameProcessInitializationHandler : ILaunchExecutionDelegateHandler
|
||||
{
|
||||
public async ValueTask OnExecutionAsync(LaunchExecutionContext context, LaunchExecutionDelegate next)
|
||||
{
|
||||
if (!context.TryGetGameFileSystem(out GameFileSystem? gameFileSystem))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
context.Progress.Report(new(LaunchPhase.ProcessInitializing, SH.ServiceGameLaunchPhaseProcessInitializing));
|
||||
using (context.Process = InitializeGameProcess(context, gameFileSystem))
|
||||
{
|
||||
await next().ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
private static System.Diagnostics.Process InitializeGameProcess(LaunchExecutionContext context, GameFileSystem gameFileSystem)
|
||||
{
|
||||
LaunchOptions launchOptions = context.Options;
|
||||
|
||||
string commandLine = string.Empty;
|
||||
if (launchOptions.IsEnabled)
|
||||
{
|
||||
// https://docs.unity.cn/cn/current/Manual/PlayerCommandLineArguments.html
|
||||
// https://docs.unity3d.com/2017.4/Documentation/Manual/CommandLineArguments.html
|
||||
commandLine = new CommandLineBuilder()
|
||||
.AppendIf(launchOptions.IsBorderless, "-popupwindow")
|
||||
.AppendIf(launchOptions.IsExclusive, "-window-mode", "exclusive")
|
||||
.Append("-screen-fullscreen", launchOptions.IsFullScreen ? 1 : 0)
|
||||
.AppendIf(launchOptions.IsScreenWidthEnabled, "-screen-width", launchOptions.ScreenWidth)
|
||||
.AppendIf(launchOptions.IsScreenHeightEnabled, "-screen-height", launchOptions.ScreenHeight)
|
||||
.AppendIf(launchOptions.IsMonitorEnabled, "-monitor", launchOptions.Monitor.Value)
|
||||
.AppendIf(launchOptions.IsUseCloudThirdPartyMobile, "-platform_type CLOUD_THIRD_PARTY_MOBILE")
|
||||
.ToString();
|
||||
}
|
||||
|
||||
context.Logger.LogInformation("Command Line Arguments: {commandLine}", commandLine);
|
||||
|
||||
return new()
|
||||
{
|
||||
StartInfo = new()
|
||||
{
|
||||
Arguments = commandLine,
|
||||
FileName = gameFileSystem.GameFilePath,
|
||||
UseShellExecute = true,
|
||||
Verb = "runas",
|
||||
WorkingDirectory = gameFileSystem.GameDirectory,
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Windows.Win32.Foundation;
|
||||
|
||||
namespace Snap.Hutao.Service.Game.Launching.Handler;
|
||||
|
||||
internal sealed class LaunchExecutionGameProcessStartHandler : ILaunchExecutionDelegateHandler
|
||||
{
|
||||
public async ValueTask OnExecutionAsync(LaunchExecutionContext context, LaunchExecutionDelegate next)
|
||||
{
|
||||
try
|
||||
{
|
||||
context.Process.Start();
|
||||
context.Logger.LogInformation("Process started");
|
||||
}
|
||||
catch (Win32Exception ex) when (ex.HResult == HRESULT.E_FAIL)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
context.Progress.Report(new(LaunchPhase.ProcessStarted, SH.ServiceGameLaunchPhaseProcessStarted));
|
||||
await next().ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Snap.Hutao.Core.IO.Ini;
|
||||
using Snap.Hutao.Service.Game.Configuration;
|
||||
using System.IO;
|
||||
|
||||
namespace Snap.Hutao.Service.Game.Launching.Handler;
|
||||
|
||||
internal sealed class LaunchExecutionSetChannelOptionsHandler : ILaunchExecutionDelegateHandler
|
||||
{
|
||||
public async ValueTask OnExecutionAsync(LaunchExecutionContext context, LaunchExecutionDelegate next)
|
||||
{
|
||||
if (!context.TryGetGameFileSystem(out GameFileSystem? gameFileSystem))
|
||||
{
|
||||
// context.Result is set in TryGetGameFileSystem
|
||||
return;
|
||||
}
|
||||
|
||||
string configPath = gameFileSystem.GameConfigFilePath;
|
||||
context.Logger.LogInformation("Game config file path: {ConfigPath}", configPath);
|
||||
|
||||
List<IniElement> elements = default!;
|
||||
try
|
||||
{
|
||||
elements = [.. IniSerializer.DeserializeFromFile(configPath)];
|
||||
}
|
||||
catch (FileNotFoundException)
|
||||
{
|
||||
context.Result.Kind = LaunchExecutionResultKind.GameConfigFileNotFound;
|
||||
context.Result.ErrorMessage = SH.FormatServiceGameSetMultiChannelConfigFileNotFound(configPath);
|
||||
return;
|
||||
}
|
||||
catch (DirectoryNotFoundException)
|
||||
{
|
||||
context.Result.Kind = LaunchExecutionResultKind.GameConfigDirectoryNotFound;
|
||||
context.Result.ErrorMessage = SH.FormatServiceGameSetMultiChannelConfigFileNotFound(configPath);
|
||||
return;
|
||||
}
|
||||
catch (UnauthorizedAccessException)
|
||||
{
|
||||
context.Result.Kind = LaunchExecutionResultKind.GameConfigInsufficientPermissions;
|
||||
context.Result.ErrorMessage = SH.ServiceGameSetMultiChannelUnauthorizedAccess;
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (IniElement element in elements)
|
||||
{
|
||||
if (element is IniParameter parameter)
|
||||
{
|
||||
if (parameter.Key is ChannelOptions.ChannelName)
|
||||
{
|
||||
context.ChannelOptionsChanged = parameter.Set(context.Scheme.Channel.ToString("D")) || context.ChannelOptionsChanged;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (parameter.Key is ChannelOptions.SubChannelName)
|
||||
{
|
||||
context.ChannelOptionsChanged = parameter.Set(context.Scheme.SubChannel.ToString("D")) || context.ChannelOptionsChanged;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (context.ChannelOptionsChanged)
|
||||
{
|
||||
IniSerializer.SerializeToFile(configPath, elements);
|
||||
}
|
||||
|
||||
await next().ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Snap.Hutao.Service.Discord;
|
||||
|
||||
namespace Snap.Hutao.Service.Game.Launching.Handler;
|
||||
|
||||
internal sealed class LaunchExecutionSetDiscordActivityHandler : ILaunchExecutionDelegateHandler
|
||||
{
|
||||
public async ValueTask OnExecutionAsync(LaunchExecutionContext context, LaunchExecutionDelegate next)
|
||||
{
|
||||
bool previousSetDiscordActivityWhenPlaying = context.Options.SetDiscordActivityWhenPlaying;
|
||||
|
||||
try
|
||||
{
|
||||
if (previousSetDiscordActivityWhenPlaying)
|
||||
{
|
||||
context.Logger.LogInformation("Set discord activity as playing");
|
||||
await context.ServiceProvider
|
||||
.GetRequiredService<IDiscordService>()
|
||||
.SetPlayingActivityAsync(context.Scheme.IsOversea)
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
|
||||
await next().ConfigureAwait(false);
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (previousSetDiscordActivityWhenPlaying)
|
||||
{
|
||||
context.Logger.LogInformation("Recover discord activity");
|
||||
await context.ServiceProvider
|
||||
.GetRequiredService<IDiscordService>()
|
||||
.SetNormalActivityAsync()
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Snap.Hutao.Service.Game.Account;
|
||||
|
||||
namespace Snap.Hutao.Service.Game.Launching.Handler;
|
||||
|
||||
internal sealed class LaunchExecutionSetGameAccountHandler : ILaunchExecutionDelegateHandler
|
||||
{
|
||||
public async ValueTask OnExecutionAsync(LaunchExecutionContext context, LaunchExecutionDelegate next)
|
||||
{
|
||||
if (context.Account is not null)
|
||||
{
|
||||
context.Logger.LogInformation("Set game account to [{Account}]", context.Account.Name);
|
||||
|
||||
if (!RegistryInterop.Set(context.Account))
|
||||
{
|
||||
context.Result.Kind = LaunchExecutionResultKind.GameAccountRegistryWriteResultNotMatch;
|
||||
context.Result.ErrorMessage = SH.ViewModelLaunchGameSwitchGameAccountFail;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
await next().ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Snap.Hutao.Service.Game.Account;
|
||||
|
||||
namespace Snap.Hutao.Service.Game.Launching.Handler;
|
||||
|
||||
internal sealed class LaunchExecutionSetWindowsHDRHandler : ILaunchExecutionDelegateHandler
|
||||
{
|
||||
public async ValueTask OnExecutionAsync(LaunchExecutionContext context, LaunchExecutionDelegate next)
|
||||
{
|
||||
if (context.Options.IsWindowsHDREnabled)
|
||||
{
|
||||
context.Logger.LogInformation("Set Windows HDR");
|
||||
RegistryInterop.SetWindowsHDR(context.Scheme.IsOversea);
|
||||
}
|
||||
|
||||
await next().ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Windows.System;
|
||||
|
||||
namespace Snap.Hutao.Service.Game.Launching.Handler;
|
||||
|
||||
internal sealed class LaunchExecutionStarwardPlayTimeStatisticsHandler : ILaunchExecutionDelegateHandler
|
||||
{
|
||||
public async ValueTask OnExecutionAsync(LaunchExecutionContext context, LaunchExecutionDelegate next)
|
||||
{
|
||||
if (context.Options.UseStarwardPlayTimeStatistics)
|
||||
{
|
||||
context.Logger.LogInformation("Using starward to count game time");
|
||||
await LaunchStarwardForPlayTimeStatisticsAsync(context).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
await next().ConfigureAwait(false);
|
||||
}
|
||||
|
||||
private static async ValueTask LaunchStarwardForPlayTimeStatisticsAsync(LaunchExecutionContext context)
|
||||
{
|
||||
string gameBiz = context.Scheme.IsOversea ? "hk4e_global" : "hk4e_cn";
|
||||
Uri starwardPlayTimeUri = $"starward://playtime/{gameBiz}".ToUri();
|
||||
if (await Launcher.QueryUriSupportAsync(starwardPlayTimeUri, LaunchQuerySupportType.Uri) is LaunchQuerySupportStatus.Available)
|
||||
{
|
||||
context.Logger.LogInformation("Launching starward");
|
||||
await Launcher.LaunchUriAsync(starwardPlayTimeUri);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Snap.Hutao.Factory.Progress;
|
||||
|
||||
namespace Snap.Hutao.Service.Game.Launching.Handler;
|
||||
|
||||
internal sealed class LaunchExecutionStatusProgressHandler : ILaunchExecutionDelegateHandler
|
||||
{
|
||||
public async ValueTask OnExecutionAsync(LaunchExecutionContext context, LaunchExecutionDelegate next)
|
||||
{
|
||||
IProgressFactory progressFactory = context.ServiceProvider.GetRequiredService<IProgressFactory>();
|
||||
LaunchStatusOptions statusOptions = context.ServiceProvider.GetRequiredService<LaunchStatusOptions>();
|
||||
context.Progress = progressFactory.CreateForMainThread<LaunchStatus>(status => statusOptions.LaunchStatus = status);
|
||||
|
||||
await next().ConfigureAwait(false);
|
||||
|
||||
// Clear status
|
||||
context.Progress.Report(default!);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Snap.Hutao.Core;
|
||||
using Snap.Hutao.Factory.Progress;
|
||||
using Snap.Hutao.Service.Game.Unlocker;
|
||||
|
||||
namespace Snap.Hutao.Service.Game.Launching.Handler;
|
||||
|
||||
internal sealed class LaunchExecutionUnlockFpsHandler : ILaunchExecutionDelegateHandler
|
||||
{
|
||||
public async ValueTask OnExecutionAsync(LaunchExecutionContext context, LaunchExecutionDelegate next)
|
||||
{
|
||||
RuntimeOptions runtimeOptions = context.ServiceProvider.GetRequiredService<RuntimeOptions>();
|
||||
if (runtimeOptions.IsElevated && context.Options.IsAdvancedLaunchOptionsEnabled && context.Options.UnlockFps)
|
||||
{
|
||||
context.Logger.LogInformation("Unlocking FPS");
|
||||
context.Progress.Report(new(LaunchPhase.UnlockingFps, SH.ServiceGameLaunchPhaseUnlockingFps));
|
||||
|
||||
IProgressFactory progressFactory = context.ServiceProvider.GetRequiredService<IProgressFactory>();
|
||||
IProgress<UnlockerStatus> progress = progressFactory.CreateForMainThread<UnlockerStatus>(status => context.Progress.Report(LaunchStatus.FromUnlockStatus(status)));
|
||||
GameFpsUnlocker unlocker = context.ServiceProvider.CreateInstance<GameFpsUnlocker>(context.Process);
|
||||
|
||||
try
|
||||
{
|
||||
await unlocker.UnlockAsync(new(100, 20000, 3000), progress, context.CancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
catch (InvalidOperationException ex)
|
||||
{
|
||||
context.Logger.LogCritical(ex, "Unlocking FPS failed");
|
||||
|
||||
context.Result.Kind = LaunchExecutionResultKind.GameFpsUnlockingFailed;
|
||||
context.Result.ErrorMessage = ex.Message;
|
||||
|
||||
// The Unlocker can't unlock the process
|
||||
context.Process.Kill();
|
||||
}
|
||||
}
|
||||
|
||||
await next().ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
namespace Snap.Hutao.Service.Game.Launching;
|
||||
|
||||
internal delegate ValueTask<LaunchExecutionContext> LaunchExecutionDelegate();
|
||||
|
||||
internal interface ILaunchExecutionDelegateHandler
|
||||
{
|
||||
ValueTask OnExecutionAsync(LaunchExecutionContext context, LaunchExecutionDelegate next);
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Snap.Hutao.Model.Entity;
|
||||
using Snap.Hutao.Service.Game.PathAbstraction;
|
||||
using Snap.Hutao.Service.Game.Scheme;
|
||||
using Snap.Hutao.ViewModel.Game;
|
||||
using System.Collections.Immutable;
|
||||
|
||||
namespace Snap.Hutao.Service.Game.Launching;
|
||||
|
||||
[ConstructorGenerated]
|
||||
internal sealed partial class LaunchExecutionContext
|
||||
{
|
||||
private readonly ILogger<LaunchExecutionContext> logger;
|
||||
private readonly IServiceProvider serviceProvider;
|
||||
private readonly ITaskContext taskContext;
|
||||
private readonly LaunchOptions options;
|
||||
|
||||
private GameFileSystem? gameFileSystem;
|
||||
|
||||
[SuppressMessage("", "SH007")]
|
||||
public LaunchExecutionContext(IServiceProvider serviceProvider, IViewModelSupportLaunchExecution viewModel, LaunchScheme? scheme, GameAccount? account)
|
||||
: this(serviceProvider)
|
||||
{
|
||||
ViewModel = viewModel;
|
||||
Scheme = scheme!;
|
||||
Account = account;
|
||||
}
|
||||
|
||||
public LaunchExecutionResult Result { get; } = new();
|
||||
|
||||
public CancellationToken CancellationToken { get; set; }
|
||||
|
||||
public IServiceProvider ServiceProvider { get => serviceProvider; }
|
||||
|
||||
public ITaskContext TaskContext { get => taskContext; }
|
||||
|
||||
public ILogger Logger { get => logger; }
|
||||
|
||||
public LaunchOptions Options { get => options; }
|
||||
|
||||
public IViewModelSupportLaunchExecution ViewModel { get; private set; } = default!;
|
||||
|
||||
public LaunchScheme Scheme { get; private set; } = default!;
|
||||
|
||||
public GameAccount? Account { get; private set; }
|
||||
|
||||
public bool ChannelOptionsChanged { get; set; }
|
||||
|
||||
public IProgress<LaunchStatus> Progress { get; set; } = default!;
|
||||
|
||||
public System.Diagnostics.Process Process { get; set; } = default!;
|
||||
|
||||
public bool TryGetGameFileSystem([NotNullWhen(true)] out GameFileSystem? gameFileSystem)
|
||||
{
|
||||
if (this.gameFileSystem is not null)
|
||||
{
|
||||
gameFileSystem = this.gameFileSystem;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!Options.TryGetGameFileSystem(out gameFileSystem))
|
||||
{
|
||||
Result.Kind = LaunchExecutionResultKind.NoActiveGamePath;
|
||||
Result.ErrorMessage = SH.ServiceGameLaunchExecutionGamePathNotValid;
|
||||
return false;
|
||||
}
|
||||
|
||||
this.gameFileSystem = gameFileSystem;
|
||||
return true;
|
||||
}
|
||||
|
||||
public void UpdateGamePathEntry()
|
||||
{
|
||||
ImmutableList<GamePathEntry> gamePathEntries = Options.GetGamePathEntries(out GamePathEntry? selectedEntry);
|
||||
ViewModel.SetGamePathEntriesAndSelectedGamePathEntry(gamePathEntries, selectedEntry);
|
||||
|
||||
// invalidate game file system
|
||||
gameFileSystem = null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Snap.Hutao.Core;
|
||||
using Snap.Hutao.Service.Game.Launching.Handler;
|
||||
|
||||
namespace Snap.Hutao.Service.Game.Launching;
|
||||
|
||||
[Injection(InjectAs.Transient)]
|
||||
internal sealed class LaunchExecutionInvoker
|
||||
{
|
||||
private readonly Queue<ILaunchExecutionDelegateHandler> handlers;
|
||||
|
||||
public LaunchExecutionInvoker()
|
||||
{
|
||||
handlers = [];
|
||||
handlers.Enqueue(new LaunchExecutionEnsureGameNotRunningHandler());
|
||||
handlers.Enqueue(new LaunchExecutionEnsureSchemeHandler());
|
||||
handlers.Enqueue(new LaunchExecutionSetChannelOptionsHandler());
|
||||
handlers.Enqueue(new LaunchExecutionEnsureGameResourceHandler());
|
||||
handlers.Enqueue(new LaunchExecutionSetGameAccountHandler());
|
||||
handlers.Enqueue(new LaunchExecutionSetWindowsHDRHandler());
|
||||
handlers.Enqueue(new LaunchExecutionStatusProgressHandler());
|
||||
handlers.Enqueue(new LaunchExecutionGameProcessInitializationHandler());
|
||||
handlers.Enqueue(new LaunchExecutionSetDiscordActivityHandler());
|
||||
handlers.Enqueue(new LaunchExecutionGameProcessStartHandler());
|
||||
handlers.Enqueue(new LaunchExecutionStarwardPlayTimeStatisticsHandler());
|
||||
handlers.Enqueue(new LaunchExecutionUnlockFpsHandler());
|
||||
handlers.Enqueue(new LaunchExecutionGameProcessExitHandler());
|
||||
}
|
||||
|
||||
public async ValueTask<LaunchExecutionResult> InvokeAsync(LaunchExecutionContext context)
|
||||
{
|
||||
await InvokeHandlerAsync(context).ConfigureAwait(false);
|
||||
return context.Result;
|
||||
}
|
||||
|
||||
private async ValueTask<LaunchExecutionContext> InvokeHandlerAsync(LaunchExecutionContext context)
|
||||
{
|
||||
if (handlers.TryDequeue(out ILaunchExecutionDelegateHandler? handler))
|
||||
{
|
||||
string typeName = TypeNameHelper.GetTypeDisplayName(handler, false);
|
||||
context.Logger.LogInformation("Handler[{Handler}] begin execution", typeName);
|
||||
await handler.OnExecutionAsync(context, () => InvokeHandlerAsync(context)).ConfigureAwait(false);
|
||||
context.Logger.LogInformation("Handler[{Handler}] end execution", typeName);
|
||||
}
|
||||
|
||||
return context;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
namespace Snap.Hutao.Service.Game.Launching;
|
||||
|
||||
internal sealed class LaunchExecutionResult
|
||||
{
|
||||
public LaunchExecutionResultKind Kind { get; set; }
|
||||
|
||||
public string ErrorMessage { get; set; } = default!;
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
namespace Snap.Hutao.Service.Game.Launching;
|
||||
|
||||
internal enum LaunchExecutionResultKind
|
||||
{
|
||||
Ok,
|
||||
NoActiveScheme,
|
||||
NoActiveGamePath,
|
||||
GameProcessRunning,
|
||||
GameConfigFileNotFound,
|
||||
GameConfigDirectoryNotFound,
|
||||
GameConfigInsufficientPermissions,
|
||||
GameDirectoryInsufficientPermissions,
|
||||
GameResourceIndexQueryInvalidResponse,
|
||||
GameResourcePackageConvertInternalError,
|
||||
GameAccountRegistryWriteResultNotMatch,
|
||||
GameFpsUnlockingFailed,
|
||||
}
|
||||
@@ -1,102 +0,0 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Microsoft.Win32.SafeHandles;
|
||||
using Snap.Hutao.Service.Game.Scheme;
|
||||
using Snap.Hutao.Web.Hoyolab.SdkStatic.Hk4e.Launcher;
|
||||
using Snap.Hutao.Web.Response;
|
||||
using System.IO;
|
||||
using static Snap.Hutao.Service.Game.GameConstants;
|
||||
|
||||
namespace Snap.Hutao.Service.Game.Package;
|
||||
|
||||
[ConstructorGenerated]
|
||||
[Injection(InjectAs.Singleton, typeof(IGamePackageService))]
|
||||
internal sealed partial class GamePackageService : IGamePackageService
|
||||
{
|
||||
private readonly PackageConverter packageConverter;
|
||||
private readonly IServiceProvider serviceProvider;
|
||||
private readonly LaunchOptions launchOptions;
|
||||
private readonly ITaskContext taskContext;
|
||||
|
||||
public async ValueTask<bool> EnsureGameResourceAsync(LaunchScheme launchScheme, IProgress<PackageReplaceStatus> progress)
|
||||
{
|
||||
if (!launchOptions.TryGetGameDirectoryAndGameFileName(out string? gameFolder, out string? gameFileName))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!CheckDirectoryPermissions(gameFolder))
|
||||
{
|
||||
progress.Report(new(SH.ServiceGameEnsureGameResourceInsufficientDirectoryPermissions));
|
||||
return false;
|
||||
}
|
||||
|
||||
progress.Report(new(SH.ServiceGameEnsureGameResourceQueryResourceInformation));
|
||||
Response<GameResource> response = await serviceProvider
|
||||
.GetRequiredService<ResourceClient>()
|
||||
.GetResourceAsync(launchScheme)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
if (!response.IsOk())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
GameResource resource = response.Data;
|
||||
|
||||
if (!launchScheme.ExecutableMatches(gameFileName))
|
||||
{
|
||||
// We can't start the game when we failed to convert game
|
||||
if (!await packageConverter.EnsureGameResourceAsync(launchScheme, resource, gameFolder, progress).ConfigureAwait(false))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// We need to change the gamePath if we switched.
|
||||
string exeName = launchScheme.IsOversea ? GenshinImpactFileName : YuanShenFileName;
|
||||
|
||||
await taskContext.SwitchToMainThreadAsync();
|
||||
launchOptions.UpdateGamePathAndRefreshEntries(Path.Combine(gameFolder, exeName));
|
||||
}
|
||||
|
||||
await packageConverter.EnsureDeprecatedFilesAndSdkAsync(resource, gameFolder).ConfigureAwait(false);
|
||||
return true;
|
||||
}
|
||||
|
||||
private static bool CheckDirectoryPermissions(string folder)
|
||||
{
|
||||
// Program Files has special permissions limitation.
|
||||
string programFiles = Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles);
|
||||
if (folder.StartsWith(programFiles, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
string tempFilePath = Path.Combine(folder, $"{Guid.NewGuid():N}.tmp");
|
||||
string tempFilePathMove = Path.Combine(folder, $"{Guid.NewGuid():N}.tmp");
|
||||
|
||||
// Test create file
|
||||
using (SafeFileHandle handle = File.OpenHandle(tempFilePath, FileMode.CreateNew, FileAccess.ReadWrite, FileShare.None, preallocationSize: 32 * 1024))
|
||||
{
|
||||
// Test write file
|
||||
RandomAccess.Write(handle, "SNAP HUTAO DIRECTORY PERMISSION CHECK"u8, 0);
|
||||
RandomAccess.FlushToDisk(handle);
|
||||
}
|
||||
|
||||
// Test move file
|
||||
File.Move(tempFilePath, tempFilePathMove);
|
||||
|
||||
// Test delete file
|
||||
File.Delete(tempFilePathMove);
|
||||
|
||||
return true;
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Snap.Hutao.Service.Game.Scheme;
|
||||
|
||||
namespace Snap.Hutao.Service.Game.Package;
|
||||
|
||||
internal interface IGamePackageService
|
||||
{
|
||||
ValueTask<bool> EnsureGameResourceAsync(LaunchScheme launchScheme, IProgress<PackageReplaceStatus> progress);
|
||||
}
|
||||
@@ -8,25 +8,15 @@ namespace Snap.Hutao.Service.Game.Package;
|
||||
/// <summary>
|
||||
/// 包更新状态
|
||||
/// </summary>
|
||||
internal sealed class PackageReplaceStatus
|
||||
internal sealed class PackageConvertStatus
|
||||
{
|
||||
/// <summary>
|
||||
/// 构造一个新的包更新状态
|
||||
/// </summary>
|
||||
/// <param name="name">描述</param>
|
||||
public PackageReplaceStatus(string name)
|
||||
public PackageConvertStatus(string name)
|
||||
{
|
||||
Name = name;
|
||||
Description = name;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 构造一个新的包更新状态
|
||||
/// </summary>
|
||||
/// <param name="name">名称</param>
|
||||
/// <param name="bytesRead">读取的字节数</param>
|
||||
/// <param name="totalBytes">总字节数</param>
|
||||
public PackageReplaceStatus(string name, long bytesRead, long totalBytes)
|
||||
public PackageConvertStatus(string name, long bytesRead, long totalBytes)
|
||||
{
|
||||
Percent = (double)bytesRead / totalBytes;
|
||||
Name = name;
|
||||
@@ -34,7 +34,7 @@ internal sealed partial class PackageConverter
|
||||
private readonly HttpClient httpClient;
|
||||
private readonly ILogger<PackageConverter> logger;
|
||||
|
||||
public async ValueTask<bool> EnsureGameResourceAsync(LaunchScheme targetScheme, GameResource gameResource, string gameFolder, IProgress<PackageReplaceStatus> progress)
|
||||
public async ValueTask<bool> EnsureGameResourceAsync(LaunchScheme targetScheme, GameResource gameResource, string gameFolder, IProgress<PackageConvertStatus> progress)
|
||||
{
|
||||
// 以 国服 => 国际 为例
|
||||
// 1. 下载国际服的 pkg_version 文件,转换为索引字典
|
||||
@@ -93,7 +93,6 @@ internal sealed partial class PackageConverter
|
||||
ZipFile.ExtractToDirectory(sdkWebStream, gameFolder, true);
|
||||
}
|
||||
|
||||
// TODO: verify sdk md5
|
||||
if (File.Exists(sdkDllBackup) && File.Exists(sdkVersionBackup))
|
||||
{
|
||||
File.Delete(sdkDllBackup);
|
||||
@@ -188,7 +187,7 @@ internal sealed partial class PackageConverter
|
||||
}
|
||||
}
|
||||
|
||||
private async ValueTask PrepareCacheFilesAsync(List<PackageItemOperationInfo> operations, PackageConverterFileSystemContext context, IProgress<PackageReplaceStatus> progress)
|
||||
private async ValueTask PrepareCacheFilesAsync(List<PackageItemOperationInfo> operations, PackageConverterFileSystemContext context, IProgress<PackageConvertStatus> progress)
|
||||
{
|
||||
foreach (PackageItemOperationInfo info in operations)
|
||||
{
|
||||
@@ -204,7 +203,7 @@ internal sealed partial class PackageConverter
|
||||
}
|
||||
}
|
||||
|
||||
private async ValueTask SkipOrDownloadAsync(PackageItemOperationInfo info, PackageConverterFileSystemContext context, IProgress<PackageReplaceStatus> progress)
|
||||
private async ValueTask SkipOrDownloadAsync(PackageItemOperationInfo info, PackageConverterFileSystemContext context, IProgress<PackageConvertStatus> progress)
|
||||
{
|
||||
// 还原正确的远程地址
|
||||
string remoteName = string.Format(CultureInfo.CurrentCulture, info.Remote.RelativePath, context.ToDataFolderName);
|
||||
@@ -230,7 +229,7 @@ internal sealed partial class PackageConverter
|
||||
Directory.CreateDirectory(directory);
|
||||
|
||||
string remoteUrl = context.GetScatteredFilesUrl(remoteName);
|
||||
HttpShardCopyWorkerOptions<PackageReplaceStatus> options = new()
|
||||
HttpShardCopyWorkerOptions<PackageConvertStatus> options = new()
|
||||
{
|
||||
HttpClient = httpClient,
|
||||
SourceUrl = remoteUrl,
|
||||
@@ -238,7 +237,7 @@ internal sealed partial class PackageConverter
|
||||
StatusFactory = (bytesRead, totalBytes) => new(remoteName, bytesRead, totalBytes),
|
||||
};
|
||||
|
||||
using (HttpShardCopyWorker<PackageReplaceStatus> worker = await HttpShardCopyWorker<PackageReplaceStatus>.CreateAsync(options).ConfigureAwait(false))
|
||||
using (HttpShardCopyWorker<PackageConvertStatus> worker = await HttpShardCopyWorker<PackageConvertStatus>.CreateAsync(options).ConfigureAwait(false))
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -258,7 +257,7 @@ internal sealed partial class PackageConverter
|
||||
}
|
||||
}
|
||||
|
||||
private async ValueTask<bool> ReplaceGameResourceAsync(List<PackageItemOperationInfo> operations, PackageConverterFileSystemContext context, IProgress<PackageReplaceStatus> progress)
|
||||
private async ValueTask<bool> ReplaceGameResourceAsync(List<PackageItemOperationInfo> operations, PackageConverterFileSystemContext context, IProgress<PackageConvertStatus> progress)
|
||||
{
|
||||
// 执行下载与移动操作
|
||||
foreach (PackageItemOperationInfo info in operations)
|
||||
|
||||
@@ -9,7 +9,7 @@ internal sealed class GamePathEntry
|
||||
public string Path { get; set; } = default!;
|
||||
|
||||
[JsonIgnore]
|
||||
public GamePathKind Kind { get => GetKind(Path); }
|
||||
public GamePathEntryKind Kind { get => GetKind(Path); }
|
||||
|
||||
public static GamePathEntry Create(string path)
|
||||
{
|
||||
@@ -19,8 +19,8 @@ internal sealed class GamePathEntry
|
||||
};
|
||||
}
|
||||
|
||||
private static GamePathKind GetKind(string path)
|
||||
private static GamePathEntryKind GetKind(string path)
|
||||
{
|
||||
return GamePathKind.None;
|
||||
return GamePathEntryKind.None;
|
||||
}
|
||||
}
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
namespace Snap.Hutao.Service.Game.PathAbstraction;
|
||||
|
||||
internal enum GamePathKind
|
||||
internal enum GamePathEntryKind
|
||||
{
|
||||
None,
|
||||
ChineseClient,
|
||||
@@ -1,186 +0,0 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Snap.Hutao.Core;
|
||||
using Snap.Hutao.Factory.Progress;
|
||||
using Snap.Hutao.Service.Discord;
|
||||
using Snap.Hutao.Service.Game.Account;
|
||||
using Snap.Hutao.Service.Game.Scheme;
|
||||
using Snap.Hutao.Service.Game.Unlocker;
|
||||
using System.IO;
|
||||
using static Snap.Hutao.Service.Game.GameConstants;
|
||||
|
||||
namespace Snap.Hutao.Service.Game.Process;
|
||||
|
||||
/// <summary>
|
||||
/// 进程互操作
|
||||
/// </summary>
|
||||
[ConstructorGenerated]
|
||||
[Injection(InjectAs.Singleton, typeof(IGameProcessService))]
|
||||
internal sealed partial class GameProcessService : IGameProcessService
|
||||
{
|
||||
private readonly IServiceProvider serviceProvider;
|
||||
private readonly IProgressFactory progressFactory;
|
||||
private readonly IDiscordService discordService;
|
||||
private readonly RuntimeOptions runtimeOptions;
|
||||
private readonly LaunchOptions launchOptions;
|
||||
|
||||
private volatile bool isGameRunning;
|
||||
|
||||
public bool IsGameRunning()
|
||||
{
|
||||
if (isGameRunning)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// Original two GetProcessesByName is O(2n)
|
||||
// GetProcesses once and manually loop is O(n)
|
||||
foreach (ref System.Diagnostics.Process process in System.Diagnostics.Process.GetProcesses().AsSpan())
|
||||
{
|
||||
if (process.ProcessName is YuanShenProcessName or GenshinImpactProcessName)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public async ValueTask LaunchAsync(IProgress<LaunchStatus> progress)
|
||||
{
|
||||
if (IsGameRunning())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!launchOptions.TryGetGamePathAndGameFileName(out string gamePath, out string? gameFileName))
|
||||
{
|
||||
ArgumentException.ThrowIfNullOrEmpty(gamePath);
|
||||
return; // null check passing, actually never reach.
|
||||
}
|
||||
|
||||
bool isOversea = LaunchScheme.ExecutableIsOversea(gameFileName);
|
||||
|
||||
if (launchOptions.IsWindowsHDREnabled)
|
||||
{
|
||||
RegistryInterop.SetWindowsHDR(isOversea);
|
||||
}
|
||||
|
||||
progress.Report(new(LaunchPhase.ProcessInitializing, SH.ServiceGameLaunchPhaseProcessInitializing));
|
||||
using (System.Diagnostics.Process game = InitializeGameProcess(gamePath))
|
||||
{
|
||||
await using (await GameRunningTracker.CreateAsync(this, isOversea).ConfigureAwait(false))
|
||||
{
|
||||
game.Start();
|
||||
progress.Report(new(LaunchPhase.ProcessStarted, SH.ServiceGameLaunchPhaseProcessStarted));
|
||||
|
||||
if (launchOptions.UseStarwardPlayTimeStatistics)
|
||||
{
|
||||
await Starward.LaunchForPlayTimeStatisticsAsync(isOversea).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
if (runtimeOptions.IsElevated && launchOptions.IsAdvancedLaunchOptionsEnabled && launchOptions.UnlockFps)
|
||||
{
|
||||
progress.Report(new(LaunchPhase.UnlockingFps, SH.ServiceGameLaunchPhaseUnlockingFps));
|
||||
try
|
||||
{
|
||||
await UnlockFpsAsync(game, progress).ConfigureAwait(false);
|
||||
}
|
||||
catch (InvalidOperationException)
|
||||
{
|
||||
// The Unlocker can't unlock the process
|
||||
game.Kill();
|
||||
throw;
|
||||
}
|
||||
finally
|
||||
{
|
||||
progress.Report(new(LaunchPhase.ProcessExited, SH.ServiceGameLaunchPhaseProcessExited));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
progress.Report(new(LaunchPhase.WaitingForExit, SH.ServiceGameLaunchPhaseWaitingProcessExit));
|
||||
await game.WaitForExitAsync().ConfigureAwait(false);
|
||||
progress.Report(new(LaunchPhase.ProcessExited, SH.ServiceGameLaunchPhaseProcessExited));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private System.Diagnostics.Process InitializeGameProcess(string gamePath)
|
||||
{
|
||||
string commandLine = string.Empty;
|
||||
|
||||
if (launchOptions.IsEnabled)
|
||||
{
|
||||
// https://docs.unity.cn/cn/current/Manual/PlayerCommandLineArguments.html
|
||||
// https://docs.unity3d.com/2017.4/Documentation/Manual/CommandLineArguments.html
|
||||
commandLine = new CommandLineBuilder()
|
||||
.AppendIf(launchOptions.IsBorderless, "-popupwindow")
|
||||
.AppendIf(launchOptions.IsExclusive, "-window-mode", "exclusive")
|
||||
.Append("-screen-fullscreen", launchOptions.IsFullScreen ? 1 : 0)
|
||||
.AppendIf(launchOptions.IsScreenWidthEnabled, "-screen-width", launchOptions.ScreenWidth)
|
||||
.AppendIf(launchOptions.IsScreenHeightEnabled, "-screen-height", launchOptions.ScreenHeight)
|
||||
.AppendIf(launchOptions.IsMonitorEnabled, "-monitor", launchOptions.Monitor.Value)
|
||||
.AppendIf(launchOptions.IsUseCloudThirdPartyMobile, "-platform_type CLOUD_THIRD_PARTY_MOBILE")
|
||||
.ToString();
|
||||
}
|
||||
|
||||
return new()
|
||||
{
|
||||
StartInfo = new()
|
||||
{
|
||||
Arguments = commandLine,
|
||||
FileName = gamePath,
|
||||
UseShellExecute = true,
|
||||
Verb = "runas",
|
||||
WorkingDirectory = Path.GetDirectoryName(gamePath),
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
private ValueTask UnlockFpsAsync(System.Diagnostics.Process game, IProgress<LaunchStatus> progress, CancellationToken token = default)
|
||||
{
|
||||
#pragma warning disable CA1859
|
||||
IGameFpsUnlocker unlocker = serviceProvider.CreateInstance<GameFpsUnlocker>(game);
|
||||
#pragma warning restore CA1859
|
||||
UnlockTimingOptions options = new(100, 20000, 3000);
|
||||
IProgress<UnlockerStatus> lockerProgress = progressFactory.CreateForMainThread<UnlockerStatus>(unlockStatus => progress.Report(LaunchStatus.FromUnlockStatus(unlockStatus)));
|
||||
return unlocker.UnlockAsync(options, lockerProgress, token);
|
||||
}
|
||||
|
||||
private class GameRunningTracker : IAsyncDisposable
|
||||
{
|
||||
private readonly GameProcessService service;
|
||||
private readonly bool previousSetDiscordActivityWhenPlaying;
|
||||
|
||||
private GameRunningTracker(GameProcessService service, bool isOversea)
|
||||
{
|
||||
service.isGameRunning = true;
|
||||
previousSetDiscordActivityWhenPlaying = service.launchOptions.SetDiscordActivityWhenPlaying;
|
||||
this.service = service;
|
||||
}
|
||||
|
||||
public static async ValueTask<GameRunningTracker> CreateAsync(GameProcessService service, bool isOversea)
|
||||
{
|
||||
GameRunningTracker tracker = new(service, isOversea);
|
||||
if (tracker.previousSetDiscordActivityWhenPlaying)
|
||||
{
|
||||
await service.discordService.SetPlayingActivity(isOversea).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
return tracker;
|
||||
}
|
||||
|
||||
public async ValueTask DisposeAsync()
|
||||
{
|
||||
if (previousSetDiscordActivityWhenPlaying)
|
||||
{
|
||||
await service.discordService.SetNormalActivity().ConfigureAwait(false);
|
||||
}
|
||||
|
||||
service.isGameRunning = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
namespace Snap.Hutao.Service.Game.Process;
|
||||
|
||||
internal interface IGameProcessService
|
||||
{
|
||||
bool IsGameRunning();
|
||||
|
||||
ValueTask LaunchAsync(IProgress<LaunchStatus> progress);
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Windows.System;
|
||||
|
||||
namespace Snap.Hutao.Service.Game.Process;
|
||||
|
||||
internal static class Starward
|
||||
{
|
||||
public static async ValueTask LaunchForPlayTimeStatisticsAsync(bool isOversea)
|
||||
{
|
||||
string gameBiz = isOversea ? "hk4e_global" : "hk4e_cn";
|
||||
Uri starwardPlayTimeUri = $"starward://playtime/{gameBiz}".ToUri();
|
||||
if (await Launcher.QueryUriSupportAsync(starwardPlayTimeUri, LaunchQuerySupportType.Uri) is LaunchQuerySupportStatus.Available)
|
||||
{
|
||||
await Launcher.LaunchUriAsync(starwardPlayTimeUri);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
namespace Snap.Hutao.Service.Metadata;
|
||||
namespace Snap.Hutao.Service;
|
||||
|
||||
/// <summary>
|
||||
/// 本地化名称
|
||||
@@ -74,4 +74,15 @@ internal static class LocaleNames
|
||||
|
||||
return !string.IsNullOrEmpty(languageCode);
|
||||
}
|
||||
|
||||
public static string GetLanguageCodeForDocumentationSearchFromLocaleName(string localeName)
|
||||
{
|
||||
return localeName switch
|
||||
{
|
||||
ID => "id-id",
|
||||
RU => "ru-ru",
|
||||
CHS => "zh-cn",
|
||||
_ => "en-us",
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -10,10 +10,9 @@ namespace Snap.Hutao.Service.Metadata;
|
||||
[Injection(InjectAs.Singleton)]
|
||||
internal sealed partial class MetadataOptions
|
||||
{
|
||||
private readonly AppOptions appOptions;
|
||||
private readonly RuntimeOptions hutaoOptions;
|
||||
private readonly CultureOptions cultureOptions;
|
||||
private readonly RuntimeOptions runtimeOptions;
|
||||
|
||||
private string? localeName;
|
||||
private string? fallbackDataFolder;
|
||||
private string? localizedDataFolder;
|
||||
|
||||
@@ -23,7 +22,7 @@ internal sealed partial class MetadataOptions
|
||||
{
|
||||
if (fallbackDataFolder is null)
|
||||
{
|
||||
fallbackDataFolder = Path.Combine(hutaoOptions.DataFolder, "Metadata", "CHS");
|
||||
fallbackDataFolder = Path.Combine(runtimeOptions.DataFolder, "Metadata", LocaleNames.CHS);
|
||||
Directory.CreateDirectory(fallbackDataFolder);
|
||||
}
|
||||
|
||||
@@ -37,7 +36,7 @@ internal sealed partial class MetadataOptions
|
||||
{
|
||||
if (localizedDataFolder is null)
|
||||
{
|
||||
localizedDataFolder = Path.Combine(hutaoOptions.DataFolder, "Metadata", LocaleName);
|
||||
localizedDataFolder = Path.Combine(runtimeOptions.DataFolder, "Metadata", cultureOptions.LocaleName);
|
||||
Directory.CreateDirectory(localizedDataFolder);
|
||||
}
|
||||
|
||||
@@ -45,21 +44,13 @@ internal sealed partial class MetadataOptions
|
||||
}
|
||||
}
|
||||
|
||||
public string LocaleName
|
||||
public string GetLocalizedLocalFile(string fileNameWithExtension)
|
||||
{
|
||||
get => localeName ??= MetadataOptionsExtension.GetLocaleName(appOptions.CurrentCulture);
|
||||
return Path.Combine(LocalizedDataFolder, fileNameWithExtension);
|
||||
}
|
||||
|
||||
public string LanguageCode
|
||||
public string GetLocalizedRemoteFile(string fileNameWithExtension)
|
||||
{
|
||||
get
|
||||
{
|
||||
if (LocaleNames.TryGetLanguageCodeFromLocaleName(LocaleName, out string? languageCode))
|
||||
{
|
||||
return languageCode;
|
||||
}
|
||||
|
||||
throw new KeyNotFoundException($"Invalid localeName: '{LocaleName}'");
|
||||
}
|
||||
return Web.HutaoEndpoints.Metadata(cultureOptions.LocaleName, fileNameWithExtension);
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user