From 28ea71cf775a7f82d81e530a26587592374af098 Mon Sep 17 00:00:00 2001 From: Lightczx <1686188646@qq.com> Date: Tue, 21 Nov 2023 16:17:53 +0800 Subject: [PATCH 01/42] Add CollectionsMarshal Test --- .../CollectionsMarshalTest.cs | 51 +++++++++++++++++++ 1 file changed, 51 insertions(+) create mode 100644 src/Snap.Hutao/Snap.Hutao.Test/BaseClassLibrary/CollectionsMarshalTest.cs diff --git a/src/Snap.Hutao/Snap.Hutao.Test/BaseClassLibrary/CollectionsMarshalTest.cs b/src/Snap.Hutao/Snap.Hutao.Test/BaseClassLibrary/CollectionsMarshalTest.cs new file mode 100644 index 00000000..a7090302 --- /dev/null +++ b/src/Snap.Hutao/Snap.Hutao.Test/BaseClassLibrary/CollectionsMarshalTest.cs @@ -0,0 +1,51 @@ +using System.Collections.Generic; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace Snap.Hutao.Test.BaseClassLibrary; + +[TestClass] +public class CollectionsMarshalTest +{ + [TestMethod] + public void DictionaryMarshalGetValueRefOrNullRefIsNullRef() + { +#if NET8_0_OR_GREATER + Dictionary dictionaryValueKeyRefValue = []; + Dictionary dictionaryValueKeyValueValue = []; + Dictionary dictionaryRefKeyValueValue = []; + Dictionary dictionaryRefKeyRefValue = []; +#else + Dictionary dictionaryValueKeyRefValue = new(); + Dictionary dictionaryValueKeyValueValue = new(); + Dictionary dictionaryRefKeyValueValue = new(); + Dictionary dictionaryRefKeyRefValue = new(); +#endif + + Assert.IsTrue(Unsafe.IsNullRef(ref CollectionsMarshal.GetValueRefOrNullRef(dictionaryValueKeyRefValue, 1U))); + Assert.IsTrue(Unsafe.IsNullRef(ref CollectionsMarshal.GetValueRefOrNullRef(dictionaryValueKeyValueValue, 1U))); + Assert.IsTrue(Unsafe.IsNullRef(ref CollectionsMarshal.GetValueRefOrNullRef(dictionaryRefKeyValueValue, "no such key"))); + Assert.IsTrue(Unsafe.IsNullRef(ref CollectionsMarshal.GetValueRefOrNullRef(dictionaryRefKeyRefValue, "no such key"))); + } + + [TestMethod] + public void DictionaryMarshalGetValueRefOrAddDefaultIsDefault() + { +#if NET8_0_OR_GREATER + Dictionary dictionaryValueKeyRefValue = []; + Dictionary dictionaryValueKeyValueValue = []; + Dictionary dictionaryRefKeyValueValue = []; + Dictionary dictionaryRefKeyRefValue = []; +#else + Dictionary dictionaryValueKeyRefValue = new(); + Dictionary dictionaryValueKeyValueValue = new(); + Dictionary dictionaryRefKeyValueValue = new(); + Dictionary dictionaryRefKeyRefValue = new(); +#endif + + Assert.IsTrue(CollectionsMarshal.GetValueRefOrAddDefault(dictionaryValueKeyRefValue, 1U, out _) == default); + Assert.IsTrue(CollectionsMarshal.GetValueRefOrAddDefault(dictionaryValueKeyValueValue, 1U, out _) == default); + Assert.IsTrue(CollectionsMarshal.GetValueRefOrAddDefault(dictionaryRefKeyValueValue, "no such key", out _) == default); + Assert.IsTrue(CollectionsMarshal.GetValueRefOrAddDefault(dictionaryRefKeyRefValue, "no such key", out _) == default); + } +} \ No newline at end of file From cb9c9a0af2ab6d2f462aafd90e855f899a8a3e85 Mon Sep 17 00:00:00 2001 From: qhy040404 Date: Tue, 21 Nov 2023 22:14:27 +0800 Subject: [PATCH 02/42] Sync tab when navigate from GachaLog's HutaoCloudView to SpiralAbyssRecordPage --- .../Snap.Hutao/ViewModel/GachaLog/HutaoCloudViewModel.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Snap.Hutao/Snap.Hutao/ViewModel/GachaLog/HutaoCloudViewModel.cs b/src/Snap.Hutao/Snap.Hutao/ViewModel/GachaLog/HutaoCloudViewModel.cs index f7cc4040..890282ab 100644 --- a/src/Snap.Hutao/Snap.Hutao/ViewModel/GachaLog/HutaoCloudViewModel.cs +++ b/src/Snap.Hutao/Snap.Hutao/ViewModel/GachaLog/HutaoCloudViewModel.cs @@ -128,7 +128,7 @@ internal sealed partial class HutaoCloudViewModel : Abstraction.ViewModel [Command("NavigateToSpiralAbyssRecordCommand")] private void NavigateToSpiralAbyssRecord() { - navigationService.Navigate(INavigationAwaiter.Default); + navigationService.Navigate(INavigationAwaiter.Default, true); } private async ValueTask RefreshUidCollectionAsync() From 1117e322a69ef5431db4f187b6d83ca3803f46c8 Mon Sep 17 00:00:00 2001 From: qhy040404 Date: Wed, 22 Nov 2023 13:10:05 +0800 Subject: [PATCH 03/42] Support the coexistence of Snap Hutao and Snap Hutao Dev --- src/Snap.Hutao/Snap.Hutao/Snap.Hutao.csproj | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/Snap.Hutao/Snap.Hutao/Snap.Hutao.csproj b/src/Snap.Hutao/Snap.Hutao/Snap.Hutao.csproj index d4d8061b..23351fc0 100644 --- a/src/Snap.Hutao/Snap.Hutao/Snap.Hutao.csproj +++ b/src/Snap.Hutao/Snap.Hutao/Snap.Hutao.csproj @@ -760,4 +760,10 @@ MSBuild:Compile + + + + + + From 485ac1e682eda9e40bd1e946ee3a2a7c0357f6ed Mon Sep 17 00:00:00 2001 From: Lightczx <1686188646@qq.com> Date: Wed, 22 Nov 2023 13:28:40 +0800 Subject: [PATCH 04/42] trim unused xml --- .../Snap.Hutao/Package.StoreAssociation.xml | 3 --- src/Snap.Hutao/Snap.Hutao/Snap.Hutao.csproj | 14 +++++--------- 2 files changed, 5 insertions(+), 12 deletions(-) diff --git a/src/Snap.Hutao/Snap.Hutao/Package.StoreAssociation.xml b/src/Snap.Hutao/Snap.Hutao/Package.StoreAssociation.xml index 1386cb9a..7f484ba9 100644 --- a/src/Snap.Hutao/Snap.Hutao/Package.StoreAssociation.xml +++ b/src/Snap.Hutao/Snap.Hutao/Package.StoreAssociation.xml @@ -14,8 +14,5 @@ 胡桃工具箱 - - 60568DGPStudio.SnapGenshinResin - \ 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 d4d8061b..1d220b1d 100644 --- a/src/Snap.Hutao/Snap.Hutao/Snap.Hutao.csproj +++ b/src/Snap.Hutao/Snap.Hutao/Snap.Hutao.csproj @@ -187,6 +187,11 @@ + + + + + @@ -306,15 +311,6 @@ - - - - - - - - - From 126d19e96add356e968faa51ca114bc854700e48 Mon Sep 17 00:00:00 2001 From: qhy040404 Date: Wed, 22 Nov 2023 13:39:07 +0800 Subject: [PATCH 05/42] Apply suggestions --- .../Snap.Hutao/Package-Dev.appxmanifest | 68 +++++++++++++++++++ src/Snap.Hutao/Snap.Hutao/Snap.Hutao.csproj | 11 ++- 2 files changed, 73 insertions(+), 6 deletions(-) create mode 100644 src/Snap.Hutao/Snap.Hutao/Package-Dev.appxmanifest diff --git a/src/Snap.Hutao/Snap.Hutao/Package-Dev.appxmanifest b/src/Snap.Hutao/Snap.Hutao/Package-Dev.appxmanifest new file mode 100644 index 00000000..4cb960fd --- /dev/null +++ b/src/Snap.Hutao/Snap.Hutao/Package-Dev.appxmanifest @@ -0,0 +1,68 @@ + + + + + + + + Snap Hutao Dev + DGP Studio + Assets\StoreLogo.png + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 胡桃 Dev + + + + + + + + + + diff --git a/src/Snap.Hutao/Snap.Hutao/Snap.Hutao.csproj b/src/Snap.Hutao/Snap.Hutao/Snap.Hutao.csproj index 23351fc0..a7170955 100644 --- a/src/Snap.Hutao/Snap.Hutao/Snap.Hutao.csproj +++ b/src/Snap.Hutao/Snap.Hutao/Snap.Hutao.csproj @@ -38,6 +38,11 @@ x64 Debug;Release + + + + + @@ -760,10 +765,4 @@ MSBuild:Compile - - - - - - From 40b055d310da48686182975e5618dfcc71b595b1 Mon Sep 17 00:00:00 2001 From: Lightczx <1686188646@qq.com> Date: Wed, 22 Nov 2023 13:43:35 +0800 Subject: [PATCH 06/42] lock on get winrt obj disposed --- src/Snap.Hutao/Snap.Hutao/Extension/WinRTExtension.cs | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/Snap.Hutao/Snap.Hutao/Extension/WinRTExtension.cs b/src/Snap.Hutao/Snap.Hutao/Extension/WinRTExtension.cs index d88586a0..0b005287 100644 --- a/src/Snap.Hutao/Snap.Hutao/Extension/WinRTExtension.cs +++ b/src/Snap.Hutao/Snap.Hutao/Extension/WinRTExtension.cs @@ -10,10 +10,19 @@ internal static class WinRTExtension { public static bool IsDisposed(this IWinRTObject obj) { - return GetDisposed(obj.NativeObject); + IObjectReference objectReference = obj.NativeObject; + + lock (GetDisposedLock(objectReference)) + { + return GetDisposed(objectReference); + } } // protected bool disposed; [UnsafeAccessor(UnsafeAccessorKind.Field, Name ="disposed")] private static extern ref bool GetDisposed(IObjectReference objRef); + + // private object _disposedLock + [UnsafeAccessor(UnsafeAccessorKind.Field, Name = "_disposedLock")] + private static extern ref object GetDisposedLock(IObjectReference objRef); } \ No newline at end of file From 467eb13c876d7e3c61ad2ca1d03f67812df350a4 Mon Sep 17 00:00:00 2001 From: Lightczx <1686188646@qq.com> Date: Wed, 22 Nov 2023 13:48:05 +0800 Subject: [PATCH 07/42] file nesting --- src/Snap.Hutao/Snap.Hutao/.filenesting.json | 34 ++++++++++++++----- ...ifest => Package.development.appxmanifest} | 0 src/Snap.Hutao/Snap.Hutao/Snap.Hutao.csproj | 4 +-- 3 files changed, 28 insertions(+), 10 deletions(-) rename src/Snap.Hutao/Snap.Hutao/{Package-Dev.appxmanifest => Package.development.appxmanifest} (100%) diff --git a/src/Snap.Hutao/Snap.Hutao/.filenesting.json b/src/Snap.Hutao/Snap.Hutao/.filenesting.json index f88118b1..8093d3cd 100644 --- a/src/Snap.Hutao/Snap.Hutao/.filenesting.json +++ b/src/Snap.Hutao/Snap.Hutao/.filenesting.json @@ -4,12 +4,18 @@ "add": { "extensionToExtension": { "add": { - ".json": [ ".txt" ] + ".json": [ + ".txt" + ] } }, "pathSegment": { "add": { - ".*": [ ".cs", ".resx" ] + ".*": [ + ".cs", + ".resx", + ".appxmanifest" + ] } }, "fileSuffixToExtension": { @@ -19,12 +25,24 @@ }, "fileToFile": { "add": { - ".filenesting.json": [ "App.xaml.cs" ], - "app.manifest": [ "App.xaml.cs" ], - "Package.appxmanifest": [ "App.xaml" ], - "Package.StoreAssociation.xml": [ "App.xaml" ], - ".editorconfig": [ "Program.cs" ], - "GlobalUsing.cs": [ "Program.cs" ] + ".filenesting.json": [ + "App.xaml.cs" + ], + "app.manifest": [ + "App.xaml.cs" + ], + "Package.appxmanifest": [ + "App.xaml" + ], + "Package.StoreAssociation.xml": [ + "App.xaml" + ], + ".editorconfig": [ + "Program.cs" + ], + "GlobalUsing.cs": [ + "Program.cs" + ] } } } diff --git a/src/Snap.Hutao/Snap.Hutao/Package-Dev.appxmanifest b/src/Snap.Hutao/Snap.Hutao/Package.development.appxmanifest similarity index 100% rename from src/Snap.Hutao/Snap.Hutao/Package-Dev.appxmanifest rename to src/Snap.Hutao/Snap.Hutao/Package.development.appxmanifest diff --git a/src/Snap.Hutao/Snap.Hutao/Snap.Hutao.csproj b/src/Snap.Hutao/Snap.Hutao/Snap.Hutao.csproj index a7170955..c7ff07eb 100644 --- a/src/Snap.Hutao/Snap.Hutao/Snap.Hutao.csproj +++ b/src/Snap.Hutao/Snap.Hutao/Snap.Hutao.csproj @@ -40,8 +40,8 @@ - - + + From ee86f12168ed166d6d5a527f09090df7789907aa Mon Sep 17 00:00:00 2001 From: Lightczx <1686188646@qq.com> Date: Wed, 22 Nov 2023 16:59:01 +0800 Subject: [PATCH 08/42] impl #1082 --- .../Snap.Hutao/Core/LifeCycle/Activation.cs | 8 +++ .../Snap.Hutao/Core/RuntimeOptions.cs | 4 ++ .../Model/Entity/SettingEntry.Constant.cs | 2 + .../Snap.Hutao/Resource/Localization/SH.resx | 6 ++ .../Snap.Hutao/Service/AppOptionsExtension.cs | 4 +- .../Service/Discord/DiscordController.cs | 27 +++++++-- .../Service/Discord/DiscordService.cs | 17 ++++++ .../Service/Discord/IDiscordService.cs | 3 + .../Snap.Hutao/Service/Game/LaunchOptions.cs | 7 +++ .../Game/Process/GameProcessService.cs | 55 ++++++++++++++----- .../Game/Process/GameProcessTracker.cs | 24 ++++++++ src/Snap.Hutao/Snap.Hutao/Snap.Hutao.csproj | 4 +- .../Snap.Hutao/View/Page/LaunchGamePage.xaml | 6 ++ 13 files changed, 144 insertions(+), 23 deletions(-) create mode 100644 src/Snap.Hutao/Snap.Hutao/Service/Game/Process/GameProcessTracker.cs diff --git a/src/Snap.Hutao/Snap.Hutao/Core/LifeCycle/Activation.cs b/src/Snap.Hutao/Snap.Hutao/Core/LifeCycle/Activation.cs index 186f6917..45906a33 100644 --- a/src/Snap.Hutao/Snap.Hutao/Core/LifeCycle/Activation.cs +++ b/src/Snap.Hutao/Snap.Hutao/Core/LifeCycle/Activation.cs @@ -6,6 +6,7 @@ using Microsoft.Extensions.Caching.Memory; using Microsoft.Windows.AppLifecycle; using Snap.Hutao.Core.Setting; using Snap.Hutao.Service.DailyNote; +using Snap.Hutao.Service.Discord; using Snap.Hutao.Service.Hutao; using Snap.Hutao.Service.Metadata; using Snap.Hutao.Service.Navigation; @@ -165,6 +166,8 @@ internal sealed partial class Activation : IActivation serviceProvider.GetRequiredService(); + await taskContext.SwitchToBackgroundAsync(); + serviceProvider .GetRequiredService() .As()? @@ -176,6 +179,11 @@ internal sealed partial class Activation : IActivation .As()? .InitializeInternalAsync() .SafeForget(); + + serviceProvider + .GetRequiredService() + .SetNormalActivity() + .SafeForget(); } } diff --git a/src/Snap.Hutao/Snap.Hutao/Core/RuntimeOptions.cs b/src/Snap.Hutao/Snap.Hutao/Core/RuntimeOptions.cs index f5a89430..49c50cab 100644 --- a/src/Snap.Hutao/Snap.Hutao/Core/RuntimeOptions.cs +++ b/src/Snap.Hutao/Snap.Hutao/Core/RuntimeOptions.cs @@ -34,6 +34,8 @@ internal sealed class RuntimeOptions : IOptions { this.logger = logger; + AppLaunchTime = DateTimeOffset.UtcNow; + DataFolder = GetDataFolderPath(); LocalCache = ApplicationData.Current.LocalCacheFolder.Path; InstalledLocation = Package.Current.InstalledLocation.Path; @@ -96,6 +98,8 @@ internal sealed class RuntimeOptions : IOptions /// public bool IsElevated { get => isElevated ??= GetElevated(); } + public DateTimeOffset AppLaunchTime { get; } + /// public RuntimeOptions Value { get => this; } diff --git a/src/Snap.Hutao/Snap.Hutao/Model/Entity/SettingEntry.Constant.cs b/src/Snap.Hutao/Snap.Hutao/Model/Entity/SettingEntry.Constant.cs index b6e00850..97474eb7 100644 --- a/src/Snap.Hutao/Snap.Hutao/Model/Entity/SettingEntry.Constant.cs +++ b/src/Snap.Hutao/Snap.Hutao/Model/Entity/SettingEntry.Constant.cs @@ -106,6 +106,8 @@ internal sealed partial class SettingEntry public const string LaunchUseStarwardPlayTimeStatistics = "Launch.UseStarwardPlayTimeStatistics"; + public const string LaunchSetDiscordActivityWhenPlaying = "Launch.SetDiscordActivityWhenPlaying"; + /// /// 启动游戏 多倍启动 /// diff --git a/src/Snap.Hutao/Snap.Hutao/Resource/Localization/SH.resx b/src/Snap.Hutao/Snap.Hutao/Resource/Localization/SH.resx index 5ff9d3c8..da77f1ef 100644 --- a/src/Snap.Hutao/Snap.Hutao/Resource/Localization/SH.resx +++ b/src/Snap.Hutao/Snap.Hutao/Resource/Localization/SH.resx @@ -2057,6 +2057,12 @@ 所有选项仅会在启动游戏成功后保存 + + 在我游戏时设置 Discord Activity 状态 + + + Discord Activity + 文件 diff --git a/src/Snap.Hutao/Snap.Hutao/Service/AppOptionsExtension.cs b/src/Snap.Hutao/Snap.Hutao/Service/AppOptionsExtension.cs index 70e34e60..0359ed1d 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/AppOptionsExtension.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/AppOptionsExtension.cs @@ -29,9 +29,9 @@ internal static class AppOptionsExtension return true; } - public static bool TryGetGameFileName(this AppOptions appOptions, [NotNullWhen(true)] out string? gameFileName) + public static bool TryGetGamePathAndGameFileName(this AppOptions appOptions, out string gamePath, [NotNullWhen(true)] out string? gameFileName) { - string gamePath = appOptions.GamePath; + gamePath = appOptions.GamePath; gameFileName = Path.GetFileName(gamePath); if (string.IsNullOrEmpty(gameFileName)) diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Discord/DiscordController.cs b/src/Snap.Hutao/Snap.Hutao/Service/Discord/DiscordController.cs index 13c79ef0..35887797 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/Discord/DiscordController.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/Discord/DiscordController.cs @@ -10,6 +10,7 @@ namespace Snap.Hutao.Service.Discord; internal static class DiscordController { + // https://discord.com/developers/applications private const long HutaoAppId = 1173950861647552623L; private const long YuanshenId = 1175743396028088370L; private const long GenshinImpactId = 1175747474384760962L; @@ -21,11 +22,17 @@ internal static class DiscordController private static Snap.Discord.GameSDK.Discord? discordManager; private static bool isInitialized; - public static async ValueTask ClearActivityAsync() + public static async ValueTask SetDefaultActivityAsync(DateTimeOffset startTime) { ResetManagerOrIgnore(HutaoAppId); ActivityManager activityManager = discordManager.GetActivityManager(); - return await activityManager.ClearActivityAsync().ConfigureAwait(false); + + 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 SetPlayingYuanShenAsync() @@ -87,11 +94,10 @@ internal static class DiscordController lock (SyncRoot) { discordManager?.Dispose(); + discordManager = new(clientId, CreateFlags.NoRequireDiscord); + discordManager.SetLogHook(Snap.Discord.GameSDK.LogLevel.Debug, SetLogHookHandler.Create(&DebugWriteDiscordMessage)); } - discordManager = new(clientId, CreateFlags.NoRequireDiscord); - discordManager.SetLogHook(Snap.Discord.GameSDK.LogLevel.Debug, SetLogHookHandler.Create(&DebugWriteDiscordMessage)); - if (isInitialized) { return; @@ -117,7 +123,16 @@ internal static class DiscordController { lock (SyncRoot) { - discordManager?.RunCallbacks(); + try + { + discordManager?.RunCallbacks(); + } + catch (SEHException ex) + { + // Known error codes: + // 0x80004005 E_FAIL + System.Diagnostics.Debug.WriteLine($"[Discord.GameSDK]:[ERROR]:0x{ex.ErrorCode:X}"); + } } Thread.Sleep(100); diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Discord/DiscordService.cs b/src/Snap.Hutao/Snap.Hutao/Service/Discord/DiscordService.cs index 16de3814..4fb0880e 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/Discord/DiscordService.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/Discord/DiscordService.cs @@ -1,12 +1,29 @@ // Copyright (c) DGP Studio. All rights reserved. // Licensed under the MIT license. +using Snap.Discord.GameSDK; +using Snap.Hutao.Core; + namespace Snap.Hutao.Service.Discord; [ConstructorGenerated] [Injection(InjectAs.Singleton, typeof(IDiscordService))] internal sealed partial class DiscordService : IDiscordService, IDisposable { + private readonly RuntimeOptions runtimeOptions; + + public async ValueTask SetPlayingActivity(bool isOversea) + { + _ = isOversea + ? await DiscordController.SetPlayingGenshinImpactAsync().ConfigureAwait(false) + : await DiscordController.SetPlayingYuanShenAsync().ConfigureAwait(false); + } + + public async ValueTask SetNormalActivity() + { + _ = await DiscordController.SetDefaultActivityAsync(runtimeOptions.AppLaunchTime).ConfigureAwait(false); + } + public void Dispose() { DiscordController.Stop(); diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Discord/IDiscordService.cs b/src/Snap.Hutao/Snap.Hutao/Service/Discord/IDiscordService.cs index e80f0a1f..46717554 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/Discord/IDiscordService.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/Discord/IDiscordService.cs @@ -5,4 +5,7 @@ namespace Snap.Hutao.Service.Discord; internal interface IDiscordService { + ValueTask SetNormalActivity(); + + ValueTask SetPlayingActivity(bool isOversea); } \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Game/LaunchOptions.cs b/src/Snap.Hutao/Snap.Hutao/Service/Game/LaunchOptions.cs index aa265ef3..5876b870 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/Game/LaunchOptions.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/Game/LaunchOptions.cs @@ -38,6 +38,7 @@ internal sealed class LaunchOptions : DbStoreOptions private bool? isMonitorEnabled; private AspectRatio? selectedAspectRatio; private bool? useStarwardPlayTimeStatistics; + private bool? setDiscordActivityWhenPlaying; /// /// 构造一个新的启动游戏选项 @@ -190,6 +191,12 @@ internal sealed class LaunchOptions : DbStoreOptions set => SetOption(ref useStarwardPlayTimeStatistics, SettingEntry.LaunchUseStarwardPlayTimeStatistics, value); } + public bool SetDiscordActivityWhenPlaying + { + get => GetOption(ref setDiscordActivityWhenPlaying, SettingEntry.LaunchSetDiscordActivityWhenPlaying, true); + set => SetOption(ref setDiscordActivityWhenPlaying, SettingEntry.LaunchSetDiscordActivityWhenPlaying, value); + } + private static void InitializeMonitors(List> monitors) { // This list can't use foreach diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Game/Process/GameProcessService.cs b/src/Snap.Hutao/Snap.Hutao/Service/Game/Process/GameProcessService.cs index a30c6c77..ed27178f 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/Game/Process/GameProcessService.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/Game/Process/GameProcessService.cs @@ -2,6 +2,8 @@ // Licensed under the MIT license. using Snap.Hutao.Core; +using Snap.Hutao.Core.ExceptionService; +using Snap.Hutao.Service.Discord; using Snap.Hutao.Service.Game.Scheme; using Snap.Hutao.Service.Game.Unlocker; using System.IO; @@ -17,17 +19,18 @@ namespace Snap.Hutao.Service.Game.Process; internal sealed partial class GameProcessService : IGameProcessService { private readonly IServiceProvider serviceProvider; + private readonly IDiscordService discordService; private readonly RuntimeOptions runtimeOptions; private readonly LaunchOptions launchOptions; private readonly AppOptions appOptions; - private volatile int runningGamesCounter; + private volatile bool isGameRunning; public bool IsGameRunning() { - if (runningGamesCounter == 0) + if (isGameRunning) { - return false; + return true; } return System.Diagnostics.Process.GetProcessesByName(YuanShenProcessName).Length > 0 @@ -41,21 +44,24 @@ internal sealed partial class GameProcessService : IGameProcessService return; } - string gamePath = appOptions.GamePath; - ArgumentException.ThrowIfNullOrEmpty(gamePath); + if (!appOptions.TryGetGamePathAndGameFileName(out string gamePath, out string? gameFileName)) + { + ArgumentException.ThrowIfNullOrEmpty(gamePath); + return; // null check passing, actually never reach. + } + + bool isOversea = LaunchScheme.ExecutableIsOversea(gameFileName); progress.Report(new(LaunchPhase.ProcessInitializing, SH.ServiceGameLaunchPhaseProcessInitializing)); using (System.Diagnostics.Process game = InitializeGameProcess(gamePath)) { - try + using (new GameRunningTracker(this, isOversea)) { - Interlocked.Increment(ref runningGamesCounter); game.Start(); progress.Report(new(LaunchPhase.ProcessStarted, SH.ServiceGameLaunchPhaseProcessStarted)); - if (launchOptions.UseStarwardPlayTimeStatistics && appOptions.TryGetGameFileName(out string? gameFileName)) + if (launchOptions.UseStarwardPlayTimeStatistics) { - bool isOversea = LaunchScheme.ExecutableIsOversea(gameFileName); await Starward.LaunchForPlayTimeStatisticsAsync(isOversea).ConfigureAwait(false); } @@ -84,10 +90,6 @@ internal sealed partial class GameProcessService : IGameProcessService progress.Report(new(LaunchPhase.ProcessExited, SH.ServiceGameLaunchPhaseProcessExited)); } } - finally - { - Interlocked.Decrement(ref runningGamesCounter); - } } } @@ -131,4 +133,31 @@ internal sealed partial class GameProcessService : IGameProcessService Progress lockerProgress = new(unlockStatus => progress.Report(LaunchStatus.FromUnlockStatus(unlockStatus))); return unlocker.UnlockAsync(options, lockerProgress, token); } + + private class GameRunningTracker : IDisposable + { + private readonly GameProcessService service; + + public GameRunningTracker(GameProcessService service, bool isOversea) + { + service.isGameRunning = true; + + if (service.launchOptions.SetDiscordActivityWhenPlaying) + { + service.discordService.SetPlayingActivity(isOversea); + } + + this.service = service; + } + + public void Dispose() + { + if (service.launchOptions.SetDiscordActivityWhenPlaying) + { + service.discordService.SetNormalActivity(); + } + + service.isGameRunning = false; + } + } } \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Game/Process/GameProcessTracker.cs b/src/Snap.Hutao/Snap.Hutao/Service/Game/Process/GameProcessTracker.cs new file mode 100644 index 00000000..2c698e61 --- /dev/null +++ b/src/Snap.Hutao/Snap.Hutao/Service/Game/Process/GameProcessTracker.cs @@ -0,0 +1,24 @@ +// Copyright (c) DGP Studio. All rights reserved. +// Licensed under the MIT license. + +namespace Snap.Hutao.Service.Game.Process; + +internal sealed class GameProcessTracker : IDisposable +{ + private readonly Stack disposables = []; + + public TDisposable Track(TDisposable disposable) + where TDisposable : IDisposable + { + disposables.Push(disposable); + return disposable; + } + + public void Dispose() + { + while (disposables.TryPop(out IDisposable? disposable)) + { + disposable.Dispose(); + } + } +} \ 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 6f9e593b..71d0bc9e 100644 --- a/src/Snap.Hutao/Snap.Hutao/Snap.Hutao.csproj +++ b/src/Snap.Hutao/Snap.Hutao/Snap.Hutao.csproj @@ -288,11 +288,11 @@ - + - + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/src/Snap.Hutao/Snap.Hutao/View/Page/LaunchGamePage.xaml b/src/Snap.Hutao/Snap.Hutao/View/Page/LaunchGamePage.xaml index 8566e7f2..ffc86e3e 100644 --- a/src/Snap.Hutao/Snap.Hutao/View/Page/LaunchGamePage.xaml +++ b/src/Snap.Hutao/Snap.Hutao/View/Page/LaunchGamePage.xaml @@ -290,6 +290,12 @@ HeaderIcon="{shcm:FontIcon Glyph=}"> + + + From a17f9ca5432e1941763e90623c14446ecdc2554f Mon Sep 17 00:00:00 2001 From: DismissedLight <1686188646@qq.com> Date: Wed, 22 Nov 2023 22:28:06 +0800 Subject: [PATCH 09/42] optimize discord activity --- .../Snap.Hutao/Resource/Localization/SH.resx | 2 +- .../Service/Discord/DiscordController.cs | 47 ++++++++++++------- .../Service/Discord/DiscordService.cs | 1 - 3 files changed, 31 insertions(+), 19 deletions(-) diff --git a/src/Snap.Hutao/Snap.Hutao/Resource/Localization/SH.resx b/src/Snap.Hutao/Snap.Hutao/Resource/Localization/SH.resx index da77f1ef..09a2dcdf 100644 --- a/src/Snap.Hutao/Snap.Hutao/Resource/Localization/SH.resx +++ b/src/Snap.Hutao/Snap.Hutao/Resource/Localization/SH.resx @@ -2763,7 +2763,7 @@ 错误的 UID 格式 - 角色 UID 不存在 + 角色 UID 不存在,请稍候再试 游戏维护中 diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Discord/DiscordController.cs b/src/Snap.Hutao/Snap.Hutao/Service/Discord/DiscordController.cs index 35887797..df269176 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/Discord/DiscordController.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/Discord/DiscordController.cs @@ -15,7 +15,6 @@ internal static class DiscordController private const long YuanshenId = 1175743396028088370L; private const long GenshinImpactId = 1175747474384760962L; - private static readonly WaitCallback RunDiscordRunCallbacks = DiscordRunCallbacks; private static readonly CancellationTokenSource StopTokenSource = new(); private static readonly object SyncRoot = new(); @@ -103,7 +102,7 @@ internal static class DiscordController return; } - ThreadPool.UnsafeQueueUserWorkItem(RunDiscordRunCallbacks, StopTokenSource.Token); + DiscordRunCallbacksAsync(StopTokenSource.Token).SafeForget(); isInitialized = true; [UnmanagedCallersOnly(CallConvs = [typeof(CallConvStdcall)])] @@ -115,27 +114,41 @@ internal static class DiscordController } } - [SuppressMessage("", "SH007")] - private static void DiscordRunCallbacks(object? state) + private static async ValueTask DiscordRunCallbacksAsync(CancellationToken cancellationToken) { - CancellationToken cancellationToken = (CancellationToken)state!; - while (!cancellationToken.IsCancellationRequested) + using (PeriodicTimer timer = new(TimeSpan.FromMilliseconds(1000))) { - lock (SyncRoot) + try { - try + while (await timer.WaitForNextTickAsync(cancellationToken).ConfigureAwait(false)) { - discordManager?.RunCallbacks(); - } - catch (SEHException ex) - { - // Known error codes: - // 0x80004005 E_FAIL - System.Diagnostics.Debug.WriteLine($"[Discord.GameSDK]:[ERROR]:0x{ex.ErrorCode:X}"); + if (cancellationToken.IsCancellationRequested) + { + break; + } + + lock (SyncRoot) + { + try + { + discordManager?.RunCallbacks(); + } + catch (ResultException ex) + { + System.Diagnostics.Debug.WriteLine($"[Discord.GameSDK]:[ERROR]:{ex.Result}"); + } + catch (SEHException ex) + { + // Known error codes: + // 0x80004005 E_FAIL + System.Diagnostics.Debug.WriteLine($"[Discord.GameSDK]:[ERROR]:0x{ex.ErrorCode:X}"); + } + } } } - - Thread.Sleep(100); + catch (OperationCanceledException) + { + } } } } \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Discord/DiscordService.cs b/src/Snap.Hutao/Snap.Hutao/Service/Discord/DiscordService.cs index 4fb0880e..8f1654d3 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/Discord/DiscordService.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/Discord/DiscordService.cs @@ -1,7 +1,6 @@ // Copyright (c) DGP Studio. All rights reserved. // Licensed under the MIT license. -using Snap.Discord.GameSDK; using Snap.Hutao.Core; namespace Snap.Hutao.Service.Discord; From 60c8e948e802516a7f36c12070c00d920bc6e6b7 Mon Sep 17 00:00:00 2001 From: Masterain Date: Wed, 22 Nov 2023 07:43:54 -0800 Subject: [PATCH 10/42] New translations sh.resx (Japanese) --- .../Snap.Hutao/Resource/Localization/SH.ja.resx | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/Snap.Hutao/Snap.Hutao/Resource/Localization/SH.ja.resx b/src/Snap.Hutao/Snap.Hutao/Resource/Localization/SH.ja.resx index a0bcfab8..32e9ae27 100644 --- a/src/Snap.Hutao/Snap.Hutao/Resource/Localization/SH.ja.resx +++ b/src/Snap.Hutao/Snap.Hutao/Resource/Localization/SH.ja.resx @@ -2057,6 +2057,12 @@ これらの設定はゲームが正常に起動した時のみ保存されます。 + + 在我游戏时设置 Discord Activity 状态 + + + Discord Activity + ファイル @@ -2757,7 +2763,7 @@ UIDは正しくありません - UID は存在しません + 角色 UID 不存在,请稍候再试 ゲームメンテナンス中 From 29ad93949804118ce3eb1c2ab4163e98b935f96d Mon Sep 17 00:00:00 2001 From: Masterain Date: Wed, 22 Nov 2023 07:43:55 -0800 Subject: [PATCH 11/42] New translations sh.resx (Korean) --- .../Snap.Hutao/Resource/Localization/SH.ko.resx | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/Snap.Hutao/Snap.Hutao/Resource/Localization/SH.ko.resx b/src/Snap.Hutao/Snap.Hutao/Resource/Localization/SH.ko.resx index 658cc101..da2a0b3a 100644 --- a/src/Snap.Hutao/Snap.Hutao/Resource/Localization/SH.ko.resx +++ b/src/Snap.Hutao/Snap.Hutao/Resource/Localization/SH.ko.resx @@ -2057,6 +2057,12 @@ 모든 설정은 게임을 성공적으로 실행한 후에 저장됩니다 + + 在我游戏时设置 Discord Activity 状态 + + + Discord Activity + 文件 @@ -2757,7 +2763,7 @@ 错误的 UID 格式 - 角色 UID 不存在 + 角色 UID 不存在,请稍候再试 游戏维护中 From 1a438418334d5c80c0017adeb7a8026a25e10106 Mon Sep 17 00:00:00 2001 From: Masterain Date: Wed, 22 Nov 2023 07:43:56 -0800 Subject: [PATCH 12/42] New translations sh.resx (Chinese Traditional) --- .../Snap.Hutao/Resource/Localization/SH.zh-Hant.resx | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/Snap.Hutao/Snap.Hutao/Resource/Localization/SH.zh-Hant.resx b/src/Snap.Hutao/Snap.Hutao/Resource/Localization/SH.zh-Hant.resx index 3cea4d20..1bfa38cf 100644 --- a/src/Snap.Hutao/Snap.Hutao/Resource/Localization/SH.zh-Hant.resx +++ b/src/Snap.Hutao/Snap.Hutao/Resource/Localization/SH.zh-Hant.resx @@ -2057,6 +2057,12 @@ 所有選項盡會在啓動游戲成功後保存 + + 在我游戏时设置 Discord Activity 状态 + + + Discord Activity + 文件 @@ -2757,7 +2763,7 @@ 錯誤的 UID 格式 - 角色 UID 不存在 + 角色 UID 不存在,请稍候再试 遊戲維護中 From 0189c4824b1cd6adf72c9000cb47dfc27ecef41d Mon Sep 17 00:00:00 2001 From: Masterain Date: Wed, 22 Nov 2023 07:43:58 -0800 Subject: [PATCH 13/42] New translations sh.resx (English) --- .../Snap.Hutao/Resource/Localization/SH.en.resx | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/Snap.Hutao/Snap.Hutao/Resource/Localization/SH.en.resx b/src/Snap.Hutao/Snap.Hutao/Resource/Localization/SH.en.resx index 67723dd2..6d7538e4 100644 --- a/src/Snap.Hutao/Snap.Hutao/Resource/Localization/SH.en.resx +++ b/src/Snap.Hutao/Snap.Hutao/Resource/Localization/SH.en.resx @@ -948,7 +948,7 @@ Unable to find cached metadata file - HTTP {0} | Error:{1}:元数据校验文件下载失败 + HTTP {0} | Error {1}: Failed to download metadata verification file Metadata service has not been initialized or failed to initialize @@ -2057,6 +2057,12 @@ All options will be saved only after the game is launched successfully. + + Set My Discord Activity Status When I'm in the Game + + + Discord Activity + File @@ -2757,7 +2763,7 @@ Wrong UID format - Role UID does not exist + 角色 UID 不存在,请稍候再试 Game in maintenance From 74ac73823671750ec3feae5382d61487255df154 Mon Sep 17 00:00:00 2001 From: Lightczx <1686188646@qq.com> Date: Thu, 23 Nov 2023 13:08:32 +0800 Subject: [PATCH 14/42] bump static resource version --- .../ViewModel/Guide/StaticResource.cs | 46 +++++++++---------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/src/Snap.Hutao/Snap.Hutao/ViewModel/Guide/StaticResource.cs b/src/Snap.Hutao/Snap.Hutao/ViewModel/Guide/StaticResource.cs index 1774698e..6b6d5b6f 100644 --- a/src/Snap.Hutao/Snap.Hutao/ViewModel/Guide/StaticResource.cs +++ b/src/Snap.Hutao/Snap.Hutao/ViewModel/Guide/StaticResource.cs @@ -46,30 +46,30 @@ internal static class StaticResource private static readonly ApplicationDataCompositeValue LatestResourceVersionMap = new() { { "AchievementIcon", 1 }, - { "AvatarCard", 0 }, - { "AvatarIcon", 2 }, - { "Bg", 1 }, - { "ChapterIcon", 0 }, - { "CodexMonster", 0 }, - { "Costume", 0 }, - { "EmotionIcon", 0 }, - { "EquipIcon", 1 }, - { "GachaAvatarIcon", 1 }, - { "GachaAvatarImg", 1 }, - { "GachaEquipIcon", 1 }, - { "GcgCharAvatarIcon", 0 }, - { "IconElement", 1 }, - { "ItemIcon", 1 }, - { "LoadingPic", 0 }, - { "MonsterIcon", 1 }, - { "MonsterSmallIcon", 0 }, - { "NameCardIcon", 0 }, - { "NameCardPic", 1 }, - { "NameCardPicAlpha", 0 }, + { "AvatarCard", 1 }, + { "AvatarIcon", 3 }, + { "Bg", 2 }, + { "ChapterIcon", 1 }, + { "CodexMonster", 1 }, + { "Costume", 1 }, + { "EmotionIcon", 1 }, + { "EquipIcon", 2 }, + { "GachaAvatarIcon", 2 }, + { "GachaAvatarImg", 2 }, + { "GachaEquipIcon", 2 }, + { "GcgCharAvatarIcon", 1 }, + { "IconElement", 2 }, + { "ItemIcon", 2 }, + { "LoadingPic", 1 }, + { "MonsterIcon", 2 }, + { "MonsterSmallIcon", 1 }, + { "NameCardIcon", 1 }, + { "NameCardPic", 2 }, + { "NameCardPicAlpha", 1 }, { "Property", 1 }, - { "RelicIcon", 1 }, - { "Skill", 1 }, - { "Talent", 1 }, + { "RelicIcon", 2 }, + { "Skill", 2 }, + { "Talent", 2 }, }; public static void FulfillAll() From e0f967341e41f0c78fdc29f0acaa5bfaf86a7b98 Mon Sep 17 00:00:00 2001 From: DismissedLight <1686188646@qq.com> Date: Thu, 23 Nov 2023 23:34:31 +0800 Subject: [PATCH 15/42] TCG decrypt [skip ci] --- .../BaseClassLibrary/JsonSerializeTest.cs | 4 +- .../GeniusInvokationDecoding.cs | 218 ++++++++++++++++++ 2 files changed, 219 insertions(+), 3 deletions(-) create mode 100644 src/Snap.Hutao/Snap.Hutao.Test/IncomingFeature/GeniusInvokationDecoding.cs diff --git a/src/Snap.Hutao/Snap.Hutao.Test/BaseClassLibrary/JsonSerializeTest.cs b/src/Snap.Hutao/Snap.Hutao.Test/BaseClassLibrary/JsonSerializeTest.cs index f680883f..40dd8c3a 100644 --- a/src/Snap.Hutao/Snap.Hutao.Test/BaseClassLibrary/JsonSerializeTest.cs +++ b/src/Snap.Hutao/Snap.Hutao.Test/BaseClassLibrary/JsonSerializeTest.cs @@ -7,9 +7,7 @@ namespace Snap.Hutao.Test.BaseClassLibrary; [TestClass] public class JsonSerializeTest { - private TestContext? testContext; - - public TestContext? TestContext { get => testContext; set => testContext = value; } + public TestContext? TestContext { get; set; } private readonly JsonSerializerOptions AlowStringNumberOptions = new() { diff --git a/src/Snap.Hutao/Snap.Hutao.Test/IncomingFeature/GeniusInvokationDecoding.cs b/src/Snap.Hutao/Snap.Hutao.Test/IncomingFeature/GeniusInvokationDecoding.cs new file mode 100644 index 00000000..7307d894 --- /dev/null +++ b/src/Snap.Hutao/Snap.Hutao.Test/IncomingFeature/GeniusInvokationDecoding.cs @@ -0,0 +1,218 @@ +using System; +using System.Buffers.Binary; +using System.Globalization; +using System.Runtime.InteropServices; +using System.Text; + +namespace Snap.Hutao.Test.IncomingFeature; + +[TestClass] +public sealed class GeniusInvokationDecoding +{ + public TestContext? TestContext { get; set; } + + /// + /// https://www.bilibili.com/video/av278125720 + /// + [TestMethod] + public unsafe void GeniusInvokationShareCodeDecoding() + { + // 51 bytes obfuscated data + byte[] bytes = Convert.FromBase64String("BCHBwxQNAYERyVANCJGBynkOCZER2pgOCrFx8poQChGR9bYQDEGB9rkQDFKRD7oRDeEB"); + + // --------------------------------------------- + // | Data | Caesar Cipher Key | + // |----------|-------------------| + // | 50 Bytes | 1 Byte | + // --------------------------------------------- + // Data: + // 00000100 00100001 11000001 11000011 00010100 + // 00001101 00000001 10000001 00010001 11001001 + // 01010000 00001101 00001000 10010001 10000001 + // 11001010 01111001 00001110 00001001 10010001 + // 00010001 11011010 10011000 00001110 00001010 + // 10110001 01110001 11110010 10011010 00010000 + // 00001010 00010001 10010001 11110101 10110110 + // 00010000 00001100 01000001 10000001 11110110 + // 10111001 00010000 00001100 01010010 10010001 + // 00001111 10111010 00010001 00001101 11100001 + // --------------------------------------------- + // Caesar Cipher Key: + // 00000001 + // --------------------------------------------- + fixed (byte* ptr = bytes) + { + // Reinterpret as 50 byte actual data and 1 deobfuscate key byte + EncryptedDataAndKey* data = (EncryptedDataAndKey*)ptr; + byte* dataPtr = data->Data; + + // ---------------------------------------------------------- + // | First | Second | Padding | + // |-----------|----------|---------| + // | 25 Bytes | 25 Bytes | 1 Byte | + // ---------------------------------------------------------- + // We are doing two things here: + // 1. Retrieve actual data by subtracting key + // 2. Store data into two halves by alternating between them + // ---------------------------------------------------------- + // What we will get after this step: + // ---------------------------------------------------------- + // First: + // 00000011 11000000 00010011 00000000 00010000 + // 01001111 00000111 10000000 01111000 00001000 + // 00010000 10010111 00001001 01110000 10011001 + // 00001001 10010000 10110101 00001011 10000000 + // 10111000 00001011 10010000 10111001 00001100 + // ---------------------------------------------------------- + // Second: + // 00100000 11000010 00001100 10000000 11001000 + // 00001100 10010000 11001001 00001101 10010000 + // 11011001 00001101 10110000 11110001 00001111 + // 00010000 11110100 00001111 01000000 11110101 + // 00001111 01010001 00001110 00010000 11100000 + // ---------------------------------------------------------- + RearrangeBuffer rearranged = default; + byte* pFirst = rearranged.First; + byte* pSecond = rearranged.Second; + for (int i = 0; i < 50; i++) + { + // Determine which half are we going to insert + byte** ppTarget = i % 2 == 0 ? &pFirst : &pSecond; + + // (actual data = data - key) and store it directly to the target half + **ppTarget = unchecked((byte)(dataPtr[i] - data->Key)); + + (*ppTarget)++; + } + + // Prepare decoded data result storage + DecryptedData decoded = default; + ushort* pDecoded = decoded.Data; + + // ---------------------------------------------------------- + // | Data | + // |----------| x 17 = 51 Bytes + // | 3 Bytes | + // ---------------------------------------------------------- + // Grouping each 3 bytes and read out as 2 ushort with + // 12 bits each (Big Endian) + // ---------------------------------------------------------- + // 00000011 1100·0000 00010011| + // 00000000 0001·0000 01001111| + // 00000111 1000·0000 01111000| + // 00001000 0001·0000 10010111| + // 00001001 0111·0000 10011001| + // 00001001 1001·0000 10110101| + // 00001011 1000·0000 10111000| + // 00001011 1001·0000 10111001| + // 00001100 0010·0000 11000010| + // 00001100 1000·0000 11001000| + // 00001100 1001·0000 11001001| + // 00001101 1001·0000 11011001| + // 00001101 1011·0000 11110001| + // 00001111 0001·0000 11110100| + // 00001111 0100·0000 11110101| + // 00001111 0101·0001 00001110| + // 00010000 1110·0000 -padding|[padding32] + // ---------------------------------------------------------- + // reinterpret as DecodeGroupingHelper for each 3 bytes + DecodeGroupingHelper* pGroup = (DecodeGroupingHelper*)&rearranged; + for (int i = 0; i < 17; i++) + { + (ushort first, ushort second) = pGroup->GetData(); + + *pDecoded = first; + *(pDecoded + 1) = second; + + pDecoded += 2; + pGroup++; + } + + // Now we get + // 60, 19, 1, + // 79,120,120, + // 129,151,151, + // 153,153,181, + // 184,184,185, + // 185,194,194, + // 200,200,201, + // 201,217,217, + // 219,241,241, + // 244,244,245, + // 245,270,270, + StringBuilder stringBuilder = new(); + for (int i = 0; i < 33; i++) + { + stringBuilder + .AppendFormat(CultureInfo.InvariantCulture, "{0,3}", decoded.Data[i]) + .Append(','); + + if (i % 11 == 10) + { + stringBuilder.Append('\n'); + } + } + + TestContext?.WriteLine(stringBuilder.ToString(0, stringBuilder.Length - 1)); + + ushort[] resultArray = new ushort[33]; + Span result = new((ushort*)&decoded, 33); + result.CopyTo(resultArray); + + ushort[] testKnownResult = +#if NET8_0_OR_GREATER + [ + 060, 019, 001, 079, 120, 120, 129, 151, 151, 153, 153, + 181, 184, 184, 185, 185, 194, 194, 200, 200, 201, 201, + 217, 217, 219, 241, 241, 244, 244, 245, 245, 270, 270, + ]; +#else + { + 060, 019, 001, 079, 120, 120, 129, 151, 151, 153, 153, + 181, 184, 184, 185, 185, 194, 194, 200, 200, 201, 201, + 217, 217, 219, 241, 241, 244, 244, 245, 245, 270, 270, + }; +#endif + + CollectionAssert.AreEqual(resultArray, testKnownResult); + } + } + + private struct EncryptedDataAndKey + { + public unsafe fixed byte Data[50]; + public byte Key; + } + + private struct RearrangeBuffer + { + public unsafe fixed byte First[25]; + public unsafe fixed byte Second[25]; + + // Make it 51 bytes + // allow to be group as 17 DecodeGroupingHelper later + public byte padding; + + // prevent accidently int32 cast access violation + public byte paddingTo32; + } + + private struct DecodeGroupingHelper + { + public unsafe fixed byte Data[3]; + + public unsafe (ushort First, ushort Second) GetData() + { + fixed (byte* ptr = Data) + { + uint value = BinaryPrimitives.ReverseEndianness((*(uint*)ptr) & 0x00FFFFFF) >> 8; // keep low 24 bits only + return ((ushort)((value >> 12) & 0x0FFF), (ushort)(value & 0x0FFF)); + } + } + } + + private struct DecryptedData + { + public unsafe fixed ushort Data[33]; + } +} \ No newline at end of file From 3a1fc839ebdc3135738afd1aadfd8e729cdf7594 Mon Sep 17 00:00:00 2001 From: Masterain Date: Thu, 23 Nov 2023 07:49:50 -0800 Subject: [PATCH 16/42] New translations sh.resx (Japanese) --- .../Resource/Localization/SH.ja.resx | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/Snap.Hutao/Snap.Hutao/Resource/Localization/SH.ja.resx b/src/Snap.Hutao/Snap.Hutao/Resource/Localization/SH.ja.resx index 32e9ae27..7a1aefd3 100644 --- a/src/Snap.Hutao/Snap.Hutao/Resource/Localization/SH.ja.resx +++ b/src/Snap.Hutao/Snap.Hutao/Resource/Localization/SH.ja.resx @@ -130,7 +130,7 @@ キャンセル - 完了 + OK 確定 @@ -600,10 +600,10 @@ {0} つのアチーブメントを追加 | {1} つのアチーブメントを更新 |{2} つのアチーブメントを削除 - UIAF Json 文件 + UIAF Json ファイル - 打开 UIAF Json 文件 + UIAF Json ファイルを開く 複数の同一アチーブメント Idがアーカイブに混在しています @@ -792,10 +792,10 @@ 参量物質変化器は使用可能 - 正在提瓦特大陆中探索 + テイワット大陸を探索中 - 由 {0} 启动 + スタートから {0} {0}:祈願履歴を確認できません @@ -882,7 +882,7 @@ ゲーム本体を選択する - 游戏本体 + ゲームクライアント Unity ログファイルが見つかりません @@ -948,7 +948,7 @@ キャッシュされたメタデータファイルが見つかりませんでした - HTTP {0} | Error:{1}:元数据校验文件下载失败 + HTTP {0} | Error {1}: メタデータ検証ファイルのダウンロードに失敗しました。 メタデータサービスが初期化されていないか、または初期化できませんでした @@ -1350,7 +1350,7 @@ {0} を削除してもよろしいでしょうか? - 导出 UIAF Json 文件到指定路径 + UIAF Json ファイルを指定した場所へエクスポート 素材リスト取得中、しばらくお待ちください @@ -1479,13 +1479,13 @@ 胡桃クラウドで祈願履歴を同期します - 导出 UIGF Json 文件到指定路径 + UIGF Json ファイルを指定した場所へエクスポート 胡桃クラウドにアップロード中 - 导入 UIGF Json 文件 + UIGF Json ファイルをインポート 上記の規約を熟読し、それに同意します @@ -2058,7 +2058,7 @@ これらの設定はゲームが正常に起動した時のみ保存されます。 - 在我游戏时设置 Discord Activity 状态 + ゲームをプレイしている時に、Discord Activityのステータスを変更します。 Discord Activity @@ -2763,7 +2763,7 @@ UIDは正しくありません - 角色 UID 不存在,请稍候再试 + ロール UIDが存在しません。時間を置いてもう一度試してください。 ゲームメンテナンス中 From 3b43389049203729af82a2a3eec9f7b8bd8fd787 Mon Sep 17 00:00:00 2001 From: Masterain Date: Thu, 23 Nov 2023 07:49:51 -0800 Subject: [PATCH 17/42] New translations sh.resx (Chinese Traditional) --- .../Snap.Hutao/Resource/Localization/SH.zh-Hant.resx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Snap.Hutao/Snap.Hutao/Resource/Localization/SH.zh-Hant.resx b/src/Snap.Hutao/Snap.Hutao/Resource/Localization/SH.zh-Hant.resx index 1bfa38cf..7a3ffa84 100644 --- a/src/Snap.Hutao/Snap.Hutao/Resource/Localization/SH.zh-Hant.resx +++ b/src/Snap.Hutao/Snap.Hutao/Resource/Localization/SH.zh-Hant.resx @@ -522,10 +522,10 @@ 登录成功 - 注册成功 + 註冊成功 - 新密码设置成功 + 新密碼設定成功 当前邮箱尚未注册 @@ -2763,7 +2763,7 @@ 錯誤的 UID 格式 - 角色 UID 不存在,请稍候再试 + 角色UID不存在,請稍候再試 遊戲維護中 From 602b31c52da04765abc9c8e1bd0d03496b3d8eca Mon Sep 17 00:00:00 2001 From: Lightczx <1686188646@qq.com> Date: Fri, 24 Nov 2023 11:51:46 +0800 Subject: [PATCH 18/42] restrict combobox size --- .../Control/SizeRestrictedContentControl.cs | 47 +++++++++++++++++++ .../Model/Intrinsic/Frozen/IntrinsicFrozen.cs | 14 +++--- .../Snap.Hutao/Resource/Localization/SH.resx | 3 ++ .../Snap.Hutao/View/Card/LaunchGameCard.xaml | 21 +++++---- .../View/Control/DescParamComboBox.xaml | 13 +++-- .../Snap.Hutao/View/Page/AchievementPage.xaml | 15 +++--- .../Snap.Hutao/View/Page/CultivationPage.xaml | 16 ++++--- .../Snap.Hutao/View/Page/GachaLogPage.xaml | 15 +++--- .../Snap.Hutao/View/Page/LaunchGamePage.xaml | 42 +++++++++-------- .../Snap.Hutao/View/Page/SettingPage.xaml | 35 ++++++++------ .../Snap.Hutao/View/Page/TestPage.xaml | 13 ++--- 11 files changed, 152 insertions(+), 82 deletions(-) create mode 100644 src/Snap.Hutao/Snap.Hutao/Control/SizeRestrictedContentControl.cs diff --git a/src/Snap.Hutao/Snap.Hutao/Control/SizeRestrictedContentControl.cs b/src/Snap.Hutao/Snap.Hutao/Control/SizeRestrictedContentControl.cs new file mode 100644 index 00000000..48e89b61 --- /dev/null +++ b/src/Snap.Hutao/Snap.Hutao/Control/SizeRestrictedContentControl.cs @@ -0,0 +1,47 @@ +// Copyright (c) DGP Studio. All rights reserved. +// Licensed under the MIT license. + +using Microsoft.UI.Xaml; +using Microsoft.UI.Xaml.Controls; +using Windows.Foundation; + +namespace Snap.Hutao.Control; + +[DependencyProperty("IsWidthRestricted", typeof(bool), true)] +[DependencyProperty("IsHeightRestricted", typeof(bool), true)] +internal sealed partial class SizeRestrictedContentControl : ContentControl +{ + private double minContentWidth; + private double minContentHeight; + + protected override Size MeasureOverride(Size availableSize) + { + if (Content is FrameworkElement element) + { + element.Measure(availableSize); + Size contentDesiredSize = element.DesiredSize; + + if (IsWidthRestricted) + { + if (contentDesiredSize.Width > minContentWidth) + { + minContentWidth = contentDesiredSize.Width; + } + + element.MinWidth = minContentWidth; + } + + if (IsHeightRestricted) + { + if (contentDesiredSize.Height > minContentHeight) + { + minContentHeight = contentDesiredSize.Height; + } + + element.MinHeight = minContentHeight; + } + } + + return base.MeasureOverride(availableSize); + } +} \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Model/Intrinsic/Frozen/IntrinsicFrozen.cs b/src/Snap.Hutao/Snap.Hutao/Model/Intrinsic/Frozen/IntrinsicFrozen.cs index 21a0946d..6963ff37 100644 --- a/src/Snap.Hutao/Snap.Hutao/Model/Intrinsic/Frozen/IntrinsicFrozen.cs +++ b/src/Snap.Hutao/Snap.Hutao/Model/Intrinsic/Frozen/IntrinsicFrozen.cs @@ -14,32 +14,32 @@ internal static class IntrinsicFrozen /// /// 所属地区 /// - public static readonly FrozenSet AssociationTypes = Enum.GetValues().Select(e => e.GetLocalizedDescriptionOrDefault()).OfType().ToFrozenSet(); + public static FrozenSet AssociationTypes { get; } = Enum.GetValues().Select(e => e.GetLocalizedDescriptionOrDefault()).OfType().ToFrozenSet(); /// /// 武器类型 /// - public static readonly FrozenSet WeaponTypes = Enum.GetValues().Select(e => e.GetLocalizedDescriptionOrDefault()).OfType().ToFrozenSet(); + public static FrozenSet WeaponTypes { get; } = Enum.GetValues().Select(e => e.GetLocalizedDescriptionOrDefault()).OfType().ToFrozenSet(); /// /// 物品类型 /// - public static readonly FrozenSet ItemQualities = Enum.GetValues().Select(e => e.GetLocalizedDescriptionOrDefault()).OfType().ToFrozenSet(); + public static FrozenSet ItemQualities { get; } = Enum.GetValues().Select(e => e.GetLocalizedDescriptionOrDefault()).OfType().ToFrozenSet(); /// /// 身材类型 /// - public static readonly FrozenSet BodyTypes = Enum.GetValues().Select(e => e.GetLocalizedDescriptionOrDefault()).OfType().ToFrozenSet(); + public static FrozenSet BodyTypes { get; } = Enum.GetValues().Select(e => e.GetLocalizedDescriptionOrDefault()).OfType().ToFrozenSet(); /// /// 战斗属性 /// - public static readonly FrozenSet FightProperties = Enum.GetValues().Select(e => e.GetLocalizedDescriptionOrDefault()).OfType().ToFrozenSet(); + public static FrozenSet FightProperties { get; } = Enum.GetValues().Select(e => e.GetLocalizedDescriptionOrDefault()).OfType().ToFrozenSet(); /// /// 元素名称 /// - public static readonly FrozenSet ElementNames = FrozenSet.ToFrozenSet( + public static FrozenSet ElementNames { get; } = FrozenSet.ToFrozenSet( [ SH.ModelIntrinsicElementNameFire, SH.ModelIntrinsicElementNameWater, @@ -50,7 +50,7 @@ internal static class IntrinsicFrozen SH.ModelIntrinsicElementNameRock, ]); - public static readonly FrozenSet MaterialTypeDescriptions = FrozenSet.ToFrozenSet( + public static FrozenSet MaterialTypeDescriptions { get; } = FrozenSet.ToFrozenSet( [ SH.ModelMetadataMaterialCharacterAndWeaponEnhancementMaterial, SH.ModelMetadataMaterialCharacterEXPMaterial, diff --git a/src/Snap.Hutao/Snap.Hutao/Resource/Localization/SH.resx b/src/Snap.Hutao/Snap.Hutao/Resource/Localization/SH.resx index 09a2dcdf..852a1459 100644 --- a/src/Snap.Hutao/Snap.Hutao/Resource/Localization/SH.resx +++ b/src/Snap.Hutao/Snap.Hutao/Resource/Localization/SH.resx @@ -2012,6 +2012,9 @@ 分辨率 + + 快捷设置分辨率 + 将窗口创建为弹出窗口,不带框架 diff --git a/src/Snap.Hutao/Snap.Hutao/View/Card/LaunchGameCard.xaml b/src/Snap.Hutao/Snap.Hutao/View/Card/LaunchGameCard.xaml index 10e7d0ef..30c87e97 100644 --- a/src/Snap.Hutao/Snap.Hutao/View/Card/LaunchGameCard.xaml +++ b/src/Snap.Hutao/Snap.Hutao/View/Card/LaunchGameCard.xaml @@ -5,6 +5,7 @@ xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mxi="using:Microsoft.Xaml.Interactivity" + xmlns:shc="using:Snap.Hutao.Control" xmlns:shcb="using:Snap.Hutao.Control.Behavior" xmlns:shcm="using:Snap.Hutao.Control.Markup" xmlns:shvg="using:Snap.Hutao.ViewModel.Game" @@ -60,14 +61,16 @@ Content="{StaticResource FontIconContentSetting}" FontFamily="{StaticResource SymbolThemeFontFamily}" ToolTipService.ToolTip="{shcm:ResourceString Name=ViewPageHomeLaunchGameSettingAction}"/> - + + + - + \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/View/Control/DescParamComboBox.xaml b/src/Snap.Hutao/Snap.Hutao/View/Control/DescParamComboBox.xaml index b3636856..7b6262a2 100644 --- a/src/Snap.Hutao/Snap.Hutao/View/Control/DescParamComboBox.xaml +++ b/src/Snap.Hutao/Snap.Hutao/View/Control/DescParamComboBox.xaml @@ -5,6 +5,7 @@ xmlns:cwc="using:CommunityToolkit.WinUI.Controls" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" + xmlns:shc="using:Snap.Hutao.Control" xmlns:shcm="using:Snap.Hutao.Control.Markup" xmlns:shmm="using:Snap.Hutao.Model.Metadata" mc:Ignorable="d"> @@ -22,11 +23,13 @@ - + + + diff --git a/src/Snap.Hutao/Snap.Hutao/View/Page/AchievementPage.xaml b/src/Snap.Hutao/Snap.Hutao/View/Page/AchievementPage.xaml index 3de375ba..8ec0dd69 100644 --- a/src/Snap.Hutao/Snap.Hutao/View/Page/AchievementPage.xaml +++ b/src/Snap.Hutao/Snap.Hutao/View/Page/AchievementPage.xaml @@ -247,13 +247,14 @@ - + + + - + + + + - + + + diff --git a/src/Snap.Hutao/Snap.Hutao/View/Page/LaunchGamePage.xaml b/src/Snap.Hutao/Snap.Hutao/View/Page/LaunchGamePage.xaml index ffc86e3e..8d340d31 100644 --- a/src/Snap.Hutao/Snap.Hutao/View/Page/LaunchGamePage.xaml +++ b/src/Snap.Hutao/Snap.Hutao/View/Page/LaunchGamePage.xaml @@ -171,13 +171,14 @@ - + + + @@ -217,11 +218,13 @@ - + + + @@ -247,13 +250,14 @@ - + + + diff --git a/src/Snap.Hutao/Snap.Hutao/View/Page/SettingPage.xaml b/src/Snap.Hutao/Snap.Hutao/View/Page/SettingPage.xaml index 7492285c..cfb744f6 100644 --- a/src/Snap.Hutao/Snap.Hutao/View/Page/SettingPage.xaml +++ b/src/Snap.Hutao/Snap.Hutao/View/Page/SettingPage.xaml @@ -192,19 +192,23 @@ Description="{shcm:ResourceString Name=ViewPageSettingApperanceLanguageDescription}" Header="{shcm:ResourceString Name=ViewPageSettingApperanceLanguageHeader}" HeaderIcon="{shcm:FontIcon Glyph=}"> - + + + - + + + @@ -240,13 +244,14 @@ Content="Alt" IsChecked="{Binding HotKeyOptions.MouseClickRepeatForeverKeyCombination.ModifierHasAlt, Mode=TwoWay}"/> - + + + - + + + internal void Clear() { + // https://github.com/DGP-Studio/Snap.Hutao/issues/1079 + // The first element must be force refreshed otherwise + // it will use the old one realized + // https://github.com/DGP-Studio/Snap.Hutao/issues/1099 + // Now we need to refresh the first element of each column + // https://github.com/DGP-Studio/Snap.Hutao/issues/1099 + // Finally we need to refresh the whole layout when we reset + if (context.ItemCount > 0) + { + for (int i = 0; i < context.ItemCount; i++) + { + RecycleElementAt(i); + } + } + columnLayout.Clear(); items.Clear(); } diff --git a/src/Snap.Hutao/Snap.Hutao/Control/SizeRestrictedContentControl.cs b/src/Snap.Hutao/Snap.Hutao/Control/SizeRestrictedContentControl.cs index 48e89b61..986de656 100644 --- a/src/Snap.Hutao/Snap.Hutao/Control/SizeRestrictedContentControl.cs +++ b/src/Snap.Hutao/Snap.Hutao/Control/SizeRestrictedContentControl.cs @@ -3,6 +3,7 @@ using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Controls; +using Snap.Hutao.Core; using Windows.Foundation; namespace Snap.Hutao.Control; @@ -20,12 +21,15 @@ internal sealed partial class SizeRestrictedContentControl : ContentControl { element.Measure(availableSize); Size contentDesiredSize = element.DesiredSize; + Size contentActualOrDesiredSize = new( + Math.Max(element.ActualWidth, contentDesiredSize.Width), + Math.Max(element.ActualHeight, contentDesiredSize.Height)); if (IsWidthRestricted) { - if (contentDesiredSize.Width > minContentWidth) + if (contentActualOrDesiredSize.Width > minContentWidth) { - minContentWidth = contentDesiredSize.Width; + minContentWidth = contentActualOrDesiredSize.Width; } element.MinWidth = minContentWidth; @@ -33,9 +37,9 @@ internal sealed partial class SizeRestrictedContentControl : ContentControl if (IsHeightRestricted) { - if (contentDesiredSize.Height > minContentHeight) + if (contentActualOrDesiredSize.Height > minContentHeight) { - minContentHeight = contentDesiredSize.Height; + minContentHeight = contentActualOrDesiredSize.Height; } element.MinHeight = minContentHeight; diff --git a/src/Snap.Hutao/Snap.Hutao/Control/Theme/ComboBox.xaml b/src/Snap.Hutao/Snap.Hutao/Control/Theme/ComboBox.xaml new file mode 100644 index 00000000..babd8d20 --- /dev/null +++ b/src/Snap.Hutao/Snap.Hutao/Control/Theme/ComboBox.xaml @@ -0,0 +1,11 @@ + + + + 0 + diff --git a/src/Snap.Hutao/Snap.Hutao/Core/DependencyInjection/IocHttpClientConfiguration.cs b/src/Snap.Hutao/Snap.Hutao/Core/DependencyInjection/IocHttpClientConfiguration.cs index d9b10b3a..4f975aa0 100644 --- a/src/Snap.Hutao/Snap.Hutao/Core/DependencyInjection/IocHttpClientConfiguration.cs +++ b/src/Snap.Hutao/Snap.Hutao/Core/DependencyInjection/IocHttpClientConfiguration.cs @@ -63,8 +63,10 @@ internal static partial class IocHttpClientConfiguration client.DefaultRequestHeaders.Add("x-rpc-app_version", SaltConstants.CNVersion); client.DefaultRequestHeaders.Add("x-rpc-client_type", "2"); client.DefaultRequestHeaders.Add("x-rpc-device_id", HoyolabOptions.DeviceId); + client.DefaultRequestHeaders.Add("x-rpc-device_name", string.Empty); client.DefaultRequestHeaders.Add("x-rpc-game_biz", "bbs_cn"); client.DefaultRequestHeaders.Add("x-rpc-sdk_version", "2.16.0"); + //client.DefaultRequestHeaders.Add("x-rpc-tool_verison", "v4.2.2-ys"); } /// diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Discord/DiscordController.cs b/src/Snap.Hutao/Snap.Hutao/Service/Discord/DiscordController.cs index df269176..f45d2dbb 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/Discord/DiscordController.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/Discord/DiscordController.cs @@ -78,7 +78,13 @@ internal static class DiscordController lock (SyncRoot) { StopTokenSource.Cancel(); - discordManager?.Dispose(); + try + { + discordManager?.Dispose(); + } + catch (SEHException) + { + } } } @@ -135,13 +141,18 @@ internal static class DiscordController } catch (ResultException ex) { - System.Diagnostics.Debug.WriteLine($"[Discord.GameSDK]:[ERROR]:{ex.Result}"); + // If result is Ok + // Maybe the connection is reset. + if (ex.Result is not Result.Ok) + { + System.Diagnostics.Debug.WriteLine($"[Discord.GameSDK ERROR]:{ex.Result:D} {ex.Result}"); + } } catch (SEHException ex) { // Known error codes: // 0x80004005 E_FAIL - System.Diagnostics.Debug.WriteLine($"[Discord.GameSDK]:[ERROR]:0x{ex.ErrorCode:X}"); + System.Diagnostics.Debug.WriteLine($"[Discord.GameSDK ERROR]:0x{ex.ErrorCode:X}"); } } } diff --git a/src/Snap.Hutao/Snap.Hutao/Snap.Hutao.csproj b/src/Snap.Hutao/Snap.Hutao/Snap.Hutao.csproj index 71d0bc9e..55b4bf4e 100644 --- a/src/Snap.Hutao/Snap.Hutao/Snap.Hutao.csproj +++ b/src/Snap.Hutao/Snap.Hutao/Snap.Hutao.csproj @@ -83,6 +83,7 @@ + @@ -317,6 +318,11 @@ + + + MSBuild:Compile + + diff --git a/src/Snap.Hutao/Snap.Hutao/View/Page/AchievementPage.xaml b/src/Snap.Hutao/Snap.Hutao/View/Page/AchievementPage.xaml index 8ec0dd69..7677b0e1 100644 --- a/src/Snap.Hutao/Snap.Hutao/View/Page/AchievementPage.xaml +++ b/src/Snap.Hutao/Snap.Hutao/View/Page/AchievementPage.xaml @@ -247,13 +247,12 @@ - + + SelectedItem="{Binding SelectedArchive, Mode=TwoWay}" + Style="{ThemeResource CommandBarComboBoxStyle}"/> - + + SelectedItem="{Binding SelectedProject, Mode=TwoWay}" + Style="{ThemeResource CommandBarComboBoxStyle}"/> diff --git a/src/Snap.Hutao/Snap.Hutao/View/Page/GachaLogPage.xaml b/src/Snap.Hutao/Snap.Hutao/View/Page/GachaLogPage.xaml index 48da2f6e..9f079fb2 100644 --- a/src/Snap.Hutao/Snap.Hutao/View/Page/GachaLogPage.xaml +++ b/src/Snap.Hutao/Snap.Hutao/View/Page/GachaLogPage.xaml @@ -231,13 +231,12 @@ IsHitTestVisible="False"/> - + + SelectedItem="{Binding SelectedArchive, Mode=TwoWay}" + Style="{ThemeResource CommandBarComboBoxStyle}"/> diff --git a/src/Snap.Hutao/Snap.Hutao/View/Page/LaunchGamePage.xaml b/src/Snap.Hutao/Snap.Hutao/View/Page/LaunchGamePage.xaml index 8d340d31..c01951f2 100644 --- a/src/Snap.Hutao/Snap.Hutao/View/Page/LaunchGamePage.xaml +++ b/src/Snap.Hutao/Snap.Hutao/View/Page/LaunchGamePage.xaml @@ -218,9 +218,8 @@ - + @@ -252,7 +251,6 @@ > CreateVerificationAsync(User user, CancellationToken token) { HttpRequestMessageBuilder builder = httpRequestMessageBuilderFactory.Create() - .SetRequestUri(ApiEndpoints.CardCreateVerification(false)) + .SetRequestUri(ApiEndpoints.CardCreateVerification(true)) .SetUserCookieAndFpHeader(user, CookieType.LToken) .Get(); diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/GameRecord/GameRecordClient.cs b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/GameRecord/GameRecordClient.cs index dcd36a43..e7b21cac 100644 --- a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/GameRecord/GameRecordClient.cs +++ b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/GameRecord/GameRecordClient.cs @@ -40,6 +40,7 @@ internal sealed partial class GameRecordClient : IGameRecordClient HttpRequestMessageBuilder builder = httpRequestMessageBuilderFactory.Create() .SetRequestUri(ApiEndpoints.GameRecordDailyNote(userAndUid.Uid)) .SetUserCookieAndFpHeader(userAndUid, CookieType.Cookie) + .SetReferer(ApiEndpoints.WebStaticMihoyoReferer) .Get(); await builder.SignDataAsync(DataSignAlgorithmVersion.Gen2, SaltType.X4, false).ConfigureAwait(false); @@ -60,6 +61,7 @@ internal sealed partial class GameRecordClient : IGameRecordClient HttpRequestMessageBuilder verifiedbuilder = httpRequestMessageBuilderFactory.Create() .SetRequestUri(ApiEndpoints.GameRecordDailyNote(userAndUid.Uid)) .SetUserCookieAndFpHeader(userAndUid, CookieType.Cookie) + .SetReferer(ApiEndpoints.WebStaticMihoyoReferer) .SetXrpcChallenge(challenge) .Get(); @@ -86,6 +88,8 @@ internal sealed partial class GameRecordClient : IGameRecordClient HttpRequestMessageBuilder builder = httpRequestMessageBuilderFactory.Create() .SetRequestUri(ApiEndpoints.GameRecordIndex(userAndUid.Uid)) .SetUserCookieAndFpHeader(userAndUid, CookieType.Cookie) + .SetHeader("x-rpc-page", "v4.2.2-ys_#/ys/daily") + .SetReferer(ApiEndpoints.WebStaticMihoyoReferer) .Get(); await builder.SignDataAsync(DataSignAlgorithmVersion.Gen2, SaltType.X4, false).ConfigureAwait(false); @@ -106,6 +110,8 @@ internal sealed partial class GameRecordClient : IGameRecordClient HttpRequestMessageBuilder verifiedbuilder = httpRequestMessageBuilderFactory.Create() .SetRequestUri(ApiEndpoints.GameRecordIndex(userAndUid.Uid)) .SetUserCookieAndFpHeader(userAndUid, CookieType.Cookie) + .SetHeader("x-rpc-page", "v4.2.2-ys_#/ys/daily") + .SetReferer(ApiEndpoints.WebStaticMihoyoReferer) .SetXrpcChallenge(challenge) .Get(); @@ -133,6 +139,8 @@ internal sealed partial class GameRecordClient : IGameRecordClient HttpRequestMessageBuilder builder = httpRequestMessageBuilderFactory.Create() .SetRequestUri(ApiEndpoints.GameRecordSpiralAbyss(schedule, userAndUid.Uid)) .SetUserCookieAndFpHeader(userAndUid, CookieType.Cookie) + .SetHeader("x-rpc-page", "v4.2.2-ys_#/ys/daily") + .SetReferer(ApiEndpoints.WebStaticMihoyoReferer) .Get(); await builder.SignDataAsync(DataSignAlgorithmVersion.Gen2, SaltType.X4, false).ConfigureAwait(false); @@ -153,6 +161,8 @@ internal sealed partial class GameRecordClient : IGameRecordClient HttpRequestMessageBuilder verifiedbuilder = httpRequestMessageBuilderFactory.Create() .SetRequestUri(ApiEndpoints.GameRecordSpiralAbyss(schedule, userAndUid.Uid)) .SetUserCookieAndFpHeader(userAndUid, CookieType.Cookie) + .SetHeader("x-rpc-page", "v4.2.2-ys_#/ys/daily") + .SetReferer(ApiEndpoints.WebStaticMihoyoReferer) .SetXrpcChallenge(challenge) .Get(); @@ -179,6 +189,8 @@ internal sealed partial class GameRecordClient : IGameRecordClient HttpRequestMessageBuilder builder = httpRequestMessageBuilderFactory.Create() .SetRequestUri(ApiEndpoints.GameRecordRoleBasicInfo(userAndUid.Uid)) .SetUserCookieAndFpHeader(userAndUid, CookieType.Cookie) + .SetHeader("x-rpc-page", "v4.2.2-ys_#/ys/daily") + .SetReferer(ApiEndpoints.WebStaticMihoyoReferer) .Get(); await builder.SignDataAsync(DataSignAlgorithmVersion.Gen2, SaltType.X4, false).ConfigureAwait(false); @@ -203,6 +215,8 @@ internal sealed partial class GameRecordClient : IGameRecordClient HttpRequestMessageBuilder builder = httpRequestMessageBuilderFactory.Create() .SetRequestUri(ApiEndpoints.GameRecordCharacter) .SetUserCookieAndFpHeader(userAndUid, CookieType.Cookie) + .SetHeader("x-rpc-page", "v4.2.2-ys_#/ys/daily") + .SetReferer(ApiEndpoints.WebStaticMihoyoReferer) .PostJson(new CharacterData(userAndUid.Uid, playerInfo.Avatars.Select(x => x.Id))); await builder.SignDataAsync(DataSignAlgorithmVersion.Gen2, SaltType.X4, false).ConfigureAwait(false); @@ -223,6 +237,8 @@ internal sealed partial class GameRecordClient : IGameRecordClient HttpRequestMessageBuilder verifiedBuilder = httpRequestMessageBuilderFactory.Create() .SetRequestUri(ApiEndpoints.GameRecordCharacter) .SetUserCookieAndFpHeader(userAndUid, CookieType.Cookie) + .SetHeader("x-rpc-page", "v4.2.2-ys_#/ys/daily") + .SetReferer(ApiEndpoints.WebStaticMihoyoReferer) .SetXrpcChallenge(challenge) .PostJson(new CharacterData(userAndUid.Uid, playerInfo.Avatars.Select(x => x.Id))); From e54eef3aa70ef674039321a74f721a4870a88699 Mon Sep 17 00:00:00 2001 From: Masterain Date: Sat, 25 Nov 2023 08:02:24 -0800 Subject: [PATCH 26/42] New translations sh.resx (English) --- src/Snap.Hutao/Snap.Hutao/Resource/Localization/SH.en.resx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Snap.Hutao/Snap.Hutao/Resource/Localization/SH.en.resx b/src/Snap.Hutao/Snap.Hutao/Resource/Localization/SH.en.resx index a8c64d9e..70348b3e 100644 --- a/src/Snap.Hutao/Snap.Hutao/Resource/Localization/SH.en.resx +++ b/src/Snap.Hutao/Snap.Hutao/Resource/Localization/SH.en.resx @@ -2766,7 +2766,7 @@ Wrong UID format - 角色 UID 不存在,请稍候再试 + Role UID does not exist, please try again later Game in maintenance From 6999103aaabdfc1febd8ca7ed1bb6338a4ca943a Mon Sep 17 00:00:00 2001 From: DismissedLight <1686188646@qq.com> Date: Sun, 26 Nov 2023 14:35:33 +0800 Subject: [PATCH 27/42] refine wiki avatar page ui --- .../Control/Image/CompositionImage.cs | 18 - .../Snap.Hutao/Control/Image/MonoChrome.cs | 10 - .../Control/Layout/UniformStaggeredLayout.cs | 5 +- .../Layout/UniformStaggeredLayoutState.cs | 4 +- .../Snap.Hutao/Control/Theme/FontStyle.xaml | 15 + .../Control/Theme/ItemsPanelTemplate.xaml | 3 + src/Snap.Hutao/Snap.Hutao/MainWindow.xaml.cs | 4 +- src/Snap.Hutao/Snap.Hutao/Snap.Hutao.csproj | 6 + .../View/Card/Primitive/HorizontalCard.xaml | 24 + .../Card/Primitive/HorizontalCard.xaml.cs | 17 + .../View/Control/BaseValueSlider.xaml | 66 +- .../View/Control/DescParamComboBox.xaml | 40 +- .../View/Control/DescParamComboBox.xaml.cs | 12 +- .../Snap.Hutao/View/Control/SkillPivot.xaml | 23 +- .../View/Control/SkillPivot.xaml.cs | 21 +- .../Snap.Hutao/View/Page/WikiAvatarPage.xaml | 853 ++++++++++-------- .../ViewModel/Complex/ReliquarySetView.cs | 12 +- 17 files changed, 653 insertions(+), 480 deletions(-) create mode 100644 src/Snap.Hutao/Snap.Hutao/View/Card/Primitive/HorizontalCard.xaml create mode 100644 src/Snap.Hutao/Snap.Hutao/View/Card/Primitive/HorizontalCard.xaml.cs diff --git a/src/Snap.Hutao/Snap.Hutao/Control/Image/CompositionImage.cs b/src/Snap.Hutao/Snap.Hutao/Control/Image/CompositionImage.cs index 9cbc5d20..c049e8aa 100644 --- a/src/Snap.Hutao/Snap.Hutao/Control/Image/CompositionImage.cs +++ b/src/Snap.Hutao/Snap.Hutao/Control/Image/CompositionImage.cs @@ -30,7 +30,6 @@ internal abstract partial class CompositionImage : Microsoft.UI.Xaml.Controls.Co private readonly IServiceProvider serviceProvider; - private readonly RoutedEventHandler unloadEventHandler; private readonly SizeChangedEventHandler sizeChangedEventHandler; private readonly TypedEventHandler loadedImageSourceLoadCompletedEventHandler; @@ -46,9 +45,6 @@ internal abstract partial class CompositionImage : Microsoft.UI.Xaml.Controls.Co serviceProvider = this.ServiceProvider(); this.DisableInteraction(); - unloadEventHandler = OnUnload; - Unloaded += unloadEventHandler; - sizeChangedEventHandler = OnSizeChanged; SizeChanged += sizeChangedEventHandler; @@ -67,10 +63,6 @@ internal abstract partial class CompositionImage : Microsoft.UI.Xaml.Controls.Co { } - protected virtual void Unloading() - { - } - /// /// 更新视觉对象 /// @@ -240,14 +232,4 @@ internal abstract partial class CompositionImage : Microsoft.UI.Xaml.Controls.Co UpdateVisual(spriteVisual); } } - - private void OnUnload(object sender, RoutedEventArgs e) - { - Unloading(); - spriteVisual?.Dispose(); - spriteVisual = null; - - SizeChanged -= sizeChangedEventHandler; - Unloaded -= unloadEventHandler; - } } \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Control/Image/MonoChrome.cs b/src/Snap.Hutao/Snap.Hutao/Control/Image/MonoChrome.cs index ac491620..f7fac999 100644 --- a/src/Snap.Hutao/Snap.Hutao/Control/Image/MonoChrome.cs +++ b/src/Snap.Hutao/Snap.Hutao/Control/Image/MonoChrome.cs @@ -45,16 +45,6 @@ internal sealed class MonoChrome : CompositionImage return compositor.CompositeSpriteVisual(alphaMaskEffectBrush); } - protected override void Unloading() - { - ActualThemeChanged -= actualThemeChangedEventHandler; - - backgroundBrush?.Dispose(); - backgroundBrush = null; - - base.Unloading(); - } - private void OnActualThemeChanged(FrameworkElement sender, object args) { if (backgroundBrush is not null) diff --git a/src/Snap.Hutao/Snap.Hutao/Control/Layout/UniformStaggeredLayout.cs b/src/Snap.Hutao/Snap.Hutao/Control/Layout/UniformStaggeredLayout.cs index 58b31d1a..96f69e44 100644 --- a/src/Snap.Hutao/Snap.Hutao/Control/Layout/UniformStaggeredLayout.cs +++ b/src/Snap.Hutao/Snap.Hutao/Control/Layout/UniformStaggeredLayout.cs @@ -202,11 +202,8 @@ internal sealed partial class UniformStaggeredLayout : VirtualizingLayout for (int columnIndex = 0; columnIndex < state.NumberOfColumns; columnIndex++) { UniformStaggeredColumnLayout layout = state.GetColumnLayout(columnIndex); - Span layoutSpan = CollectionsMarshal.AsSpan(layout); - for (int i = 0; i < layoutSpan.Length; i++) + foreach (ref readonly UniformStaggeredItem item in CollectionsMarshal.AsSpan(layout)) { - ref readonly UniformStaggeredItem item = ref layoutSpan[i]; - double bottom = item.Top + item.Height; if (bottom < context.RealizationRect.Top) { diff --git a/src/Snap.Hutao/Snap.Hutao/Control/Layout/UniformStaggeredLayoutState.cs b/src/Snap.Hutao/Snap.Hutao/Control/Layout/UniformStaggeredLayoutState.cs index e39662f3..31f8a0ab 100644 --- a/src/Snap.Hutao/Snap.Hutao/Control/Layout/UniformStaggeredLayoutState.cs +++ b/src/Snap.Hutao/Snap.Hutao/Control/Layout/UniformStaggeredLayoutState.cs @@ -62,11 +62,9 @@ internal sealed class UniformStaggeredLayoutState } } - [SuppressMessage("", "SH007")] internal UniformStaggeredColumnLayout GetColumnLayout(int columnIndex) { - this.columnLayout.TryGetValue(columnIndex, out UniformStaggeredColumnLayout? columnLayout); - return columnLayout!; + return columnLayout[columnIndex]; } /// diff --git a/src/Snap.Hutao/Snap.Hutao/Control/Theme/FontStyle.xaml b/src/Snap.Hutao/Snap.Hutao/Control/Theme/FontStyle.xaml index df613a0d..bffb9f4e 100644 --- a/src/Snap.Hutao/Snap.Hutao/Control/Theme/FontStyle.xaml +++ b/src/Snap.Hutao/Snap.Hutao/Control/Theme/FontStyle.xaml @@ -93,4 +93,19 @@ + + + 1,0 + \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Control/Theme/ItemsPanelTemplate.xaml b/src/Snap.Hutao/Snap.Hutao/Control/Theme/ItemsPanelTemplate.xaml index 28a32452..2778290c 100644 --- a/src/Snap.Hutao/Snap.Hutao/Control/Theme/ItemsPanelTemplate.xaml +++ b/src/Snap.Hutao/Snap.Hutao/Control/Theme/ItemsPanelTemplate.xaml @@ -17,6 +17,9 @@ + + + logger; diff --git a/src/Snap.Hutao/Snap.Hutao/Snap.Hutao.csproj b/src/Snap.Hutao/Snap.Hutao/Snap.Hutao.csproj index 55b4bf4e..8400f6ce 100644 --- a/src/Snap.Hutao/Snap.Hutao/Snap.Hutao.csproj +++ b/src/Snap.Hutao/Snap.Hutao/Snap.Hutao.csproj @@ -135,6 +135,7 @@ + @@ -318,6 +319,11 @@ + + + MSBuild:Compile + + MSBuild:Compile diff --git a/src/Snap.Hutao/Snap.Hutao/View/Card/Primitive/HorizontalCard.xaml b/src/Snap.Hutao/Snap.Hutao/View/Card/Primitive/HorizontalCard.xaml new file mode 100644 index 00000000..14c6d654 --- /dev/null +++ b/src/Snap.Hutao/Snap.Hutao/View/Card/Primitive/HorizontalCard.xaml @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + diff --git a/src/Snap.Hutao/Snap.Hutao/View/Card/Primitive/HorizontalCard.xaml.cs b/src/Snap.Hutao/Snap.Hutao/View/Card/Primitive/HorizontalCard.xaml.cs new file mode 100644 index 00000000..b31a5606 --- /dev/null +++ b/src/Snap.Hutao/Snap.Hutao/View/Card/Primitive/HorizontalCard.xaml.cs @@ -0,0 +1,17 @@ +// Copyright (c) DGP Studio. All rights reserved. +// Licensed under the MIT license. + +using Microsoft.UI.Xaml; +using Microsoft.UI.Xaml.Controls; + +namespace Snap.Hutao.View.Card.Primitive; + +[DependencyProperty("Left", typeof(UIElement), default!)] +[DependencyProperty("Right", typeof(UIElement), default!)] +internal sealed partial class HorizontalCard : UserControl +{ + public HorizontalCard() + { + InitializeComponent(); + } +} diff --git a/src/Snap.Hutao/Snap.Hutao/View/Control/BaseValueSlider.xaml b/src/Snap.Hutao/Snap.Hutao/View/Control/BaseValueSlider.xaml index d50800c2..aeb7ba4b 100644 --- a/src/Snap.Hutao/Snap.Hutao/View/Control/BaseValueSlider.xaml +++ b/src/Snap.Hutao/Snap.Hutao/View/Control/BaseValueSlider.xaml @@ -13,44 +13,44 @@ 0 - + - - - - - - - - - + + + + + + - - diff --git a/src/Snap.Hutao/Snap.Hutao/View/Control/DescParamComboBox.xaml b/src/Snap.Hutao/Snap.Hutao/View/Control/DescParamComboBox.xaml index 7b6262a2..a48d31d2 100644 --- a/src/Snap.Hutao/Snap.Hutao/View/Control/DescParamComboBox.xaml +++ b/src/Snap.Hutao/Snap.Hutao/View/Control/DescParamComboBox.xaml @@ -12,31 +12,31 @@ 16,8 0 + 0 + 0 - + - - - - - - - - - - - + + + + + diff --git a/src/Snap.Hutao/Snap.Hutao/View/Control/DescParamComboBox.xaml.cs b/src/Snap.Hutao/Snap.Hutao/View/Control/DescParamComboBox.xaml.cs index 9f76e5aa..9a33c89c 100644 --- a/src/Snap.Hutao/Snap.Hutao/View/Control/DescParamComboBox.xaml.cs +++ b/src/Snap.Hutao/Snap.Hutao/View/Control/DescParamComboBox.xaml.cs @@ -30,8 +30,16 @@ internal sealed partial class DescParamComboBox : UserControl { if (args.NewValue != args.OldValue && args.NewValue is List> list) { - descParamComboBox.SelectedItem = list.ElementAtOrLastOrDefault(descParamComboBox.PreferredSelectedIndex); + LevelParameters? target = list.ElementAtOrLastOrDefault(descParamComboBox.PreferredSelectedIndex); + descParamComboBox.SelectedItem = target; + descParamComboBox.LevelSelectorComboBox.ItemsSource = list; + descParamComboBox.LevelSelectorComboBox.SelectedItem = target; } } } -} + + private void OnLevelSelectorComboBoxSelectionChanged(object sender, SelectionChangedEventArgs e) + { + SelectedItem = (LevelParameters)((ComboBox)sender).SelectedItem; + } +} \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/View/Control/SkillPivot.xaml b/src/Snap.Hutao/Snap.Hutao/View/Control/SkillPivot.xaml index 709ee786..ada71d01 100644 --- a/src/Snap.Hutao/Snap.Hutao/View/Control/SkillPivot.xaml +++ b/src/Snap.Hutao/Snap.Hutao/View/Control/SkillPivot.xaml @@ -2,6 +2,7 @@ x:Class="Snap.Hutao.View.Control.SkillPivot" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" + xmlns:cwc="using:CommunityToolkit.WinUI.Controls" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:shch="using:Snap.Hutao.Control.Helper" @@ -17,22 +18,18 @@ - + - - - + + + + diff --git a/src/Snap.Hutao/Snap.Hutao/View/Control/SkillPivot.xaml.cs b/src/Snap.Hutao/Snap.Hutao/View/Control/SkillPivot.xaml.cs index deb26f50..b8f2cf13 100644 --- a/src/Snap.Hutao/Snap.Hutao/View/Control/SkillPivot.xaml.cs +++ b/src/Snap.Hutao/Snap.Hutao/View/Control/SkillPivot.xaml.cs @@ -1,6 +1,7 @@ // Copyright (c) DGP Studio. All rights reserved. // Licensed under the MIT license. +using CommunityToolkit.WinUI.Controls; using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Controls; using System.Collections; @@ -11,7 +12,7 @@ namespace Snap.Hutao.View.Control; /// 技能展柜 /// [HighQuality] -[DependencyProperty("Skills", typeof(IList))] +[DependencyProperty("Skills", typeof(IList), null, nameof(OnSkillsChanged))] [DependencyProperty("Selected", typeof(object))] [DependencyProperty("ItemTemplate", typeof(DataTemplate))] internal sealed partial class SkillPivot : UserControl @@ -23,4 +24,22 @@ internal sealed partial class SkillPivot : UserControl { InitializeComponent(); } + + private static void OnSkillsChanged(DependencyObject sender, DependencyPropertyChangedEventArgs args) + { + if (sender is SkillPivot skillPivot) + { + if (args.OldValue != args.NewValue && args.NewValue as IList is [object target, ..] list) + { + skillPivot.Selected = target; + skillPivot.SkillSelectorSegmented.ItemsSource = list; + skillPivot.SkillSelectorSegmented.SelectedItem = target; + } + } + } + + private void OnSkillSelectorSegmentedSelectionChanged(object sender, SelectionChangedEventArgs e) + { + Selected = ((Segmented)sender).SelectedItem; + } } \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/View/Page/WikiAvatarPage.xaml b/src/Snap.Hutao/Snap.Hutao/View/Page/WikiAvatarPage.xaml index 42aaf799..5dc419d4 100644 --- a/src/Snap.Hutao/Snap.Hutao/View/Page/WikiAvatarPage.xaml +++ b/src/Snap.Hutao/Snap.Hutao/View/Page/WikiAvatarPage.xaml @@ -15,6 +15,7 @@ xmlns:shcp="using:Snap.Hutao.Control.Panel" xmlns:shct="using:Snap.Hutao.Control.Text" xmlns:shvc="using:Snap.Hutao.View.Control" + xmlns:shvcp="using:Snap.Hutao.View.Card.Primitive" xmlns:shvw="using:Snap.Hutao.ViewModel.Wiki" d:DataContext="{d:DesignInstance Type=shvw:WikiAvatarViewModel}" Background="{ThemeResource ApplicationPageBackgroundThemeBrush}" @@ -24,21 +25,27 @@ - - - - - - - - - + + + + + + + + + + + @@ -80,66 +87,110 @@ - - - + + + + + + + + - - - + + + + + + + + + + + - - - - - 0 - - - - - - - - 1 - - - - - - - - 2 - - - - - - - - - + + + + + + 0 + + + + + + + + 1 + + + + + + + + 2 + + + + + + + + + + + + + + + + @@ -169,14 +220,11 @@ - - - - - + + + + + @@ -194,7 +242,7 @@ @@ -239,7 +287,7 @@ DisplayMode="Inline" IsPaneOpen="True" OpenPaneLength="{StaticResource CompatSplitViewOpenPaneLength}" - PaneBackground="{StaticResource CardBackgroundFillColorSecondaryBrush}"> + PaneBackground="{ThemeResource CardBackgroundFillColorSecondaryBrush}"> + Padding="32" + HorizontalAlignment="Left" + Spacing="16"> - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + + + + - + + + Style="{StaticResource BaseTextBlockStyle}" + Text="{shcm:ResourceString Name=ViewPageWiKiAvatarWeaponCombinationHeader}"/> + + + - - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + - + - - - - - + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - + + + + + + + + + + + + - - - + + + + + + + + + + + + + + + + - - - + + + + + + + + + + + + + + + diff --git a/src/Snap.Hutao/Snap.Hutao/ViewModel/Complex/ReliquarySetView.cs b/src/Snap.Hutao/Snap.Hutao/ViewModel/Complex/ReliquarySetView.cs index 541345c1..d69be780 100644 --- a/src/Snap.Hutao/Snap.Hutao/ViewModel/Complex/ReliquarySetView.cs +++ b/src/Snap.Hutao/Snap.Hutao/ViewModel/Complex/ReliquarySetView.cs @@ -27,20 +27,14 @@ internal sealed class ReliquarySetView { StringBuilder nameBuilder = new(); List icons = new(2); - foreach (ReliquarySet set in CollectionsMarshal.AsSpan(sets)) + foreach (ref readonly ReliquarySet set in CollectionsMarshal.AsSpan(sets)) { Model.Metadata.Reliquary.ReliquarySet metaSet = idReliquarySetMap[set.EquipAffixId]; - - if (nameBuilder.Length > 0) - { - nameBuilder.AppendLine(); - } - - nameBuilder.Append(set.Count).Append('×').Append(metaSet.Name); + nameBuilder.Append(set.Count).Append('×').Append(metaSet.Name).Append('+'); icons.Add(RelicIconConverter.IconNameToUri(metaSet.Icon)); } - Name = nameBuilder.ToString(); + Name = nameBuilder.ToString(0, nameBuilder.Length - 1); Icons = icons; } else From c9627e19e7029f4d31c2a7f2804e8e7d7d4f91ef Mon Sep 17 00:00:00 2001 From: DismissedLight <1686188646@qq.com> Date: Sun, 26 Nov 2023 14:41:53 +0800 Subject: [PATCH 28/42] fix #1124 --- .../Snap.Hutao/ViewModel/Achievement/AchievementImporter.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Snap.Hutao/Snap.Hutao/ViewModel/Achievement/AchievementImporter.cs b/src/Snap.Hutao/Snap.Hutao/ViewModel/Achievement/AchievementImporter.cs index 8cd1a283..b26b747a 100644 --- a/src/Snap.Hutao/Snap.Hutao/ViewModel/Achievement/AchievementImporter.cs +++ b/src/Snap.Hutao/Snap.Hutao/ViewModel/Achievement/AchievementImporter.cs @@ -66,7 +66,7 @@ internal sealed partial class AchievementImporter { ValueResult pickerResult = fileSystemPickerInteraction.PickFile( SH.ServiceAchievementUIAFImportPickerTitile, - [(SH.ServiceAchievementUIAFImportPickerFilterText, ".json")]); + [(SH.ServiceAchievementUIAFImportPickerFilterText, "*.json")]); if (pickerResult.TryGetValue(out ValueFile file)) { From 7099ca43b6fa14ba62a131cf185c6a513c23d9a4 Mon Sep 17 00:00:00 2001 From: DismissedLight <1686188646@qq.com> Date: Sun, 26 Nov 2023 15:04:41 +0800 Subject: [PATCH 29/42] impl #1127 --- src/Snap.Hutao/Snap.Hutao/Resource/Localization/SH.resx | 3 +++ src/Snap.Hutao/Snap.Hutao/View/UserView.xaml | 8 ++++++++ 2 files changed, 11 insertions(+) diff --git a/src/Snap.Hutao/Snap.Hutao/Resource/Localization/SH.resx b/src/Snap.Hutao/Snap.Hutao/Resource/Localization/SH.resx index 852a1459..2a37ea23 100644 --- a/src/Snap.Hutao/Snap.Hutao/Resource/Localization/SH.resx +++ b/src/Snap.Hutao/Snap.Hutao/Resource/Localization/SH.resx @@ -2606,6 +2606,9 @@ 文档 + + 尚未登录 + 刷新 CookieToken 成功 diff --git a/src/Snap.Hutao/Snap.Hutao/View/UserView.xaml b/src/Snap.Hutao/Snap.Hutao/View/UserView.xaml index 3a652699..0fbae96e 100644 --- a/src/Snap.Hutao/Snap.Hutao/View/UserView.xaml +++ b/src/Snap.Hutao/Snap.Hutao/View/UserView.xaml @@ -107,6 +107,14 @@ Text="{Binding SelectedUser.UserInfo.Nickname, Mode=OneWay}" TextTrimming="CharacterEllipsis" TextWrapping="NoWrap"/> + Date: Sun, 26 Nov 2023 15:55:37 +0800 Subject: [PATCH 30/42] fix #1126 --- ...xtensions.cs => StringBuilderExtension.cs} | 19 ++++++++++++++++++- .../SdkStatic/Hk4e/Launcher/LatestPackage.cs | 2 ++ .../SdkStatic/Hk4e/Launcher/ResourceClient.cs | 12 ++++++++++-- 3 files changed, 30 insertions(+), 3 deletions(-) rename src/Snap.Hutao/Snap.Hutao/Extension/{StringBuilderExtensions.cs => StringBuilderExtension.cs} (72%) diff --git a/src/Snap.Hutao/Snap.Hutao/Extension/StringBuilderExtensions.cs b/src/Snap.Hutao/Snap.Hutao/Extension/StringBuilderExtension.cs similarity index 72% rename from src/Snap.Hutao/Snap.Hutao/Extension/StringBuilderExtensions.cs rename to src/Snap.Hutao/Snap.Hutao/Extension/StringBuilderExtension.cs index b6d987e7..515666c7 100644 --- a/src/Snap.Hutao/Snap.Hutao/Extension/StringBuilderExtensions.cs +++ b/src/Snap.Hutao/Snap.Hutao/Extension/StringBuilderExtension.cs @@ -10,7 +10,7 @@ namespace Snap.Hutao.Extension; /// 扩展方法 /// [HighQuality] -internal static class StringBuilderExtensions +internal static class StringBuilderExtension { /// /// 当条件符合时执行 @@ -37,4 +37,21 @@ internal static class StringBuilderExtensions { return condition ? sb.Append(value) : sb; } + + public static string ToStringTrimEndReturn(this StringBuilder builder) + { + Must.Argument(builder.Length >= 1, "StringBuilder 的长度必须大于 0"); + int remove = 0; + if (builder[^1] is '\n') + { + remove = 1; + + if (builder.Length >= 2 && builder[^2] is '\r') + { + remove = 2; + } + } + + return builder.ToString(0, builder.Length - remove); + } } \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/SdkStatic/Hk4e/Launcher/LatestPackage.cs b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/SdkStatic/Hk4e/Launcher/LatestPackage.cs index b737ac49..78b72060 100644 --- a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/SdkStatic/Hk4e/Launcher/LatestPackage.cs +++ b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/SdkStatic/Hk4e/Launcher/LatestPackage.cs @@ -7,4 +7,6 @@ internal sealed class LatestPackage : Package { [JsonPropertyName("segments")] public List Segments { get; set; } = default!; + + public new string DisplayName { get => Name; } } \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/SdkStatic/Hk4e/Launcher/ResourceClient.cs b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/SdkStatic/Hk4e/Launcher/ResourceClient.cs index 738d0d08..2b294d50 100644 --- a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/SdkStatic/Hk4e/Launcher/ResourceClient.cs +++ b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/SdkStatic/Hk4e/Launcher/ResourceClient.cs @@ -8,6 +8,7 @@ using Snap.Hutao.Web.Request.Builder.Abstraction; using Snap.Hutao.Web.Response; using System.IO; using System.Net.Http; +using System.Text; namespace Snap.Hutao.Web.Hoyolab.SdkStatic.Hk4e.Launcher; @@ -46,8 +47,15 @@ internal sealed partial class ResourceClient // 补全缺失的信息 if (resp is { Data.Game.Latest: LatestPackage latest }) { - latest.Path = latest.Segments[0].Path[..^4]; // .00X - latest.Name = Path.GetFileName(latest.Path); + StringBuilder pathBuilder = new(); + foreach (PackageSegment segment in latest.Segments) + { + pathBuilder.AppendLine(segment.Path); + } + + latest.Path = pathBuilder.ToStringTrimEndReturn(); + string path = latest.Segments[0].Path[..^4]; // .00X + latest.Name = Path.GetFileName(path); } return Response.Response.DefaultIfNull(resp); From 7128dddb5760b15685c3d95ef0ceb4050712c903 Mon Sep 17 00:00:00 2001 From: DismissedLight <1686188646@qq.com> Date: Sun, 26 Nov 2023 16:42:00 +0800 Subject: [PATCH 31/42] remove skill bottom padding --- src/Snap.Hutao/Snap.Hutao/View/Page/WikiAvatarPage.xaml | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/Snap.Hutao/Snap.Hutao/View/Page/WikiAvatarPage.xaml b/src/Snap.Hutao/Snap.Hutao/View/Page/WikiAvatarPage.xaml index 5dc419d4..230f1fae 100644 --- a/src/Snap.Hutao/Snap.Hutao/View/Page/WikiAvatarPage.xaml +++ b/src/Snap.Hutao/Snap.Hutao/View/Page/WikiAvatarPage.xaml @@ -30,10 +30,7 @@ - +