diff --git a/src/Snap.Hutao/Snap.Hutao.SourceGeneration/Snap.Hutao.SourceGeneration.csproj b/src/Snap.Hutao/Snap.Hutao.SourceGeneration/Snap.Hutao.SourceGeneration.csproj
index af34d3ef..0068ab77 100644
--- a/src/Snap.Hutao/Snap.Hutao.SourceGeneration/Snap.Hutao.SourceGeneration.csproj
+++ b/src/Snap.Hutao/Snap.Hutao.SourceGeneration/Snap.Hutao.SourceGeneration.csproj
@@ -7,12 +7,17 @@
enable
x64
true
+ Debug;Release;Debug As Fake Elevated
True
+
+ True
+
+
diff --git a/src/Snap.Hutao/Snap.Hutao.Test/Snap.Hutao.Test.csproj b/src/Snap.Hutao/Snap.Hutao.Test/Snap.Hutao.Test.csproj
index b918f411..9d41f1c7 100644
--- a/src/Snap.Hutao/Snap.Hutao.Test/Snap.Hutao.Test.csproj
+++ b/src/Snap.Hutao/Snap.Hutao.Test/Snap.Hutao.Test.csproj
@@ -7,6 +7,7 @@
false
true
True
+ Debug;Release;Debug As Fake Elevated
diff --git a/src/Snap.Hutao/Snap.Hutao.sln b/src/Snap.Hutao/Snap.Hutao.sln
index 69968c32..58661dc5 100644
--- a/src/Snap.Hutao/Snap.Hutao.sln
+++ b/src/Snap.Hutao/Snap.Hutao.sln
@@ -16,6 +16,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Snap.Hutao.Test", "Snap.Hut
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug As Fake Elevated|Any CPU = Debug As Fake Elevated|Any CPU
+ Debug As Fake Elevated|arm64 = Debug As Fake Elevated|arm64
+ Debug As Fake Elevated|x64 = Debug As Fake Elevated|x64
+ Debug As Fake Elevated|x86 = Debug As Fake Elevated|x86
Debug|Any CPU = Debug|Any CPU
Debug|arm64 = Debug|arm64
Debug|x64 = Debug|x64
@@ -26,6 +30,18 @@ Global
Release|x86 = Release|x86
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {AAAB7CF0-F299-49B8-BDB4-4C320B3EC2C7}.Debug As Fake Elevated|Any CPU.ActiveCfg = Debug As Fake Elevated|x64
+ {AAAB7CF0-F299-49B8-BDB4-4C320B3EC2C7}.Debug As Fake Elevated|Any CPU.Build.0 = Debug As Fake Elevated|x64
+ {AAAB7CF0-F299-49B8-BDB4-4C320B3EC2C7}.Debug As Fake Elevated|Any CPU.Deploy.0 = Debug As Fake Elevated|x64
+ {AAAB7CF0-F299-49B8-BDB4-4C320B3EC2C7}.Debug As Fake Elevated|arm64.ActiveCfg = Debug As Fake Elevated|x64
+ {AAAB7CF0-F299-49B8-BDB4-4C320B3EC2C7}.Debug As Fake Elevated|arm64.Build.0 = Debug As Fake Elevated|x64
+ {AAAB7CF0-F299-49B8-BDB4-4C320B3EC2C7}.Debug As Fake Elevated|arm64.Deploy.0 = Debug As Fake Elevated|x64
+ {AAAB7CF0-F299-49B8-BDB4-4C320B3EC2C7}.Debug As Fake Elevated|x64.ActiveCfg = Debug As Fake Elevated|x64
+ {AAAB7CF0-F299-49B8-BDB4-4C320B3EC2C7}.Debug As Fake Elevated|x64.Build.0 = Debug As Fake Elevated|x64
+ {AAAB7CF0-F299-49B8-BDB4-4C320B3EC2C7}.Debug As Fake Elevated|x64.Deploy.0 = Debug As Fake Elevated|x64
+ {AAAB7CF0-F299-49B8-BDB4-4C320B3EC2C7}.Debug As Fake Elevated|x86.ActiveCfg = Debug As Fake Elevated|x64
+ {AAAB7CF0-F299-49B8-BDB4-4C320B3EC2C7}.Debug As Fake Elevated|x86.Build.0 = Debug As Fake Elevated|x64
+ {AAAB7CF0-F299-49B8-BDB4-4C320B3EC2C7}.Debug As Fake Elevated|x86.Deploy.0 = Debug As Fake Elevated|x64
{AAAB7CF0-F299-49B8-BDB4-4C320B3EC2C7}.Debug|Any CPU.ActiveCfg = Debug|x64
{AAAB7CF0-F299-49B8-BDB4-4C320B3EC2C7}.Debug|Any CPU.Build.0 = Debug|x64
{AAAB7CF0-F299-49B8-BDB4-4C320B3EC2C7}.Debug|Any CPU.Deploy.0 = Debug|x64
@@ -50,6 +66,14 @@ Global
{AAAB7CF0-F299-49B8-BDB4-4C320B3EC2C7}.Release|x86.ActiveCfg = Release|x86
{AAAB7CF0-F299-49B8-BDB4-4C320B3EC2C7}.Release|x86.Build.0 = Release|x86
{AAAB7CF0-F299-49B8-BDB4-4C320B3EC2C7}.Release|x86.Deploy.0 = Release|x86
+ {8B96721E-5604-47D2-9B72-06FEBAD0CE00}.Debug As Fake Elevated|Any CPU.ActiveCfg = Debug As Fake Elevated|x64
+ {8B96721E-5604-47D2-9B72-06FEBAD0CE00}.Debug As Fake Elevated|Any CPU.Build.0 = Debug As Fake Elevated|x64
+ {8B96721E-5604-47D2-9B72-06FEBAD0CE00}.Debug As Fake Elevated|arm64.ActiveCfg = Debug As Fake Elevated|x64
+ {8B96721E-5604-47D2-9B72-06FEBAD0CE00}.Debug As Fake Elevated|arm64.Build.0 = Debug As Fake Elevated|x64
+ {8B96721E-5604-47D2-9B72-06FEBAD0CE00}.Debug As Fake Elevated|x64.ActiveCfg = Debug As Fake Elevated|x64
+ {8B96721E-5604-47D2-9B72-06FEBAD0CE00}.Debug As Fake Elevated|x64.Build.0 = Debug As Fake Elevated|x64
+ {8B96721E-5604-47D2-9B72-06FEBAD0CE00}.Debug As Fake Elevated|x86.ActiveCfg = Debug As Fake Elevated|x64
+ {8B96721E-5604-47D2-9B72-06FEBAD0CE00}.Debug As Fake Elevated|x86.Build.0 = Debug As Fake Elevated|x64
{8B96721E-5604-47D2-9B72-06FEBAD0CE00}.Debug|Any CPU.ActiveCfg = Debug|x64
{8B96721E-5604-47D2-9B72-06FEBAD0CE00}.Debug|Any CPU.Build.0 = Debug|x64
{8B96721E-5604-47D2-9B72-06FEBAD0CE00}.Debug|arm64.ActiveCfg = Debug|Any CPU
@@ -66,6 +90,14 @@ Global
{8B96721E-5604-47D2-9B72-06FEBAD0CE00}.Release|x64.Build.0 = Release|x64
{8B96721E-5604-47D2-9B72-06FEBAD0CE00}.Release|x86.ActiveCfg = Release|Any CPU
{8B96721E-5604-47D2-9B72-06FEBAD0CE00}.Release|x86.Build.0 = Release|Any CPU
+ {D691BA9F-904C-4229-87A5-E14F2EFF2F64}.Debug As Fake Elevated|Any CPU.ActiveCfg = Debug As Fake Elevated|Any CPU
+ {D691BA9F-904C-4229-87A5-E14F2EFF2F64}.Debug As Fake Elevated|Any CPU.Build.0 = Debug As Fake Elevated|Any CPU
+ {D691BA9F-904C-4229-87A5-E14F2EFF2F64}.Debug As Fake Elevated|arm64.ActiveCfg = Debug As Fake Elevated|Any CPU
+ {D691BA9F-904C-4229-87A5-E14F2EFF2F64}.Debug As Fake Elevated|arm64.Build.0 = Debug As Fake Elevated|Any CPU
+ {D691BA9F-904C-4229-87A5-E14F2EFF2F64}.Debug As Fake Elevated|x64.ActiveCfg = Debug As Fake Elevated|Any CPU
+ {D691BA9F-904C-4229-87A5-E14F2EFF2F64}.Debug As Fake Elevated|x64.Build.0 = Debug As Fake Elevated|Any CPU
+ {D691BA9F-904C-4229-87A5-E14F2EFF2F64}.Debug As Fake Elevated|x86.ActiveCfg = Debug As Fake Elevated|Any CPU
+ {D691BA9F-904C-4229-87A5-E14F2EFF2F64}.Debug As Fake Elevated|x86.Build.0 = Debug As Fake Elevated|Any CPU
{D691BA9F-904C-4229-87A5-E14F2EFF2F64}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{D691BA9F-904C-4229-87A5-E14F2EFF2F64}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D691BA9F-904C-4229-87A5-E14F2EFF2F64}.Debug|arm64.ActiveCfg = Debug|Any CPU
diff --git a/src/Snap.Hutao/Snap.Hutao/App.xaml.cs b/src/Snap.Hutao/Snap.Hutao/App.xaml.cs
index 0b41c516..5b670c58 100644
--- a/src/Snap.Hutao/Snap.Hutao/App.xaml.cs
+++ b/src/Snap.Hutao/Snap.Hutao/App.xaml.cs
@@ -64,7 +64,7 @@ public sealed partial class App : Application
Process.GetCurrentProcess().Kill();
}
}
- catch (Exception)
+ catch
{
// AppInstance.GetCurrent() calls failed
Process.GetCurrentProcess().Kill();
@@ -80,8 +80,8 @@ public sealed partial class App : Application
{
HutaoOptions hutaoOptions = serviceProvider.GetRequiredService();
- logger.LogInformation("Snap Hutao FamilyName: {name}", hutaoOptions.FamilyName);
- logger.LogInformation("Snap Hutao Version: {version}", hutaoOptions.Version);
- logger.LogInformation("Snap Hutao LocalCache: {folder}", hutaoOptions.LocalCache);
+ logger.LogInformation("FamilyName: {name}", hutaoOptions.FamilyName);
+ logger.LogInformation("Version: {version}", hutaoOptions.Version);
+ logger.LogInformation("LocalCache: {folder}", hutaoOptions.LocalCache);
}
}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/Core/LifeCycle/Activation.cs b/src/Snap.Hutao/Snap.Hutao/Core/LifeCycle/Activation.cs
index 69f99859..39208a63 100644
--- a/src/Snap.Hutao/Snap.Hutao/Core/LifeCycle/Activation.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Core/LifeCycle/Activation.cs
@@ -11,13 +11,15 @@ using Snap.Hutao.Service.Metadata;
using Snap.Hutao.Service.Navigation;
using Snap.Hutao.Service.Notification;
using System.Diagnostics;
-using System.Security.Principal;
using Windows.ApplicationModel;
+#if !DEBUG_AS_FAKE_ELEVATED
+using System.Security.Principal;
+#endif
namespace Snap.Hutao.Core.LifeCycle;
///
-/// 激活处理器
+/// 激活
///
[HighQuality]
internal static class Activation
@@ -58,16 +60,15 @@ internal static class Activation
/// 是否提升了权限
public static bool GetElevated()
{
- if (Debugger.IsAttached)
- {
- return true;
- }
-
+#if DEBUG_AS_FAKE_ELEVATED
+ return true;
+#else
using (WindowsIdentity identity = WindowsIdentity.GetCurrent())
{
WindowsPrincipal principal = new(identity);
return principal.IsInRole(WindowsBuiltInRole.Administrator);
}
+#endif
}
///
diff --git a/src/Snap.Hutao/Snap.Hutao/Core/Setting/SettingKeys.cs b/src/Snap.Hutao/Snap.Hutao/Core/Setting/SettingKeys.cs
index 9eeb38e0..a4c71725 100644
--- a/src/Snap.Hutao/Snap.Hutao/Core/Setting/SettingKeys.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Core/Setting/SettingKeys.cs
@@ -7,6 +7,7 @@ namespace Snap.Hutao.Core.Setting;
/// 设置键
///
[HighQuality]
+[SuppressMessage("", "SA1124")]
internal static class SettingKeys
{
///
diff --git a/src/Snap.Hutao/Snap.Hutao/Core/StringLiterals.cs b/src/Snap.Hutao/Snap.Hutao/Core/StringLiterals.cs
index cf13ae58..27587360 100644
--- a/src/Snap.Hutao/Snap.Hutao/Core/StringLiterals.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Core/StringLiterals.cs
@@ -29,5 +29,8 @@ internal static class StringLiterals
///
public const string False = "False";
+ ///
+ /// CRLF 换行符
+ ///
public const string CRLF = "\r\n";
}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/Core/Threading/DispatherQueueSwitchOperation.cs b/src/Snap.Hutao/Snap.Hutao/Core/Threading/DispatherQueueSwitchOperation.cs
index 7824e608..5893768c 100644
--- a/src/Snap.Hutao/Snap.Hutao/Core/Threading/DispatherQueueSwitchOperation.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Core/Threading/DispatherQueueSwitchOperation.cs
@@ -11,7 +11,7 @@ namespace Snap.Hutao.Core.Threading;
/// 等待此类型对象后上下文会被切换至主线程
///
[HighQuality]
-internal readonly struct DispatherQueueSwitchOperation : IAwaitable, IAwaiter
+internal readonly struct DispatherQueueSwitchOperation : IAwaitable, ICriticalAwaiter
{
private readonly DispatcherQueue dispatherQueue;
@@ -44,6 +44,15 @@ internal readonly struct DispatherQueueSwitchOperation : IAwaitable
public void OnCompleted(Action continuation)
{
- dispatherQueue.TryEnqueue(() => continuation());
+ dispatherQueue.TryEnqueue(continuation.Invoke);
+ }
+
+ ///
+ public void UnsafeOnCompleted(Action continuation)
+ {
+ using (ExecutionContext.SuppressFlow())
+ {
+ dispatherQueue.TryEnqueue(continuation.Invoke);
+ }
}
}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/Core/Threading/ThreadPoolSwitchOperation.cs b/src/Snap.Hutao/Snap.Hutao/Core/Threading/ThreadPoolSwitchOperation.cs
index df572106..7f30af3b 100644
--- a/src/Snap.Hutao/Snap.Hutao/Core/Threading/ThreadPoolSwitchOperation.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Core/Threading/ThreadPoolSwitchOperation.cs
@@ -10,7 +10,7 @@ namespace Snap.Hutao.Core.Threading;
/// 线程池切换操作
/// 等待此类型对象后上下文会被切换至线程池线程
///
-internal readonly struct ThreadPoolSwitchOperation : IAwaitable, IAwaiter, ICriticalAwaiter
+internal readonly struct ThreadPoolSwitchOperation : IAwaitable, ICriticalAwaiter
{
private static readonly WaitCallback WaitCallbackRunAction = RunAction;
private readonly DispatcherQueue dispatherQueue;
@@ -27,9 +27,8 @@ internal readonly struct ThreadPoolSwitchOperation : IAwaitable
public bool IsCompleted
{
- get =>
- // 如果已经处于后台就不再切换新的线程
- !dispatherQueue.HasThreadAccess;
+ // 如果已经处于后台就不再切换新的线程
+ get => !dispatherQueue.HasThreadAccess;
}
///
diff --git a/src/Snap.Hutao/Snap.Hutao/Core/Windowing/Persistence.cs b/src/Snap.Hutao/Snap.Hutao/Core/Windowing/Persistence.cs
index 3f5b4097..91dcdd27 100644
--- a/src/Snap.Hutao/Snap.Hutao/Core/Windowing/Persistence.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Core/Windowing/Persistence.cs
@@ -22,12 +22,13 @@ internal static class Persistence
///
/// 设置窗体位置
///
- /// 选项窗口param>
/// 窗体类型
+ /// 选项窗口param>
public static void RecoverOrInit(TWindow window)
where TWindow : Window, IWindowOptionsSource
{
WindowOptions options = window.WindowOptions;
+
// Set first launch size.
double scale = GetScaleForWindowHandle(options.Hwnd);
SizeInt32 transformedSize = options.InitSize.Scale(scale);
diff --git a/src/Snap.Hutao/Snap.Hutao/Core/Windowing/WindowSubclass.cs b/src/Snap.Hutao/Snap.Hutao/Core/Windowing/WindowSubclass.cs
index 29de6ea3..3dd9df26 100644
--- a/src/Snap.Hutao/Snap.Hutao/Core/Windowing/WindowSubclass.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Core/Windowing/WindowSubclass.cs
@@ -29,7 +29,7 @@ internal sealed class WindowSubclass : IDisposable
///
/// 构造一个新的窗体子类管理器
///
- /// 选项
+ /// 窗口
public WindowSubclass(TWindow window)
{
this.window = window;
diff --git a/src/Snap.Hutao/Snap.Hutao/Properties/launchSettings.json b/src/Snap.Hutao/Snap.Hutao/Properties/launchSettings.json
index 81bc67d4..a5fb2a14 100644
--- a/src/Snap.Hutao/Snap.Hutao/Properties/launchSettings.json
+++ b/src/Snap.Hutao/Snap.Hutao/Properties/launchSettings.json
@@ -1,12 +1,12 @@
{
"profiles": {
- "Snap.Hutao (Package)": {
+ "Snap.Hutao": {
"commandName": "MsixPackage",
"nativeDebugging": false,
"doNotLaunchApp": false,
"allowLocalNetworkLoopbackProperty": true
},
- "Snap.Hutao (Unpackaged)": {
+ "[Unpackaged] Snap.Hutao": {
"commandName": "Project"
}
}
diff --git a/src/Snap.Hutao/Snap.Hutao/Resource/Localization/SH.Designer.cs b/src/Snap.Hutao/Snap.Hutao/Resource/Localization/SH.Designer.cs
index db0bf0c1..c17f79cf 100644
--- a/src/Snap.Hutao/Snap.Hutao/Resource/Localization/SH.Designer.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Resource/Localization/SH.Designer.cs
@@ -3552,6 +3552,51 @@ namespace Snap.Hutao.Resource.Localization {
}
}
+ ///
+ /// 查找类似 胡桃已经为你启动了 {0} 次游戏 的本地化字符串。
+ ///
+ internal static string ViewPageHomeGreetingTextCommon1 {
+ get {
+ return ResourceManager.GetString("ViewPageHomeGreetingTextCommon1", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 你已经启动了胡桃 {0} 次 的本地化字符串。
+ ///
+ internal static string ViewPageHomeGreetingTextCommon2 {
+ get {
+ return ResourceManager.GetString("ViewPageHomeGreetingTextCommon2", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 旅行者,欢迎来到提瓦特大陆! 的本地化字符串。
+ ///
+ internal static string ViewPageHomeGreetingTextDefault {
+ get {
+ return ResourceManager.GetString("ViewPageHomeGreetingTextDefault", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 你说的对,但是《胡桃》是由 DGP Studio 自主研发的一款... 的本地化字符串。
+ ///
+ internal static string ViewPageHomeGreetingTextEasterEgg {
+ get {
+ return ResourceManager.GetString("ViewPageHomeGreetingTextEasterEgg", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 呐,旅行者,这是你来到提瓦特大陆的第 {0} 天哦 的本地化字符串。
+ ///
+ internal static string ViewPageHomeGreetingTextEpic1 {
+ get {
+ return ResourceManager.GetString("ViewPageHomeGreetingTextEpic1", resourceCulture);
+ }
+ }
+
///
/// 查找类似 设置 的本地化字符串。
///
diff --git a/src/Snap.Hutao/Snap.Hutao/Resource/Localization/SH.resx b/src/Snap.Hutao/Snap.Hutao/Resource/Localization/SH.resx
index 5789eb65..418d93e0 100644
--- a/src/Snap.Hutao/Snap.Hutao/Resource/Localization/SH.resx
+++ b/src/Snap.Hutao/Snap.Hutao/Resource/Localization/SH.resx
@@ -1995,4 +1995,19 @@
无法找到缓存的元数据文件
+
+ 旅行者,欢迎来到提瓦特大陆!
+
+
+ 胡桃已经为你启动了 {0} 次游戏
+
+
+ 你已经启动了胡桃 {0} 次
+
+
+ 你说的对,但是《胡桃》是由 DGP Studio 自主研发的一款...
+
+
+ 呐,旅行者,这是你来到提瓦特大陆的第 {0} 天哦
+
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/Service/AppOptions.cs b/src/Snap.Hutao/Snap.Hutao/Service/AppOptions.cs
index a9b9de35..f33000c5 100644
--- a/src/Snap.Hutao/Snap.Hutao/Service/AppOptions.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Service/AppOptions.cs
@@ -16,14 +16,14 @@ namespace Snap.Hutao.Service;
[Injection(InjectAs.Singleton)]
internal sealed class AppOptions : DbStoreOptions
{
- private readonly List> backdropTypes = new()
+ private static readonly List> SupportedBackdropTypes = new()
{
new("Acrylic", BackdropType.Acrylic),
new("Mica", BackdropType.Mica),
new("MicaAlt", BackdropType.MicaAlt),
};
- private readonly List> cultures = new()
+ private static readonly List> SupportedCultures = new()
{
ToNameValue(CultureInfo.GetCultureInfo("zh-Hans")),
ToNameValue(CultureInfo.GetCultureInfo("zh-Hant")),
@@ -40,7 +40,7 @@ internal sealed class AppOptions : DbStoreOptions
///
/// 构造一个新的应用程序选项
///
- /// 服务范围工厂
+ /// 服务提供器
public AppOptions(IServiceProvider serviceProvider)
: base(serviceProvider)
{
@@ -67,7 +67,7 @@ internal sealed class AppOptions : DbStoreOptions
///
/// 所有支持的背景样式
///
- public List> BackdropTypes { get => backdropTypes; }
+ public List> BackdropTypes { get => SupportedBackdropTypes; }
///
/// 背景类型 默认 Mica
@@ -81,7 +81,7 @@ internal sealed class AppOptions : DbStoreOptions
///
/// 所有支持的语言
///
- public List> Cultures { get => cultures; }
+ public List> Cultures { get => SupportedCultures; }
///
/// 初始化前的语言
diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Game/GameService.cs b/src/Snap.Hutao/Snap.Hutao/Service/Game/GameService.cs
index a98cab54..6a639c78 100644
--- a/src/Snap.Hutao/Snap.Hutao/Service/Game/GameService.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Service/Game/GameService.cs
@@ -107,23 +107,24 @@ internal sealed partial class GameService : IGameService
}
///
- public MultiChannel GetMultiChannel()
+ public ChannelOptions GetChannelOptions()
{
string gamePath = appOptions.GamePath;
string configPath = Path.Combine(Path.GetDirectoryName(gamePath) ?? string.Empty, ConfigFileName);
+ bool isOversea = string.Equals(Path.GetFileName(gamePath), GenshinImpactFileName, StringComparison.OrdinalIgnoreCase);
if (!File.Exists(configPath))
{
- return new(null, null, configPath);
+ return ChannelOptions.FileNotFound(isOversea, configPath);
}
using (FileStream stream = File.OpenRead(configPath))
{
- IEnumerable parameters = IniSerializer.Deserialize(stream).ToList().OfType();
+ List parameters = IniSerializer.Deserialize(stream).OfType().ToList();
string? channel = parameters.FirstOrDefault(p => p.Key == "channel")?.Value;
string? subChannel = parameters.FirstOrDefault(p => p.Key == "sub_channel")?.Value;
- return new(channel, subChannel);
+ return new(channel, subChannel, isOversea);
}
}
diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Game/IGameService.cs b/src/Snap.Hutao/Snap.Hutao/Service/Game/IGameService.cs
index 883d251b..9f02387b 100644
--- a/src/Snap.Hutao/Snap.Hutao/Service/Game/IGameService.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Service/Game/IGameService.cs
@@ -42,7 +42,7 @@ internal interface IGameService
/// 获取多通道值
///
/// 多通道值
- MultiChannel GetMultiChannel();
+ ChannelOptions GetChannelOptions();
///
/// 游戏是否正在运行
diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Game/LaunchOptions.cs b/src/Snap.Hutao/Snap.Hutao/Service/Game/LaunchOptions.cs
index ec36ae13..23926f28 100644
--- a/src/Snap.Hutao/Snap.Hutao/Service/Game/LaunchOptions.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Service/Game/LaunchOptions.cs
@@ -35,7 +35,7 @@ internal sealed class LaunchOptions : DbStoreOptions
///
/// 构造一个新的启动游戏选项
///
- /// 服务范围工厂
+ /// 服务提供器
public LaunchOptions(IServiceProvider serviceProvider)
: base(serviceProvider)
{
diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Game/LaunchScheme.KnownSchemes.cs b/src/Snap.Hutao/Snap.Hutao/Service/Game/LaunchScheme.KnownSchemes.cs
new file mode 100644
index 00000000..0c064687
--- /dev/null
+++ b/src/Snap.Hutao/Snap.Hutao/Service/Game/LaunchScheme.KnownSchemes.cs
@@ -0,0 +1,96 @@
+// Copyright (c) DGP Studio. All rights reserved.
+// Licensed under the MIT license.
+
+using Snap.Hutao.Model.Intrinsic;
+
+namespace Snap.Hutao.Service.Game;
+
+///
+/// 方案列表部分
+///
+internal sealed partial class LaunchScheme
+{
+ private const int SdkStaticLauncherChineseId = 18;
+ private const int SdkStaticLauncherBilibiliId = 17;
+ private const int SdkStaticLauncherGlobalId = 10;
+
+ private const string SdkStaticLauncherChineseKey = "eYd89JmJ";
+ private const string SdkStaticLauncherBilibiliKey = "KAtdSsoQ";
+ private const string SdkStaticLauncherGlobalKey = "gcStgarh";
+
+ private static readonly LaunchScheme ServerChineseChannelOfficialSubChannelOfficial = new()
+ {
+ LauncherId = SdkStaticLauncherChineseId,
+ Key = SdkStaticLauncherChineseKey,
+ Channel = ChannelType.Official,
+ SubChannel = SubChannelType.Official,
+ IsOversea = false,
+ };
+
+ private static readonly LaunchScheme ServerChineseChannelOfficialSubChannelNoTapTap = new()
+ {
+ LauncherId = SdkStaticLauncherChineseId,
+ Key = SdkStaticLauncherChineseKey,
+ Channel = ChannelType.Official,
+ SubChannel = SubChannelType.NoTapTap,
+ IsOversea = false,
+ };
+
+ private static readonly LaunchScheme ServerChineseChannelBilibiliSubChannelDefault = new()
+ {
+ LauncherId = SdkStaticLauncherBilibiliId,
+ Key = SdkStaticLauncherBilibiliKey,
+ Channel = ChannelType.Bili,
+ SubChannel = SubChannelType.Default,
+ IsOversea = false,
+ };
+
+ private static readonly LaunchScheme ServerGlobalChannelOfficialSubChannelDefault = new()
+ {
+ LauncherId = SdkStaticLauncherGlobalId,
+ Key = SdkStaticLauncherGlobalKey,
+ Channel = ChannelType.Official,
+ SubChannel = SubChannelType.Default,
+ IsOversea = false,
+ };
+
+ private static readonly LaunchScheme ServerGlobalChannelOfficialSubChannelEpic = new()
+ {
+ LauncherId = SdkStaticLauncherGlobalId,
+ Key = SdkStaticLauncherGlobalKey,
+ Channel = ChannelType.Official,
+ SubChannel = SubChannelType.Epic,
+ IsOversea = false,
+ };
+
+ private static readonly LaunchScheme ServerGlobalChannelOfficialSubChannelGoogle = new()
+ {
+ LauncherId = SdkStaticLauncherGlobalId,
+ Key = SdkStaticLauncherGlobalKey,
+ Channel = ChannelType.Official,
+ SubChannel = SubChannelType.Google,
+ IsOversea = false,
+ };
+
+ ///
+ /// 获取已知的启动方案
+ ///
+ /// 已知的启动方案
+ public static List GetKnownSchemes()
+ {
+ return new List()
+ {
+ // 官服
+ ServerChineseChannelOfficialSubChannelOfficial,
+ ServerChineseChannelOfficialSubChannelNoTapTap,
+
+ // 渠道服
+ ServerChineseChannelBilibiliSubChannelDefault,
+
+ // 国际服
+ ServerGlobalChannelOfficialSubChannelDefault,
+ ServerGlobalChannelOfficialSubChannelEpic,
+ ServerGlobalChannelOfficialSubChannelGoogle,
+ };
+ }
+}
diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Game/LaunchScheme.cs b/src/Snap.Hutao/Snap.Hutao/Service/Game/LaunchScheme.cs
index 613e2c3e..7a28f664 100644
--- a/src/Snap.Hutao/Snap.Hutao/Service/Game/LaunchScheme.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Service/Game/LaunchScheme.cs
@@ -2,7 +2,6 @@
// Licensed under the MIT license.
using Snap.Hutao.Model.Intrinsic;
-using System.Collections.Immutable;
namespace Snap.Hutao.Service.Game;
@@ -10,70 +9,8 @@ namespace Snap.Hutao.Service.Game;
/// 启动方案
///
[HighQuality]
-internal sealed class LaunchScheme
+internal sealed partial class LaunchScheme
{
- // TODO: fix detection
-
- ///
- /// 已知的启动方案
- ///
- public static readonly ImmutableList KnownSchemes = new List()
- {
- // 官服
- new()
- {
- LauncherId = "18",
- Key = "eYd89JmJ",
- Channel = ChannelType.Official,
- SubChannel = SubChannelType.Official,
- IsOversea = false,
- },
- new()
- {
- LauncherId = "18",
- Key = "eYd89JmJ",
- Channel = ChannelType.Official,
- SubChannel = SubChannelType.NoTapTap,
- IsOversea = false,
- },
-
- // 渠道服
- new()
- {
- LauncherId = "17",
- Key = "KAtdSsoQ",
- Channel = ChannelType.Bili,
- SubChannel = SubChannelType.Default,
- IsOversea = false,
- },
-
- // 国际服
- new()
- {
- LauncherId = "10",
- Key = "gcStgarh",
- Channel = ChannelType.Official,
- SubChannel = SubChannelType.Default,
- IsOversea = true,
- },
- new()
- {
- LauncherId = "10",
- Key = "gcStgarh",
- Channel = ChannelType.Official,
- SubChannel = SubChannelType.Epic,
- IsOversea = true,
- },
- new()
- {
- LauncherId = "10",
- Key = "gcStgarh",
- Channel = ChannelType.Official,
- SubChannel = SubChannelType.Google,
- IsOversea = true,
- },
- }.ToImmutableList();
-
///
/// 显示名称
///
@@ -104,7 +41,7 @@ internal sealed class LaunchScheme
///
/// 启动器 Id
///
- public string LauncherId { get; private set; } = default!;
+ public int LauncherId { get; private set; }
///
/// API Key
@@ -121,7 +58,7 @@ internal sealed class LaunchScheme
///
/// 多通道
/// 是否相等
- public bool MultiChannelEqual(in MultiChannel multiChannel)
+ public bool MultiChannelEqual(in ChannelOptions multiChannel)
{
return Channel == multiChannel.Channel && SubChannel == multiChannel.SubChannel;
}
diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Game/MultiChannel.cs b/src/Snap.Hutao/Snap.Hutao/Service/Game/MultiChannel.cs
index b3888a98..2f8a06ac 100644
--- a/src/Snap.Hutao/Snap.Hutao/Service/Game/MultiChannel.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Service/Game/MultiChannel.cs
@@ -9,7 +9,7 @@ namespace Snap.Hutao.Service.Game;
/// 多通道
///
[HighQuality]
-internal readonly struct MultiChannel
+internal readonly struct ChannelOptions
{
///
/// 通道
@@ -21,6 +21,11 @@ internal readonly struct MultiChannel
///
public readonly SubChannelType SubChannel;
+ ///
+ /// 是否为国际服
+ ///
+ public readonly bool IsOversea;
+
///
/// 配置文件路径 当不为 null 时则存在文件读写问题
///
@@ -31,12 +36,30 @@ internal readonly struct MultiChannel
///
/// 通道
/// 子通道
+ /// 是否为国际服
/// 配置文件路径
- public MultiChannel(string? channel, string? subChannel, string? configFilePath = null)
+ public ChannelOptions(string? channel, string? subChannel, bool isOversea, string? configFilePath = null)
{
- Channel = string.IsNullOrEmpty(channel) ? ChannelType.Default : Enum.Parse(channel);
- SubChannel = string.IsNullOrEmpty(subChannel) ? SubChannelType.Default : Enum.Parse(subChannel);
-
+ _ = Enum.TryParse(channel, out Channel);
+ _ = Enum.TryParse(subChannel, out SubChannel);
+ IsOversea = isOversea;
ConfigFilePath = configFilePath;
}
+
+ ///
+ /// 配置文件未找到
+ ///
+ /// 是否为国际服
+ /// 配置文件期望路径
+ /// 选项
+ public static ChannelOptions FileNotFound(bool isOversea, string configFilePath)
+ {
+ return new(null, null, isOversea, configFilePath);
+ }
+
+ ///
+ public override string ToString()
+ {
+ return $"[ChannelType:{Channel}] [SubChannel:{SubChannel}] [IsOversea: {IsOversea}]";
+ }
}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/Snap.Hutao.csproj b/src/Snap.Hutao/Snap.Hutao/Snap.Hutao.csproj
index db330003..7bacf5d0 100644
--- a/src/Snap.Hutao/Snap.Hutao/Snap.Hutao.csproj
+++ b/src/Snap.Hutao/Snap.Hutao/Snap.Hutao.csproj
@@ -35,6 +35,7 @@
Assets\Logo.ico
True
x64
+ Debug;Release;Debug As Fake Elevated
diff --git a/src/Snap.Hutao/Snap.Hutao/View/Control/StatisticsCard.xaml b/src/Snap.Hutao/Snap.Hutao/View/Control/StatisticsCard.xaml
index ac42e6bc..acc02f78 100644
--- a/src/Snap.Hutao/Snap.Hutao/View/Control/StatisticsCard.xaml
+++ b/src/Snap.Hutao/Snap.Hutao/View/Control/StatisticsCard.xaml
@@ -132,6 +132,7 @@
TextWrapping="NoWrap"/>
diff --git a/src/Snap.Hutao/Snap.Hutao/View/Page/AnnouncementPage.xaml b/src/Snap.Hutao/Snap.Hutao/View/Page/AnnouncementPage.xaml
index a62a1d84..51b2239f 100644
--- a/src/Snap.Hutao/Snap.Hutao/View/Page/AnnouncementPage.xaml
+++ b/src/Snap.Hutao/Snap.Hutao/View/Page/AnnouncementPage.xaml
@@ -2,7 +2,6 @@
x:Class="Snap.Hutao.View.Page.AnnouncementPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
- xmlns:clw="using:CommunityToolkit.Labs.WinUI"
xmlns:cwu="using:CommunityToolkit.WinUI.UI"
xmlns:cwua="using:CommunityToolkit.WinUI.UI.Animations"
xmlns:cwub="using:CommunityToolkit.WinUI.UI.Behaviors"
@@ -16,10 +15,10 @@
xmlns:shcb="using:Snap.Hutao.Control.Behavior"
xmlns:shci="using:Snap.Hutao.Control.Image"
xmlns:shcm="using:Snap.Hutao.Control.Markup"
- xmlns:shv="using:Snap.Hutao.ViewModel"
xmlns:shvca="using:Snap.Hutao.View.Card"
xmlns:shvco="using:Snap.Hutao.View.Control"
- d:DataContext="{d:DesignInstance shv:AnnouncementViewModel}"
+ xmlns:shvh="using:Snap.Hutao.ViewModel.Home"
+ d:DataContext="{d:DesignInstance shvh:AnnouncementViewModel}"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"
mc:Ignorable="d">
@@ -160,8 +159,8 @@
-
+ Text="{Binding GreetingText}"/>
+
diff --git a/src/Snap.Hutao/Snap.Hutao/ViewModel/AnnouncementViewModel.cs b/src/Snap.Hutao/Snap.Hutao/ViewModel/AnnouncementViewModel.cs
deleted file mode 100644
index a50ccfe4..00000000
--- a/src/Snap.Hutao/Snap.Hutao/ViewModel/AnnouncementViewModel.cs
+++ /dev/null
@@ -1,37 +0,0 @@
-// Copyright (c) DGP Studio. All rights reserved.
-// Licensed under the MIT license.
-
-using Snap.Hutao.Service.Abstraction;
-using Snap.Hutao.Web.Hoyolab.Hk4e.Common.Announcement;
-
-namespace Snap.Hutao.ViewModel;
-
-///
-/// 公告视图模型
-///
-[HighQuality]
-[ConstructorGenerated]
-[Injection(InjectAs.Scoped)]
-internal sealed partial class AnnouncementViewModel : Abstraction.ViewModel
-{
- private readonly IAnnouncementService announcementService;
-
- private AnnouncementWrapper? announcement;
-
- ///
- /// 公告
- ///
- public AnnouncementWrapper? Announcement { get => announcement; set => SetProperty(ref announcement, value); }
-
- ///
- protected override async Task OpenUIAsync()
- {
- try
- {
- Announcement = await announcementService.GetAnnouncementsAsync(CancellationToken).ConfigureAwait(true);
- }
- catch (OperationCanceledException)
- {
- }
- }
-}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/ViewModel/GachaLog/HutaoCloudViewModel.cs b/src/Snap.Hutao/Snap.Hutao/ViewModel/GachaLog/HutaoCloudViewModel.cs
index 1b1cfc73..f6b1b48d 100644
--- a/src/Snap.Hutao/Snap.Hutao/ViewModel/GachaLog/HutaoCloudViewModel.cs
+++ b/src/Snap.Hutao/Snap.Hutao/ViewModel/GachaLog/HutaoCloudViewModel.cs
@@ -133,8 +133,8 @@ internal sealed partial class HutaoCloudViewModel : Abstraction.ViewModel
.Navigate(INavigationAwaiter.Default);
}
- [Command("NavigateToAfdianSKuCommand")]
- private async Task NavigateToAfdianSKuAsync()
+ [Command("NavigateToAfdianSkuCommand")]
+ private async Task NavigateToAfdianSkuAsync()
{
await Windows.System.Launcher.LaunchUriAsync(new(@"ms-windows-store://pdp/?productid=9PH4NXJ2JN52"));
}
diff --git a/src/Snap.Hutao/Snap.Hutao/ViewModel/Game/LaunchGameViewModel.cs b/src/Snap.Hutao/Snap.Hutao/ViewModel/Game/LaunchGameViewModel.cs
index 216595d5..64e7e63c 100644
--- a/src/Snap.Hutao/Snap.Hutao/ViewModel/Game/LaunchGameViewModel.cs
+++ b/src/Snap.Hutao/Snap.Hutao/ViewModel/Game/LaunchGameViewModel.cs
@@ -47,7 +47,7 @@ internal sealed partial class LaunchGameViewModel : Abstraction.ViewModel
///
/// 已知的服务器方案
///
- public List KnownSchemes { get => LaunchScheme.KnownSchemes.ToList(); }
+ public List KnownSchemes { get => LaunchScheme.GetKnownSchemes(); }
///
/// 当前选择的服务器方案
@@ -108,22 +108,24 @@ internal sealed partial class LaunchGameViewModel : Abstraction.ViewModel
{
using (await EnterCriticalExecutionAsync().ConfigureAwait(false))
{
- MultiChannel multi = gameService.GetMultiChannel();
- if (string.IsNullOrEmpty(multi.ConfigFilePath))
+ ChannelOptions options = gameService.GetChannelOptions();
+ if (string.IsNullOrEmpty(options.ConfigFilePath))
{
try
{
- SelectedScheme = KnownSchemes.Single(scheme => scheme.MultiChannelEqual(multi));
+ SelectedScheme = KnownSchemes
+ .Where(scheme => scheme.IsOversea == options.IsOversea)
+ .Single(scheme => scheme.MultiChannelEqual(options));
}
catch (InvalidOperationException)
{
- // 后台收集用
- throw new NotSupportedException($"不支持的 MultiChannel: [ChannelType:{multi.Channel}] [SubChannel:{multi.SubChannel}]");
+ // 后台收集
+ throw new NotSupportedException($"不支持的 MultiChannel: {options}");
}
}
else
{
- infoBarService.Warning(string.Format(SH.ViewModelLaunchGameMultiChannelReadFail, multi.ConfigFilePath));
+ infoBarService.Warning(string.Format(SH.ViewModelLaunchGameMultiChannelReadFail, options.ConfigFilePath));
}
ObservableCollection accounts = gameService.GameAccountCollection;
diff --git a/src/Snap.Hutao/Snap.Hutao/ViewModel/Home/AnnouncementViewModel.cs b/src/Snap.Hutao/Snap.Hutao/ViewModel/Home/AnnouncementViewModel.cs
new file mode 100644
index 00000000..63f48964
--- /dev/null
+++ b/src/Snap.Hutao/Snap.Hutao/ViewModel/Home/AnnouncementViewModel.cs
@@ -0,0 +1,84 @@
+// Copyright (c) DGP Studio. All rights reserved.
+// Licensed under the MIT license.
+
+using Snap.Hutao.Core.Setting;
+using Snap.Hutao.Service.Abstraction;
+using Snap.Hutao.Service.Hutao;
+using Snap.Hutao.Web.Hoyolab.Hk4e.Common.Announcement;
+
+namespace Snap.Hutao.ViewModel.Home;
+
+///
+/// 公告视图模型
+///
+[HighQuality]
+[ConstructorGenerated]
+[Injection(InjectAs.Scoped)]
+internal sealed partial class AnnouncementViewModel : Abstraction.ViewModel
+{
+ private readonly IAnnouncementService announcementService;
+ private readonly HutaoUserOptions hutaoUserOptions;
+ private readonly ITaskContext taskContext;
+
+ private AnnouncementWrapper? announcement;
+ private string greetingText = SH.ViewPageHomeGreetingTextDefault;
+
+ ///
+ /// 公告
+ ///
+ public AnnouncementWrapper? Announcement { get => announcement; set => SetProperty(ref announcement, value); }
+
+ ///
+ /// 用户选项
+ ///
+ public HutaoUserOptions UserOptions { get => hutaoUserOptions; }
+
+ ///
+ /// 欢迎语
+ ///
+ public string GreetingText { get => greetingText; set => SetProperty(ref greetingText, value); }
+
+ ///
+ protected override async Task OpenUIAsync()
+ {
+ try
+ {
+ AnnouncementWrapper announcement = await announcementService.GetAnnouncementsAsync(CancellationToken).ConfigureAwait(false);
+ await taskContext.SwitchToMainThreadAsync();
+ Announcement = announcement;
+ UpdateGreetingText();
+ }
+ catch (OperationCanceledException)
+ {
+ }
+ }
+
+ private void UpdateGreetingText()
+ {
+ // TODO avatar birthday override.
+ int rand = Random.Shared.Next(0, 1000);
+
+ if (rand >= 0 && rand < 6)
+ {
+ GreetingText = SH.ViewPageHomeGreetingTextEasterEgg;
+ }
+ else if (rand >= 6 && rand < 57)
+ {
+ // TODO: retrieve days
+ GreetingText = string.Format(SH.ViewPageHomeGreetingTextEpic1, 0);
+ }
+ else if (rand >= 57 && rand < 1000)
+ {
+ rand = Random.Shared.Next(0, 2);
+ if (rand == 0)
+ {
+ // TODO: impl game launch times
+ GreetingText = string.Format(SH.ViewPageHomeGreetingTextCommon1, 0);
+ }
+ else if (rand == 1)
+ {
+ GreetingText = string.Format(SH.ViewPageHomeGreetingTextCommon2, LocalSetting.Get(SettingKeys.LaunchTimes, 0));
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/ViewModel/Home/GreetingTier.cs b/src/Snap.Hutao/Snap.Hutao/ViewModel/Home/GreetingTier.cs
new file mode 100644
index 00000000..f7c992b3
--- /dev/null
+++ b/src/Snap.Hutao/Snap.Hutao/ViewModel/Home/GreetingTier.cs
@@ -0,0 +1,35 @@
+// Copyright (c) DGP Studio. All rights reserved.
+// Licensed under the MIT license.
+
+namespace Snap.Hutao.ViewModel.Home;
+
+///
+/// 欢迎语等级
+///
+internal enum GreetingTier
+{
+ ///
+ /// 默认
+ ///
+ Default,
+
+ ///
+ /// 普通
+ ///
+ Common,
+
+ ///
+ /// 稀有
+ ///
+ Rare,
+
+ ///
+ /// 史诗
+ ///
+ Epic,
+
+ ///
+ /// 传说
+ ///
+ Legendary,
+}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/Win32/StructMarshal.cs b/src/Snap.Hutao/Snap.Hutao/Win32/StructMarshal.cs
index 585501ec..d5a5b378 100644
--- a/src/Snap.Hutao/Snap.Hutao/Win32/StructMarshal.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Win32/StructMarshal.cs
@@ -44,7 +44,11 @@ internal static class StructMarshal
public static unsafe Windows.UI.Color Color(uint value)
{
Unsafe.SkipInit(out Windows.UI.Color color);
- *(uint*)&color = BinaryPrimitives.ReverseEndianness(value);
+
+ // *(uint*)&color = BinaryPrimitives.ReverseEndianness(value);
+ // How .NET store struct in BE host?
+ Span colorSpan = new(&color, sizeof(uint));
+ BinaryPrimitives.WriteUInt32BigEndian(colorSpan, value);
return color;
}