From 42a19239e6b02ac1869cfaf860ff538c57e03b3a Mon Sep 17 00:00:00 2001 From: Lightczx <1686188646@qq.com> Date: Wed, 10 May 2023 13:44:09 +0800 Subject: [PATCH] fix launch scheme detection --- .../Snap.Hutao.SourceGeneration.csproj | 5 + .../Snap.Hutao.Test/Snap.Hutao.Test.csproj | 1 + src/Snap.Hutao/Snap.Hutao.sln | 32 +++++++ src/Snap.Hutao/Snap.Hutao/App.xaml.cs | 8 +- .../Snap.Hutao/Core/LifeCycle/Activation.cs | 15 +-- .../Snap.Hutao/Core/Setting/SettingKeys.cs | 1 + .../Snap.Hutao/Core/StringLiterals.cs | 3 + .../DispatherQueueSwitchOperation.cs | 13 ++- .../Threading/ThreadPoolSwitchOperation.cs | 7 +- .../Snap.Hutao/Core/Windowing/Persistence.cs | 3 +- .../Core/Windowing/WindowSubclass.cs | 2 +- .../Snap.Hutao/Properties/launchSettings.json | 4 +- .../Resource/Localization/SH.Designer.cs | 45 +++++++++ .../Snap.Hutao/Resource/Localization/SH.resx | 15 +++ .../Snap.Hutao/Service/AppOptions.cs | 10 +- .../Snap.Hutao/Service/Game/GameService.cs | 9 +- .../Snap.Hutao/Service/Game/IGameService.cs | 2 +- .../Snap.Hutao/Service/Game/LaunchOptions.cs | 2 +- .../Service/Game/LaunchScheme.KnownSchemes.cs | 96 +++++++++++++++++++ .../Snap.Hutao/Service/Game/LaunchScheme.cs | 69 +------------ .../Snap.Hutao/Service/Game/MultiChannel.cs | 33 ++++++- src/Snap.Hutao/Snap.Hutao/Snap.Hutao.csproj | 1 + .../View/Control/StatisticsCard.xaml | 3 +- .../View/Page/AnnouncementPage.xaml | 9 +- .../View/Page/AnnouncementPage.xaml.cs | 2 +- .../Snap.Hutao/View/Page/GachaLogPage.xaml | 2 +- .../ViewModel/AnnouncementViewModel.cs | 37 ------- .../ViewModel/GachaLog/HutaoCloudViewModel.cs | 4 +- .../ViewModel/Game/LaunchGameViewModel.cs | 16 ++-- .../ViewModel/Home/AnnouncementViewModel.cs | 84 ++++++++++++++++ .../Snap.Hutao/ViewModel/Home/GreetingTier.cs | 35 +++++++ .../Snap.Hutao/Win32/StructMarshal.cs | 6 +- 32 files changed, 416 insertions(+), 158 deletions(-) create mode 100644 src/Snap.Hutao/Snap.Hutao/Service/Game/LaunchScheme.KnownSchemes.cs delete mode 100644 src/Snap.Hutao/Snap.Hutao/ViewModel/AnnouncementViewModel.cs create mode 100644 src/Snap.Hutao/Snap.Hutao/ViewModel/Home/AnnouncementViewModel.cs create mode 100644 src/Snap.Hutao/Snap.Hutao/ViewModel/Home/GreetingTier.cs 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; }