Compare commits

..

2 Commits

Author SHA1 Message Date
DismissedLight
98c003ae77 Merge branch 'develop' into feat/1239 2024-01-02 13:18:32 +08:00
qhy040404
d26611ccf7 impl #1239 2024-01-01 20:13:11 +08:00
153 changed files with 1619 additions and 3965 deletions

View File

@@ -22,7 +22,7 @@ body:
- label: 我知道文档站的导航栏中有**搜索功能**,且已经搜索过相关关键词
required: true
- label: 我的问题不是[已完成](https://github.com/DGP-Studio/Snap.Hutao/issues?q=is%3Aopen+is%3Aissue+label%3A%E5%B7%B2%E5%AE%8C%E6%88%90)的问题也不是一个别人已发布的**重复的**问题
- label: 我的问题不是[已修复](https://github.com/DGP-Studio/Snap.Hutao/issues?q=is%3Aopen+is%3Aissue+label%3A%E5%B7%B2%E4%BF%AE%E5%A4%8D)的问题也不是一个别人已发布的**重复的**问题
required: true
- type: input
@@ -51,7 +51,7 @@ body:
description: |
在胡桃工具箱的设置界面,你可以找到并复制你的设备 ID
如果你的问题涉及程序崩溃,请填写该项,这将有助于我们定位问题
如果你的程序已经无法启动,请下载并运行[此工具](https://github.com/DGP-Automation/ISSUE_TEMPLATES/releases/download/diagnosis_tools/Snap.Hutao.DiagTools.exe),它将显示你的设备 ID
如果你的程序已经无法启动,请下载并运行[此PowerShell脚本](https://github.com/DGP-Studio/ISSUE_TEMPLATES/releases/download/get_device_id/GetHutaoDeviceId.ps1),它将显示你的设备 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`
如果你无法找到该日志,请下载并运行[此PowerShell脚本](https://github.com/DGP-Studio/ISSUE_TEMPLATES/releases/download/dump_log_script/dump_log_zh.ps1),它将输出错误日志
validations:
required: true

View File

@@ -22,7 +22,7 @@ body:
- label: I and tried **search feature** in Snap Hutao document site, and no associated article
required: true
- label: My issue is not a [finished issue](https://github.com/DGP-Studio/Snap.Hutao/issues?q=is%3Aopen+is%3Aissue+label%3A%E5%B7%B2%E5%AE%8C%E6%88%90), and it's not a duplicated issue
- label: My issue is not a [fixed issue](https://github.com/DGP-Studio/Snap.Hutao/issues?q=is%3Aopen+is%3Aissue+label%3A%E5%B7%B2%E4%BF%AE%E5%A4%8D), and it's not a duplicated issue
required: true
- type: input
@@ -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 [this PowerShell script](https://github.com/DGP-Studio/ISSUE_TEMPLATES/releases/download/get_device_id/GetHutaoDeviceId.ps1), 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 [this PowerShell script](https://github.com/DGP-Studio/ISSUE_TEMPLATES/releases/download/dump_log_script/dump_log_en.ps1), it will dump the error log.
validations:
required: true

View File

@@ -12,26 +12,50 @@ body:
value: |
## 创建版本
- [ ] 同步一次 [Crowdin](https://crowdin.com/project/snap-hutao) 翻译
- [ ] 发布 RC 版本Optional
- [ ] 合并入主分支
- [ ] 整理更新内容,等待翻译
- [ ] 打包
- [ ] 提交微软商店
- [ ] 包含更新日志
- [ ] 在 [Snap.Hutao.Docs@next-patch](https://github.com/DGP-Studio/Snap.Hutao.Docs/tree/next-patch) 分支更新文档并直接开 PR
- [ ] 更新日志
- [ ] 功能文档更新
## 发布版本 [半自动]
- [ ] 在 GitHub 个人设置中更新 [Publish-Automate Beta PAT](https://github.com/settings/tokens?type=beta),有效期需小于预计发版需要天数
- [ ] 将更新的 PAT 更新至 Publish-Automate 库的 [Actions Secrets](https://github.com/DGP-Studio/Publish-Automate/settings/secrets/actions) 中
***
***
- [ ] 主分支合并入 release 分支
- [ ] 等待 Release 自动发布
- [ ] 检查极狐是否同步完成 Release
- [ ] 通知用户
- [ ] 运行 [Auto Publish Action](https://github.com/DGP-Studio/Publish-Automate/actions/workflows/auto-publish.yml)
- [ ] 在 https://store.rg-adguard.net/ 下载新版本安装包
- [ ] Store URL: https://apps.microsoft.com/store/detail/snap-hutao/9PH4NXJ2JN52
- [ ] 命名格式为 `Snap.Hutao x.x.x.msix`
- [ ] Merge 文档 PR
- [ ] 发布 Release
- [ ] 更新日志格式(以 1.6.2 版本为例)
```jsx
## Update log
https://hut.ao/en/statements/update-log.html#_1-6-2
## 更新日志
[此处从文档复制]
## What's Changed
**Full Changelog**: https://github.com/DGP-Studio/Snap.Hutao/compare/1.6.0...1.6.2
```
- [ ] 通知用户
- type: checkboxes
id: checklist-final
attributes:
label: Final Check
description: Understand what you are doing
description: Understand what you are doing
options:
- label: I understand that I will get banned from repository if I don't have permission to use this template
required: true
required: true

View File

@@ -12,7 +12,6 @@ on:
- '.gitmodules'
- '**.md'
- 'LICENSE'
- '**.yml'
jobs:
build:

View File

@@ -1,73 +0,0 @@
stages:
- fetch
- release
- refresh
Fetch:
stage: fetch
rules:
- if: $CI_COMMIT_TAG
tags:
- us3
script:
- apt-get update -qy
- apt-get install -y curl jq
- RELEASE_INFO=$(curl -sSL "https://api.github.com/repos/$CI_PROJECT_PATH/releases/latest")
- ASSET_URL=$(echo "$RELEASE_INFO" | jq -r '.assets[] | select(.name | endswith(".msix")) | .browser_download_url')
- SHA256SUMS_URL=$(echo "$RELEASE_INFO" | jq -r '.assets[] | select(.name == "SHA256SUMS") | .browser_download_url')
- curl -LJO "$ASSET_URL"
- curl -LJO "$SHA256SUMS_URL"
- FILE_NAME=$(basename "$ASSET_URL")
- SHA256SUMS_NAME=$(basename "$SHA256SUMS_URL")
- echo "File name at script stage is $FILE_NAME"
- echo "SHA256SUMS name at script stage is $SHA256SUMS_NAME"
- echo "THIS_FILE_NAME=$FILE_NAME" >> next.env
- echo "THIS_SHA256SUMS_NAME=$SHA256SUMS_NAME" >> next.env
after_script:
- echo "Current Job ID is $CI_JOB_ID"
- echo "THIS_JOB_ID=$CI_JOB_ID" >> next.env
artifacts:
paths:
- "*.msix"
- "SHA256SUMS"
expire_in: 180 days
reports:
dotenv: next.env
release:
stage: release
image: registry.gitlab.com/gitlab-org/release-cli:latest
rules:
- if: $CI_COMMIT_TAG
needs:
- job: Fetch
artifacts: true
variables:
TAG: '$CI_COMMIT_TAG'
script:
- echo "Create Release $TAG"
- echo "$THIS_JOB_ID"
- echo "$THIS_FILE_NAME"
release:
name: '$TAG'
tag_name: '$TAG'
ref: '$TAG'
description: 'Release $TAG by CI'
assets:
links:
- name: "$THIS_FILE_NAME"
url: "https://$CI_SERVER_SHELL_SSH_HOST/$CI_PROJECT_PATH/-/jobs/$THIS_JOB_ID/artifacts/raw/$THIS_FILE_NAME?inline=false"
link_type: package
- 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
Refresh:
stage: refresh
rules:
- if: $CI_COMMIT_TAG
needs:
- job: release
script:
- apt-get install -y curl
- curl -X PATCH "$PURGE_URL"

View File

@@ -4,15 +4,13 @@
### Setup Snap.Hutao Project
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.
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
### Start Pull Request

View File

@@ -1,4 +1,4 @@
![HutaoRepoBanner2-20231222](https://github.com/DGP-Studio/Snap.Hutao/assets/10614984/2d178de1-95bc-44a1-a95e-20c5f11a8628)
![Banner2-large](https://github.com/DGP-Studio/Snap.Hutao/assets/10614984/742129f4-f903-4d16-bf1e-3cbfad873ee4)
胡桃工具箱是一款以 MIT 协议开源的原神工具箱,专为现代化 Windows 平台设计,旨在改善桌面端玩家的游戏体验。通过将既有的官方资源与开发团队设计的全新 功能相结合,它提供了一套完整且实用的工具集,且无需依赖任何移动设备。它不对游戏客户端进行任何破坏性修改以确保工具箱的安全性
@@ -7,29 +7,7 @@ Snap Hutao is an open-source Genshin Impact toolkit under MIT license, designed
## 下载使用 / Download
![](https://ci.appveyor.com/api/projects/status/n4s40t9llru4si9y?svg=true) [![GitHub Release](https://img.shields.io/github/release/DGP-Studio/Snap.Hutao?style=flat)](https://github.com/DGP-Studio/Snap.Hutao/releases/latest) [![Github All Releases](https://img.shields.io/github/downloads/DGP-Studio/Snap.Hutao/total.svg?style=flat)]()
---
#### 使用安装器安装 / Install with Snap.Hutao.Depolyment Installer
Snap.Hutao.Depolyment 是一个由 DGP-Studio 重新包装的 Windows 应用安装器,适用于缺少专业计算机知识的一般用户,可以在安装时同时解决缺少必要系统环境的问题。
Snap.Hutao.Depolyment is a Windows application installer repackaged by DGP-Studio for the users who lacks computer knowledge and can solve the problem of missing necessary system environment at the same time as the installation.
[从 GitHub 发布页获取 / Download from GitHub release](https://github.com/DGP-Studio/Snap.Hutao.Deployment/releases/latest)
[从极狐Lab 发布页获取 / Download from Jihu Gitlab release](https://jihulab.com/DGP-Studio/Snap.Hutao.Deployment/-/releases)
#### 使用 MSIX 包安装 / Install with MSIX Package
直接使用 Snap Hutao MSIX 安装包,使用 Windows 内置的 App Installer 即可安装。如在安装中出现问题,请查阅我们的[常见问题](https://hut.ao/zh/advanced/FAQ.html)文档
Install with Snap Hutao MSIX package, can be installed with Windows built-in App Installer. If you faced any issue, please check our [FAQ](https://hut.ao/en/advanced/FAQ.html) document.
[从 GitHub 发布页获取 / Download from GitHub release](https://github.com/DGP-Studio/Snap.Hutao/releases/latest)
[从极狐Lab 发布页获取 / Download from Jihu Gitlab release](https://jihulab.com/DGP-Studio/Snap.Hutao/-/releases)
[<img src="https://get.microsoft.com/images/zh-cn%20light.svg" width="30%" height="30%">](https://apps.microsoft.com/store/detail/snap-hutao/9PH4NXJ2JN52)
## 贡献 / Contribute

View File

@@ -124,6 +124,9 @@ 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
@@ -322,6 +325,7 @@ 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]
#### 命名样式 ####

View File

@@ -1,11 +0,0 @@
{
"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"
]
}

View File

@@ -11,12 +11,6 @@ namespace Snap.Hutao.Test.IncomingFeature;
[TestClass]
public class GameRegistryContentTest
{
private static readonly JsonSerializerOptions RegistryContentSerializerOptions = new()
{
WriteIndented = true,
Encoder = System.Text.Encodings.Web.JavaScriptEncoder.UnsafeRelaxedJsonEscaping,
};
[TestMethod]
[SupportedOSPlatform("windows")]
public void GetRegistryContent()
@@ -39,29 +33,28 @@ public class GameRegistryContentTest
data[valueName] = gameKey.GetValueKind(valueName) switch
{
RegistryValueKind.DWord => (int)gameKey.GetValue(valueName)!,
RegistryValueKind.Binary => GetStringOrObject((byte[])gameKey.GetValue(valueName)!),
RegistryValueKind.Binary => GetString((byte[])gameKey.GetValue(valueName)!),
_ => throw new NotImplementedException()
};
}
JsonSerializerOptions options = new()
{
WriteIndented = true,
Encoder = System.Text.Encodings.Web.JavaScriptEncoder.UnsafeRelaxedJsonEscaping,
};
Console.WriteLine($"Subkey: {subkey}");
Console.WriteLine(JsonSerializer.Serialize(data, RegistryContentSerializerOptions));
Console.WriteLine(JsonSerializer.Serialize(data, options));
}
}
private static unsafe object GetStringOrObject(byte[] bytes)
private static unsafe string GetString(byte[] bytes)
{
fixed (byte* pByte = bytes)
{
ReadOnlySpan<byte> span = MemoryMarshal.CreateReadOnlySpanFromNullTerminated(pByte);
string temp = Encoding.UTF8.GetString(span);
if (temp.AsSpan()[0] is '{' or '[')
{
return JsonSerializer.Deserialize<JsonElement>(temp);
}
return temp;
return Encoding.UTF8.GetString(span);
}
}
}

View File

@@ -1,9 +1,4 @@
// ADVAPI32
RegCloseKey
RegOpenKeyExW
RegNotifyChangeKeyValue
// COMCTL32
// COMCTL32
DefSubclassProc
RemoveWindowSubclass
SetWindowSubclass
@@ -52,14 +47,12 @@ GetCursorPos
GetDC
GetDpiForWindow
GetForegroundWindow
GetWindowLongPtrW
GetWindowPlacement
GetWindowThreadProcessId
ReleaseDC
RegisterHotKey
SendInput
SetForegroundWindow
SetWindowLongPtrW
UnregisterHotKey
// COM
@@ -76,42 +69,26 @@ 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
// 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
// Type & Enum definition
HRESULT_FROM_WIN32
SLGP_FLAGS
// System.Threading
LPTHREAD_START_ROUTINE
// UI.Shell
SLGP_FLAGS
// UI.WindowsAndMessaging
MINMAXINFO
WINDOW_EX_STYLE
// System.Com
CWMO_FLAGS

View File

@@ -1,10 +1,9 @@
<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>

View File

@@ -0,0 +1,72 @@
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);
}
}
}

View File

@@ -8,7 +8,6 @@ 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}"
@@ -88,11 +87,11 @@ Global
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
RESX_Rules = {"EnabledRules":["StringFormat","WhiteSpaceLead","WhiteSpaceTail","PunctuationLead"]}
RESX_ShowErrorsInErrorList = False
RESX_SortFileContentOnSave = True
SolutionGuid = {E4449B1C-0E6A-4D19-955E-1CA491656ABA}
RESX_NeutralResourcesLanguage = zh-CN
RESX_AutoApplyExistingTranslations = False
RESX_NeutralResourcesLanguage = zh-CN
SolutionGuid = {E4449B1C-0E6A-4D19-955E-1CA491656ABA}
RESX_SortFileContentOnSave = True
RESX_ShowErrorsInErrorList = False
RESX_Rules = {"EnabledRules":["StringFormat","WhiteSpaceLead","WhiteSpaceTail","PunctuationLead"]}
EndGlobalSection
EndGlobal

View File

@@ -4,6 +4,8 @@
using Microsoft.UI.Xaml.Media;
using Microsoft.UI.Xaml.Media.Imaging;
using Snap.Hutao.Core.Caching;
using Snap.Hutao.Model.Metadata.Converter;
using Snap.Hutao.Web;
using System.Runtime.InteropServices;
namespace Snap.Hutao.Control.Image;

View File

@@ -7,7 +7,6 @@ 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));

View File

@@ -3,9 +3,7 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:shc="using:Snap.Hutao.Control">
<Style BasedOn="{StaticResource DefaultLoadingStyle}" TargetType="shc:Loading"/>
<Style x:Key="DefaultLoadingStyle" TargetType="shc:Loading">
<Style TargetType="shc:Loading">
<Setter Property="HorizontalContentAlignment" Value="Center"/>
<Setter Property="VerticalContentAlignment" Value="Center"/>
<Setter Property="HorizontalAlignment" Value="Stretch"/>

View File

@@ -18,15 +18,12 @@ 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,
};
}
}

View File

@@ -156,7 +156,7 @@ internal sealed partial class HtmlDescriptionTextBlock : ContentControl
text.Inlines.Add(new Run
{
Text = slice.ToString(),
FontWeight = FontWeights.SemiBold,
FontWeight = FontWeights.Bold,
});
}

View File

@@ -20,9 +20,6 @@
<ItemsPanelTemplate x:Key="StackPanelSpacing4Template">
<StackPanel Spacing="4"/>
</ItemsPanelTemplate>
<ItemsPanelTemplate x:Key="StackPanelSpacing8Template">
<StackPanel Spacing="8"/>
</ItemsPanelTemplate>
<ItemsPanelTemplate x:Key="UniformGridColumns2Spacing2Template">
<cwcont:UniformGrid
ColumnSpacing="2"

View File

@@ -6,12 +6,6 @@
<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"

View File

@@ -27,7 +27,6 @@
<!-- 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>

View File

@@ -2,11 +2,9 @@
// 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;
@@ -43,7 +41,6 @@ internal static class DependencyInjection
serviceProvider.InitializeConsoleWindow();
serviceProvider.InitializeCulture();
serviceProvider.InitializedDynamicHttpProxy();
return serviceProvider;
}
@@ -51,10 +48,10 @@ internal static class DependencyInjection
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static void InitializeCulture(this IServiceProvider serviceProvider)
{
CultureOptions cultureOptions = serviceProvider.GetRequiredService<CultureOptions>();
cultureOptions.SystemCulture = CultureInfo.CurrentCulture;
AppOptions appOptions = serviceProvider.GetRequiredService<AppOptions>();
appOptions.PreviousCulture = CultureInfo.CurrentCulture;
CultureInfo cultureInfo = cultureOptions.CurrentCulture;
CultureInfo cultureInfo = appOptions.CurrentCulture;
CultureInfo.DefaultThreadCurrentCulture = cultureInfo;
CultureInfo.DefaultThreadCurrentUICulture = cultureInfo;
@@ -70,9 +67,4 @@ internal static class DependencyInjection
{
_ = serviceProvider.GetRequiredService<ConsoleWindowLifeTime>();
}
private static void InitializedDynamicHttpProxy(this IServiceProvider serviceProvider)
{
HttpClient.DefaultProxy = serviceProvider.GetRequiredService<DynamicHttpProxy>();
}
}

View File

@@ -38,8 +38,8 @@ internal static class IocConfiguration
private static void AddDbContextCore(IServiceProvider provider, DbContextOptionsBuilder builder)
{
RuntimeOptions runtimeOptions = provider.GetRequiredService<RuntimeOptions>();
string dbFile = System.IO.Path.Combine(runtimeOptions.DataFolder, "Userdata.db");
RuntimeOptions hutaoOptions = provider.GetRequiredService<RuntimeOptions>();
string dbFile = System.IO.Path.Combine(hutaoOptions.DataFolder, "Userdata.db");
string sqlConnectionString = $"Data Source={dbFile}";
// Temporarily create a context

View File

@@ -29,10 +29,10 @@ internal static partial class IocHttpClientConfiguration
/// <param name="client">配置后的客户端</param>
private static void DefaultConfiguration(IServiceProvider serviceProvider, HttpClient client)
{
RuntimeOptions runtimeOptions = serviceProvider.GetRequiredService<RuntimeOptions>();
RuntimeOptions hutaoOptions = serviceProvider.GetRequiredService<RuntimeOptions>();
client.Timeout = Timeout.InfiniteTimeSpan;
client.DefaultRequestHeaders.UserAgent.ParseAdd(runtimeOptions.UserAgent);
client.DefaultRequestHeaders.UserAgent.ParseAdd(hutaoOptions.UserAgent);
}
/// <summary>

View File

@@ -1,87 +0,0 @@
// 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();
}
}

View File

@@ -11,14 +11,11 @@ namespace Snap.Hutao.Core.IO.Ini;
[HighQuality]
internal static class IniSerializer
{
public static List<IniElement> DeserializeFromFile(string filePath)
{
using (FileStream readStream = File.OpenRead(filePath))
{
return Deserialize(readStream);
}
}
/// <summary>
/// 反序列化
/// </summary>
/// <param name="fileStream">文件流</param>
/// <returns>Ini 元素集合</returns>
public static List<IniElement> Deserialize(FileStream fileStream)
{
List<IniElement> results = [];
@@ -53,14 +50,11 @@ internal static class IniSerializer
return results;
}
public static void SerializeToFile(string filePath, IEnumerable<IniElement> elements)
{
using (FileStream writeStream = File.Create(filePath))
{
Serialize(writeStream, elements);
}
}
/// <summary>
/// 序列化
/// </summary>
/// <param name="fileStream">写入的流</param>
/// <param name="elements">元素</param>
public static void Serialize(FileStream fileStream, IEnumerable<IniElement> elements)
{
using (StreamWriter writer = new(fileStream))

View File

@@ -190,7 +190,7 @@ internal sealed partial class Activation : IActivation
serviceProvider
.GetRequiredService<IDiscordService>()
.SetNormalActivityAsync()
.SetNormalActivity()
.SafeForget();
}

View File

@@ -13,9 +13,4 @@ internal static class RuntimeOptionsExtension
Directory.CreateDirectory(directory);
return Path.Combine(directory, fileName);
}
public static string GetDataFolderServerCacheFolder(this RuntimeOptions options)
{
return Path.Combine(options.DataFolder, "ServerCache");
}
}

View File

@@ -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 = "IsAllocConsoleDebugModeEnabled2";
public const string IsAllocConsoleDebugModeEnabled = "IsAllocConsoleDebugModeEnabled";
#endregion
#region Passport
@@ -57,9 +57,4 @@ internal static class SettingKeys
public const string OverrideElevationRequirement = "OverrideElevationRequirement";
public const string OverrideUpdateVersionComparison = "OverrideUpdateVersionComparison";
#endregion
#region Obsolete
[Obsolete("重置调试控制台开关")]
public const string IsAllocConsoleDebugModeEnabledLegacy1 = "IsAllocConsoleDebugModeEnabled";
#endregion
}

View File

@@ -39,11 +39,6 @@ internal sealed class ScheduleTaskInterop : IScheduleTaskInterop
}
catch (Exception)
{
if (WScriptExists(DailyNoteRefreshScriptName, out string fullPath))
{
File.Delete(fullPath);
}
return false;
}
}
@@ -68,7 +63,7 @@ internal sealed class ScheduleTaskInterop : IScheduleTaskInterop
public bool IsDailyNoteRefreshEnabled()
{
return TaskService.Instance.RootFolder.Tasks.Any(task => task.Name is DailyNoteRefreshTaskName);
return WScriptExists(DailyNoteRefreshScriptName, out _);
}
/// <summary>

View File

@@ -69,9 +69,4 @@ 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);
}
}

View File

@@ -8,12 +8,12 @@ internal readonly struct Delay
/// <summary>
/// 随机延迟
/// </summary>
/// <param name="min">最小,闭</param>
/// <param name="max">最小,开</param>
/// <param name="minMilliSeconds">最小,闭</param>
/// <param name="maxMilliSeconds">最小,开</param>
/// <returns>任务</returns>
public static ValueTask RandomMilliSeconds(int min, int max)
public static ValueTask Random(int minMilliSeconds, int maxMilliSeconds)
{
return Task.Delay((int)(System.Random.Shared.NextDouble() * (max - min)) + min).AsValueTask();
return Task.Delay((int)(System.Random.Shared.NextDouble() * (maxMilliSeconds - minMilliSeconds)) + minMilliSeconds).AsValueTask();
}
public static ValueTask FromSeconds(int seconds)

View File

@@ -1,18 +0,0 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
namespace Snap.Hutao.Core.Threading;
internal delegate bool SpinWaitPredicate<T>(ref readonly T state);
internal static class SpinWaitPolyfill
{
public static unsafe void SpinUntil<T>(ref T state, delegate*<ref readonly T, bool> condition)
{
SpinWait spinner = default;
while (!condition(ref state))
{
spinner.SpinOnce();
}
}
}

View File

@@ -1,67 +0,0 @@
// 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;

View File

@@ -9,8 +9,6 @@ namespace Snap.Hutao.Core.Windowing;
[HighQuality]
internal enum BackdropType
{
Transparent = -1,
/// <summary>
/// 无
/// </summary>

View File

@@ -53,10 +53,10 @@ internal sealed class WindowController
private void InitializeCore()
{
RuntimeOptions runtimeOptions = serviceProvider.GetRequiredService<RuntimeOptions>();
RuntimeOptions hutaoOptions = serviceProvider.GetRequiredService<RuntimeOptions>();
window.AppWindow.Title = SH.FormatAppNameAndVersion(runtimeOptions.Version);
window.AppWindow.SetIcon(Path.Combine(runtimeOptions.InstalledLocation, "Assets/Logo.ico"));
window.AppWindow.Title = SH.FormatAppNameAndVersion(hutaoOptions.Version);
window.AppWindow.SetIcon(Path.Combine(hutaoOptions.InstalledLocation, "Assets/Logo.ico"));
ExtendsContentIntoTitleBar();
RecoverOrInitWindowSize();
@@ -157,7 +157,6 @@ 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(),

View File

@@ -3,10 +3,6 @@
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;
@@ -20,12 +16,4 @@ 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);
}
}

View File

@@ -2,7 +2,6 @@
// 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;
@@ -111,16 +110,6 @@ 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);

View File

@@ -1,19 +0,0 @@
<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>

View File

@@ -1,30 +0,0 @@
// 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; }
}

View File

@@ -11,12 +11,6 @@ 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))];

View File

@@ -8,45 +8,123 @@ namespace Snap.Hutao.Model.Entity;
/// </summary>
internal sealed partial class SettingEntry
{
/// <summary>
/// 游戏路径
/// </summary>
public const string GamePath = "GamePath";
public const string GamePathEntries = "GamePathEntries";
public const string Culture = "Culture";
public const string SystemBackdropType = "SystemBackdropType";
public const string AnnouncementRegion = "AnnouncementRegion";
public const string IsEmptyHistoryWishVisible = "IsEmptyHistoryWishVisible";
public const string GeetestCustomCompositeUrl = "GeetestCustomCompositeUrl";
public const string DailyNoteRefreshSeconds = "DailyNote.RefreshSeconds";
public const string DailyNoteReminderNotify = "DailyNote.ReminderNotify";
public const string DailyNoteSilentWhenPlayingGame = "DailyNote.SilentWhenPlayingGame";
public const string DailyNoteWebhookUrl = "DailyNote.WebhookUrl";
public const string IsAdvancedLaunchOptionsEnabled = "IsAdvancedLaunchOptionsEnabled";
public const string LaunchIsLaunchOptionsEnabled = "Launch.IsLaunchOptionsEnabled";
public const string LaunchIsExclusive = "Launch.IsExclusive";
public const string LaunchIsFullScreen = "Launch.IsFullScreen";
public const string LaunchIsBorderless = "Launch.IsBorderless";
public const string LaunchScreenWidth = "Launch.ScreenWidth";
public const string LaunchIsScreenWidthEnabled = "Launch.IsScreenWidthEnabled";
public const string LaunchScreenHeight = "Launch.ScreenHeight";
public const string LaunchIsScreenHeightEnabled = "Launch.IsScreenHeightEnabled";
public const string LaunchUnlockFps = "Launch.UnlockFps";
public const string LaunchTargetFps = "Launch.TargetFps";
public const string LaunchMonitor = "Launch.Monitor";
public const string LaunchIsMonitorEnabled = "Launch.IsMonitorEnabled";
public const string LaunchIsUseCloudThirdPartyMobile = "Launch.IsUseCloudThirdPartyMobile";
public const string LaunchIsWindowsHDREnabled = "Launch.IsWindowsHDREnabled";
public const string LaunchUseStarwardPlayTimeStatistics = "Launch.UseStarwardPlayTimeStatistics";
public const string LaunchSetDiscordActivityWhenPlaying = "Launch.SetDiscordActivityWhenPlaying";
[Obsolete("不再支持多开")]
public const string MultipleInstances = "Launch.MultipleInstances";
[Obsolete("不再使用 PowerShell")]
public const string PowerShellPath = "PowerShellPath";
/// <summary>
/// 空的历史记录卡池是否可见
/// </summary>
public const string IsEmptyHistoryWishVisible = "IsEmptyHistoryWishVisible";
/// <summary>
/// 窗口背景类型
/// </summary>
public const string SystemBackdropType = "SystemBackdropType";
/// <summary>
/// 启用高级功能
/// </summary>
public const string IsAdvancedLaunchOptionsEnabled = "IsAdvancedLaunchOptionsEnabled";
/// <summary>
/// 实时便笺刷新时间
/// </summary>
public const string DailyNoteRefreshSeconds = "DailyNote.RefreshSeconds";
/// <summary>
/// 实时便笺提醒式通知
/// </summary>
public const string DailyNoteReminderNotify = "DailyNote.ReminderNotify";
/// <summary>
/// 实时便笺免打扰模式
/// </summary>
public const string DailyNoteSilentWhenPlayingGame = "DailyNote.SilentWhenPlayingGame";
/// <summary>
/// 实时便笺 WebhookUrl
/// </summary>
public const string DailyNoteWebhookUrl = "DailyNote.WebhookUrl";
/// <summary>
/// 启动游戏 总开关
/// </summary>
public const string LaunchIsLaunchOptionsEnabled = "Launch.IsLaunchOptionsEnabled";
/// <summary>
/// 启动游戏 独占全屏
/// </summary>
public const string LaunchIsExclusive = "Launch.IsExclusive";
/// <summary>
/// 启动游戏 全屏
/// </summary>
public const string LaunchIsFullScreen = "Launch.IsFullScreen";
/// <summary>
/// 启动游戏 无边框
/// </summary>
public const string LaunchIsBorderless = "Launch.IsBorderless";
/// <summary>
/// 启动游戏 宽度
/// </summary>
public const string LaunchScreenWidth = "Launch.ScreenWidth";
public const string LaunchIsScreenWidthEnabled = "Launch.IsScreenWidthEnabled";
/// <summary>
/// 启动游戏 高度
/// </summary>
public const string LaunchScreenHeight = "Launch.ScreenHeight";
public const string LaunchIsScreenHeightEnabled = "Launch.IsScreenHeightEnabled";
/// <summary>
/// 启动游戏 解锁帧率
/// </summary>
public const string LaunchUnlockFps = "Launch.UnlockFps";
/// <summary>
/// 启动游戏 目标帧率
/// </summary>
public const string LaunchTargetFps = "Launch.TargetFps";
/// <summary>
/// 启动游戏 显示器编号
/// </summary>
public const string LaunchMonitor = "Launch.Monitor";
public const string LaunchIsMonitorEnabled = "Launch.IsMonitorEnabled";
public const string LaunchIsUseCloudThirdPartyMobile = "Launch.IsUseCloudThirdPartyMobile";
public const string LaunchUseStarwardPlayTimeStatistics = "Launch.UseStarwardPlayTimeStatistics";
public const string LaunchSetDiscordActivityWhenPlaying = "Launch.SetDiscordActivityWhenPlaying";
/// <summary>
/// 启动游戏 多倍启动
/// </summary>
[Obsolete("不再支持多开")]
public const string MultipleInstances = "Launch.MultipleInstances";
/// <summary>
/// 语言
/// </summary>
public const string Culture = "Culture";
/// <summary>
/// 自定义极验接口
/// </summary>
public const string GeetestCustomCompositeUrl = "GeetestCustomCompositeUrl";
public const string AnnouncementRegion = "AnnouncementRegion";
}

View File

@@ -3,7 +3,7 @@
using Snap.Hutao.Core;
using Snap.Hutao.Core.Abstraction;
using Snap.Hutao.Service;
using Snap.Hutao.Service.Metadata;
using Snap.Hutao.Web.Hoyolab;
namespace Snap.Hutao.Model.InterChange.GachaLog;
@@ -12,7 +12,7 @@ namespace Snap.Hutao.Model.InterChange.GachaLog;
/// UIGF格式的信息
/// </summary>
[HighQuality]
internal sealed class UIGFInfo : IMappingFrom<UIGFInfo, RuntimeOptions, CultureOptions, string>
internal sealed class UIGFInfo : IMappingFrom<UIGFInfo, RuntimeOptions, MetadataOptions, string>
{
/// <summary>
/// 用户Uid
@@ -65,12 +65,12 @@ internal sealed class UIGFInfo : IMappingFrom<UIGFInfo, RuntimeOptions, CultureO
[JsonPropertyName("region_time_zone")]
public int? RegionTimeZone { get; set; } = default!;
public static UIGFInfo From(RuntimeOptions runtimeOptions, CultureOptions cultureOptions, string uid)
public static UIGFInfo From(RuntimeOptions runtimeOptions, MetadataOptions metadataOptions, string uid)
{
return new()
{
Uid = uid,
Language = cultureOptions.LanguageCode,
Language = metadataOptions.LanguageCode,
ExportTimestamp = DateTimeOffset.UtcNow.ToUnixTimeSeconds(),
ExportApp = SH.AppName,
ExportAppVersion = runtimeOptions.Version.ToString(),

View File

@@ -64,14 +64,14 @@ internal sealed class UIIFInfo
/// <returns>专用 UIGF 信息</returns>
public static UIIFInfo From(IServiceProvider serviceProvider, string uid)
{
RuntimeOptions runtimeOptions = serviceProvider.GetRequiredService<RuntimeOptions>();
RuntimeOptions hutaoOptions = serviceProvider.GetRequiredService<RuntimeOptions>();
return new()
{
Uid = uid,
ExportTimestamp = DateTimeOffset.UtcNow.ToUnixTimeSeconds(),
ExportApp = SH.AppName,
ExportAppVersion = runtimeOptions.Version.ToString(),
ExportAppVersion = hutaoOptions.Version.ToString(),
UIIFVersion = UIIF.CurrentVersion,
};
}

View File

@@ -13,7 +13,7 @@
<Identity
Name="60568DGPStudio.SnapHutao"
Publisher="CN=35C8E923-85DF-49A7-9172-B39DC6312C52"
Version="1.9.5.0" />
Version="1.9.1.0" />
<Properties>
<DisplayName>Snap Hutao</DisplayName>

View File

@@ -13,7 +13,7 @@
<Identity
Name="60568DGPStudio.SnapHutaoDev"
Publisher="CN=35C8E923-85DF-49A7-9172-B39DC6312C52"
Version="1.9.5.0" />
Version="1.9.1.0" />
<Properties>
<DisplayName>Snap Hutao Dev</DisplayName>

View File

@@ -120,12 +120,6 @@
<data name="AppDevNameAndVersion" xml:space="preserve">
<value>Snap Hutao Dev {0}</value>
</data>
<data name="AppElevatedDevNameAndVersion" xml:space="preserve">
<value>Snap Hutao Dev {0} [Administrator]</value>
</data>
<data name="AppElevatedNameAndVersion" xml:space="preserve">
<value>Snap Hutao {0} [Administrator]</value>
</data>
<data name="AppName" xml:space="preserve">
<value>Snap Hutao</value>
</data>
@@ -247,7 +241,7 @@
<value>Not refreshed</value>
</data>
<data name="ModelEntityDailyNoteRefreshTimeFormat" xml:space="preserve">
<value>Refreshed at {0:MM.dd HH:mm:ss}</value>
<value>Refresh at {0:MM.dd HH:mm:ss}</value>
</data>
<data name="ModelEntitySpiralAbyssScheduleFormat" xml:space="preserve">
<value>Schedule {0}</value>
@@ -738,10 +732,10 @@
<value>Avatar Calculator: {0:MM-dd HH:mm}</value>
</data>
<data name="ServiceAvatarInfoSummaryGameRecordNotRefreshed" xml:space="preserve">
<value>Battle Chronicle: Not Refreshed</value>
<value>My Characters: N/A</value>
</data>
<data name="ServiceAvatarInfoSummaryGameRecordRefreshTimeFormat" xml:space="preserve">
<value>Battle Chronicle: {0:MM-dd HH:mm}</value>
<value>My Characters: {0:MM-dd HH:mm}</value>
</data>
<data name="ServiceAvatarInfoSummaryShowcaseNotRefreshed" xml:space="preserve">
<value>Enka: N/A</value>
@@ -870,23 +864,11 @@
<value>No writing permission in file system, unable to start the server conversion.</value>
</data>
<data name="ServiceGameEnsureGameResourceQueryResourceInformation" xml:space="preserve">
<value>Download game resource index</value>
<value>Querying Game Resource Information</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>
@@ -1283,12 +1265,6 @@
<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 &lt;b&gt;title name&lt;/b&gt; 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>
@@ -1310,9 +1286,6 @@
<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>
@@ -1389,7 +1362,7 @@
<value>This operation is irreversible. The achievement archive will be lost.</value>
</data>
<data name="ViewModelAchievementRemoveArchiveTitle" xml:space="preserve">
<value>Are you sure to delete archive {0}?</value>
<value>Are you sure you want to delete archive {0}?</value>
</data>
<data name="ViewModelAchievementUIAFExportPickerTitle" xml:space="preserve">
<value>Export UIAF Json file to the selected path</value>
@@ -1451,9 +1424,6 @@
<data name="ViewModelDailyNoteHoyolabVerificationUnsupported" xml:space="preserve">
<value>HoYoLab account does not support Realtime Notes verification</value>
</data>
<data name="ViewModelDailyNoteModifyTaskFail" xml:space="preserve">
<value>Failed to modify Scheduled Task</value>
</data>
<data name="ViewModelDailyNoteRefreshTime30" xml:space="preserve">
<value>30 min | 3.75 Resin</value>
</data>
@@ -1469,6 +1439,9 @@
<data name="ViewModelDailyNoteRefreshTime8" xml:space="preserve">
<value>8 min | 1 Resin</value>
</data>
<data name="ViewModelDailyNoteRegisterTaskFail" xml:space="preserve">
<value>Failed to add Scheduled Task, please try again in administrator mode</value>
</data>
<data name="ViewModelDailyNoteRequestProgressTitle" xml:space="preserve">
<value>Fetching Realtime Notes data, please wait</value>
</data>
@@ -1562,9 +1535,6 @@
<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>
@@ -1577,9 +1547,6 @@
<data name="ViewModelLaunchGameSwitchGameAccountFail" xml:space="preserve">
<value>Switch game account failed</value>
</data>
<data name="ViewModelLaunchGameUnableToSwitchUidAttachedGameAccount" xml:space="preserve">
<value>Cannot select account [{1}] for Uid [{0}], the account does not belong to current server</value>
</data>
<data name="ViewModelSettingActionComplete" xml:space="preserve">
<value>Operation completed</value>
</data>
@@ -1598,12 +1565,6 @@
<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>
@@ -1656,7 +1617,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>
@@ -1677,7 +1638,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>
@@ -1701,7 +1662,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>
@@ -1728,7 +1689,7 @@
<value>Sync character talents data</value>
</data>
<data name="ViewPageAvatarPropertyRefreshFromHoyolabGameRecord" xml:space="preserve">
<value>Sync from MiHoYo BBS Battle Chronicle</value>
<value>Sync from MiHoYo BBS My Characters</value>
</data>
<data name="ViewPageAvatarPropertyRefreshFromHoyolabGameRecordDescription" xml:space="preserve">
<value>Sync most data other than character talent</value>
@@ -1752,7 +1713,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>
@@ -1853,36 +1814,6 @@
<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>
@@ -1935,7 +1866,7 @@
<value>Input</value>
</data>
<data name="ViewPageGachaLogRecoverFromHutaoCloudDescription" xml:space="preserve">
<value>Recover Wish Records from Snap Hutao Cloud</value>
<value>Recover Wish Record from Snap Hutao Cloud</value>
</data>
<data name="ViewPageGachaLogRefresh" xml:space="preserve">
<value>Refresh</value>
@@ -2115,10 +2046,10 @@
<value>Borderless</value>
</data>
<data name="ViewPageLaunchGameAppearanceCloudThirdPartyMobileDescription" xml:space="preserve">
<value>Enable touchscreen layout, but the keyboard &amp; mouse are no longer usable</value>
<value>Enable touchscreen layout, but the keyboard &amp; 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>
@@ -2148,7 +2079,7 @@
<value>Modify its default behavior at game startup</value>
</data>
<data name="ViewPageLaunchGameArgumentsHeader" xml:space="preserve">
<value>Launch Parameters</value>
<value>Start-up Arguments</value>
</data>
<data name="ViewPageLaunchGameCommonHeader" xml:space="preserve">
<value>General</value>
@@ -2202,14 +2133,11 @@
<value>Resource Download</value>
</data>
<data name="ViewPageLaunchGameResourceLatestHeader" xml:space="preserve">
<value>Full Package</value>
<value>Client</value>
</data>
<data name="ViewPageLaunchGameResourcePreDownloadHeader" xml:space="preserve">
<value>Pre-download</value>
</data>
<data name="ViewPageLaunchGameSelectGamePath" xml:space="preserve">
<value>Select Game Path</value>
</data>
<data name="ViewPageLaunchGameSwitchAccountAttachUidNull" xml:space="preserve">
<value>This account has not been bound to the Real-time Note notification UID.</value>
</data>
@@ -2252,12 +2180,6 @@
<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>
@@ -2271,7 +2193,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>
@@ -2348,15 +2270,6 @@
<data name="ViewPageSettingDeviceIpHeader" xml:space="preserve">
<value>Device IP</value>
</data>
<data name="ViewPageSettingElevatedModeDescription" xml:space="preserve">
<value>Administrator mode will change some features' availability and behaviors</value>
</data>
<data name="ViewPageSettingElevatedModeHeader" xml:space="preserve">
<value>Administrator Mode</value>
</data>
<data name="ViewPageSettingElevatedModeRestartAction" xml:space="preserve">
<value>Restart with Administrator Mode</value>
</data>
<data name="ViewPageSettingEmptyHistoryVisibleDescription" xml:space="preserve">
<value>Show or hide wish event with no wish history</value>
</data>
@@ -2475,7 +2388,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>Advanced Features</value>
<value>Enable Advanced Features</value>
</data>
<data name="ViewPageSettingKeyShortcutAutoClickingDescription" xml:space="preserve">
<value>Change Auto Click Shortcut</value>
@@ -2630,18 +2543,9 @@
<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>
@@ -2736,7 +2640,7 @@
<value>Web Login</value>
</data>
<data name="ViewUserCookieOperationLoginQRCodeAction" xml:space="preserve">
<value>Login via QR code</value>
<value>Login via scanning QR code</value>
</data>
<data name="ViewUserCookieOperationManualInputAction" xml:space="preserve">
<value>Input Manually</value>
@@ -2822,21 +2726,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>
<data name="WebDailyNoteArchonQuestStatusFinished" xml:space="preserve">
<value>Completed</value>
</data>
<data name="WebDailyNoteArchonQuestStatusNotOpen" xml:space="preserve">
<value>Locked</value>
</data>
<data name="WebDailyNoteArchonQuestStatusOngoing" xml:space="preserve">
<value>In Progress</value>
</data>
<data name="WebDailyNoteAttendanceRewardStatusFinishedNonReward" xml:space="preserve">
<value>Finished</value>
</data>
@@ -2889,10 +2781,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>
@@ -2925,7 +2817,7 @@
<value>{0} Seconds</value>
</data>
<data name="WebDailyNoteVerificationFailed" xml:space="preserve">
<value>Verification failed. Please check MiHoYo BBS - Toolbox - Realtime Notes manually</value>
<value>Verification failed. Please check MiHoYo BBS - My Character - Realtime Notes manually</value>
</data>
<data name="WebEnkaResponseStatusCode400" xml:space="preserve">
<value>Wrong UID format</value>
@@ -2994,7 +2886,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 - Battle Chronicle page</value>
<value>Verification failed. Please verify manually or check MiHoYo BBS - My Characters page</value>
</data>
<data name="WebResponseFormat" xml:space="preserve">
<value>Return Code: {0} | Message: {1}</value>
@@ -3005,7 +2897,4 @@
<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>

View File

@@ -120,12 +120,6 @@
<data name="AppDevNameAndVersion" xml:space="preserve">
<value>Snap Hutao Dev {0}</value>
</data>
<data name="AppElevatedDevNameAndVersion" xml:space="preserve">
<value>胡桃Dev {0} [管理员]</value>
</data>
<data name="AppElevatedNameAndVersion" xml:space="preserve">
<value>Snap Hutao {0} [Administrator]</value>
</data>
<data name="AppName" xml:space="preserve">
<value>Snap Hutao</value>
</data>
@@ -738,10 +732,10 @@
<value>Kalkulator Avatar: {0:MM-dd HH:mm}</value>
</data>
<data name="ServiceAvatarInfoSummaryGameRecordNotRefreshed" xml:space="preserve">
<value>原神战绩:尚未刷新</value>
<value>Karakter Saya: N/A</value>
</data>
<data name="ServiceAvatarInfoSummaryGameRecordRefreshTimeFormat" xml:space="preserve">
<value>原神战绩:{0:MM-dd HH:mm}</value>
<value>Karakter Saya: {0:MM-dd HH:mm}</value>
</data>
<data name="ServiceAvatarInfoSummaryShowcaseNotRefreshed" xml:space="preserve">
<value>Enka: N/A</value>
@@ -870,23 +864,11 @@
<value>Tidak ada izin menulis dalam sistem berkas, tidak dapat memulai konversi server.</value>
</data>
<data name="ServiceGameEnsureGameResourceQueryResourceInformation" xml:space="preserve">
<value>Unduh indeks sumber daya game</value>
<value>Mencari Informasi 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>
@@ -1008,7 +990,7 @@
<value>Versi UIGF tidak didukung</value>
</data>
<data name="ServiceUpdateStatusVersionDescription" xml:space="preserve">
<value>Versi terbau {0} sudah tersedia.</value>
<value>发现新版本 {0}</value>
</data>
<data name="ServiceUserCurrentMultiMatched" xml:space="preserve">
<value>Beberapa pengguna tercatat sebagai dipilih</value>
@@ -1283,12 +1265,6 @@
<data name="ViewDialogQRCodeTitle" xml:space="preserve">
<value>Pindai kode QR dengan Aplikasi MiHoYo BBS</value>
</data>
<data name="ViewDialogReconfirmTextHeader" xml:space="preserve">
<value>为防止你在无意间启用,请输入正在启用的功能开关的&lt;b&gt;标题名称&lt;/b&gt;</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>
@@ -1310,9 +1286,6 @@
<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>
@@ -1451,9 +1424,6 @@
<data name="ViewModelDailyNoteHoyolabVerificationUnsupported" xml:space="preserve">
<value>Akun HoYoLab tidak mendukung verifikasi Catatan Realtime.</value>
</data>
<data name="ViewModelDailyNoteModifyTaskFail" xml:space="preserve">
<value>Gagal mengubah tugas terjadwal</value>
</data>
<data name="ViewModelDailyNoteRefreshTime30" xml:space="preserve">
<value>30 min | 3.75 Resin</value>
</data>
@@ -1469,6 +1439,9 @@
<data name="ViewModelDailyNoteRefreshTime8" xml:space="preserve">
<value>8 min | 1 Resin</value>
</data>
<data name="ViewModelDailyNoteRegisterTaskFail" xml:space="preserve">
<value>Gagal menambahkan Tugas Terjadwal, silakan coba lagi dalam mode administrator.</value>
</data>
<data name="ViewModelDailyNoteRequestProgressTitle" xml:space="preserve">
<value>Mengambil data Catatan Realtime, mohon tunggu.</value>
</data>
@@ -1562,9 +1535,6 @@
<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>
@@ -1577,9 +1547,6 @@
<data name="ViewModelLaunchGameSwitchGameAccountFail" xml:space="preserve">
<value>Gagal ganti akun game</value>
</data>
<data name="ViewModelLaunchGameUnableToSwitchUidAttachedGameAccount" xml:space="preserve">
<value>Tidak bisa memilih akun [{1}] dengan UID [{0}], Akun itu tidak berada pada server tersebut</value>
</data>
<data name="ViewModelSettingActionComplete" xml:space="preserve">
<value>Tindakan selesai</value>
</data>
@@ -1598,12 +1565,6 @@
<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>
@@ -1728,7 +1689,7 @@
<value>Sinkronisasi Data Talenta Karakter</value>
</data>
<data name="ViewPageAvatarPropertyRefreshFromHoyolabGameRecord" xml:space="preserve">
<value>从米游社原神战绩同步</value>
<value>Sinkronkan dari Karakter Saya MiHoYo BBS</value>
</data>
<data name="ViewPageAvatarPropertyRefreshFromHoyolabGameRecordDescription" xml:space="preserve">
<value>Sinkronkan sebagian besar data kecuali talent karakter.</value>
@@ -1853,36 +1814,6 @@
<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>
@@ -2160,7 +2091,7 @@
<value>Mengatur Status Aktivitas Discord Saya Ketika Saya Sedang Bermain Game</value>
</data>
<data name="ViewPageLaunchGameDiscordActivityHeader" xml:space="preserve">
<value>Aktifitas Discord</value>
<value>Discord Activity</value>
</data>
<data name="ViewPageLaunchGameFileHeader" xml:space="preserve">
<value>Berkas</value>
@@ -2207,9 +2138,6 @@
<data name="ViewPageLaunchGameResourcePreDownloadHeader" xml:space="preserve">
<value>Prapengunduhan</value>
</data>
<data name="ViewPageLaunchGameSelectGamePath" xml:space="preserve">
<value>Pilih Path Game</value>
</data>
<data name="ViewPageLaunchGameSwitchAccountAttachUidNull" xml:space="preserve">
<value>Akun ini belum terikat ke UID notifikasi Catatan Realtime.</value>
</data>
@@ -2252,12 +2180,6 @@
<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>
@@ -2348,15 +2270,6 @@
<data name="ViewPageSettingDeviceIpHeader" xml:space="preserve">
<value>ID Perangkat</value>
</data>
<data name="ViewPageSettingElevatedModeDescription" xml:space="preserve">
<value>Mode administrator akan mengubah ketersediaan dan perilaku beberapa fitur</value>
</data>
<data name="ViewPageSettingElevatedModeHeader" xml:space="preserve">
<value>Mode Admin</value>
</data>
<data name="ViewPageSettingElevatedModeRestartAction" xml:space="preserve">
<value>Restart menggunakan Mode Administrator</value>
</data>
<data name="ViewPageSettingEmptyHistoryVisibleDescription" xml:space="preserve">
<value>Tampilkan atau sembunyikan acara keinginan tanpa riwayat Wish</value>
</data>
@@ -2630,18 +2543,9 @@
<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>
@@ -2709,10 +2613,10 @@
<value>Mengunggah Data</value>
</data>
<data name="ViewTitileUpdatePackageReadyContent" xml:space="preserve">
<value>Pasang sekarang?</value>
<value>是否立即安装?</value>
</data>
<data name="ViewTitileUpdatePackageReadyTitle" xml:space="preserve">
<value>Snap Hutao versi {0} sudah siap</value>
<value>胡桃 {0} 版本已准备就绪</value>
</data>
<data name="ViewTitleAutoClicking" xml:space="preserve">
<value>Auto Click</value>
@@ -2822,21 +2726,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>
<data name="WebDailyNoteArchonQuestStatusFinished" xml:space="preserve">
<value>Selesai</value>
</data>
<data name="WebDailyNoteArchonQuestStatusNotOpen" xml:space="preserve">
<value>Terkunci</value>
</data>
<data name="WebDailyNoteArchonQuestStatusOngoing" xml:space="preserve">
<value>Dalam proses</value>
</data>
<data name="WebDailyNoteAttendanceRewardStatusFinishedNonReward" xml:space="preserve">
<value>Selesai</value>
</data>
@@ -2925,7 +2817,7 @@
<value>{0} detik</value>
</data>
<data name="WebDailyNoteVerificationFailed" xml:space="preserve">
<value>验证失败,请手动验证或前往「米游社-旅行工具-原神战绩-实时便笺」页面查看</value>
<value>Verifikasi gagal. Silakan periksa MiHoYo BBS - Karakter Saya - Catatan Realtime secara manual.</value>
</data>
<data name="WebEnkaResponseStatusCode400" xml:space="preserve">
<value>Format UID Salah</value>
@@ -2982,7 +2874,7 @@
<value>Server Internasional Server Asia</value>
</data>
<data name="WebHoyolabRegionOSCHT" xml:space="preserve">
<value>Server International: Server TW/HK/MU</value>
<value>国际服 港澳台服</value>
</data>
<data name="WebHoyolabRegionOSEURO" xml:space="preserve">
<value>Server Internasional Server Eropa</value>
@@ -2994,7 +2886,7 @@
<value>Server Snap Hutao sedang dalam perbaikan</value>
</data>
<data name="WebIndexOrSpiralAbyssVerificationFailed" xml:space="preserve">
<value>验证失败,请手动验证或前往「米游社-旅行工具-原神战绩」页面查看</value>
<value>Verifikasi gagal. Harap verifikasi secara manual atau periksa halaman Karakter Saya di MiHoYo BBS.</value>
</data>
<data name="WebResponseFormat" xml:space="preserve">
<value>Kode Kembali: {0} | Pesan: {1}</value>
@@ -3005,7 +2897,4 @@
<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>

View File

@@ -120,12 +120,6 @@
<data name="AppDevNameAndVersion" xml:space="preserve">
<value>胡桃 Dev {0}</value>
</data>
<data name="AppElevatedDevNameAndVersion" xml:space="preserve">
<value>胡桃Dev {0} [管理者]</value>
</data>
<data name="AppElevatedNameAndVersion" xml:space="preserve">
<value>胡桃 {0} [管理者]</value>
</data>
<data name="AppName" xml:space="preserve">
<value>胡桃</value>
</data>
@@ -738,10 +732,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,23 +864,11 @@
<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>
@@ -1008,7 +990,7 @@
<value>サポートされていないUIGFバージョン</value>
</data>
<data name="ServiceUpdateStatusVersionDescription" xml:space="preserve">
<value>新しいバージョン {0} が利用できます</value>
<value>发现新版本 {0}</value>
</data>
<data name="ServiceUserCurrentMultiMatched" xml:space="preserve">
<value>ユーザー情報を複数選択しています。</value>
@@ -1283,12 +1265,6 @@
<data name="ViewDialogQRCodeTitle" xml:space="preserve">
<value>MiHoYo BBS を使用して QR コードをスキャンします</value>
</data>
<data name="ViewDialogReconfirmTextHeader" xml:space="preserve">
<value>謝って有効にしないようにするためには、有効にする機能の&lt;b&gt;タイトル名&lt;/b&gt;を入力してください</value>
</data>
<data name="ViewDialogReconfirmTitle" xml:space="preserve">
<value>危険な機能を有効にしています</value>
</data>
<data name="ViewDialogSettingDeleteUserDataContent" xml:space="preserve">
<value>この操作は取り消せません。すべてのユーザーのログイン状態が解除されます。</value>
</data>
@@ -1310,9 +1286,6 @@
<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>
@@ -1451,9 +1424,6 @@
<data name="ViewModelDailyNoteHoyolabVerificationUnsupported" xml:space="preserve">
<value>HoYoLabユーザーのリアルタイムートはサポートしていません。</value>
</data>
<data name="ViewModelDailyNoteModifyTaskFail" xml:space="preserve">
<value>スケジュールされたタスクの変更に失敗しました</value>
</data>
<data name="ViewModelDailyNoteRefreshTime30" xml:space="preserve">
<value>30 分 | 3.75樹脂</value>
</data>
@@ -1469,6 +1439,9 @@
<data name="ViewModelDailyNoteRefreshTime8" xml:space="preserve">
<value>8 分 | 1樹脂</value>
</data>
<data name="ViewModelDailyNoteRegisterTaskFail" xml:space="preserve">
<value>タスク スケジューラーへの追加に失敗しました。管理者モードで再度お試しください。</value>
</data>
<data name="ViewModelDailyNoteRequestProgressTitle" xml:space="preserve">
<value>リアルタイムノートの情報を取得中、しばらくお待ちください</value>
</data>
@@ -1562,9 +1535,6 @@
<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>
@@ -1577,9 +1547,6 @@
<data name="ViewModelLaunchGameSwitchGameAccountFail" xml:space="preserve">
<value>アカウントの切り替えができませんでした</value>
</data>
<data name="ViewModelLaunchGameUnableToSwitchUidAttachedGameAccount" xml:space="preserve">
<value>UID [{0}] に対するアカウント [{1}] を選択できませんでした。このアカウントは現在のサーバーに属していません。</value>
</data>
<data name="ViewModelSettingActionComplete" xml:space="preserve">
<value>操作は完了しました</value>
</data>
@@ -1598,12 +1565,6 @@
<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>
@@ -1686,7 +1647,7 @@
<value>重要</value>
</data>
<data name="ViewPageAnnouncementViewDetails" xml:space="preserve">
<value>詳細を表示</value>
<value>查看详情</value>
</data>
<data name="ViewPageAvatarPropertyArtifactScore" xml:space="preserve">
<value>聖遺物スコア</value>
@@ -1728,7 +1689,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>
@@ -1853,36 +1814,6 @@
<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>
@@ -2207,9 +2138,6 @@
<data name="ViewPageLaunchGameResourcePreDownloadHeader" xml:space="preserve">
<value>事前ダウンロード</value>
</data>
<data name="ViewPageLaunchGameSelectGamePath" xml:space="preserve">
<value>ゲームのフォルダを選択</value>
</data>
<data name="ViewPageLaunchGameSwitchAccountAttachUidNull" xml:space="preserve">
<value>このアカウントはリアルタイムノート通知 UID として連携されていません。</value>
</data>
@@ -2252,12 +2180,6 @@
<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>
@@ -2348,15 +2270,6 @@
<data name="ViewPageSettingDeviceIpHeader" xml:space="preserve">
<value>デバイスのIP</value>
</data>
<data name="ViewPageSettingElevatedModeDescription" xml:space="preserve">
<value>管理者モードは一部の機能の使用可否と動作に影響します。</value>
</data>
<data name="ViewPageSettingElevatedModeHeader" xml:space="preserve">
<value>管理者モード</value>
</data>
<data name="ViewPageSettingElevatedModeRestartAction" xml:space="preserve">
<value>管理者モードで再起動</value>
</data>
<data name="ViewPageSettingEmptyHistoryVisibleDescription" xml:space="preserve">
<value>祈願履歴のないイベント限定祈願を祈願履歴に表示するかを変更します。</value>
</data>
@@ -2394,10 +2307,10 @@
<value>CAPTCHA</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>
@@ -2630,18 +2543,9 @@
<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>
@@ -2709,10 +2613,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>
@@ -2822,21 +2726,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>
<data name="WebDailyNoteArchonQuestStatusFinished" xml:space="preserve">
<value>すべて完了</value>
</data>
<data name="WebDailyNoteArchonQuestStatusNotOpen" xml:space="preserve">
<value>未解放</value>
</data>
<data name="WebDailyNoteArchonQuestStatusOngoing" xml:space="preserve">
<value>進行中</value>
</data>
<data name="WebDailyNoteAttendanceRewardStatusFinishedNonReward" xml:space="preserve">
<value>完了しました。</value>
</data>
@@ -2925,7 +2817,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>
@@ -2967,34 +2859,34 @@
<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>中国サーバー: ビリビリ(bilibili)</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>認証に失敗しました。 手動で認証するか、MiHoYo BBS - 戦績 - マイ キャラクター を確認してください。</value>
<value>認証に失敗しました。 手動で認証するか、MiHoYo BBS - 戦績 を確認してください。</value>
</data>
<data name="WebResponseFormat" xml:space="preserve">
<value>リターンコード:{0} | メッセージ:{1}</value>
@@ -3005,7 +2897,4 @@
<data name="WebResponseRequestExceptionFormat" xml:space="preserve">
<value>[{0}] の[{1}] のリクエストにエラーが発生、時間をおいてから試してください</value>
</data>
<data name="WindowIdentifyMonitorHeader" xml:space="preserve">
<value>モニターID</value>
</data>
</root>

View File

@@ -120,12 +120,6 @@
<data name="AppDevNameAndVersion" xml:space="preserve">
<value>호두 Dev {0}</value>
</data>
<data name="AppElevatedDevNameAndVersion" xml:space="preserve">
<value>胡桃Dev {0} [管理员]</value>
</data>
<data name="AppElevatedNameAndVersion" xml:space="preserve">
<value>胡桃 {0} [管理员]</value>
</data>
<data name="AppName" xml:space="preserve">
<value>호두</value>
</data>
@@ -738,10 +732,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,23 +864,11 @@
<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>
@@ -1283,12 +1265,6 @@
<data name="ViewDialogQRCodeTitle" xml:space="preserve">
<value>使用米游社扫描二维码</value>
</data>
<data name="ViewDialogReconfirmTextHeader" xml:space="preserve">
<value>为防止你在无意间启用,请输入正在启用的功能开关的&lt;b&gt;标题名称&lt;/b&gt;</value>
</data>
<data name="ViewDialogReconfirmTitle" xml:space="preserve">
<value>你正在启用一个危险功能</value>
</data>
<data name="ViewDialogSettingDeleteUserDataContent" xml:space="preserve">
<value>이 작업은 되돌릴 수 없으며, 모든 사용자 로그인 상태가 해제됩니다</value>
</data>
@@ -1310,9 +1286,6 @@
<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>
@@ -1451,9 +1424,6 @@
<data name="ViewModelDailyNoteHoyolabVerificationUnsupported" xml:space="preserve">
<value>HoYoLab 계정은 실시간 메모 확인 기능을 지원하지 않습니다</value>
</data>
<data name="ViewModelDailyNoteModifyTaskFail" xml:space="preserve">
<value>예약된 작업을 수정하지 못했습니다</value>
</data>
<data name="ViewModelDailyNoteRefreshTime30" xml:space="preserve">
<value>30분 | 3.75 레진</value>
</data>
@@ -1469,6 +1439,9 @@
<data name="ViewModelDailyNoteRefreshTime8" xml:space="preserve">
<value>8분 | 1 레진</value>
</data>
<data name="ViewModelDailyNoteRegisterTaskFail" xml:space="preserve">
<value>작업 등록에 실패했습니다. 관리자 모드로 실행하세요</value>
</data>
<data name="ViewModelDailyNoteRequestProgressTitle" xml:space="preserve">
<value>正在获取实时便笺信息,请稍候</value>
</data>
@@ -1562,9 +1535,6 @@
<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>
@@ -1577,9 +1547,6 @@
<data name="ViewModelLaunchGameSwitchGameAccountFail" xml:space="preserve">
<value>계정 전환 살패</value>
</data>
<data name="ViewModelLaunchGameUnableToSwitchUidAttachedGameAccount" xml:space="preserve">
<value>无法选择UID [{0}] 对应的账号 [{1}],该账号不属于当前服务器</value>
</data>
<data name="ViewModelSettingActionComplete" xml:space="preserve">
<value>操作完成</value>
</data>
@@ -1598,12 +1565,6 @@
<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>
@@ -1728,7 +1689,7 @@
<value>캐릭터 특성 정보 동기화</value>
</data>
<data name="ViewPageAvatarPropertyRefreshFromHoyolabGameRecord" xml:space="preserve">
<value>从米游社原神战绩同步</value>
<value>HoYoLAB에서 내 캐릭터 동기화</value>
</data>
<data name="ViewPageAvatarPropertyRefreshFromHoyolabGameRecordDescription" xml:space="preserve">
<value>캐릭터 특성을 제외한 대부분의 정보 동기화</value>
@@ -1853,36 +1814,6 @@
<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>
@@ -2207,9 +2138,6 @@
<data name="ViewPageLaunchGameResourcePreDownloadHeader" xml:space="preserve">
<value>사전 다운로드</value>
</data>
<data name="ViewPageLaunchGameSelectGamePath" xml:space="preserve">
<value>选择游戏路径</value>
</data>
<data name="ViewPageLaunchGameSwitchAccountAttachUidNull" xml:space="preserve">
<value>该账号尚未绑定实时便笺通知 UID</value>
</data>
@@ -2252,12 +2180,6 @@
<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>
@@ -2348,15 +2270,6 @@
<data name="ViewPageSettingDeviceIpHeader" xml:space="preserve">
<value>设备 IP</value>
</data>
<data name="ViewPageSettingElevatedModeDescription" xml:space="preserve">
<value>管理员模式会影响部分功能的可用性与行为</value>
</data>
<data name="ViewPageSettingElevatedModeHeader" xml:space="preserve">
<value>管理员模式</value>
</data>
<data name="ViewPageSettingElevatedModeRestartAction" xml:space="preserve">
<value>以管理员身份重启</value>
</data>
<data name="ViewPageSettingEmptyHistoryVisibleDescription" xml:space="preserve">
<value>기원 기록 페이지에 기록되지 않은 오래된 기원 이벤트를 표시하거나 숨깁니다</value>
</data>
@@ -2630,18 +2543,9 @@
<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>
@@ -2822,21 +2726,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>
<data name="WebDailyNoteArchonQuestStatusFinished" xml:space="preserve">
<value>全部完成</value>
</data>
<data name="WebDailyNoteArchonQuestStatusNotOpen" xml:space="preserve">
<value>尚未开启</value>
</data>
<data name="WebDailyNoteArchonQuestStatusOngoing" xml:space="preserve">
<value>进行中</value>
</data>
<data name="WebDailyNoteAttendanceRewardStatusFinishedNonReward" xml:space="preserve">
<value>已完成</value>
</data>
@@ -2925,7 +2817,7 @@
<value>{0}초</value>
</data>
<data name="WebDailyNoteVerificationFailed" xml:space="preserve">
<value>验证失败,请手动验证或前往「米游社-旅行工具-原神战绩-实时便笺」页面查看</value>
<value>인증에 실패했습니다. 수동으로 인증하거나 'HoYoLAB-전적-실시간 메모' 페이지에서 확인하시기 바랍니다.</value>
</data>
<data name="WebEnkaResponseStatusCode400" xml:space="preserve">
<value>错误的 UID 格式</value>
@@ -2994,7 +2886,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>
@@ -3005,7 +2897,4 @@
<data name="WebResponseRequestExceptionFormat" xml:space="preserve">
<value>[{0}] 中的 [{1}] 网络请求异常,请稍后再试</value>
</data>
<data name="WindowIdentifyMonitorHeader" xml:space="preserve">
<value>显示器编号</value>
</data>
</root>

View File

@@ -121,7 +121,7 @@
<value>胡桃 Dev {0}</value>
</data>
<data name="AppElevatedDevNameAndVersion" xml:space="preserve">
<value>胡桃 Dev {0} [管理员]</value>
<value>胡桃Dev {0} [管理员]</value>
</data>
<data name="AppElevatedNameAndVersion" xml:space="preserve">
<value>胡桃 {0} [管理员]</value>
@@ -738,10 +738,10 @@
<value>养成计算:{0:MM-dd HH:mm}</value>
</data>
<data name="ServiceAvatarInfoSummaryGameRecordNotRefreshed" xml:space="preserve">
<value>原神战绩:尚未刷新</value>
<value>我的角色:尚未刷新</value>
</data>
<data name="ServiceAvatarInfoSummaryGameRecordRefreshTimeFormat" xml:space="preserve">
<value>原神战绩{0:MM-dd HH:mm}</value>
<value>我的角色{0:MM-dd HH:mm}</value>
</data>
<data name="ServiceAvatarInfoSummaryShowcaseNotRefreshed" xml:space="preserve">
<value>角色橱窗:尚未刷新</value>
@@ -870,23 +870,11 @@
<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>
@@ -1283,12 +1271,6 @@
<data name="ViewDialogQRCodeTitle" xml:space="preserve">
<value>使用米游社扫描二维码</value>
</data>
<data name="ViewDialogReconfirmTextHeader" xml:space="preserve">
<value>为防止你在无意间启用,请输入正在启用的功能开关的&lt;b&gt;标题名称&lt;/b&gt;</value>
</data>
<data name="ViewDialogReconfirmTitle" xml:space="preserve">
<value>你正在启用一个危险功能</value>
</data>
<data name="ViewDialogSettingDeleteUserDataContent" xml:space="preserve">
<value>该操作是不可逆的,所有用户登录状态会丢失</value>
</data>
@@ -1310,9 +1292,6 @@
<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>
@@ -1451,9 +1430,6 @@
<data name="ViewModelDailyNoteHoyolabVerificationUnsupported" xml:space="preserve">
<value>HoYoLab 账号不支持验证实时便笺</value>
</data>
<data name="ViewModelDailyNoteModifyTaskFail" xml:space="preserve">
<value>修改计划任务失败</value>
</data>
<data name="ViewModelDailyNoteRefreshTime30" xml:space="preserve">
<value>30 分钟 | 3.75 树脂</value>
</data>
@@ -1469,6 +1445,9 @@
<data name="ViewModelDailyNoteRefreshTime8" xml:space="preserve">
<value>8 分钟 | 1 树脂</value>
</data>
<data name="ViewModelDailyNoteRegisterTaskFail" xml:space="preserve">
<value>注册计划任务失败,请使用管理员模式重试</value>
</data>
<data name="ViewModelDailyNoteRequestProgressTitle" xml:space="preserve">
<value>正在获取实时便笺信息,请稍候</value>
</data>
@@ -1562,9 +1541,6 @@
<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>
@@ -1598,12 +1574,6 @@
<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>
@@ -1728,7 +1698,7 @@
<value>同步角色天赋信息</value>
</data>
<data name="ViewPageAvatarPropertyRefreshFromHoyolabGameRecord" xml:space="preserve">
<value>从米游社原神战绩同步</value>
<value>从米游社我的角色同步</value>
</data>
<data name="ViewPageAvatarPropertyRefreshFromHoyolabGameRecordDescription" xml:space="preserve">
<value>同步角色天赋外的大部分信息</value>
@@ -1853,36 +1823,6 @@
<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>
@@ -2252,12 +2192,6 @@
<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>
@@ -2475,7 +2409,7 @@
<value>在完整阅读原神和胡桃工具箱用户协议后,我选择启用「启动游戏-高级功能」</value>
</data>
<data name="ViewPageSettingIsAdvancedLaunchOptionsEnabledHeader" xml:space="preserve">
<value>高级功能</value>
<value>启动高级功能</value>
</data>
<data name="ViewPageSettingKeyShortcutAutoClickingDescription" xml:space="preserve">
<value>更改自动连点功能的快捷键</value>
@@ -2630,18 +2564,9 @@
<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>
@@ -2822,9 +2747,6 @@
<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>
@@ -2925,7 +2847,7 @@
<value>{0} 秒</value>
</data>
<data name="WebDailyNoteVerificationFailed" xml:space="preserve">
<value>验证失败,请手动验证或前往「米游社-旅行工具-原神战绩-实时便笺」页面查看</value>
<value>验证失败,请手动验证或前往「米游社-我的角色-实时便笺」页面查看</value>
</data>
<data name="WebEnkaResponseStatusCode400" xml:space="preserve">
<value>错误的 UID 格式</value>
@@ -2994,7 +2916,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>
@@ -3005,7 +2927,4 @@
<data name="WebResponseRequestExceptionFormat" xml:space="preserve">
<value>[{0}] 中的 [{1}] 网络请求异常,请稍后再试</value>
</data>
<data name="WindowIdentifyMonitorHeader" xml:space="preserve">
<value>显示器编号</value>
</data>
</root>

View File

@@ -120,17 +120,11 @@
<data name="AppDevNameAndVersion" xml:space="preserve">
<value>Snap Hutao Dev {0}</value>
</data>
<data name="AppElevatedDevNameAndVersion" xml:space="preserve">
<value>Ху Тао Dev {0} [Администратор]</value>
</data>
<data name="AppElevatedNameAndVersion" xml:space="preserve">
<value>Ху Тао {0} [Администратор]</value>
</data>
<data name="AppName" xml:space="preserve">
<value>Ху Тао</value>
<value>Snap Hutao</value>
</data>
<data name="AppNameAndVersion" xml:space="preserve">
<value>Ху Тао {0}</value>
<value>Snap Hutao {0}</value>
</data>
<data name="ContentDialogCancelCloseButtonText" xml:space="preserve">
<value>Отмена</value>
@@ -495,156 +489,156 @@
<value>Волна 3: Волна появится только после убийства всех врагов в предыдущей волне.</value>
</data>
<data name="ModelMetadataTowerWaveTypeWave4" xml:space="preserve">
<value>Четвертая волна: Победите всех монстров, прежде чем появится следующая волна</value>
<value>第四波:击败所有怪物,下一波才会出现</value>
</data>
<data name="ModelNameValueDefaultDescription" xml:space="preserve">
<value>Обновите данные витрины персонажей</value>
<value>请更新角色橱窗数据</value>
</data>
<data name="ModelNameValueDefaultName" xml:space="preserve">
<value>Нет данных</value>
<value>暂无数据</value>
</data>
<data name="ModelWeaponAffixFormat" xml:space="preserve">
<value>Уровень совершенствования {0}</value>
<value>精炼 {0}</value>
</data>
<data name="MustSelectUserAndUid" xml:space="preserve">
<value>Необходимо войти в учетную запись miHoYo/HoYoLAB и выбрать пользователя с персонажем</value>
<value>必须登录 米游社/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>Достигнуто максимально допустимое количество архивов истории желаний в облаке Snap Hutao</value>
<value>胡桃云保存的祈愿记录存档数已达当前账号上限</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>Пожалуйста, сначала войдите или зарегистрируйтесь Snap Hutao аккаунт</value>
<value>请先登录或注册胡桃账号</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 заблокирован в базе данных Walnut</value>
<value>上传深渊记录失败,当前 Uid 已被胡桃数据库封禁</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>Загрузка записей из Бездны успешна, Но без входа в учетную Snap Hutao пропускной билет, Невозможно получить бесплатный период обслуживания для загрузки записей о желаниях</value>
<value>上传深渊记录成功,但未登录胡桃通行证,无法获赠祈愿记录上传服务时长</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>В одном архиве достижений обнаружено несколько одинаковых идентификаторов достижений</value>
<value>单个成就存档内发现多个相同的成就 Id</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 +732,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,23 +864,11 @@
<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>
@@ -1283,12 +1265,6 @@
<data name="ViewDialogQRCodeTitle" xml:space="preserve">
<value>使用米游社扫描二维码</value>
</data>
<data name="ViewDialogReconfirmTextHeader" xml:space="preserve">
<value>为防止你在无意间启用,请输入正在启用的功能开关的&lt;b&gt;标题名称&lt;/b&gt;</value>
</data>
<data name="ViewDialogReconfirmTitle" xml:space="preserve">
<value>你正在启用一个危险功能</value>
</data>
<data name="ViewDialogSettingDeleteUserDataContent" xml:space="preserve">
<value>该操作是不可逆的,所有用户登录状态会丢失</value>
</data>
@@ -1310,9 +1286,6 @@
<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>
@@ -1451,9 +1424,6 @@
<data name="ViewModelDailyNoteHoyolabVerificationUnsupported" xml:space="preserve">
<value>HoYoLab 账号不支持验证实时便笺</value>
</data>
<data name="ViewModelDailyNoteModifyTaskFail" xml:space="preserve">
<value>修改计划任务失败</value>
</data>
<data name="ViewModelDailyNoteRefreshTime30" xml:space="preserve">
<value>30 分钟 | 3.75 树脂</value>
</data>
@@ -1469,6 +1439,9 @@
<data name="ViewModelDailyNoteRefreshTime8" xml:space="preserve">
<value>8 分钟 | 1 树脂</value>
</data>
<data name="ViewModelDailyNoteRegisterTaskFail" xml:space="preserve">
<value>注册计划任务失败,请使用管理员模式重试</value>
</data>
<data name="ViewModelDailyNoteRequestProgressTitle" xml:space="preserve">
<value>正在获取实时便笺信息,请稍候</value>
</data>
@@ -1562,9 +1535,6 @@
<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>
@@ -1577,9 +1547,6 @@
<data name="ViewModelLaunchGameSwitchGameAccountFail" xml:space="preserve">
<value>切换账号失败</value>
</data>
<data name="ViewModelLaunchGameUnableToSwitchUidAttachedGameAccount" xml:space="preserve">
<value>无法选择UID [{0}] 对应的账号 [{1}],该账号不属于当前服务器</value>
</data>
<data name="ViewModelSettingActionComplete" xml:space="preserve">
<value>操作完成</value>
</data>
@@ -1598,12 +1565,6 @@
<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>
@@ -1728,7 +1689,7 @@
<value>同步角色天赋信息</value>
</data>
<data name="ViewPageAvatarPropertyRefreshFromHoyolabGameRecord" xml:space="preserve">
<value>从米游社原神战绩同步</value>
<value>从米游社我的角色同步</value>
</data>
<data name="ViewPageAvatarPropertyRefreshFromHoyolabGameRecordDescription" xml:space="preserve">
<value>同步角色天赋外的大部分信息</value>
@@ -1853,36 +1814,6 @@
<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>
@@ -2207,9 +2138,6 @@
<data name="ViewPageLaunchGameResourcePreDownloadHeader" xml:space="preserve">
<value>预下载</value>
</data>
<data name="ViewPageLaunchGameSelectGamePath" xml:space="preserve">
<value>选择游戏路径</value>
</data>
<data name="ViewPageLaunchGameSwitchAccountAttachUidNull" xml:space="preserve">
<value>该账号尚未绑定实时便笺通知 UID</value>
</data>
@@ -2252,12 +2180,6 @@
<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>
@@ -2348,15 +2270,6 @@
<data name="ViewPageSettingDeviceIpHeader" xml:space="preserve">
<value>设备 IP</value>
</data>
<data name="ViewPageSettingElevatedModeDescription" xml:space="preserve">
<value>管理员模式会影响部分功能的可用性与行为</value>
</data>
<data name="ViewPageSettingElevatedModeHeader" xml:space="preserve">
<value>管理员模式</value>
</data>
<data name="ViewPageSettingElevatedModeRestartAction" xml:space="preserve">
<value>以管理员身份重启</value>
</data>
<data name="ViewPageSettingEmptyHistoryVisibleDescription" xml:space="preserve">
<value>在祈愿记录页面显示或隐藏无记录的历史祈愿活动</value>
</data>
@@ -2630,18 +2543,9 @@
<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>
@@ -2808,204 +2712,189 @@
<value>〓更新时间〓.+?&amp;lt;t class=\"t_(?:gl|lc)\".*?&amp;gt;(.*?)&amp;lt;/t&amp;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>
</data>
<data name="WebBridgeShareCopyToClipboardFailed" xml:space="preserve">
<value>Не удалось открыть буфер обмена</value>
<value>{0} 小时后结束</value>
</data>
<data name="WebBridgeShareCopyToClipboardSuccess" xml:space="preserve">
<value>Скопировано в буфер обмена</value>
</data>
<data name="WebDailyNoteArchonQuestStatusFinished" xml:space="preserve">
<value>Завершенно</value>
</data>
<data name="WebDailyNoteArchonQuestStatusNotOpen" xml:space="preserve">
<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>Forbid to Claim</value>
<value>禁止领取</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>Ready to claim</value>
<value>等待领取</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>Incomplete Daily Commissions</value>
<value>今日完成委托数量不足</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>Serenitea Pot not Unlocked</value>
<value>尚未解锁洞天</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>Transformer not obtained</value>
<value>尚未获得参量质变仪</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>Invalid UID</value>
<value>无效的 UID</value>
</data>
<data name="WebHoyolabRegionCNGF01" xml:space="preserve">
<value>CN Server: Official</value>
<value>国服 官方服</value>
</data>
<data name="WebHoyolabRegionCNQD01" xml:space="preserve">
<value>CN Server: bilibili</value>
<value>国服 渠道服</value>
</data>
<data name="WebHoyolabRegionOSASIA" xml:space="preserve">
<value>Oversea Server: Asian</value>
<value>国际服 亚服</value>
</data>
<data name="WebHoyolabRegionOSCHT" xml:space="preserve">
<value>Oversea Server: TW/HK/MU server</value>
<value>国际服 港澳台服</value>
</data>
<data name="WebHoyolabRegionOSEURO" xml:space="preserve">
<value>Oversea Server: EU</value>
<value>国际服 欧服</value>
</data>
<data name="WebHoyolabRegionOSUSA" xml:space="preserve">
<value>Oversea Server: NA</value>
<value>国际服 美服</value>
</data>
<data name="WebHutaoServiceUnAvailable" xml:space="preserve">
<value>Сервер Snap Hutao находится на техническом обслуживании</value>
<value>胡桃服务维护中</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>[{1}] исключение сетевого запроса в [{0}] пожалуйста, повторите попытку позже</value>
</data>
<data name="WindowIdentifyMonitorHeader" xml:space="preserve">
<value>显示器编号</value>
<value>[{0}] 中的 [{1}] 网络请求异常,请稍后再试</value>
</data>
</root>

View File

@@ -120,12 +120,6 @@
<data name="AppDevNameAndVersion" xml:space="preserve">
<value>胡桃 Dev {0}</value>
</data>
<data name="AppElevatedDevNameAndVersion" xml:space="preserve">
<value>胡桃Dev {0} [管理员]</value>
</data>
<data name="AppElevatedNameAndVersion" xml:space="preserve">
<value>胡桃 {0} [系統管理員]</value>
</data>
<data name="AppName" xml:space="preserve">
<value>胡桃</value>
</data>
@@ -196,7 +190,7 @@
<value>歡迎使用胡桃</value>
</data>
<data name="LaunchGameTitle" xml:space="preserve">
<value>選擇號並動</value>
<value>選擇號並動</value>
</data>
<data name="ModelBindingAvatarPropertyWeaponAffixFormat" xml:space="preserve">
<value>精煉 {0}</value>
@@ -298,7 +292,7 @@
<comment>Need EXACT same string in game</comment>
</data>
<data name="ModelIntrinsicBodyTypeBoy" xml:space="preserve">
<value>少</value>
<value>少</value>
</data>
<data name="ModelIntrinsicBodyTypeGirl" xml:space="preserve">
<value>少女</value>
@@ -310,7 +304,7 @@
<value>蘿莉</value>
</data>
<data name="ModelIntrinsicBodyTypeMale" xml:space="preserve">
<value>成年男子</value>
<value>成</value>
</data>
<data name="ModelIntrinsicElementNameElec" xml:space="preserve">
<value>雷</value>
@@ -379,7 +373,7 @@
<comment>Need EXACT same string in game</comment>
</data>
<data name="ModelMetadataAvatarPlayerName" xml:space="preserve">
<value>旅</value>
<value>旅行者</value>
</data>
<data name="ModelMetadataFetterInfoBirthdayFormat" xml:space="preserve">
<value>{0} 月 {1} 日</value>
@@ -486,7 +480,7 @@
<value>第一波:擊敗所有怪物,下一波才會出現</value>
</data>
<data name="ModelMetadataTowerWaveTypeWave1Additional" xml:space="preserve">
<value>第一波加:增援第一波怪物</value>
<value>第一波加:增援第一波怪物</value>
</data>
<data name="ModelMetadataTowerWaveTypeWave2" xml:space="preserve">
<value>第二波:擊敗所有怪物,下一波才會出現</value>
@@ -738,10 +732,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>
@@ -846,7 +840,7 @@
<value>找不到原神內置瀏覽器緩存路徑:\n{0}</value>
</data>
<data name="ServiceGachaLogUrlProviderCacheUrlNotFound" xml:space="preserve">
<value>找到可用的 Url</value>
<value>找到可用的 Url</value>
</data>
<data name="ServiceGachaLogUrlProviderManualInputInvalid" xml:space="preserve">
<value>提供的 Url 無效</value>
@@ -870,23 +864,11 @@
<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>
@@ -897,13 +879,13 @@
<value>遊戲進程已啟動</value>
</data>
<data name="ServiceGameLaunchPhaseUnlockFpsFailed" xml:space="preserve">
<value>解鎖 FPS 上限失敗,正在結束遊戲程</value>
<value>解鎖幀率上限失敗,正在結束遊戲程</value>
</data>
<data name="ServiceGameLaunchPhaseUnlockFpsSucceed" xml:space="preserve">
<value>解鎖 FPS 上限成功</value>
<value>解鎖幀率上限成功</value>
</data>
<data name="ServiceGameLaunchPhaseUnlockingFps" xml:space="preserve">
<value>正在嘗試解鎖 FPS 上限</value>
<value>正在嘗試解鎖幀率上限</value>
</data>
<data name="ServiceGameLaunchPhaseWaitingProcessExit" xml:space="preserve">
<value>等待遊戲進程退出</value>
@@ -1008,7 +990,7 @@
<value>不支援的 UIGF 版本</value>
</data>
<data name="ServiceUpdateStatusVersionDescription" xml:space="preserve">
<value>發現新版本 {0}</value>
<value>发现新版本 {0}</value>
</data>
<data name="ServiceUserCurrentMultiMatched" xml:space="preserve">
<value>已选中多条用户记录</value>
@@ -1283,12 +1265,6 @@
<data name="ViewDialogQRCodeTitle" xml:space="preserve">
<value>使用米遊社掃描 QR 碼</value>
</data>
<data name="ViewDialogReconfirmTextHeader" xml:space="preserve">
<value>為防止你在無意間啟用,請輸入正在啟用的功能開關的&lt;b&gt;標題名稱&lt;/b&gt;</value>
</data>
<data name="ViewDialogReconfirmTitle" xml:space="preserve">
<value>你正在啟用一個危險功能</value>
</data>
<data name="ViewDialogSettingDeleteUserDataContent" xml:space="preserve">
<value>該操作是不可逆,所有用戶登錄狀態會遺失</value>
</data>
@@ -1310,9 +1286,6 @@
<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>
@@ -1451,9 +1424,6 @@
<data name="ViewModelDailyNoteHoyolabVerificationUnsupported" xml:space="preserve">
<value>HoYoLAB 賬號不支持驗證實时便箋</value>
</data>
<data name="ViewModelDailyNoteModifyTaskFail" xml:space="preserve">
<value>修改計劃任務失敗</value>
</data>
<data name="ViewModelDailyNoteRefreshTime30" xml:space="preserve">
<value>30 分鐘 | 3.75 樹脂</value>
</data>
@@ -1469,6 +1439,9 @@
<data name="ViewModelDailyNoteRefreshTime8" xml:space="preserve">
<value>8 分鐘 | 1 樹脂</value>
</data>
<data name="ViewModelDailyNoteRegisterTaskFail" xml:space="preserve">
<value>注冊計劃任務失敗,請以管理員模式重試</value>
</data>
<data name="ViewModelDailyNoteRequestProgressTitle" xml:space="preserve">
<value>正在獲取實時便箋信息,請稍候</value>
</data>
@@ -1562,9 +1535,6 @@
<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>
@@ -1577,9 +1547,6 @@
<data name="ViewModelLaunchGameSwitchGameAccountFail" xml:space="preserve">
<value>切換帳號失敗</value>
</data>
<data name="ViewModelLaunchGameUnableToSwitchUidAttachedGameAccount" xml:space="preserve">
<value>無法選擇 UID [{0}] 對應的帳號 [{1}],該帳號不屬於當前伺服器</value>
</data>
<data name="ViewModelSettingActionComplete" xml:space="preserve">
<value>操作完成</value>
</data>
@@ -1598,12 +1565,6 @@
<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>
@@ -1686,7 +1647,7 @@
<value>遊戲公告</value>
</data>
<data name="ViewPageAnnouncementViewDetails" xml:space="preserve">
<value>檢視詳情</value>
<value>查看详情</value>
</data>
<data name="ViewPageAvatarPropertyArtifactScore" xml:space="preserve">
<value>聖遺物評分</value>
@@ -1728,7 +1689,7 @@
<value>同步角色天賦信息</value>
</data>
<data name="ViewPageAvatarPropertyRefreshFromHoyolabGameRecord" xml:space="preserve">
<value>从米游社原神战绩同步</value>
<value>從 HoYoLAB - 戰績中同步</value>
</data>
<data name="ViewPageAvatarPropertyRefreshFromHoyolabGameRecordDescription" xml:space="preserve">
<value>同步角色天賦外的大部分信息</value>
@@ -1853,36 +1814,6 @@
<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>
@@ -2076,7 +2007,7 @@
<value>至少需要八個字元</value>
</data>
<data name="ViewPageHutaoPassportRegisterHeader" xml:space="preserve">
<value>建立帳號</value>
<value>註冊</value>
</data>
<data name="ViewPageHutaoPassportResetPasswordHeader" xml:space="preserve">
<value>重設密碼</value>
@@ -2172,10 +2103,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>
@@ -2207,9 +2138,6 @@
<data name="ViewPageLaunchGameResourcePreDownloadHeader" xml:space="preserve">
<value>預下載</value>
</data>
<data name="ViewPageLaunchGameSelectGamePath" xml:space="preserve">
<value>選擇遊戲路徑</value>
</data>
<data name="ViewPageLaunchGameSwitchAccountAttachUidNull" xml:space="preserve">
<value>該用戶尚未綁定即時便箋通知 UID</value>
</data>
@@ -2223,7 +2151,7 @@
<value>檢測</value>
</data>
<data name="ViewPageLaunchGameSwitchAccountHeader" xml:space="preserve">
<value>檢測號</value>
<value>檢測號</value>
</data>
<data name="ViewPageLaunchGameSwitchAccountRemoveToolTip" xml:space="preserve">
<value>刪除</value>
@@ -2232,7 +2160,7 @@
<value>重新命名</value>
</data>
<data name="ViewPageLaunchGameSwitchSchemeDescription" xml:space="preserve">
<value>切換戲伺服器(服/渠道服/國際服)</value>
<value>切換戲伺服器(服/渠道服/國際服)</value>
</data>
<data name="ViewPageLaunchGameSwitchSchemeHeader" xml:space="preserve">
<value>伺服器</value>
@@ -2252,12 +2180,6 @@
<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>
@@ -2280,7 +2202,7 @@
<value>外觀</value>
</data>
<data name="ViewPageSettingApperanceLanguageDescription" xml:space="preserve">
<value>設定呈現語言</value>
<value>設定系統語言</value>
</data>
<data name="ViewPageSettingApperanceLanguageHeader" xml:space="preserve">
<value>語言</value>
@@ -2292,7 +2214,7 @@
<value>背景材質</value>
</data>
<data name="ViewPageSettingCacheFolderDescription" xml:space="preserve">
<value>圖片存存放在此</value>
<value>圖片存存放在此</value>
</data>
<data name="ViewPageSettingCacheFolderHeader" xml:space="preserve">
<value>暫存檔案夾</value>
@@ -2304,7 +2226,7 @@
<value>創建</value>
</data>
<data name="ViewPageSettingCreateDesktopShortcutDescription" xml:space="preserve">
<value>在桌面上創建預設以系統管理員方式啟動的捷徑</value>
<value>在桌面上創建預設以管理員方式啟動的快捷方式</value>
</data>
<data name="ViewPageSettingCreateDesktopShortcutHeader" xml:space="preserve">
<value>創建快捷方式</value>
@@ -2348,15 +2270,6 @@
<data name="ViewPageSettingDeviceIpHeader" xml:space="preserve">
<value>設備 IP</value>
</data>
<data name="ViewPageSettingElevatedModeDescription" xml:space="preserve">
<value>系統管理員模式會影響部分功能的可用性與行為</value>
</data>
<data name="ViewPageSettingElevatedModeHeader" xml:space="preserve">
<value>系統管理員模式</value>
</data>
<data name="ViewPageSettingElevatedModeRestartAction" xml:space="preserve">
<value>以系統管理員身分重啟動</value>
</data>
<data name="ViewPageSettingEmptyHistoryVisibleDescription" xml:space="preserve">
<value>在祈願紀錄頁面顯示或隱藏無記錄的歷史祈願活動</value>
</data>
@@ -2379,7 +2292,7 @@
<value>祈願記錄</value>
</data>
<data name="ViewPageSettingGameHeader" xml:space="preserve">
<value>戲</value>
<value>戲</value>
</data>
<data name="ViewPageSettingGeetestCustomUrlAction" xml:space="preserve">
<value>配置</value>
@@ -2394,10 +2307,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>
@@ -2427,7 +2340,7 @@
<value>主頁</value>
</data>
<data name="ViewPageSettingHutaoPassportDangerZoneDescription" xml:space="preserve">
<value>三思而行</value>
<value>三思而行</value>
</data>
<data name="ViewPageSettingHutaoPassportDangerZoneHeader" xml:space="preserve">
<value>危險操作</value>
@@ -2508,10 +2421,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>
@@ -2622,7 +2535,7 @@
<value>登入失敗,請重新登入</value>
</data>
<data name="ViewServiceHutaoUserLoginOrRegisterHint" xml:space="preserve">
<value>立即登入或建立帳號</value>
<value>立即登入或註冊</value>
</data>
<data name="ViewSettingAllocConsoleDescription" xml:space="preserve">
<value>控制胡桃啟動時是否開啟主控台,重新啟動後生效</value>
@@ -2630,18 +2543,9 @@
<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>
@@ -2709,10 +2613,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>
@@ -2748,10 +2652,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>
@@ -2760,10 +2664,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>
@@ -2775,7 +2679,7 @@
<value>用戶</value>
</data>
<data name="ViewWelcomeBase" xml:space="preserve">
<value>我們將你下載最基本的圖像資源</value>
<value>我們將你下載最基本的圖像資源</value>
</data>
<data name="ViewWelcomeBody" xml:space="preserve">
<value>你可以繼續使用電腦,絲毫不受影響</value>
@@ -2822,21 +2726,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>
<data name="WebDailyNoteArchonQuestStatusFinished" xml:space="preserve">
<value>全部完成</value>
</data>
<data name="WebDailyNoteArchonQuestStatusNotOpen" xml:space="preserve">
<value>尚未開啟</value>
</data>
<data name="WebDailyNoteArchonQuestStatusOngoing" xml:space="preserve">
<value>進行中</value>
</data>
<data name="WebDailyNoteAttendanceRewardStatusFinishedNonReward" xml:space="preserve">
<value>已完成</value>
</data>
@@ -2925,7 +2817,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>
@@ -2967,45 +2859,42 @@
<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>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.0 KiB

View File

@@ -6,6 +6,7 @@ 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;
@@ -15,6 +16,7 @@ internal sealed partial class AppOptions : DbStoreOptions
{
private bool? isEmptyHistoryWishVisible;
private BackdropType? backdropType;
private CultureInfo? currentCulture;
private Region? region;
private string? geetestCustomCompositeUrl;
@@ -24,7 +26,7 @@ internal sealed partial class AppOptions : DbStoreOptions
set => SetOption(ref isEmptyHistoryWishVisible, SettingEntry.IsEmptyHistoryWishVisible, value);
}
public List<NameValue<BackdropType>> BackdropTypes { get; } = CollectionsNameValue.FromEnum<BackdropType>(type => type >= 0);
public List<NameValue<BackdropType>> BackdropTypes { get; } = CollectionsNameValue.FromEnum<BackdropType>();
public BackdropType BackdropType
{
@@ -32,6 +34,14 @@ 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
@@ -45,4 +55,6 @@ 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!;
}

View File

@@ -3,11 +3,17 @@
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);

View File

@@ -1,43 +0,0 @@
// 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;
}
}
}

View File

@@ -58,18 +58,12 @@ internal sealed partial class DailyNoteOptions : DbStoreOptions
{
if (SelectedRefreshTime is not null)
{
if (!scheduleTaskInterop.RegisterForDailyNoteRefresh(SelectedRefreshTime.Value))
{
serviceProvider.GetRequiredService<IInfoBarService>().Warning(SH.ViewModelDailyNoteModifyTaskFail);
}
scheduleTaskInterop.RegisterForDailyNoteRefresh(SelectedRefreshTime.Value);
}
}
else
{
if (!scheduleTaskInterop.UnregisterForDailyNoteRefresh())
{
serviceProvider.GetRequiredService<IInfoBarService>().Warning(SH.ViewModelDailyNoteModifyTaskFail);
}
scheduleTaskInterop.UnregisterForDailyNoteRefresh();
}
OnPropertyChanged();
@@ -108,7 +102,7 @@ internal sealed partial class DailyNoteOptions : DbStoreOptions
}
else
{
serviceProvider.GetRequiredService<IInfoBarService>().Warning(SH.ViewModelDailyNoteModifyTaskFail);
serviceProvider.GetRequiredService<IInfoBarService>().Warning(SH.ViewModelDailyNoteRegisterTaskFail);
}
}
}

View File

@@ -1,10 +1,10 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using Snap.Discord.GameSDK;
using Snap.Discord.GameSDK.ABI;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Text.Unicode;
namespace Snap.Hutao.Service.Discord;
@@ -18,94 +18,75 @@ internal static class DiscordController
private static readonly CancellationTokenSource StopTokenSource = new();
private static readonly object SyncRoot = new();
private static long currentClientId;
private static unsafe IDiscordCore* discordCorePtr;
private static Snap.Discord.GameSDK.Discord? discordManager;
private static bool isInitialized;
public static async ValueTask<DiscordResult> SetDefaultActivityAsync(DateTimeOffset startTime)
public static async ValueTask<Result> SetDefaultActivityAsync(DateTimeOffset startTime)
{
await Task.CompletedTask.ConfigureAwait(ConfigureAwaitOptions.ForceYielding);
return SetDefaultActivity(startTime);
ResetManagerOrIgnore(HutaoAppId);
static unsafe DiscordResult SetDefaultActivity(in DateTimeOffset startTime)
if (discordManager is null)
{
ResetManagerOrIgnore(HutaoAppId);
if (discordCorePtr is null)
{
return DiscordResult.Ok;
}
IDiscordActivityManager* activityManagerPtr = discordCorePtr->get_activity_manager(discordCorePtr);
DiscordActivity activity = default;
activity.timestamps.start = startTime.ToUnixTimeSeconds();
SetString(activity.assets.large_image, 128, "icon"u8);
SetString(activity.assets.large_text, 128, SH.AppName);
return new DiscordUpdateActivityAsyncAction(activityManagerPtr).WaitUpdateActivity(activity);
return Result.Ok;
}
ActivityManager activityManager = discordManager.GetActivityManager();
Activity activity = default;
activity.Timestamps.Start = startTime.ToUnixTimeSeconds();
activity.Assets.LargeImage = "icon";
activity.Assets.LargeText = SH.AppName;
return await activityManager.UpdateActivityAsync(activity).ConfigureAwait(false);
}
public static async ValueTask<DiscordResult> SetPlayingYuanShenAsync()
public static async ValueTask<Result> SetPlayingYuanShenAsync()
{
await Task.CompletedTask.ConfigureAwait(ConfigureAwaitOptions.ForceYielding);
return SetPlayingYuanShen();
ResetManagerOrIgnore(YuanshenId);
static unsafe DiscordResult SetPlayingYuanShen()
if (discordManager is null)
{
ResetManagerOrIgnore(YuanshenId);
if (discordCorePtr is null)
{
return DiscordResult.Ok;
}
IDiscordActivityManager* activityManagerPtr = discordCorePtr->get_activity_manager(discordCorePtr);
DiscordActivity activity = default;
SetString(activity.state, 128, SH.FormatServiceDiscordGameLaunchedBy(SH.AppName));
SetString(activity.details, 128, SH.ServiceDiscordGameActivityDetails);
activity.timestamps.start = DateTimeOffset.UtcNow.ToUnixTimeSeconds();
SetString(activity.assets.large_image, 128, "icon"u8);
SetString(activity.assets.large_text, 128, "原神"u8);
SetString(activity.assets.small_image, 128, "app"u8);
SetString(activity.assets.small_text, 128, SH.AppName);
return new DiscordUpdateActivityAsyncAction(activityManagerPtr).WaitUpdateActivity(activity);
return Result.Ok;
}
ActivityManager activityManager = discordManager.GetActivityManager();
Activity activity = default;
activity.State = SH.FormatServiceDiscordGameLaunchedBy(SH.AppName);
activity.Details = SH.ServiceDiscordGameActivityDetails;
activity.Timestamps.Start = DateTimeOffset.UtcNow.ToUnixTimeSeconds();
activity.Assets.LargeImage = "icon";
activity.Assets.LargeText = "原神";
activity.Assets.SmallImage = "app";
activity.Assets.SmallText = SH.AppName;
return await activityManager.UpdateActivityAsync(activity).ConfigureAwait(false);
}
public static async ValueTask<DiscordResult> SetPlayingGenshinImpactAsync()
public static async ValueTask<Result> SetPlayingGenshinImpactAsync()
{
await Task.CompletedTask.ConfigureAwait(ConfigureAwaitOptions.ForceYielding);
return SetPlayingGenshinImpact();
ResetManagerOrIgnore(GenshinImpactId);
static unsafe DiscordResult SetPlayingGenshinImpact()
if (discordManager is null)
{
ResetManagerOrIgnore(GenshinImpactId);
if (discordCorePtr is null)
{
return DiscordResult.Ok;
}
IDiscordActivityManager* activityManagerPtr = discordCorePtr->get_activity_manager(discordCorePtr);
DiscordActivity activity = default;
SetString(activity.state, 128, SH.FormatServiceDiscordGameLaunchedBy(SH.AppName));
SetString(activity.details, 128, SH.ServiceDiscordGameActivityDetails);
activity.timestamps.start = DateTimeOffset.UtcNow.ToUnixTimeSeconds();
SetString(activity.assets.large_image, 128, "icon"u8);
SetString(activity.assets.large_text, 128, "Genshin Impact"u8);
SetString(activity.assets.small_image, 128, "app"u8);
SetString(activity.assets.small_text, 128, SH.AppName);
return new DiscordUpdateActivityAsyncAction(activityManagerPtr).WaitUpdateActivity(activity);
return Result.Ok;
}
ActivityManager activityManager = discordManager.GetActivityManager();
Activity activity = default;
activity.State = SH.FormatServiceDiscordGameLaunchedBy(SH.AppName);
activity.Details = SH.ServiceDiscordGameActivityDetails;
activity.Timestamps.Start = DateTimeOffset.UtcNow.ToUnixTimeSeconds();
activity.Assets.LargeImage = "icon";
activity.Assets.LargeText = "Genshin Impact";
activity.Assets.SmallImage = "app";
activity.Assets.SmallText = SH.AppName;
return await activityManager.UpdateActivityAsync(activity).ConfigureAwait(false);
}
public static unsafe void Stop()
public static void Stop()
{
if (!isInitialized)
{
@@ -117,7 +98,7 @@ internal static class DiscordController
StopTokenSource.Cancel();
try
{
discordCorePtr = default;
discordManager?.Dispose();
}
catch (SEHException)
{
@@ -127,30 +108,23 @@ internal static class DiscordController
private static unsafe void ResetManagerOrIgnore(long clientId)
{
if (currentClientId == clientId)
if (discordManager?.ClientId == clientId)
{
return;
}
// Actually requires a discord client to be running on Windows platform.
// If not, the following creation code will throw.
if (System.Diagnostics.Process.GetProcessesByName("Discord").Length <= 0)
if (System.Diagnostics.Process.GetProcessesByName("Discord").Length == 0)
{
return;
}
lock (SyncRoot)
{
DiscordCreateParams @params = default;
Methods.DiscordCreateParamsSetDefault(&@params);
@params.client_id = clientId;
@params.flags = (uint)DiscordCreateFlags.Default;
IDiscordCore* ptr = default;
Methods.DiscordCreate(3, &@params, &ptr);
currentClientId = clientId;
discordCorePtr = ptr;
discordCorePtr->set_log_hook(discordCorePtr, DiscordLogLevel.Debug, default, &DebugWriteDiscordMessage);
discordManager?.Dispose();
discordManager = new(clientId, CreateFlags.NoRequireDiscord);
discordManager.SetLogHook(Snap.Discord.GameSDK.LogLevel.Debug, SetLogHookHandler.Create(&DebugWriteDiscordMessage));
}
if (isInitialized)
@@ -161,10 +135,10 @@ internal static class DiscordController
DiscordRunCallbacksAsync(StopTokenSource.Token).SafeForget();
isInitialized = true;
[UnmanagedCallersOnly(CallConvs = [typeof(CallConvCdecl)])]
static unsafe void DebugWriteDiscordMessage(void* state, DiscordLogLevel logLevel, sbyte* ptr)
[UnmanagedCallersOnly(CallConvs = [typeof(CallConvStdcall)])]
static unsafe void DebugWriteDiscordMessage(Snap.Discord.GameSDK.LogLevel logLevel, byte* ptr)
{
ReadOnlySpan<byte> utf8 = MemoryMarshal.CreateReadOnlySpanFromNullTerminated((byte*)ptr);
ReadOnlySpan<byte> utf8 = MemoryMarshal.CreateReadOnlySpanFromNullTerminated(ptr);
string message = System.Text.Encoding.UTF8.GetString(utf8);
System.Diagnostics.Debug.WriteLine($"[Discord.GameSDK]:[{logLevel}]:{message}");
}
@@ -172,9 +146,7 @@ internal static class DiscordController
private static async ValueTask DiscordRunCallbacksAsync(CancellationToken cancellationToken)
{
int notRunningCounter = 0;
using (PeriodicTimer timer = new(TimeSpan.FromMilliseconds(500)))
using (PeriodicTimer timer = new(TimeSpan.FromMilliseconds(1000)))
{
try
{
@@ -189,21 +161,15 @@ internal static class DiscordController
{
try
{
DiscordResult result = DiscordCoreRunRunCallbacks();
if (result is not DiscordResult.Ok)
discordManager?.RunCallbacks();
}
catch (ResultException ex)
{
// If result is Ok
// Maybe the connection is reset.
if (ex.Result is not Result.Ok)
{
if (result is DiscordResult.NotRunning)
{
if (++notRunningCounter > 20)
{
Stop();
}
}
else
{
notRunningCounter = 0;
System.Diagnostics.Debug.WriteLine($"[Discord.GameSDK ERROR]:{result:D} {result}");
}
System.Diagnostics.Debug.WriteLine($"[Discord.GameSDK ERROR]:{ex.Result:D} {ex.Result}");
}
}
catch (SEHException ex)
@@ -219,70 +185,5 @@ internal static class DiscordController
{
}
}
unsafe DiscordResult DiscordCoreRunRunCallbacks()
{
if (discordCorePtr is not null)
{
return discordCorePtr->run_callbacks(discordCorePtr);
}
return DiscordResult.Ok;
}
}
private static unsafe void SetString(sbyte* reference, int length, string source)
{
Span<sbyte> sbytes = new(reference, length);
sbytes.Clear();
Utf8.FromUtf16(source.AsSpan(), MemoryMarshal.Cast<sbyte, byte>(sbytes), out _, out _);
}
private static unsafe void SetString(sbyte* reference, int length, in ReadOnlySpan<byte> source)
{
Span<sbyte> sbytes = new(reference, length);
sbytes.Clear();
source.CopyTo(MemoryMarshal.Cast<sbyte, byte>(sbytes));
}
private struct DiscordAsyncAction
{
public DiscordResult Result;
public bool IsCompleted;
}
private unsafe struct DiscordUpdateActivityAsyncAction
{
private readonly IDiscordActivityManager* activityManagerPtr;
private DiscordAsyncAction discordAsyncAction;
public DiscordUpdateActivityAsyncAction(IDiscordActivityManager* activityManagerPtr)
{
this.activityManagerPtr = activityManagerPtr;
}
public DiscordResult WaitUpdateActivity(DiscordActivity activity)
{
fixed (DiscordAsyncAction* actionPtr = &discordAsyncAction)
{
activityManagerPtr->update_activity(activityManagerPtr, &activity, actionPtr, &HandleResult);
}
SpinWaitPolyfill.SpinUntil(ref discordAsyncAction, &CheckActionCompleted);
return discordAsyncAction.Result;
}
[UnmanagedCallersOnly(CallConvs = [typeof(CallConvCdecl)])]
private static void HandleResult(void* state, DiscordResult result)
{
DiscordAsyncAction* action = (DiscordAsyncAction*)state;
action->Result = result;
action->IsCompleted = true;
}
private static bool CheckActionCompleted(ref readonly DiscordAsyncAction state)
{
return state.IsCompleted;
}
}
}

View File

@@ -11,14 +11,14 @@ internal sealed partial class DiscordService : IDiscordService, IDisposable
{
private readonly RuntimeOptions runtimeOptions;
public async ValueTask SetPlayingActivityAsync(bool isOversea)
public async ValueTask SetPlayingActivity(bool isOversea)
{
_ = isOversea
? await DiscordController.SetPlayingGenshinImpactAsync().ConfigureAwait(false)
: await DiscordController.SetPlayingYuanShenAsync().ConfigureAwait(false);
}
public async ValueTask SetNormalActivityAsync()
public async ValueTask SetNormalActivity()
{
_ = await DiscordController.SetDefaultActivityAsync(runtimeOptions.AppLaunchTime).ConfigureAwait(false);
}

View File

@@ -5,7 +5,7 @@ namespace Snap.Hutao.Service.Discord;
internal interface IDiscordService
{
ValueTask SetNormalActivityAsync();
ValueTask SetNormalActivity();
ValueTask SetPlayingActivityAsync(bool isOversea);
ValueTask SetPlayingActivity(bool isOversea);
}

View File

@@ -226,7 +226,7 @@ internal sealed partial class GachaLogService : IGachaLogService
break;
}
await Delay.RandomMilliSeconds(1000, 2000).ConfigureAwait(false);
await Delay.Random(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.RandomMilliSeconds(1000, 2000).ConfigureAwait(false);
await Delay.Random(1000, 2000).ConfigureAwait(false);
}
return new(!fetchContext.FetchStatus.AuthKeyTimeout, fetchContext.TargetArchive);

View File

@@ -2,6 +2,7 @@
// 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;
@@ -17,7 +18,7 @@ namespace Snap.Hutao.Service.GachaLog.QueryProvider;
internal sealed partial class GachaLogQueryManualInputProvider : IGachaLogQueryProvider
{
private readonly IContentDialogFactory contentDialogFactory;
private readonly CultureOptions cultureOptions;
private readonly MetadataOptions metadataOptions;
/// <inheritdoc/>
public async ValueTask<ValueResult<bool, GachaLogQuery>> GetQueryAsync()
@@ -32,13 +33,13 @@ internal sealed partial class GachaLogQueryManualInputProvider : IGachaLogQueryP
if (query.TryGetSingleValue("auth_appid", out string? appId) && appId is "webview_gacha")
{
string? queryLanguageCode = query["lang"];
if (cultureOptions.LanguageCodeFitsCurrentLocale(queryLanguageCode))
if (metadataOptions.LanguageCodeFitsCurrentLocale(queryLanguageCode))
{
return new(true, new(queryString));
}
else
{
string message = SH.FormatServiceGachaLogUrlProviderUrlLanguageNotMatchCurrentLocale(queryLanguageCode, cultureOptions.LanguageCode);
string message = SH.FormatServiceGachaLogUrlProviderUrlLanguageNotMatchCurrentLocale(queryLanguageCode, metadataOptions.LanguageCode);
return new(false, message);
}
}

View File

@@ -1,6 +1,7 @@
// 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;
@@ -19,7 +20,7 @@ namespace Snap.Hutao.Service.GachaLog.QueryProvider;
internal sealed partial class GachaLogQuerySTokenProvider : IGachaLogQueryProvider
{
private readonly BindingClient2 bindingClient2;
private readonly CultureOptions cultureOptions;
private readonly MetadataOptions metadataOptions;
private readonly IUserService userService;
/// <inheritdoc/>
@@ -37,7 +38,7 @@ internal sealed partial class GachaLogQuerySTokenProvider : IGachaLogQueryProvid
if (authkeyResponse.IsOk())
{
return new(true, new(ComposeQueryString(data, authkeyResponse.Data, cultureOptions.LanguageCode)));
return new(true, new(ComposeQueryString(data, authkeyResponse.Data, metadataOptions.LanguageCode)));
}
else
{

View File

@@ -3,6 +3,7 @@
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;
@@ -21,7 +22,7 @@ namespace Snap.Hutao.Service.GachaLog.QueryProvider;
internal sealed partial class GachaLogQueryWebCacheProvider : IGachaLogQueryProvider
{
private readonly IGameServiceFacade gameService;
private readonly CultureOptions cultureOptions;
private readonly MetadataOptions metadataOptions;
/// <summary>
/// 获取缓存文件路径
@@ -89,12 +90,12 @@ internal sealed partial class GachaLogQueryWebCacheProvider : IGachaLogQueryProv
NameValueCollection query = HttpUtility.ParseQueryString(result.TrimEnd("#/log"));
string? queryLanguageCode = query["lang"];
if (cultureOptions.LanguageCodeFitsCurrentLocale(queryLanguageCode))
if (metadataOptions.LanguageCodeFitsCurrentLocale(queryLanguageCode))
{
return new(true, new(result));
}
string message = SH.FormatServiceGachaLogUrlProviderUrlLanguageNotMatchCurrentLocale(queryLanguageCode, cultureOptions.LanguageCode);
string message = SH.FormatServiceGachaLogUrlProviderUrlLanguageNotMatchCurrentLocale(queryLanguageCode, metadataOptions.LanguageCode);
return new(false, message);
}
}

View File

@@ -4,6 +4,7 @@
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;
@@ -16,7 +17,7 @@ internal sealed partial class UIGFExportService : IUIGFExportService
{
private readonly IGachaLogDbService gachaLogDbService;
private readonly RuntimeOptions runtimeOptions;
private readonly CultureOptions cultureOptions;
private readonly MetadataOptions metadataOptions;
private readonly ITaskContext taskContext;
/// <inheritdoc/>
@@ -30,7 +31,7 @@ internal sealed partial class UIGFExportService : IUIGFExportService
UIGF uigf = new()
{
Info = UIGFInfo.From(runtimeOptions, cultureOptions, archive.Uid),
Info = UIGFInfo.From(runtimeOptions, metadataOptions, archive.Uid),
List = list,
};

View File

@@ -4,6 +4,7 @@
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;
@@ -17,7 +18,7 @@ namespace Snap.Hutao.Service.GachaLog;
internal sealed partial class UIGFImportService : IUIGFImportService
{
private readonly ILogger<UIGFImportService> logger;
private readonly CultureOptions cultureOptions;
private readonly MetadataOptions metadataOptions;
private readonly IGachaLogDbService gachaLogDbService;
private readonly ITaskContext taskContext;
@@ -36,9 +37,9 @@ internal sealed partial class UIGFImportService : IUIGFImportService
// v2.1 only support CHS
if (version is UIGFVersion.Major2Minor2OrLower)
{
if (!cultureOptions.LanguageCodeFitsCurrentLocale(uigf.Info.Language))
if (!metadataOptions.LanguageCodeFitsCurrentLocale(uigf.Info.Language))
{
string message = SH.FormatServiceGachaUIGFImportLanguageNotMatch(uigf.Info.Language, cultureOptions.LanguageCode);
string message = SH.FormatServiceGachaUIGFImportLanguageNotMatch(uigf.Info.Language, metadataOptions.LanguageCode);
ThrowHelper.InvalidOperation(message);
}

View File

@@ -29,11 +29,6 @@ internal sealed partial class GameAccountService : IGameAccountService
{
ArgumentNullException.ThrowIfNull(gameAccounts);
if (schemeType is SchemeType.ChineseBilibili)
{
return default;
}
string? registrySdk = RegistryInterop.Get(schemeType);
if (string.IsNullOrEmpty(registrySdk))
{
@@ -67,11 +62,6 @@ internal sealed partial class GameAccountService : IGameAccountService
{
ArgumentNullException.ThrowIfNull(gameAccounts);
if (schemeType is SchemeType.ChineseBilibili)
{
return default;
}
string? registrySdk = RegistryInterop.Get(schemeType);
if (string.IsNullOrEmpty(registrySdk))

View File

@@ -20,8 +20,6 @@ internal static class RegistryInterop
private const string SdkChineseValueName = "MIHOYOSDK_ADL_PROD_CN_h3123967166";
private const string SdkOverseaValueName = "MIHOYOSDK_ADL_PROD_OVERSEA_h1158948810";
private const string WindowsHDROnValueName = "WINDOWS_HDR_ON_h3132281285";
public static bool Set(GameAccount? account)
{
if (account is not null)
@@ -58,12 +56,6 @@ internal static class RegistryInterop
}
}
public static void SetWindowsHDR(bool isOversea)
{
string keyName = isOversea ? OverseaKeyName : ChineseKeyName;
Registry.SetValue(keyName, WindowsHDROnValueName, 1);
}
private static (string KeyName, string ValueName) GetKeyValueName(SchemeType scheme)
{
return scheme switch

View File

@@ -29,9 +29,10 @@ internal readonly struct ChannelOptions
/// </summary>
public readonly bool IsOversea;
public readonly ChannelOptionsErrorKind ErrorKind;
public readonly string? FilePath;
/// <summary>
/// 配置文件路径 当不为 null 时则存在文件读写问题
/// </summary>
public readonly string? ConfigFilePath;
public ChannelOptions(ChannelType channel, SubChannelType subChannel, bool isOversea)
{
@@ -47,20 +48,15 @@ internal readonly struct ChannelOptions
IsOversea = isOversea;
}
private ChannelOptions(ChannelOptionsErrorKind errorKind, string? filePath)
private ChannelOptions(bool isOversea, string? configFilePath)
{
ErrorKind = errorKind;
FilePath = filePath;
IsOversea = isOversea;
ConfigFilePath = configFilePath;
}
public static ChannelOptions ConfigurationFileNotFound(string filePath)
public static ChannelOptions FileNotFound(bool isOversea, string configFilePath)
{
return new(ChannelOptionsErrorKind.ConfigurationFileNotFound, filePath);
}
public static ChannelOptions GamePathNullOrEmpty()
{
return new(ChannelOptionsErrorKind.GamePathNullOrEmpty, string.Empty);
return new(isOversea, configFilePath);
}
/// <inheritdoc/>

View File

@@ -1,11 +0,0 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
namespace Snap.Hutao.Service.Game.Configuration;
internal enum ChannelOptionsErrorKind
{
None,
ConfigurationFileNotFound,
GamePathNullOrEmpty,
}

View File

@@ -1,9 +1,11 @@
// 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;
@@ -15,22 +17,84 @@ internal sealed partial class GameChannelOptionsService : IGameChannelOptionsSer
public ChannelOptions GetChannelOptions()
{
if (!launchOptions.TryGetGameFileSystem(out GameFileSystem? gameFileSystem))
if (!launchOptions.TryGetGamePathAndFilePathByName(ConfigFileName, out string gamePath, out string? configPath))
{
return ChannelOptions.GamePathNullOrEmpty();
throw ThrowHelper.InvalidOperation($"Invalid game path: {gamePath}");
}
bool isOversea = LaunchScheme.ExecutableIsOversea(gameFileSystem.GameFileName);
bool isOversea = LaunchScheme.ExecutableIsOversea(Path.GetFileName(gamePath));
if (!File.Exists(gameFileSystem.GameConfigFilePath))
if (!File.Exists(configPath))
{
return ChannelOptions.ConfigurationFileNotFound(gameFileSystem.GameConfigFilePath);
return ChannelOptions.FileNotFound(isOversea, configPath);
}
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;
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;
return new(channel, subChannel, isOversea);
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;
}
}

View File

@@ -1,9 +1,13 @@
// 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);
}

View File

@@ -10,7 +10,6 @@ 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";

View File

@@ -1,39 +0,0 @@
// 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); }
}

View File

@@ -5,8 +5,10 @@ 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.Launching.Handler;
using Snap.Hutao.Service.Game.Package;
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;
@@ -21,6 +23,8 @@ 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/>
@@ -41,6 +45,12 @@ 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)
{
@@ -53,6 +63,12 @@ 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)
{
@@ -74,6 +90,18 @@ internal sealed partial class GameServiceFacade : IGameServiceFacade
/// <inheritdoc/>
public bool IsGameRunning()
{
return LaunchExecutionEnsureGameNotRunningHandler.IsGameRunning(out _);
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);
}
}

View File

@@ -4,6 +4,8 @@
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;
@@ -47,6 +49,8 @@ internal interface IGameServiceFacade
/// <returns>是否正在运行</returns>
bool IsGameRunning();
ValueTask LaunchAsync(IProgress<LaunchStatus> progress);
/// <summary>
/// 异步修改游戏账号名称
/// </summary>
@@ -61,5 +65,27 @@ 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);
}

View File

@@ -42,7 +42,6 @@ internal sealed class LaunchOptions : DbStoreOptions
private NameValue<int>? monitor;
private bool? isMonitorEnabled;
private bool? isUseCloudThirdPartyMobile;
private bool? isWindowsHDREnabled;
private AspectRatio? selectedAspectRatio;
private bool? useStarwardPlayTimeStatistics;
private bool? setDiscordActivityWhenPlaying;
@@ -199,12 +198,6 @@ internal sealed class LaunchOptions : DbStoreOptions
set => SetOption(ref isUseCloudThirdPartyMobile, SettingEntry.LaunchIsUseCloudThirdPartyMobile, value);
}
public bool IsWindowsHDREnabled
{
get => GetOption(ref isWindowsHDREnabled, SettingEntry.LaunchIsWindowsHDREnabled, false);
set => SetOption(ref isWindowsHDREnabled, SettingEntry.LaunchIsWindowsHDREnabled, value);
}
public List<AspectRatio> AspectRatios { get; } =
[
new(2560, 1440),
@@ -218,7 +211,8 @@ internal sealed class LaunchOptions : DbStoreOptions
{
if (SetProperty(ref selectedAspectRatio, value) && value is AspectRatio aspectRatio)
{
(ScreenWidth, ScreenHeight) = ((int)aspectRatio.Width, (int)aspectRatio.Height);
ScreenWidth = (int)aspectRatio.Width;
ScreenHeight = (int)aspectRatio.Height;
}
}
}

View File

@@ -3,25 +3,70 @@
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 TryGetGameFileSystem(this LaunchOptions options, [NotNullWhen(true)] out GameFileSystem? fileSystem)
public static bool TryGetGamePathAndGameDirectory(this LaunchOptions options, out string gamePath, [NotNullWhen(true)] out string? gameDirectory)
{
string gamePath = options.GamePath;
gamePath = options.GamePath;
if (string.IsNullOrEmpty(gamePath))
gameDirectory = Path.GetDirectoryName(gamePath);
if (string.IsNullOrEmpty(gameDirectory))
{
fileSystem = default;
return false;
}
fileSystem = new GameFileSystem(gamePath);
return true;
}
public static bool TryGetGameDirectoryAndGameFileName(this LaunchOptions options, [NotNullWhen(true)] out string? gameDirectory, [NotNullWhen(true)] out string? gameFileName)
{
string gamePath = options.GamePath;
gameDirectory = Path.GetDirectoryName(gamePath);
if (string.IsNullOrEmpty(gameDirectory))
{
gameFileName = default;
return false;
}
gameFileName = Path.GetFileName(gamePath);
if (string.IsNullOrEmpty(gameFileName))
{
return false;
}
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;

View File

@@ -1,38 +0,0 @@
// 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);
}
}

View File

@@ -1,160 +0,0 @@
// 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;
}
}
}

View File

@@ -1,20 +0,0 @@
// 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);
}
}

View File

@@ -1,20 +0,0 @@
// 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);
}
}

View File

@@ -1,58 +0,0 @@
// 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,
},
};
}
}

View File

@@ -1,25 +0,0 @@
// 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);
}
}

View File

@@ -1,72 +0,0 @@
// 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);
}
}

View File

@@ -1,39 +0,0 @@
// 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);
}
}
}
}

View File

@@ -1,26 +0,0 @@
// 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);
}
}

View File

@@ -1,20 +0,0 @@
// 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);
}
}

View File

@@ -1,31 +0,0 @@
// 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);
}
}
}

View File

@@ -1,21 +0,0 @@
// 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!);
}
}

View File

@@ -1,42 +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.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);
}
}

View File

@@ -1,11 +0,0 @@
// 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);
}

View File

@@ -1,82 +0,0 @@
// 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;
}
}

View File

@@ -1,50 +0,0 @@
// 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;
}
}

View File

@@ -1,11 +0,0 @@
// 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!;
}

View File

@@ -1,20 +0,0 @@
// 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,
}

View File

@@ -0,0 +1,102 @@
// 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;
}
}
}

View File

@@ -0,0 +1,11 @@
// 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);
}

Some files were not shown because too many files have changed in this diff Show More